##// 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()
@@ -1,104 +1,126 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2017 RhodeCode GmbH
3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21
21
22 from rhodecode.apps._base import ADMIN_PREFIX
22 from rhodecode.apps._base import ADMIN_PREFIX
23
23
24
24
25 def includeme(config):
25 def includeme(config):
26
26
27 config.add_route(
27 config.add_route(
28 name='my_account_profile',
28 name='my_account_profile',
29 pattern=ADMIN_PREFIX + '/my_account/profile')
29 pattern=ADMIN_PREFIX + '/my_account/profile')
30
30
31 # my account edit details
31 # my account edit details
32 config.add_route(
32 config.add_route(
33 name='my_account_edit',
33 name='my_account_edit',
34 pattern=ADMIN_PREFIX + '/my_account/edit')
34 pattern=ADMIN_PREFIX + '/my_account/edit')
35 config.add_route(
35 config.add_route(
36 name='my_account_update',
36 name='my_account_update',
37 pattern=ADMIN_PREFIX + '/my_account/update')
37 pattern=ADMIN_PREFIX + '/my_account/update')
38
38
39 # my account password
39 # my account password
40 config.add_route(
40 config.add_route(
41 name='my_account_password',
41 name='my_account_password',
42 pattern=ADMIN_PREFIX + '/my_account/password')
42 pattern=ADMIN_PREFIX + '/my_account/password')
43
43
44 config.add_route(
44 config.add_route(
45 name='my_account_password_update',
45 name='my_account_password_update',
46 pattern=ADMIN_PREFIX + '/my_account/password')
46 pattern=ADMIN_PREFIX + '/my_account/password')
47
47
48 # my account tokens
48 # my account tokens
49 config.add_route(
49 config.add_route(
50 name='my_account_auth_tokens',
50 name='my_account_auth_tokens',
51 pattern=ADMIN_PREFIX + '/my_account/auth_tokens')
51 pattern=ADMIN_PREFIX + '/my_account/auth_tokens')
52 config.add_route(
52 config.add_route(
53 name='my_account_auth_tokens_add',
53 name='my_account_auth_tokens_add',
54 pattern=ADMIN_PREFIX + '/my_account/auth_tokens/new')
54 pattern=ADMIN_PREFIX + '/my_account/auth_tokens/new')
55 config.add_route(
55 config.add_route(
56 name='my_account_auth_tokens_delete',
56 name='my_account_auth_tokens_delete',
57 pattern=ADMIN_PREFIX + '/my_account/auth_tokens/delete')
57 pattern=ADMIN_PREFIX + '/my_account/auth_tokens/delete')
58
58
59 # my account emails
59 # my account emails
60 config.add_route(
60 config.add_route(
61 name='my_account_emails',
61 name='my_account_emails',
62 pattern=ADMIN_PREFIX + '/my_account/emails')
62 pattern=ADMIN_PREFIX + '/my_account/emails')
63 config.add_route(
63 config.add_route(
64 name='my_account_emails_add',
64 name='my_account_emails_add',
65 pattern=ADMIN_PREFIX + '/my_account/emails/new')
65 pattern=ADMIN_PREFIX + '/my_account/emails/new')
66 config.add_route(
66 config.add_route(
67 name='my_account_emails_delete',
67 name='my_account_emails_delete',
68 pattern=ADMIN_PREFIX + '/my_account/emails/delete')
68 pattern=ADMIN_PREFIX + '/my_account/emails/delete')
69
69
70 config.add_route(
70 config.add_route(
71 name='my_account_repos',
71 name='my_account_repos',
72 pattern=ADMIN_PREFIX + '/my_account/repos')
72 pattern=ADMIN_PREFIX + '/my_account/repos')
73
73
74 config.add_route(
74 config.add_route(
75 name='my_account_watched',
75 name='my_account_watched',
76 pattern=ADMIN_PREFIX + '/my_account/watched')
76 pattern=ADMIN_PREFIX + '/my_account/watched')
77
77
78 config.add_route(
78 config.add_route(
79 name='my_account_perms',
79 name='my_account_perms',
80 pattern=ADMIN_PREFIX + '/my_account/perms')
80 pattern=ADMIN_PREFIX + '/my_account/perms')
81
81
82 config.add_route(
82 config.add_route(
83 name='my_account_notifications',
83 name='my_account_notifications',
84 pattern=ADMIN_PREFIX + '/my_account/notifications')
84 pattern=ADMIN_PREFIX + '/my_account/notifications')
85
85
86 config.add_route(
86 config.add_route(
87 name='my_account_notifications_toggle_visibility',
87 name='my_account_notifications_toggle_visibility',
88 pattern=ADMIN_PREFIX + '/my_account/toggle_visibility')
88 pattern=ADMIN_PREFIX + '/my_account/toggle_visibility')
89
89
90 # my account pull requests
90 # my account pull requests
91 config.add_route(
91 config.add_route(
92 name='my_account_pullrequests',
92 name='my_account_pullrequests',
93 pattern=ADMIN_PREFIX + '/my_account/pull_requests')
93 pattern=ADMIN_PREFIX + '/my_account/pull_requests')
94 config.add_route(
94 config.add_route(
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',
101 pattern=ADMIN_PREFIX + '/my_account/test_channelstream')
123 pattern=ADMIN_PREFIX + '/my_account/test_channelstream')
102
124
103 # Scan module for configuration decorators.
125 # Scan module for configuration decorators.
104 config.scan()
126 config.scan()
@@ -1,172 +1,196 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 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
27 from rhodecode.model.user import UserModel
30 from rhodecode.model.user import UserModel
28 from rhodecode.model.notification import NotificationModel
31 from rhodecode.model.notification import NotificationModel
29 from rhodecode.model.meta import Session
32 from rhodecode.model.meta import Session
30
33
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():
43 inst = Notification.get(n.notification_id)
59 inst = Notification.get(n.notification_id)
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(
87 created_by=cur_user, notification_subject=u'test',
121 created_by=cur_user, notification_subject=u'test',
88 notification_body=u'hi there', recipients=[cur_user, u1, u2])
122 notification_body=u'hi there', recipients=[cur_user, u1, u2])
89 Session().commit()
123 Session().commit()
90 u1 = User.get(u1.user_id)
124 u1 = User.get(u1.user_id)
91 u2 = User.get(u2.user_id)
125 u2 = User.get(u2.user_id)
92
126
93 # check DB
127 # check DB
94 get_notif = lambda un: [x.notification for x in un]
128 get_notif = lambda un: [x.notification for x in un]
95 assert get_notif(cur_user.notifications) == [notification]
129 assert get_notif(cur_user.notifications) == [notification]
96 assert get_notif(u1.notifications) == [notification]
130 assert get_notif(u1.notifications) == [notification]
97 assert get_notif(u2.notifications) == [notification]
131 assert get_notif(u2.notifications) == [notification]
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 == []
107
142
108 @pytest.mark.parametrize('user,password', [
143 @pytest.mark.parametrize('user,password', [
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'
126 notification = NotificationModel().create(
155 notification = NotificationModel().create(
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)
135
165
136 @pytest.mark.parametrize('user,password', [
166 @pytest.mark.parametrize('user,password', [
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]
155 notification = NotificationModel().create(
178 notification = NotificationModel().create(
156 created_by=cur_user, notification_subject=u'test',
179 created_by=cur_user, notification_subject=u'test',
157 notification_body=u'hi there', recipients=recipients)
180 notification_body=u'hi there', recipients=recipients)
158 Session().commit()
181 Session().commit()
159
182
160 for u_obj in recipients:
183 for u_obj in recipients:
161 # if it's current user, he has his message already read
184 # if it's current user, he has his message already read
162 read = u_obj.username == user
185 read = u_obj.username == user
163 assert len(u_obj.notifications) == 1
186 assert len(u_obj.notifications) == 1
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
@@ -1,884 +1,870 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 """
21 """
22 Routes configuration
22 Routes configuration
23
23
24 The more specific and detailed routes should be defined first so they
24 The more specific and detailed routes should be defined first so they
25 may take precedent over the more generic routes. For more information
25 may take precedent over the more generic routes. For more information
26 refer to the routes manual at http://routes.groovie.org/docs/
26 refer to the routes manual at http://routes.groovie.org/docs/
27
27
28 IMPORTANT: if you change any routing here, make sure to take a look at lib/base.py
28 IMPORTANT: if you change any routing here, make sure to take a look at lib/base.py
29 and _route_name variable which uses some of stored naming here to do redirects.
29 and _route_name variable which uses some of stored naming here to do redirects.
30 """
30 """
31 import os
31 import os
32 import re
32 import re
33 from routes import Mapper
33 from routes import Mapper
34
34
35 # prefix for non repository related links needs to be prefixed with `/`
35 # prefix for non repository related links needs to be prefixed with `/`
36 ADMIN_PREFIX = '/_admin'
36 ADMIN_PREFIX = '/_admin'
37 STATIC_FILE_PREFIX = '/_static'
37 STATIC_FILE_PREFIX = '/_static'
38
38
39 # Default requirements for URL parts
39 # Default requirements for URL parts
40 URL_NAME_REQUIREMENTS = {
40 URL_NAME_REQUIREMENTS = {
41 # group name can have a slash in them, but they must not end with a slash
41 # group name can have a slash in them, but they must not end with a slash
42 'group_name': r'.*?[^/]',
42 'group_name': r'.*?[^/]',
43 'repo_group_name': r'.*?[^/]',
43 'repo_group_name': r'.*?[^/]',
44 # repo names can have a slash in them, but they must not end with a slash
44 # repo names can have a slash in them, but they must not end with a slash
45 'repo_name': r'.*?[^/]',
45 'repo_name': r'.*?[^/]',
46 # file path eats up everything at the end
46 # file path eats up everything at the end
47 'f_path': r'.*',
47 'f_path': r'.*',
48 # reference types
48 # reference types
49 'source_ref_type': '(branch|book|tag|rev|\%\(source_ref_type\)s)',
49 'source_ref_type': '(branch|book|tag|rev|\%\(source_ref_type\)s)',
50 'target_ref_type': '(branch|book|tag|rev|\%\(target_ref_type\)s)',
50 'target_ref_type': '(branch|book|tag|rev|\%\(target_ref_type\)s)',
51 }
51 }
52
52
53
53
54 def add_route_requirements(route_path, requirements):
54 def add_route_requirements(route_path, requirements):
55 """
55 """
56 Adds regex requirements to pyramid routes using a mapping dict
56 Adds regex requirements to pyramid routes using a mapping dict
57
57
58 >>> add_route_requirements('/{action}/{id}', {'id': r'\d+'})
58 >>> add_route_requirements('/{action}/{id}', {'id': r'\d+'})
59 '/{action}/{id:\d+}'
59 '/{action}/{id:\d+}'
60
60
61 """
61 """
62 for key, regex in requirements.items():
62 for key, regex in requirements.items():
63 route_path = route_path.replace('{%s}' % key, '{%s:%s}' % (key, regex))
63 route_path = route_path.replace('{%s}' % key, '{%s:%s}' % (key, regex))
64 return route_path
64 return route_path
65
65
66
66
67 class JSRoutesMapper(Mapper):
67 class JSRoutesMapper(Mapper):
68 """
68 """
69 Wrapper for routes.Mapper to make pyroutes compatible url definitions
69 Wrapper for routes.Mapper to make pyroutes compatible url definitions
70 """
70 """
71 _named_route_regex = re.compile(r'^[a-z-_0-9A-Z]+$')
71 _named_route_regex = re.compile(r'^[a-z-_0-9A-Z]+$')
72 _argument_prog = re.compile('\{(.*?)\}|:\((.*)\)')
72 _argument_prog = re.compile('\{(.*?)\}|:\((.*)\)')
73 def __init__(self, *args, **kw):
73 def __init__(self, *args, **kw):
74 super(JSRoutesMapper, self).__init__(*args, **kw)
74 super(JSRoutesMapper, self).__init__(*args, **kw)
75 self._jsroutes = []
75 self._jsroutes = []
76
76
77 def connect(self, *args, **kw):
77 def connect(self, *args, **kw):
78 """
78 """
79 Wrapper for connect to take an extra argument jsroute=True
79 Wrapper for connect to take an extra argument jsroute=True
80
80
81 :param jsroute: boolean, if True will add the route to the pyroutes list
81 :param jsroute: boolean, if True will add the route to the pyroutes list
82 """
82 """
83 if kw.pop('jsroute', False):
83 if kw.pop('jsroute', False):
84 if not self._named_route_regex.match(args[0]):
84 if not self._named_route_regex.match(args[0]):
85 raise Exception('only named routes can be added to pyroutes')
85 raise Exception('only named routes can be added to pyroutes')
86 self._jsroutes.append(args[0])
86 self._jsroutes.append(args[0])
87
87
88 super(JSRoutesMapper, self).connect(*args, **kw)
88 super(JSRoutesMapper, self).connect(*args, **kw)
89
89
90 def _extract_route_information(self, route):
90 def _extract_route_information(self, route):
91 """
91 """
92 Convert a route into tuple(name, path, args), eg:
92 Convert a route into tuple(name, path, args), eg:
93 ('show_user', '/profile/%(username)s', ['username'])
93 ('show_user', '/profile/%(username)s', ['username'])
94 """
94 """
95 routepath = route.routepath
95 routepath = route.routepath
96 def replace(matchobj):
96 def replace(matchobj):
97 if matchobj.group(1):
97 if matchobj.group(1):
98 return "%%(%s)s" % matchobj.group(1).split(':')[0]
98 return "%%(%s)s" % matchobj.group(1).split(':')[0]
99 else:
99 else:
100 return "%%(%s)s" % matchobj.group(2)
100 return "%%(%s)s" % matchobj.group(2)
101
101
102 routepath = self._argument_prog.sub(replace, routepath)
102 routepath = self._argument_prog.sub(replace, routepath)
103 return (
103 return (
104 route.name,
104 route.name,
105 routepath,
105 routepath,
106 [(arg[0].split(':')[0] if arg[0] != '' else arg[1])
106 [(arg[0].split(':')[0] if arg[0] != '' else arg[1])
107 for arg in self._argument_prog.findall(route.routepath)]
107 for arg in self._argument_prog.findall(route.routepath)]
108 )
108 )
109
109
110 def jsroutes(self):
110 def jsroutes(self):
111 """
111 """
112 Return a list of pyroutes.js compatible routes
112 Return a list of pyroutes.js compatible routes
113 """
113 """
114 for route_name in self._jsroutes:
114 for route_name in self._jsroutes:
115 yield self._extract_route_information(self._routenames[route_name])
115 yield self._extract_route_information(self._routenames[route_name])
116
116
117
117
118 def make_map(config):
118 def make_map(config):
119 """Create, configure and return the routes Mapper"""
119 """Create, configure and return the routes Mapper"""
120 rmap = JSRoutesMapper(
120 rmap = JSRoutesMapper(
121 directory=config['pylons.paths']['controllers'],
121 directory=config['pylons.paths']['controllers'],
122 always_scan=config['debug'])
122 always_scan=config['debug'])
123 rmap.minimization = False
123 rmap.minimization = False
124 rmap.explicit = False
124 rmap.explicit = False
125
125
126 from rhodecode.lib.utils2 import str2bool
126 from rhodecode.lib.utils2 import str2bool
127 from rhodecode.model import repo, repo_group
127 from rhodecode.model import repo, repo_group
128
128
129 def check_repo(environ, match_dict):
129 def check_repo(environ, match_dict):
130 """
130 """
131 check for valid repository for proper 404 handling
131 check for valid repository for proper 404 handling
132
132
133 :param environ:
133 :param environ:
134 :param match_dict:
134 :param match_dict:
135 """
135 """
136 repo_name = match_dict.get('repo_name')
136 repo_name = match_dict.get('repo_name')
137
137
138 if match_dict.get('f_path'):
138 if match_dict.get('f_path'):
139 # fix for multiple initial slashes that causes errors
139 # fix for multiple initial slashes that causes errors
140 match_dict['f_path'] = match_dict['f_path'].lstrip('/')
140 match_dict['f_path'] = match_dict['f_path'].lstrip('/')
141 repo_model = repo.RepoModel()
141 repo_model = repo.RepoModel()
142 by_name_match = repo_model.get_by_repo_name(repo_name)
142 by_name_match = repo_model.get_by_repo_name(repo_name)
143 # if we match quickly from database, short circuit the operation,
143 # if we match quickly from database, short circuit the operation,
144 # and validate repo based on the type.
144 # and validate repo based on the type.
145 if by_name_match:
145 if by_name_match:
146 return True
146 return True
147
147
148 by_id_match = repo_model.get_repo_by_id(repo_name)
148 by_id_match = repo_model.get_repo_by_id(repo_name)
149 if by_id_match:
149 if by_id_match:
150 repo_name = by_id_match.repo_name
150 repo_name = by_id_match.repo_name
151 match_dict['repo_name'] = repo_name
151 match_dict['repo_name'] = repo_name
152 return True
152 return True
153
153
154 return False
154 return False
155
155
156 def check_group(environ, match_dict):
156 def check_group(environ, match_dict):
157 """
157 """
158 check for valid repository group path for proper 404 handling
158 check for valid repository group path for proper 404 handling
159
159
160 :param environ:
160 :param environ:
161 :param match_dict:
161 :param match_dict:
162 """
162 """
163 repo_group_name = match_dict.get('group_name')
163 repo_group_name = match_dict.get('group_name')
164 repo_group_model = repo_group.RepoGroupModel()
164 repo_group_model = repo_group.RepoGroupModel()
165 by_name_match = repo_group_model.get_by_group_name(repo_group_name)
165 by_name_match = repo_group_model.get_by_group_name(repo_group_name)
166 if by_name_match:
166 if by_name_match:
167 return True
167 return True
168
168
169 return False
169 return False
170
170
171 def check_user_group(environ, match_dict):
171 def check_user_group(environ, match_dict):
172 """
172 """
173 check for valid user group for proper 404 handling
173 check for valid user group for proper 404 handling
174
174
175 :param environ:
175 :param environ:
176 :param match_dict:
176 :param match_dict:
177 """
177 """
178 return True
178 return True
179
179
180 def check_int(environ, match_dict):
180 def check_int(environ, match_dict):
181 return match_dict.get('id').isdigit()
181 return match_dict.get('id').isdigit()
182
182
183
183
184 #==========================================================================
184 #==========================================================================
185 # CUSTOM ROUTES HERE
185 # CUSTOM ROUTES HERE
186 #==========================================================================
186 #==========================================================================
187
187
188 # ping and pylons error test
188 # ping and pylons error test
189 rmap.connect('ping', '%s/ping' % (ADMIN_PREFIX,), controller='home', action='ping')
189 rmap.connect('ping', '%s/ping' % (ADMIN_PREFIX,), controller='home', action='ping')
190 rmap.connect('error_test', '%s/error_test' % (ADMIN_PREFIX,), controller='home', action='error_test')
190 rmap.connect('error_test', '%s/error_test' % (ADMIN_PREFIX,), controller='home', action='error_test')
191
191
192 # ADMIN REPOSITORY ROUTES
192 # ADMIN REPOSITORY ROUTES
193 with rmap.submapper(path_prefix=ADMIN_PREFIX,
193 with rmap.submapper(path_prefix=ADMIN_PREFIX,
194 controller='admin/repos') as m:
194 controller='admin/repos') as m:
195 m.connect('repos', '/repos',
195 m.connect('repos', '/repos',
196 action='create', conditions={'method': ['POST']})
196 action='create', conditions={'method': ['POST']})
197 m.connect('repos', '/repos',
197 m.connect('repos', '/repos',
198 action='index', conditions={'method': ['GET']})
198 action='index', conditions={'method': ['GET']})
199 m.connect('new_repo', '/create_repository', jsroute=True,
199 m.connect('new_repo', '/create_repository', jsroute=True,
200 action='create_repository', conditions={'method': ['GET']})
200 action='create_repository', conditions={'method': ['GET']})
201 m.connect('delete_repo', '/repos/{repo_name}',
201 m.connect('delete_repo', '/repos/{repo_name}',
202 action='delete', conditions={'method': ['DELETE']},
202 action='delete', conditions={'method': ['DELETE']},
203 requirements=URL_NAME_REQUIREMENTS)
203 requirements=URL_NAME_REQUIREMENTS)
204 m.connect('repo', '/repos/{repo_name}',
204 m.connect('repo', '/repos/{repo_name}',
205 action='show', conditions={'method': ['GET'],
205 action='show', conditions={'method': ['GET'],
206 'function': check_repo},
206 'function': check_repo},
207 requirements=URL_NAME_REQUIREMENTS)
207 requirements=URL_NAME_REQUIREMENTS)
208
208
209 # ADMIN REPOSITORY GROUPS ROUTES
209 # ADMIN REPOSITORY GROUPS ROUTES
210 with rmap.submapper(path_prefix=ADMIN_PREFIX,
210 with rmap.submapper(path_prefix=ADMIN_PREFIX,
211 controller='admin/repo_groups') as m:
211 controller='admin/repo_groups') as m:
212 m.connect('repo_groups', '/repo_groups',
212 m.connect('repo_groups', '/repo_groups',
213 action='create', conditions={'method': ['POST']})
213 action='create', conditions={'method': ['POST']})
214 m.connect('repo_groups', '/repo_groups',
214 m.connect('repo_groups', '/repo_groups',
215 action='index', conditions={'method': ['GET']})
215 action='index', conditions={'method': ['GET']})
216 m.connect('new_repo_group', '/repo_groups/new',
216 m.connect('new_repo_group', '/repo_groups/new',
217 action='new', conditions={'method': ['GET']})
217 action='new', conditions={'method': ['GET']})
218 m.connect('update_repo_group', '/repo_groups/{group_name}',
218 m.connect('update_repo_group', '/repo_groups/{group_name}',
219 action='update', conditions={'method': ['PUT'],
219 action='update', conditions={'method': ['PUT'],
220 'function': check_group},
220 'function': check_group},
221 requirements=URL_NAME_REQUIREMENTS)
221 requirements=URL_NAME_REQUIREMENTS)
222
222
223 # EXTRAS REPO GROUP ROUTES
223 # EXTRAS REPO GROUP ROUTES
224 m.connect('edit_repo_group', '/repo_groups/{group_name}/edit',
224 m.connect('edit_repo_group', '/repo_groups/{group_name}/edit',
225 action='edit',
225 action='edit',
226 conditions={'method': ['GET'], 'function': check_group},
226 conditions={'method': ['GET'], 'function': check_group},
227 requirements=URL_NAME_REQUIREMENTS)
227 requirements=URL_NAME_REQUIREMENTS)
228 m.connect('edit_repo_group', '/repo_groups/{group_name}/edit',
228 m.connect('edit_repo_group', '/repo_groups/{group_name}/edit',
229 action='edit',
229 action='edit',
230 conditions={'method': ['PUT'], 'function': check_group},
230 conditions={'method': ['PUT'], 'function': check_group},
231 requirements=URL_NAME_REQUIREMENTS)
231 requirements=URL_NAME_REQUIREMENTS)
232
232
233 m.connect('edit_repo_group_advanced', '/repo_groups/{group_name}/edit/advanced',
233 m.connect('edit_repo_group_advanced', '/repo_groups/{group_name}/edit/advanced',
234 action='edit_repo_group_advanced',
234 action='edit_repo_group_advanced',
235 conditions={'method': ['GET'], 'function': check_group},
235 conditions={'method': ['GET'], 'function': check_group},
236 requirements=URL_NAME_REQUIREMENTS)
236 requirements=URL_NAME_REQUIREMENTS)
237 m.connect('edit_repo_group_advanced', '/repo_groups/{group_name}/edit/advanced',
237 m.connect('edit_repo_group_advanced', '/repo_groups/{group_name}/edit/advanced',
238 action='edit_repo_group_advanced',
238 action='edit_repo_group_advanced',
239 conditions={'method': ['PUT'], 'function': check_group},
239 conditions={'method': ['PUT'], 'function': check_group},
240 requirements=URL_NAME_REQUIREMENTS)
240 requirements=URL_NAME_REQUIREMENTS)
241
241
242 m.connect('edit_repo_group_perms', '/repo_groups/{group_name}/edit/permissions',
242 m.connect('edit_repo_group_perms', '/repo_groups/{group_name}/edit/permissions',
243 action='edit_repo_group_perms',
243 action='edit_repo_group_perms',
244 conditions={'method': ['GET'], 'function': check_group},
244 conditions={'method': ['GET'], 'function': check_group},
245 requirements=URL_NAME_REQUIREMENTS)
245 requirements=URL_NAME_REQUIREMENTS)
246 m.connect('edit_repo_group_perms', '/repo_groups/{group_name}/edit/permissions',
246 m.connect('edit_repo_group_perms', '/repo_groups/{group_name}/edit/permissions',
247 action='update_perms',
247 action='update_perms',
248 conditions={'method': ['PUT'], 'function': check_group},
248 conditions={'method': ['PUT'], 'function': check_group},
249 requirements=URL_NAME_REQUIREMENTS)
249 requirements=URL_NAME_REQUIREMENTS)
250
250
251 m.connect('delete_repo_group', '/repo_groups/{group_name}',
251 m.connect('delete_repo_group', '/repo_groups/{group_name}',
252 action='delete', conditions={'method': ['DELETE'],
252 action='delete', conditions={'method': ['DELETE'],
253 'function': check_group},
253 'function': check_group},
254 requirements=URL_NAME_REQUIREMENTS)
254 requirements=URL_NAME_REQUIREMENTS)
255
255
256 # ADMIN USER ROUTES
256 # ADMIN USER ROUTES
257 with rmap.submapper(path_prefix=ADMIN_PREFIX,
257 with rmap.submapper(path_prefix=ADMIN_PREFIX,
258 controller='admin/users') as m:
258 controller='admin/users') as m:
259 m.connect('users', '/users',
259 m.connect('users', '/users',
260 action='create', conditions={'method': ['POST']})
260 action='create', conditions={'method': ['POST']})
261 m.connect('new_user', '/users/new',
261 m.connect('new_user', '/users/new',
262 action='new', conditions={'method': ['GET']})
262 action='new', conditions={'method': ['GET']})
263 m.connect('update_user', '/users/{user_id}',
263 m.connect('update_user', '/users/{user_id}',
264 action='update', conditions={'method': ['PUT']})
264 action='update', conditions={'method': ['PUT']})
265 m.connect('delete_user', '/users/{user_id}',
265 m.connect('delete_user', '/users/{user_id}',
266 action='delete', conditions={'method': ['DELETE']})
266 action='delete', conditions={'method': ['DELETE']})
267 m.connect('edit_user', '/users/{user_id}/edit',
267 m.connect('edit_user', '/users/{user_id}/edit',
268 action='edit', conditions={'method': ['GET']}, jsroute=True)
268 action='edit', conditions={'method': ['GET']}, jsroute=True)
269 m.connect('user', '/users/{user_id}',
269 m.connect('user', '/users/{user_id}',
270 action='show', conditions={'method': ['GET']})
270 action='show', conditions={'method': ['GET']})
271 m.connect('force_password_reset_user', '/users/{user_id}/password_reset',
271 m.connect('force_password_reset_user', '/users/{user_id}/password_reset',
272 action='reset_password', conditions={'method': ['POST']})
272 action='reset_password', conditions={'method': ['POST']})
273 m.connect('create_personal_repo_group', '/users/{user_id}/create_repo_group',
273 m.connect('create_personal_repo_group', '/users/{user_id}/create_repo_group',
274 action='create_personal_repo_group', conditions={'method': ['POST']})
274 action='create_personal_repo_group', conditions={'method': ['POST']})
275
275
276 # EXTRAS USER ROUTES
276 # EXTRAS USER ROUTES
277 m.connect('edit_user_advanced', '/users/{user_id}/edit/advanced',
277 m.connect('edit_user_advanced', '/users/{user_id}/edit/advanced',
278 action='edit_advanced', conditions={'method': ['GET']})
278 action='edit_advanced', conditions={'method': ['GET']})
279 m.connect('edit_user_advanced', '/users/{user_id}/edit/advanced',
279 m.connect('edit_user_advanced', '/users/{user_id}/edit/advanced',
280 action='update_advanced', conditions={'method': ['PUT']})
280 action='update_advanced', conditions={'method': ['PUT']})
281
281
282 m.connect('edit_user_global_perms', '/users/{user_id}/edit/global_permissions',
282 m.connect('edit_user_global_perms', '/users/{user_id}/edit/global_permissions',
283 action='edit_global_perms', conditions={'method': ['GET']})
283 action='edit_global_perms', conditions={'method': ['GET']})
284 m.connect('edit_user_global_perms', '/users/{user_id}/edit/global_permissions',
284 m.connect('edit_user_global_perms', '/users/{user_id}/edit/global_permissions',
285 action='update_global_perms', conditions={'method': ['PUT']})
285 action='update_global_perms', conditions={'method': ['PUT']})
286
286
287 m.connect('edit_user_perms_summary', '/users/{user_id}/edit/permissions_summary',
287 m.connect('edit_user_perms_summary', '/users/{user_id}/edit/permissions_summary',
288 action='edit_perms_summary', conditions={'method': ['GET']})
288 action='edit_perms_summary', conditions={'method': ['GET']})
289
289
290
290
291 # ADMIN USER GROUPS REST ROUTES
291 # ADMIN USER GROUPS REST ROUTES
292 with rmap.submapper(path_prefix=ADMIN_PREFIX,
292 with rmap.submapper(path_prefix=ADMIN_PREFIX,
293 controller='admin/user_groups') as m:
293 controller='admin/user_groups') as m:
294 m.connect('users_groups', '/user_groups',
294 m.connect('users_groups', '/user_groups',
295 action='create', conditions={'method': ['POST']})
295 action='create', conditions={'method': ['POST']})
296 m.connect('users_groups', '/user_groups',
296 m.connect('users_groups', '/user_groups',
297 action='index', conditions={'method': ['GET']})
297 action='index', conditions={'method': ['GET']})
298 m.connect('new_users_group', '/user_groups/new',
298 m.connect('new_users_group', '/user_groups/new',
299 action='new', conditions={'method': ['GET']})
299 action='new', conditions={'method': ['GET']})
300 m.connect('update_users_group', '/user_groups/{user_group_id}',
300 m.connect('update_users_group', '/user_groups/{user_group_id}',
301 action='update', conditions={'method': ['PUT']})
301 action='update', conditions={'method': ['PUT']})
302 m.connect('delete_users_group', '/user_groups/{user_group_id}',
302 m.connect('delete_users_group', '/user_groups/{user_group_id}',
303 action='delete', conditions={'method': ['DELETE']})
303 action='delete', conditions={'method': ['DELETE']})
304 m.connect('edit_users_group', '/user_groups/{user_group_id}/edit',
304 m.connect('edit_users_group', '/user_groups/{user_group_id}/edit',
305 action='edit', conditions={'method': ['GET']},
305 action='edit', conditions={'method': ['GET']},
306 function=check_user_group)
306 function=check_user_group)
307
307
308 # EXTRAS USER GROUP ROUTES
308 # EXTRAS USER GROUP ROUTES
309 m.connect('edit_user_group_global_perms',
309 m.connect('edit_user_group_global_perms',
310 '/user_groups/{user_group_id}/edit/global_permissions',
310 '/user_groups/{user_group_id}/edit/global_permissions',
311 action='edit_global_perms', conditions={'method': ['GET']})
311 action='edit_global_perms', conditions={'method': ['GET']})
312 m.connect('edit_user_group_global_perms',
312 m.connect('edit_user_group_global_perms',
313 '/user_groups/{user_group_id}/edit/global_permissions',
313 '/user_groups/{user_group_id}/edit/global_permissions',
314 action='update_global_perms', conditions={'method': ['PUT']})
314 action='update_global_perms', conditions={'method': ['PUT']})
315 m.connect('edit_user_group_perms_summary',
315 m.connect('edit_user_group_perms_summary',
316 '/user_groups/{user_group_id}/edit/permissions_summary',
316 '/user_groups/{user_group_id}/edit/permissions_summary',
317 action='edit_perms_summary', conditions={'method': ['GET']})
317 action='edit_perms_summary', conditions={'method': ['GET']})
318
318
319 m.connect('edit_user_group_perms',
319 m.connect('edit_user_group_perms',
320 '/user_groups/{user_group_id}/edit/permissions',
320 '/user_groups/{user_group_id}/edit/permissions',
321 action='edit_perms', conditions={'method': ['GET']})
321 action='edit_perms', conditions={'method': ['GET']})
322 m.connect('edit_user_group_perms',
322 m.connect('edit_user_group_perms',
323 '/user_groups/{user_group_id}/edit/permissions',
323 '/user_groups/{user_group_id}/edit/permissions',
324 action='update_perms', conditions={'method': ['PUT']})
324 action='update_perms', conditions={'method': ['PUT']})
325
325
326 m.connect('edit_user_group_advanced',
326 m.connect('edit_user_group_advanced',
327 '/user_groups/{user_group_id}/edit/advanced',
327 '/user_groups/{user_group_id}/edit/advanced',
328 action='edit_advanced', conditions={'method': ['GET']})
328 action='edit_advanced', conditions={'method': ['GET']})
329
329
330 m.connect('edit_user_group_advanced_sync',
330 m.connect('edit_user_group_advanced_sync',
331 '/user_groups/{user_group_id}/edit/advanced/sync',
331 '/user_groups/{user_group_id}/edit/advanced/sync',
332 action='edit_advanced_set_synchronization', conditions={'method': ['POST']})
332 action='edit_advanced_set_synchronization', conditions={'method': ['POST']})
333
333
334 m.connect('edit_user_group_members',
334 m.connect('edit_user_group_members',
335 '/user_groups/{user_group_id}/edit/members', jsroute=True,
335 '/user_groups/{user_group_id}/edit/members', jsroute=True,
336 action='user_group_members', conditions={'method': ['GET']})
336 action='user_group_members', conditions={'method': ['GET']})
337
337
338 # ADMIN PERMISSIONS ROUTES
338 # ADMIN PERMISSIONS ROUTES
339 with rmap.submapper(path_prefix=ADMIN_PREFIX,
339 with rmap.submapper(path_prefix=ADMIN_PREFIX,
340 controller='admin/permissions') as m:
340 controller='admin/permissions') as m:
341 m.connect('admin_permissions_application', '/permissions/application',
341 m.connect('admin_permissions_application', '/permissions/application',
342 action='permission_application_update', conditions={'method': ['POST']})
342 action='permission_application_update', conditions={'method': ['POST']})
343 m.connect('admin_permissions_application', '/permissions/application',
343 m.connect('admin_permissions_application', '/permissions/application',
344 action='permission_application', conditions={'method': ['GET']})
344 action='permission_application', conditions={'method': ['GET']})
345
345
346 m.connect('admin_permissions_global', '/permissions/global',
346 m.connect('admin_permissions_global', '/permissions/global',
347 action='permission_global_update', conditions={'method': ['POST']})
347 action='permission_global_update', conditions={'method': ['POST']})
348 m.connect('admin_permissions_global', '/permissions/global',
348 m.connect('admin_permissions_global', '/permissions/global',
349 action='permission_global', conditions={'method': ['GET']})
349 action='permission_global', conditions={'method': ['GET']})
350
350
351 m.connect('admin_permissions_object', '/permissions/object',
351 m.connect('admin_permissions_object', '/permissions/object',
352 action='permission_objects_update', conditions={'method': ['POST']})
352 action='permission_objects_update', conditions={'method': ['POST']})
353 m.connect('admin_permissions_object', '/permissions/object',
353 m.connect('admin_permissions_object', '/permissions/object',
354 action='permission_objects', conditions={'method': ['GET']})
354 action='permission_objects', conditions={'method': ['GET']})
355
355
356 m.connect('admin_permissions_ips', '/permissions/ips',
356 m.connect('admin_permissions_ips', '/permissions/ips',
357 action='permission_ips', conditions={'method': ['POST']})
357 action='permission_ips', conditions={'method': ['POST']})
358 m.connect('admin_permissions_ips', '/permissions/ips',
358 m.connect('admin_permissions_ips', '/permissions/ips',
359 action='permission_ips', conditions={'method': ['GET']})
359 action='permission_ips', conditions={'method': ['GET']})
360
360
361 m.connect('admin_permissions_overview', '/permissions/overview',
361 m.connect('admin_permissions_overview', '/permissions/overview',
362 action='permission_perms', conditions={'method': ['GET']})
362 action='permission_perms', conditions={'method': ['GET']})
363
363
364 # ADMIN DEFAULTS REST ROUTES
364 # ADMIN DEFAULTS REST ROUTES
365 with rmap.submapper(path_prefix=ADMIN_PREFIX,
365 with rmap.submapper(path_prefix=ADMIN_PREFIX,
366 controller='admin/defaults') as m:
366 controller='admin/defaults') as m:
367 m.connect('admin_defaults_repositories', '/defaults/repositories',
367 m.connect('admin_defaults_repositories', '/defaults/repositories',
368 action='update_repository_defaults', conditions={'method': ['POST']})
368 action='update_repository_defaults', conditions={'method': ['POST']})
369 m.connect('admin_defaults_repositories', '/defaults/repositories',
369 m.connect('admin_defaults_repositories', '/defaults/repositories',
370 action='index', conditions={'method': ['GET']})
370 action='index', conditions={'method': ['GET']})
371
371
372 # ADMIN SETTINGS ROUTES
372 # ADMIN SETTINGS ROUTES
373 with rmap.submapper(path_prefix=ADMIN_PREFIX,
373 with rmap.submapper(path_prefix=ADMIN_PREFIX,
374 controller='admin/settings') as m:
374 controller='admin/settings') as m:
375
375
376 # default
376 # default
377 m.connect('admin_settings', '/settings',
377 m.connect('admin_settings', '/settings',
378 action='settings_global_update',
378 action='settings_global_update',
379 conditions={'method': ['POST']})
379 conditions={'method': ['POST']})
380 m.connect('admin_settings', '/settings',
380 m.connect('admin_settings', '/settings',
381 action='settings_global', conditions={'method': ['GET']})
381 action='settings_global', conditions={'method': ['GET']})
382
382
383 m.connect('admin_settings_vcs', '/settings/vcs',
383 m.connect('admin_settings_vcs', '/settings/vcs',
384 action='settings_vcs_update',
384 action='settings_vcs_update',
385 conditions={'method': ['POST']})
385 conditions={'method': ['POST']})
386 m.connect('admin_settings_vcs', '/settings/vcs',
386 m.connect('admin_settings_vcs', '/settings/vcs',
387 action='settings_vcs',
387 action='settings_vcs',
388 conditions={'method': ['GET']})
388 conditions={'method': ['GET']})
389 m.connect('admin_settings_vcs', '/settings/vcs',
389 m.connect('admin_settings_vcs', '/settings/vcs',
390 action='delete_svn_pattern',
390 action='delete_svn_pattern',
391 conditions={'method': ['DELETE']})
391 conditions={'method': ['DELETE']})
392
392
393 m.connect('admin_settings_mapping', '/settings/mapping',
393 m.connect('admin_settings_mapping', '/settings/mapping',
394 action='settings_mapping_update',
394 action='settings_mapping_update',
395 conditions={'method': ['POST']})
395 conditions={'method': ['POST']})
396 m.connect('admin_settings_mapping', '/settings/mapping',
396 m.connect('admin_settings_mapping', '/settings/mapping',
397 action='settings_mapping', conditions={'method': ['GET']})
397 action='settings_mapping', conditions={'method': ['GET']})
398
398
399 m.connect('admin_settings_global', '/settings/global',
399 m.connect('admin_settings_global', '/settings/global',
400 action='settings_global_update',
400 action='settings_global_update',
401 conditions={'method': ['POST']})
401 conditions={'method': ['POST']})
402 m.connect('admin_settings_global', '/settings/global',
402 m.connect('admin_settings_global', '/settings/global',
403 action='settings_global', conditions={'method': ['GET']})
403 action='settings_global', conditions={'method': ['GET']})
404
404
405 m.connect('admin_settings_visual', '/settings/visual',
405 m.connect('admin_settings_visual', '/settings/visual',
406 action='settings_visual_update',
406 action='settings_visual_update',
407 conditions={'method': ['POST']})
407 conditions={'method': ['POST']})
408 m.connect('admin_settings_visual', '/settings/visual',
408 m.connect('admin_settings_visual', '/settings/visual',
409 action='settings_visual', conditions={'method': ['GET']})
409 action='settings_visual', conditions={'method': ['GET']})
410
410
411 m.connect('admin_settings_issuetracker',
411 m.connect('admin_settings_issuetracker',
412 '/settings/issue-tracker', action='settings_issuetracker',
412 '/settings/issue-tracker', action='settings_issuetracker',
413 conditions={'method': ['GET']})
413 conditions={'method': ['GET']})
414 m.connect('admin_settings_issuetracker_save',
414 m.connect('admin_settings_issuetracker_save',
415 '/settings/issue-tracker/save',
415 '/settings/issue-tracker/save',
416 action='settings_issuetracker_save',
416 action='settings_issuetracker_save',
417 conditions={'method': ['POST']})
417 conditions={'method': ['POST']})
418 m.connect('admin_issuetracker_test', '/settings/issue-tracker/test',
418 m.connect('admin_issuetracker_test', '/settings/issue-tracker/test',
419 action='settings_issuetracker_test',
419 action='settings_issuetracker_test',
420 conditions={'method': ['POST']})
420 conditions={'method': ['POST']})
421 m.connect('admin_issuetracker_delete',
421 m.connect('admin_issuetracker_delete',
422 '/settings/issue-tracker/delete',
422 '/settings/issue-tracker/delete',
423 action='settings_issuetracker_delete',
423 action='settings_issuetracker_delete',
424 conditions={'method': ['DELETE']})
424 conditions={'method': ['DELETE']})
425
425
426 m.connect('admin_settings_email', '/settings/email',
426 m.connect('admin_settings_email', '/settings/email',
427 action='settings_email_update',
427 action='settings_email_update',
428 conditions={'method': ['POST']})
428 conditions={'method': ['POST']})
429 m.connect('admin_settings_email', '/settings/email',
429 m.connect('admin_settings_email', '/settings/email',
430 action='settings_email', conditions={'method': ['GET']})
430 action='settings_email', conditions={'method': ['GET']})
431
431
432 m.connect('admin_settings_hooks', '/settings/hooks',
432 m.connect('admin_settings_hooks', '/settings/hooks',
433 action='settings_hooks_update',
433 action='settings_hooks_update',
434 conditions={'method': ['POST', 'DELETE']})
434 conditions={'method': ['POST', 'DELETE']})
435 m.connect('admin_settings_hooks', '/settings/hooks',
435 m.connect('admin_settings_hooks', '/settings/hooks',
436 action='settings_hooks', conditions={'method': ['GET']})
436 action='settings_hooks', conditions={'method': ['GET']})
437
437
438 m.connect('admin_settings_search', '/settings/search',
438 m.connect('admin_settings_search', '/settings/search',
439 action='settings_search', conditions={'method': ['GET']})
439 action='settings_search', conditions={'method': ['GET']})
440
440
441 m.connect('admin_settings_supervisor', '/settings/supervisor',
441 m.connect('admin_settings_supervisor', '/settings/supervisor',
442 action='settings_supervisor', conditions={'method': ['GET']})
442 action='settings_supervisor', conditions={'method': ['GET']})
443 m.connect('admin_settings_supervisor_log', '/settings/supervisor/{procid}/log',
443 m.connect('admin_settings_supervisor_log', '/settings/supervisor/{procid}/log',
444 action='settings_supervisor_log', conditions={'method': ['GET']})
444 action='settings_supervisor_log', conditions={'method': ['GET']})
445
445
446 m.connect('admin_settings_labs', '/settings/labs',
446 m.connect('admin_settings_labs', '/settings/labs',
447 action='settings_labs_update',
447 action='settings_labs_update',
448 conditions={'method': ['POST']})
448 conditions={'method': ['POST']})
449 m.connect('admin_settings_labs', '/settings/labs',
449 m.connect('admin_settings_labs', '/settings/labs',
450 action='settings_labs', conditions={'method': ['GET']})
450 action='settings_labs', conditions={'method': ['GET']})
451
451
452 # ADMIN MY ACCOUNT
452 # ADMIN MY ACCOUNT
453 with rmap.submapper(path_prefix=ADMIN_PREFIX,
453 with rmap.submapper(path_prefix=ADMIN_PREFIX,
454 controller='admin/my_account') as m:
454 controller='admin/my_account') as m:
455
455
456 # NOTE(marcink): this needs to be kept for password force flag to be
456 # NOTE(marcink): this needs to be kept for password force flag to be
457 # handled in pylons controllers, remove after full migration to pyramid
457 # handled in pylons controllers, remove after full migration to pyramid
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')
478 rmap.connect('journal_rss', '%s/journal/rss' % (ADMIN_PREFIX,),
464 rmap.connect('journal_rss', '%s/journal/rss' % (ADMIN_PREFIX,),
479 controller='journal', action='journal_rss')
465 controller='journal', action='journal_rss')
480 rmap.connect('journal_atom', '%s/journal/atom' % (ADMIN_PREFIX,),
466 rmap.connect('journal_atom', '%s/journal/atom' % (ADMIN_PREFIX,),
481 controller='journal', action='journal_atom')
467 controller='journal', action='journal_atom')
482
468
483 rmap.connect('public_journal', '%s/public_journal' % (ADMIN_PREFIX,),
469 rmap.connect('public_journal', '%s/public_journal' % (ADMIN_PREFIX,),
484 controller='journal', action='public_journal')
470 controller='journal', action='public_journal')
485
471
486 rmap.connect('public_journal_rss', '%s/public_journal/rss' % (ADMIN_PREFIX,),
472 rmap.connect('public_journal_rss', '%s/public_journal/rss' % (ADMIN_PREFIX,),
487 controller='journal', action='public_journal_rss')
473 controller='journal', action='public_journal_rss')
488
474
489 rmap.connect('public_journal_rss_old', '%s/public_journal_rss' % (ADMIN_PREFIX,),
475 rmap.connect('public_journal_rss_old', '%s/public_journal_rss' % (ADMIN_PREFIX,),
490 controller='journal', action='public_journal_rss')
476 controller='journal', action='public_journal_rss')
491
477
492 rmap.connect('public_journal_atom',
478 rmap.connect('public_journal_atom',
493 '%s/public_journal/atom' % (ADMIN_PREFIX,), controller='journal',
479 '%s/public_journal/atom' % (ADMIN_PREFIX,), controller='journal',
494 action='public_journal_atom')
480 action='public_journal_atom')
495
481
496 rmap.connect('public_journal_atom_old',
482 rmap.connect('public_journal_atom_old',
497 '%s/public_journal_atom' % (ADMIN_PREFIX,), controller='journal',
483 '%s/public_journal_atom' % (ADMIN_PREFIX,), controller='journal',
498 action='public_journal_atom')
484 action='public_journal_atom')
499
485
500 rmap.connect('toggle_following', '%s/toggle_following' % (ADMIN_PREFIX,),
486 rmap.connect('toggle_following', '%s/toggle_following' % (ADMIN_PREFIX,),
501 controller='journal', action='toggle_following', jsroute=True,
487 controller='journal', action='toggle_following', jsroute=True,
502 conditions={'method': ['POST']})
488 conditions={'method': ['POST']})
503
489
504 #==========================================================================
490 #==========================================================================
505 # REPOSITORY ROUTES
491 # REPOSITORY ROUTES
506 #==========================================================================
492 #==========================================================================
507
493
508 rmap.connect('repo_creating_home', '/{repo_name}/repo_creating',
494 rmap.connect('repo_creating_home', '/{repo_name}/repo_creating',
509 controller='admin/repos', action='repo_creating',
495 controller='admin/repos', action='repo_creating',
510 requirements=URL_NAME_REQUIREMENTS)
496 requirements=URL_NAME_REQUIREMENTS)
511 rmap.connect('repo_check_home', '/{repo_name}/crepo_check',
497 rmap.connect('repo_check_home', '/{repo_name}/crepo_check',
512 controller='admin/repos', action='repo_check',
498 controller='admin/repos', action='repo_check',
513 requirements=URL_NAME_REQUIREMENTS)
499 requirements=URL_NAME_REQUIREMENTS)
514
500
515 rmap.connect('changeset_home', '/{repo_name}/changeset/{revision}',
501 rmap.connect('changeset_home', '/{repo_name}/changeset/{revision}',
516 controller='changeset', revision='tip',
502 controller='changeset', revision='tip',
517 conditions={'function': check_repo},
503 conditions={'function': check_repo},
518 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
504 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
519 rmap.connect('changeset_children', '/{repo_name}/changeset_children/{revision}',
505 rmap.connect('changeset_children', '/{repo_name}/changeset_children/{revision}',
520 controller='changeset', revision='tip', action='changeset_children',
506 controller='changeset', revision='tip', action='changeset_children',
521 conditions={'function': check_repo},
507 conditions={'function': check_repo},
522 requirements=URL_NAME_REQUIREMENTS)
508 requirements=URL_NAME_REQUIREMENTS)
523 rmap.connect('changeset_parents', '/{repo_name}/changeset_parents/{revision}',
509 rmap.connect('changeset_parents', '/{repo_name}/changeset_parents/{revision}',
524 controller='changeset', revision='tip', action='changeset_parents',
510 controller='changeset', revision='tip', action='changeset_parents',
525 conditions={'function': check_repo},
511 conditions={'function': check_repo},
526 requirements=URL_NAME_REQUIREMENTS)
512 requirements=URL_NAME_REQUIREMENTS)
527
513
528 # repo edit options
514 # repo edit options
529 rmap.connect('edit_repo_fields', '/{repo_name}/settings/fields',
515 rmap.connect('edit_repo_fields', '/{repo_name}/settings/fields',
530 controller='admin/repos', action='edit_fields',
516 controller='admin/repos', action='edit_fields',
531 conditions={'method': ['GET'], 'function': check_repo},
517 conditions={'method': ['GET'], 'function': check_repo},
532 requirements=URL_NAME_REQUIREMENTS)
518 requirements=URL_NAME_REQUIREMENTS)
533 rmap.connect('create_repo_fields', '/{repo_name}/settings/fields/new',
519 rmap.connect('create_repo_fields', '/{repo_name}/settings/fields/new',
534 controller='admin/repos', action='create_repo_field',
520 controller='admin/repos', action='create_repo_field',
535 conditions={'method': ['PUT'], 'function': check_repo},
521 conditions={'method': ['PUT'], 'function': check_repo},
536 requirements=URL_NAME_REQUIREMENTS)
522 requirements=URL_NAME_REQUIREMENTS)
537 rmap.connect('delete_repo_fields', '/{repo_name}/settings/fields/{field_id}',
523 rmap.connect('delete_repo_fields', '/{repo_name}/settings/fields/{field_id}',
538 controller='admin/repos', action='delete_repo_field',
524 controller='admin/repos', action='delete_repo_field',
539 conditions={'method': ['DELETE'], 'function': check_repo},
525 conditions={'method': ['DELETE'], 'function': check_repo},
540 requirements=URL_NAME_REQUIREMENTS)
526 requirements=URL_NAME_REQUIREMENTS)
541
527
542 rmap.connect('toggle_locking', '/{repo_name}/settings/advanced/locking_toggle',
528 rmap.connect('toggle_locking', '/{repo_name}/settings/advanced/locking_toggle',
543 controller='admin/repos', action='toggle_locking',
529 controller='admin/repos', action='toggle_locking',
544 conditions={'method': ['GET'], 'function': check_repo},
530 conditions={'method': ['GET'], 'function': check_repo},
545 requirements=URL_NAME_REQUIREMENTS)
531 requirements=URL_NAME_REQUIREMENTS)
546
532
547 rmap.connect('edit_repo_remote', '/{repo_name}/settings/remote',
533 rmap.connect('edit_repo_remote', '/{repo_name}/settings/remote',
548 controller='admin/repos', action='edit_remote_form',
534 controller='admin/repos', action='edit_remote_form',
549 conditions={'method': ['GET'], 'function': check_repo},
535 conditions={'method': ['GET'], 'function': check_repo},
550 requirements=URL_NAME_REQUIREMENTS)
536 requirements=URL_NAME_REQUIREMENTS)
551 rmap.connect('edit_repo_remote', '/{repo_name}/settings/remote',
537 rmap.connect('edit_repo_remote', '/{repo_name}/settings/remote',
552 controller='admin/repos', action='edit_remote',
538 controller='admin/repos', action='edit_remote',
553 conditions={'method': ['PUT'], 'function': check_repo},
539 conditions={'method': ['PUT'], 'function': check_repo},
554 requirements=URL_NAME_REQUIREMENTS)
540 requirements=URL_NAME_REQUIREMENTS)
555
541
556 rmap.connect('edit_repo_statistics', '/{repo_name}/settings/statistics',
542 rmap.connect('edit_repo_statistics', '/{repo_name}/settings/statistics',
557 controller='admin/repos', action='edit_statistics_form',
543 controller='admin/repos', action='edit_statistics_form',
558 conditions={'method': ['GET'], 'function': check_repo},
544 conditions={'method': ['GET'], 'function': check_repo},
559 requirements=URL_NAME_REQUIREMENTS)
545 requirements=URL_NAME_REQUIREMENTS)
560 rmap.connect('edit_repo_statistics', '/{repo_name}/settings/statistics',
546 rmap.connect('edit_repo_statistics', '/{repo_name}/settings/statistics',
561 controller='admin/repos', action='edit_statistics',
547 controller='admin/repos', action='edit_statistics',
562 conditions={'method': ['PUT'], 'function': check_repo},
548 conditions={'method': ['PUT'], 'function': check_repo},
563 requirements=URL_NAME_REQUIREMENTS)
549 requirements=URL_NAME_REQUIREMENTS)
564 rmap.connect('repo_settings_issuetracker',
550 rmap.connect('repo_settings_issuetracker',
565 '/{repo_name}/settings/issue-tracker',
551 '/{repo_name}/settings/issue-tracker',
566 controller='admin/repos', action='repo_issuetracker',
552 controller='admin/repos', action='repo_issuetracker',
567 conditions={'method': ['GET'], 'function': check_repo},
553 conditions={'method': ['GET'], 'function': check_repo},
568 requirements=URL_NAME_REQUIREMENTS)
554 requirements=URL_NAME_REQUIREMENTS)
569 rmap.connect('repo_issuetracker_test',
555 rmap.connect('repo_issuetracker_test',
570 '/{repo_name}/settings/issue-tracker/test',
556 '/{repo_name}/settings/issue-tracker/test',
571 controller='admin/repos', action='repo_issuetracker_test',
557 controller='admin/repos', action='repo_issuetracker_test',
572 conditions={'method': ['POST'], 'function': check_repo},
558 conditions={'method': ['POST'], 'function': check_repo},
573 requirements=URL_NAME_REQUIREMENTS)
559 requirements=URL_NAME_REQUIREMENTS)
574 rmap.connect('repo_issuetracker_delete',
560 rmap.connect('repo_issuetracker_delete',
575 '/{repo_name}/settings/issue-tracker/delete',
561 '/{repo_name}/settings/issue-tracker/delete',
576 controller='admin/repos', action='repo_issuetracker_delete',
562 controller='admin/repos', action='repo_issuetracker_delete',
577 conditions={'method': ['DELETE'], 'function': check_repo},
563 conditions={'method': ['DELETE'], 'function': check_repo},
578 requirements=URL_NAME_REQUIREMENTS)
564 requirements=URL_NAME_REQUIREMENTS)
579 rmap.connect('repo_issuetracker_save',
565 rmap.connect('repo_issuetracker_save',
580 '/{repo_name}/settings/issue-tracker/save',
566 '/{repo_name}/settings/issue-tracker/save',
581 controller='admin/repos', action='repo_issuetracker_save',
567 controller='admin/repos', action='repo_issuetracker_save',
582 conditions={'method': ['POST'], 'function': check_repo},
568 conditions={'method': ['POST'], 'function': check_repo},
583 requirements=URL_NAME_REQUIREMENTS)
569 requirements=URL_NAME_REQUIREMENTS)
584 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
570 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
585 controller='admin/repos', action='repo_settings_vcs_update',
571 controller='admin/repos', action='repo_settings_vcs_update',
586 conditions={'method': ['POST'], 'function': check_repo},
572 conditions={'method': ['POST'], 'function': check_repo},
587 requirements=URL_NAME_REQUIREMENTS)
573 requirements=URL_NAME_REQUIREMENTS)
588 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
574 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
589 controller='admin/repos', action='repo_settings_vcs',
575 controller='admin/repos', action='repo_settings_vcs',
590 conditions={'method': ['GET'], 'function': check_repo},
576 conditions={'method': ['GET'], 'function': check_repo},
591 requirements=URL_NAME_REQUIREMENTS)
577 requirements=URL_NAME_REQUIREMENTS)
592 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
578 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
593 controller='admin/repos', action='repo_delete_svn_pattern',
579 controller='admin/repos', action='repo_delete_svn_pattern',
594 conditions={'method': ['DELETE'], 'function': check_repo},
580 conditions={'method': ['DELETE'], 'function': check_repo},
595 requirements=URL_NAME_REQUIREMENTS)
581 requirements=URL_NAME_REQUIREMENTS)
596 rmap.connect('repo_pullrequest_settings', '/{repo_name}/settings/pullrequest',
582 rmap.connect('repo_pullrequest_settings', '/{repo_name}/settings/pullrequest',
597 controller='admin/repos', action='repo_settings_pullrequest',
583 controller='admin/repos', action='repo_settings_pullrequest',
598 conditions={'method': ['GET', 'POST'], 'function': check_repo},
584 conditions={'method': ['GET', 'POST'], 'function': check_repo},
599 requirements=URL_NAME_REQUIREMENTS)
585 requirements=URL_NAME_REQUIREMENTS)
600
586
601 # still working url for backward compat.
587 # still working url for backward compat.
602 rmap.connect('raw_changeset_home_depraced',
588 rmap.connect('raw_changeset_home_depraced',
603 '/{repo_name}/raw-changeset/{revision}',
589 '/{repo_name}/raw-changeset/{revision}',
604 controller='changeset', action='changeset_raw',
590 controller='changeset', action='changeset_raw',
605 revision='tip', conditions={'function': check_repo},
591 revision='tip', conditions={'function': check_repo},
606 requirements=URL_NAME_REQUIREMENTS)
592 requirements=URL_NAME_REQUIREMENTS)
607
593
608 # new URLs
594 # new URLs
609 rmap.connect('changeset_raw_home',
595 rmap.connect('changeset_raw_home',
610 '/{repo_name}/changeset-diff/{revision}',
596 '/{repo_name}/changeset-diff/{revision}',
611 controller='changeset', action='changeset_raw',
597 controller='changeset', action='changeset_raw',
612 revision='tip', conditions={'function': check_repo},
598 revision='tip', conditions={'function': check_repo},
613 requirements=URL_NAME_REQUIREMENTS)
599 requirements=URL_NAME_REQUIREMENTS)
614
600
615 rmap.connect('changeset_patch_home',
601 rmap.connect('changeset_patch_home',
616 '/{repo_name}/changeset-patch/{revision}',
602 '/{repo_name}/changeset-patch/{revision}',
617 controller='changeset', action='changeset_patch',
603 controller='changeset', action='changeset_patch',
618 revision='tip', conditions={'function': check_repo},
604 revision='tip', conditions={'function': check_repo},
619 requirements=URL_NAME_REQUIREMENTS)
605 requirements=URL_NAME_REQUIREMENTS)
620
606
621 rmap.connect('changeset_download_home',
607 rmap.connect('changeset_download_home',
622 '/{repo_name}/changeset-download/{revision}',
608 '/{repo_name}/changeset-download/{revision}',
623 controller='changeset', action='changeset_download',
609 controller='changeset', action='changeset_download',
624 revision='tip', conditions={'function': check_repo},
610 revision='tip', conditions={'function': check_repo},
625 requirements=URL_NAME_REQUIREMENTS)
611 requirements=URL_NAME_REQUIREMENTS)
626
612
627 rmap.connect('changeset_comment',
613 rmap.connect('changeset_comment',
628 '/{repo_name}/changeset/{revision}/comment', jsroute=True,
614 '/{repo_name}/changeset/{revision}/comment', jsroute=True,
629 controller='changeset', revision='tip', action='comment',
615 controller='changeset', revision='tip', action='comment',
630 conditions={'function': check_repo},
616 conditions={'function': check_repo},
631 requirements=URL_NAME_REQUIREMENTS)
617 requirements=URL_NAME_REQUIREMENTS)
632
618
633 rmap.connect('changeset_comment_preview',
619 rmap.connect('changeset_comment_preview',
634 '/{repo_name}/changeset/comment/preview', jsroute=True,
620 '/{repo_name}/changeset/comment/preview', jsroute=True,
635 controller='changeset', action='preview_comment',
621 controller='changeset', action='preview_comment',
636 conditions={'function': check_repo, 'method': ['POST']},
622 conditions={'function': check_repo, 'method': ['POST']},
637 requirements=URL_NAME_REQUIREMENTS)
623 requirements=URL_NAME_REQUIREMENTS)
638
624
639 rmap.connect('changeset_comment_delete',
625 rmap.connect('changeset_comment_delete',
640 '/{repo_name}/changeset/comment/{comment_id}/delete',
626 '/{repo_name}/changeset/comment/{comment_id}/delete',
641 controller='changeset', action='delete_comment',
627 controller='changeset', action='delete_comment',
642 conditions={'function': check_repo, 'method': ['DELETE']},
628 conditions={'function': check_repo, 'method': ['DELETE']},
643 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
629 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
644
630
645 rmap.connect('changeset_info', '/{repo_name}/changeset_info/{revision}',
631 rmap.connect('changeset_info', '/{repo_name}/changeset_info/{revision}',
646 controller='changeset', action='changeset_info',
632 controller='changeset', action='changeset_info',
647 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
633 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
648
634
649 rmap.connect('compare_home',
635 rmap.connect('compare_home',
650 '/{repo_name}/compare',
636 '/{repo_name}/compare',
651 controller='compare', action='index',
637 controller='compare', action='index',
652 conditions={'function': check_repo},
638 conditions={'function': check_repo},
653 requirements=URL_NAME_REQUIREMENTS)
639 requirements=URL_NAME_REQUIREMENTS)
654
640
655 rmap.connect('compare_url',
641 rmap.connect('compare_url',
656 '/{repo_name}/compare/{source_ref_type}@{source_ref:.*?}...{target_ref_type}@{target_ref:.*?}',
642 '/{repo_name}/compare/{source_ref_type}@{source_ref:.*?}...{target_ref_type}@{target_ref:.*?}',
657 controller='compare', action='compare',
643 controller='compare', action='compare',
658 conditions={'function': check_repo},
644 conditions={'function': check_repo},
659 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
645 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
660
646
661 rmap.connect('pullrequest_home',
647 rmap.connect('pullrequest_home',
662 '/{repo_name}/pull-request/new', controller='pullrequests',
648 '/{repo_name}/pull-request/new', controller='pullrequests',
663 action='index', conditions={'function': check_repo,
649 action='index', conditions={'function': check_repo,
664 'method': ['GET']},
650 'method': ['GET']},
665 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
651 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
666
652
667 rmap.connect('pullrequest',
653 rmap.connect('pullrequest',
668 '/{repo_name}/pull-request/new', controller='pullrequests',
654 '/{repo_name}/pull-request/new', controller='pullrequests',
669 action='create', conditions={'function': check_repo,
655 action='create', conditions={'function': check_repo,
670 'method': ['POST']},
656 'method': ['POST']},
671 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
657 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
672
658
673 rmap.connect('pullrequest_repo_refs',
659 rmap.connect('pullrequest_repo_refs',
674 '/{repo_name}/pull-request/refs/{target_repo_name:.*?[^/]}',
660 '/{repo_name}/pull-request/refs/{target_repo_name:.*?[^/]}',
675 controller='pullrequests',
661 controller='pullrequests',
676 action='get_repo_refs',
662 action='get_repo_refs',
677 conditions={'function': check_repo, 'method': ['GET']},
663 conditions={'function': check_repo, 'method': ['GET']},
678 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
664 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
679
665
680 rmap.connect('pullrequest_repo_destinations',
666 rmap.connect('pullrequest_repo_destinations',
681 '/{repo_name}/pull-request/repo-destinations',
667 '/{repo_name}/pull-request/repo-destinations',
682 controller='pullrequests',
668 controller='pullrequests',
683 action='get_repo_destinations',
669 action='get_repo_destinations',
684 conditions={'function': check_repo, 'method': ['GET']},
670 conditions={'function': check_repo, 'method': ['GET']},
685 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
671 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
686
672
687 rmap.connect('pullrequest_show',
673 rmap.connect('pullrequest_show',
688 '/{repo_name}/pull-request/{pull_request_id}',
674 '/{repo_name}/pull-request/{pull_request_id}',
689 controller='pullrequests',
675 controller='pullrequests',
690 action='show', conditions={'function': check_repo,
676 action='show', conditions={'function': check_repo,
691 'method': ['GET']},
677 'method': ['GET']},
692 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
678 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
693
679
694 rmap.connect('pullrequest_update',
680 rmap.connect('pullrequest_update',
695 '/{repo_name}/pull-request/{pull_request_id}',
681 '/{repo_name}/pull-request/{pull_request_id}',
696 controller='pullrequests',
682 controller='pullrequests',
697 action='update', conditions={'function': check_repo,
683 action='update', conditions={'function': check_repo,
698 'method': ['PUT']},
684 'method': ['PUT']},
699 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
685 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
700
686
701 rmap.connect('pullrequest_merge',
687 rmap.connect('pullrequest_merge',
702 '/{repo_name}/pull-request/{pull_request_id}',
688 '/{repo_name}/pull-request/{pull_request_id}',
703 controller='pullrequests',
689 controller='pullrequests',
704 action='merge', conditions={'function': check_repo,
690 action='merge', conditions={'function': check_repo,
705 'method': ['POST']},
691 'method': ['POST']},
706 requirements=URL_NAME_REQUIREMENTS)
692 requirements=URL_NAME_REQUIREMENTS)
707
693
708 rmap.connect('pullrequest_delete',
694 rmap.connect('pullrequest_delete',
709 '/{repo_name}/pull-request/{pull_request_id}',
695 '/{repo_name}/pull-request/{pull_request_id}',
710 controller='pullrequests',
696 controller='pullrequests',
711 action='delete', conditions={'function': check_repo,
697 action='delete', conditions={'function': check_repo,
712 'method': ['DELETE']},
698 'method': ['DELETE']},
713 requirements=URL_NAME_REQUIREMENTS)
699 requirements=URL_NAME_REQUIREMENTS)
714
700
715 rmap.connect('pullrequest_comment',
701 rmap.connect('pullrequest_comment',
716 '/{repo_name}/pull-request-comment/{pull_request_id}',
702 '/{repo_name}/pull-request-comment/{pull_request_id}',
717 controller='pullrequests',
703 controller='pullrequests',
718 action='comment', conditions={'function': check_repo,
704 action='comment', conditions={'function': check_repo,
719 'method': ['POST']},
705 'method': ['POST']},
720 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
706 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
721
707
722 rmap.connect('pullrequest_comment_delete',
708 rmap.connect('pullrequest_comment_delete',
723 '/{repo_name}/pull-request-comment/{comment_id}/delete',
709 '/{repo_name}/pull-request-comment/{comment_id}/delete',
724 controller='pullrequests', action='delete_comment',
710 controller='pullrequests', action='delete_comment',
725 conditions={'function': check_repo, 'method': ['DELETE']},
711 conditions={'function': check_repo, 'method': ['DELETE']},
726 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
712 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
727
713
728 rmap.connect('changelog_home', '/{repo_name}/changelog', jsroute=True,
714 rmap.connect('changelog_home', '/{repo_name}/changelog', jsroute=True,
729 controller='changelog', conditions={'function': check_repo},
715 controller='changelog', conditions={'function': check_repo},
730 requirements=URL_NAME_REQUIREMENTS)
716 requirements=URL_NAME_REQUIREMENTS)
731
717
732 rmap.connect('changelog_file_home',
718 rmap.connect('changelog_file_home',
733 '/{repo_name}/changelog/{revision}/{f_path}',
719 '/{repo_name}/changelog/{revision}/{f_path}',
734 controller='changelog', f_path=None,
720 controller='changelog', f_path=None,
735 conditions={'function': check_repo},
721 conditions={'function': check_repo},
736 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
722 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
737
723
738 rmap.connect('changelog_elements', '/{repo_name}/changelog_details',
724 rmap.connect('changelog_elements', '/{repo_name}/changelog_details',
739 controller='changelog', action='changelog_elements',
725 controller='changelog', action='changelog_elements',
740 conditions={'function': check_repo},
726 conditions={'function': check_repo},
741 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
727 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
742
728
743 rmap.connect('files_home', '/{repo_name}/files/{revision}/{f_path}',
729 rmap.connect('files_home', '/{repo_name}/files/{revision}/{f_path}',
744 controller='files', revision='tip', f_path='',
730 controller='files', revision='tip', f_path='',
745 conditions={'function': check_repo},
731 conditions={'function': check_repo},
746 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
732 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
747
733
748 rmap.connect('files_home_simple_catchrev',
734 rmap.connect('files_home_simple_catchrev',
749 '/{repo_name}/files/{revision}',
735 '/{repo_name}/files/{revision}',
750 controller='files', revision='tip', f_path='',
736 controller='files', revision='tip', f_path='',
751 conditions={'function': check_repo},
737 conditions={'function': check_repo},
752 requirements=URL_NAME_REQUIREMENTS)
738 requirements=URL_NAME_REQUIREMENTS)
753
739
754 rmap.connect('files_home_simple_catchall',
740 rmap.connect('files_home_simple_catchall',
755 '/{repo_name}/files',
741 '/{repo_name}/files',
756 controller='files', revision='tip', f_path='',
742 controller='files', revision='tip', f_path='',
757 conditions={'function': check_repo},
743 conditions={'function': check_repo},
758 requirements=URL_NAME_REQUIREMENTS)
744 requirements=URL_NAME_REQUIREMENTS)
759
745
760 rmap.connect('files_history_home',
746 rmap.connect('files_history_home',
761 '/{repo_name}/history/{revision}/{f_path}',
747 '/{repo_name}/history/{revision}/{f_path}',
762 controller='files', action='history', revision='tip', f_path='',
748 controller='files', action='history', revision='tip', f_path='',
763 conditions={'function': check_repo},
749 conditions={'function': check_repo},
764 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
750 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
765
751
766 rmap.connect('files_authors_home',
752 rmap.connect('files_authors_home',
767 '/{repo_name}/authors/{revision}/{f_path}',
753 '/{repo_name}/authors/{revision}/{f_path}',
768 controller='files', action='authors', revision='tip', f_path='',
754 controller='files', action='authors', revision='tip', f_path='',
769 conditions={'function': check_repo},
755 conditions={'function': check_repo},
770 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
756 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
771
757
772 rmap.connect('files_diff_home', '/{repo_name}/diff/{f_path}',
758 rmap.connect('files_diff_home', '/{repo_name}/diff/{f_path}',
773 controller='files', action='diff', f_path='',
759 controller='files', action='diff', f_path='',
774 conditions={'function': check_repo},
760 conditions={'function': check_repo},
775 requirements=URL_NAME_REQUIREMENTS)
761 requirements=URL_NAME_REQUIREMENTS)
776
762
777 rmap.connect('files_diff_2way_home',
763 rmap.connect('files_diff_2way_home',
778 '/{repo_name}/diff-2way/{f_path}',
764 '/{repo_name}/diff-2way/{f_path}',
779 controller='files', action='diff_2way', f_path='',
765 controller='files', action='diff_2way', f_path='',
780 conditions={'function': check_repo},
766 conditions={'function': check_repo},
781 requirements=URL_NAME_REQUIREMENTS)
767 requirements=URL_NAME_REQUIREMENTS)
782
768
783 rmap.connect('files_rawfile_home',
769 rmap.connect('files_rawfile_home',
784 '/{repo_name}/rawfile/{revision}/{f_path}',
770 '/{repo_name}/rawfile/{revision}/{f_path}',
785 controller='files', action='rawfile', revision='tip',
771 controller='files', action='rawfile', revision='tip',
786 f_path='', conditions={'function': check_repo},
772 f_path='', conditions={'function': check_repo},
787 requirements=URL_NAME_REQUIREMENTS)
773 requirements=URL_NAME_REQUIREMENTS)
788
774
789 rmap.connect('files_raw_home',
775 rmap.connect('files_raw_home',
790 '/{repo_name}/raw/{revision}/{f_path}',
776 '/{repo_name}/raw/{revision}/{f_path}',
791 controller='files', action='raw', revision='tip', f_path='',
777 controller='files', action='raw', revision='tip', f_path='',
792 conditions={'function': check_repo},
778 conditions={'function': check_repo},
793 requirements=URL_NAME_REQUIREMENTS)
779 requirements=URL_NAME_REQUIREMENTS)
794
780
795 rmap.connect('files_render_home',
781 rmap.connect('files_render_home',
796 '/{repo_name}/render/{revision}/{f_path}',
782 '/{repo_name}/render/{revision}/{f_path}',
797 controller='files', action='index', revision='tip', f_path='',
783 controller='files', action='index', revision='tip', f_path='',
798 rendered=True, conditions={'function': check_repo},
784 rendered=True, conditions={'function': check_repo},
799 requirements=URL_NAME_REQUIREMENTS)
785 requirements=URL_NAME_REQUIREMENTS)
800
786
801 rmap.connect('files_annotate_home',
787 rmap.connect('files_annotate_home',
802 '/{repo_name}/annotate/{revision}/{f_path}',
788 '/{repo_name}/annotate/{revision}/{f_path}',
803 controller='files', action='index', revision='tip',
789 controller='files', action='index', revision='tip',
804 f_path='', annotate=True, conditions={'function': check_repo},
790 f_path='', annotate=True, conditions={'function': check_repo},
805 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
791 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
806
792
807 rmap.connect('files_annotate_previous',
793 rmap.connect('files_annotate_previous',
808 '/{repo_name}/annotate-previous/{revision}/{f_path}',
794 '/{repo_name}/annotate-previous/{revision}/{f_path}',
809 controller='files', action='annotate_previous', revision='tip',
795 controller='files', action='annotate_previous', revision='tip',
810 f_path='', annotate=True, conditions={'function': check_repo},
796 f_path='', annotate=True, conditions={'function': check_repo},
811 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
797 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
812
798
813 rmap.connect('files_edit',
799 rmap.connect('files_edit',
814 '/{repo_name}/edit/{revision}/{f_path}',
800 '/{repo_name}/edit/{revision}/{f_path}',
815 controller='files', action='edit', revision='tip',
801 controller='files', action='edit', revision='tip',
816 f_path='',
802 f_path='',
817 conditions={'function': check_repo, 'method': ['POST']},
803 conditions={'function': check_repo, 'method': ['POST']},
818 requirements=URL_NAME_REQUIREMENTS)
804 requirements=URL_NAME_REQUIREMENTS)
819
805
820 rmap.connect('files_edit_home',
806 rmap.connect('files_edit_home',
821 '/{repo_name}/edit/{revision}/{f_path}',
807 '/{repo_name}/edit/{revision}/{f_path}',
822 controller='files', action='edit_home', revision='tip',
808 controller='files', action='edit_home', revision='tip',
823 f_path='', conditions={'function': check_repo},
809 f_path='', conditions={'function': check_repo},
824 requirements=URL_NAME_REQUIREMENTS)
810 requirements=URL_NAME_REQUIREMENTS)
825
811
826 rmap.connect('files_add',
812 rmap.connect('files_add',
827 '/{repo_name}/add/{revision}/{f_path}',
813 '/{repo_name}/add/{revision}/{f_path}',
828 controller='files', action='add', revision='tip',
814 controller='files', action='add', revision='tip',
829 f_path='',
815 f_path='',
830 conditions={'function': check_repo, 'method': ['POST']},
816 conditions={'function': check_repo, 'method': ['POST']},
831 requirements=URL_NAME_REQUIREMENTS)
817 requirements=URL_NAME_REQUIREMENTS)
832
818
833 rmap.connect('files_add_home',
819 rmap.connect('files_add_home',
834 '/{repo_name}/add/{revision}/{f_path}',
820 '/{repo_name}/add/{revision}/{f_path}',
835 controller='files', action='add_home', revision='tip',
821 controller='files', action='add_home', revision='tip',
836 f_path='', conditions={'function': check_repo},
822 f_path='', conditions={'function': check_repo},
837 requirements=URL_NAME_REQUIREMENTS)
823 requirements=URL_NAME_REQUIREMENTS)
838
824
839 rmap.connect('files_delete',
825 rmap.connect('files_delete',
840 '/{repo_name}/delete/{revision}/{f_path}',
826 '/{repo_name}/delete/{revision}/{f_path}',
841 controller='files', action='delete', revision='tip',
827 controller='files', action='delete', revision='tip',
842 f_path='',
828 f_path='',
843 conditions={'function': check_repo, 'method': ['POST']},
829 conditions={'function': check_repo, 'method': ['POST']},
844 requirements=URL_NAME_REQUIREMENTS)
830 requirements=URL_NAME_REQUIREMENTS)
845
831
846 rmap.connect('files_delete_home',
832 rmap.connect('files_delete_home',
847 '/{repo_name}/delete/{revision}/{f_path}',
833 '/{repo_name}/delete/{revision}/{f_path}',
848 controller='files', action='delete_home', revision='tip',
834 controller='files', action='delete_home', revision='tip',
849 f_path='', conditions={'function': check_repo},
835 f_path='', conditions={'function': check_repo},
850 requirements=URL_NAME_REQUIREMENTS)
836 requirements=URL_NAME_REQUIREMENTS)
851
837
852 rmap.connect('files_archive_home', '/{repo_name}/archive/{fname}',
838 rmap.connect('files_archive_home', '/{repo_name}/archive/{fname}',
853 controller='files', action='archivefile',
839 controller='files', action='archivefile',
854 conditions={'function': check_repo},
840 conditions={'function': check_repo},
855 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
841 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
856
842
857 rmap.connect('files_nodelist_home',
843 rmap.connect('files_nodelist_home',
858 '/{repo_name}/nodelist/{revision}/{f_path}',
844 '/{repo_name}/nodelist/{revision}/{f_path}',
859 controller='files', action='nodelist',
845 controller='files', action='nodelist',
860 conditions={'function': check_repo},
846 conditions={'function': check_repo},
861 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
847 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
862
848
863 rmap.connect('files_nodetree_full',
849 rmap.connect('files_nodetree_full',
864 '/{repo_name}/nodetree_full/{commit_id}/{f_path}',
850 '/{repo_name}/nodetree_full/{commit_id}/{f_path}',
865 controller='files', action='nodetree_full',
851 controller='files', action='nodetree_full',
866 conditions={'function': check_repo},
852 conditions={'function': check_repo},
867 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
853 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
868
854
869 rmap.connect('repo_fork_create_home', '/{repo_name}/fork',
855 rmap.connect('repo_fork_create_home', '/{repo_name}/fork',
870 controller='forks', action='fork_create',
856 controller='forks', action='fork_create',
871 conditions={'function': check_repo, 'method': ['POST']},
857 conditions={'function': check_repo, 'method': ['POST']},
872 requirements=URL_NAME_REQUIREMENTS)
858 requirements=URL_NAME_REQUIREMENTS)
873
859
874 rmap.connect('repo_fork_home', '/{repo_name}/fork',
860 rmap.connect('repo_fork_home', '/{repo_name}/fork',
875 controller='forks', action='fork',
861 controller='forks', action='fork',
876 conditions={'function': check_repo},
862 conditions={'function': check_repo},
877 requirements=URL_NAME_REQUIREMENTS)
863 requirements=URL_NAME_REQUIREMENTS)
878
864
879 rmap.connect('repo_forks_home', '/{repo_name}/forks',
865 rmap.connect('repo_forks_home', '/{repo_name}/forks',
880 controller='forks', action='forks',
866 controller='forks', action='forks',
881 conditions={'function': check_repo},
867 conditions={'function': check_repo},
882 requirements=URL_NAME_REQUIREMENTS)
868 requirements=URL_NAME_REQUIREMENTS)
883
869
884 return rmap
870 return rmap
@@ -1,379 +1,381 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2017 RhodeCode GmbH
3 # Copyright (C) 2011-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21
21
22 """
22 """
23 Model for notifications
23 Model for notifications
24 """
24 """
25
25
26
26
27 import logging
27 import logging
28 import traceback
28 import traceback
29
29
30 from pylons.i18n.translation import _, ungettext
30 from pylons.i18n.translation import _, ungettext
31 from sqlalchemy.sql.expression import false, true
31 from sqlalchemy.sql.expression import false, true
32 from mako import exceptions
32 from mako import exceptions
33
33
34 import rhodecode
34 import rhodecode
35 from rhodecode.lib import helpers as h
35 from rhodecode.lib import helpers as h
36 from rhodecode.lib.utils import PartialRenderer
36 from rhodecode.lib.utils import PartialRenderer
37 from rhodecode.model import BaseModel
37 from rhodecode.model import BaseModel
38 from rhodecode.model.db import Notification, User, UserNotification
38 from rhodecode.model.db import Notification, User, UserNotification
39 from rhodecode.model.meta import Session
39 from rhodecode.model.meta import Session
40 from rhodecode.model.settings import SettingsModel
40 from rhodecode.model.settings import SettingsModel
41 from rhodecode.translation import TranslationString
41 from rhodecode.translation import TranslationString
42
42
43 log = logging.getLogger(__name__)
43 log = logging.getLogger(__name__)
44
44
45
45
46 class NotificationModel(BaseModel):
46 class NotificationModel(BaseModel):
47
47
48 cls = Notification
48 cls = Notification
49
49
50 def __get_notification(self, notification):
50 def __get_notification(self, notification):
51 if isinstance(notification, Notification):
51 if isinstance(notification, Notification):
52 return notification
52 return notification
53 elif isinstance(notification, (int, long)):
53 elif isinstance(notification, (int, long)):
54 return Notification.get(notification)
54 return Notification.get(notification)
55 else:
55 else:
56 if notification:
56 if notification:
57 raise Exception('notification must be int, long or Instance'
57 raise Exception('notification must be int, long or Instance'
58 ' of Notification got %s' % type(notification))
58 ' of Notification got %s' % type(notification))
59
59
60 def create(
60 def create(
61 self, created_by, notification_subject, notification_body,
61 self, created_by, notification_subject, notification_body,
62 notification_type=Notification.TYPE_MESSAGE, recipients=None,
62 notification_type=Notification.TYPE_MESSAGE, recipients=None,
63 mention_recipients=None, with_email=True, email_kwargs=None):
63 mention_recipients=None, with_email=True, email_kwargs=None):
64 """
64 """
65
65
66 Creates notification of given type
66 Creates notification of given type
67
67
68 :param created_by: int, str or User instance. User who created this
68 :param created_by: int, str or User instance. User who created this
69 notification
69 notification
70 :param notification_subject: subject of notification itself
70 :param notification_subject: subject of notification itself
71 :param notification_body: body of notification text
71 :param notification_body: body of notification text
72 :param notification_type: type of notification, based on that we
72 :param notification_type: type of notification, based on that we
73 pick templates
73 pick templates
74
74
75 :param recipients: list of int, str or User objects, when None
75 :param recipients: list of int, str or User objects, when None
76 is given send to all admins
76 is given send to all admins
77 :param mention_recipients: list of int, str or User objects,
77 :param mention_recipients: list of int, str or User objects,
78 that were mentioned
78 that were mentioned
79 :param with_email: send email with this notification
79 :param with_email: send email with this notification
80 :param email_kwargs: dict with arguments to generate email
80 :param email_kwargs: dict with arguments to generate email
81 """
81 """
82
82
83 from rhodecode.lib.celerylib import tasks, run_task
83 from rhodecode.lib.celerylib import tasks, run_task
84
84
85 if recipients and not getattr(recipients, '__iter__', False):
85 if recipients and not getattr(recipients, '__iter__', False):
86 raise Exception('recipients must be an iterable object')
86 raise Exception('recipients must be an iterable object')
87
87
88 created_by_obj = self._get_user(created_by)
88 created_by_obj = self._get_user(created_by)
89 # default MAIN body if not given
89 # default MAIN body if not given
90 email_kwargs = email_kwargs or {'body': notification_body}
90 email_kwargs = email_kwargs or {'body': notification_body}
91 mention_recipients = mention_recipients or set()
91 mention_recipients = mention_recipients or set()
92
92
93 if not created_by_obj:
93 if not created_by_obj:
94 raise Exception('unknown user %s' % created_by)
94 raise Exception('unknown user %s' % created_by)
95
95
96 if recipients is None:
96 if recipients is None:
97 # recipients is None means to all admins
97 # recipients is None means to all admins
98 recipients_objs = User.query().filter(User.admin == true()).all()
98 recipients_objs = User.query().filter(User.admin == true()).all()
99 log.debug('sending notifications %s to admins: %s',
99 log.debug('sending notifications %s to admins: %s',
100 notification_type, recipients_objs)
100 notification_type, recipients_objs)
101 else:
101 else:
102 recipients_objs = []
102 recipients_objs = []
103 for u in recipients:
103 for u in recipients:
104 obj = self._get_user(u)
104 obj = self._get_user(u)
105 if obj:
105 if obj:
106 recipients_objs.append(obj)
106 recipients_objs.append(obj)
107 else: # we didn't find this user, log the error and carry on
107 else: # we didn't find this user, log the error and carry on
108 log.error('cannot notify unknown user %r', u)
108 log.error('cannot notify unknown user %r', u)
109
109
110 recipients_objs = set(recipients_objs)
110 recipients_objs = set(recipients_objs)
111 if not recipients_objs:
111 if not recipients_objs:
112 raise Exception('no valid recipients specified')
112 raise Exception('no valid recipients specified')
113
113
114 log.debug('sending notifications %s to %s',
114 log.debug('sending notifications %s to %s',
115 notification_type, recipients_objs)
115 notification_type, recipients_objs)
116
116
117 # add mentioned users into recipients
117 # add mentioned users into recipients
118 final_recipients = set(recipients_objs).union(mention_recipients)
118 final_recipients = set(recipients_objs).union(mention_recipients)
119 notification = Notification.create(
119 notification = Notification.create(
120 created_by=created_by_obj, subject=notification_subject,
120 created_by=created_by_obj, subject=notification_subject,
121 body=notification_body, recipients=final_recipients,
121 body=notification_body, recipients=final_recipients,
122 type_=notification_type
122 type_=notification_type
123 )
123 )
124
124
125 if not with_email: # skip sending email, and just create notification
125 if not with_email: # skip sending email, and just create notification
126 return notification
126 return notification
127
127
128 # don't send email to person who created this comment
128 # don't send email to person who created this comment
129 rec_objs = set(recipients_objs).difference(set([created_by_obj]))
129 rec_objs = set(recipients_objs).difference(set([created_by_obj]))
130
130
131 # now notify all recipients in question
131 # now notify all recipients in question
132
132
133 for recipient in rec_objs.union(mention_recipients):
133 for recipient in rec_objs.union(mention_recipients):
134 # inject current recipient
134 # inject current recipient
135 email_kwargs['recipient'] = recipient
135 email_kwargs['recipient'] = recipient
136 email_kwargs['mention'] = recipient in mention_recipients
136 email_kwargs['mention'] = recipient in mention_recipients
137 (subject, headers, email_body,
137 (subject, headers, email_body,
138 email_body_plaintext) = EmailNotificationModel().render_email(
138 email_body_plaintext) = EmailNotificationModel().render_email(
139 notification_type, **email_kwargs)
139 notification_type, **email_kwargs)
140
140
141 log.debug(
141 log.debug(
142 'Creating notification email task for user:`%s`', recipient)
142 'Creating notification email task for user:`%s`', recipient)
143 task = run_task(
143 task = run_task(
144 tasks.send_email, recipient.email, subject,
144 tasks.send_email, recipient.email, subject,
145 email_body_plaintext, email_body)
145 email_body_plaintext, email_body)
146 log.debug('Created email task: %s', task)
146 log.debug('Created email task: %s', task)
147
147
148 return notification
148 return notification
149
149
150 def delete(self, user, notification):
150 def delete(self, user, notification):
151 # we don't want to remove actual notification just the assignment
151 # we don't want to remove actual notification just the assignment
152 try:
152 try:
153 notification = self.__get_notification(notification)
153 notification = self.__get_notification(notification)
154 user = self._get_user(user)
154 user = self._get_user(user)
155 if notification and user:
155 if notification and user:
156 obj = UserNotification.query()\
156 obj = UserNotification.query()\
157 .filter(UserNotification.user == user)\
157 .filter(UserNotification.user == user)\
158 .filter(UserNotification.notification == notification)\
158 .filter(UserNotification.notification == notification)\
159 .one()
159 .one()
160 Session().delete(obj)
160 Session().delete(obj)
161 return True
161 return True
162 except Exception:
162 except Exception:
163 log.error(traceback.format_exc())
163 log.error(traceback.format_exc())
164 raise
164 raise
165
165
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
175 q = UserNotification.query()\
172 q = UserNotification.query()\
176 .filter(UserNotification.user == user)\
173 .filter(UserNotification.user == user)\
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:
188 notification = self.__get_notification(notification)
188 notification = self.__get_notification(notification)
189 user = self._get_user(user)
189 user = self._get_user(user)
190 if notification and user:
190 if notification and user:
191 obj = UserNotification.query()\
191 obj = UserNotification.query()\
192 .filter(UserNotification.user == user)\
192 .filter(UserNotification.user == user)\
193 .filter(UserNotification.notification == notification)\
193 .filter(UserNotification.notification == notification)\
194 .one()
194 .one()
195 obj.read = True
195 obj.read = True
196 Session().add(obj)
196 Session().add(obj)
197 return True
197 return True
198 except Exception:
198 except Exception:
199 log.error(traceback.format_exc())
199 log.error(traceback.format_exc())
200 raise
200 raise
201
201
202 def mark_all_read_for_user(self, user, filter_=None):
202 def mark_all_read_for_user(self, user, filter_=None):
203 user = self._get_user(user)
203 user = self._get_user(user)
204 q = UserNotification.query()\
204 q = UserNotification.query()\
205 .filter(UserNotification.user == user)\
205 .filter(UserNotification.user == user)\
206 .filter(UserNotification.read == false())\
206 .filter(UserNotification.read == false())\
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
214 # update on joined tables :(
216 # update on joined tables :(
215 for obj in q.all():
217 for obj in q.all():
216 obj.read = True
218 obj.read = True
217 Session().add(obj)
219 Session().add(obj)
218
220
219 def get_unread_cnt_for_user(self, user):
221 def get_unread_cnt_for_user(self, user):
220 user = self._get_user(user)
222 user = self._get_user(user)
221 return UserNotification.query()\
223 return UserNotification.query()\
222 .filter(UserNotification.read == false())\
224 .filter(UserNotification.read == false())\
223 .filter(UserNotification.user == user).count()
225 .filter(UserNotification.user == user).count()
224
226
225 def get_unread_for_user(self, user):
227 def get_unread_for_user(self, user):
226 user = self._get_user(user)
228 user = self._get_user(user)
227 return [x.notification for x in UserNotification.query()
229 return [x.notification for x in UserNotification.query()
228 .filter(UserNotification.read == false())
230 .filter(UserNotification.read == false())
229 .filter(UserNotification.user == user).all()]
231 .filter(UserNotification.user == user).all()]
230
232
231 def get_user_notification(self, user, notification):
233 def get_user_notification(self, user, notification):
232 user = self._get_user(user)
234 user = self._get_user(user)
233 notification = self.__get_notification(notification)
235 notification = self.__get_notification(notification)
234
236
235 return UserNotification.query()\
237 return UserNotification.query()\
236 .filter(UserNotification.notification == notification)\
238 .filter(UserNotification.notification == notification)\
237 .filter(UserNotification.user == user).scalar()
239 .filter(UserNotification.user == user).scalar()
238
240
239 def make_description(self, notification, show_age=True, translate=None):
241 def make_description(self, notification, show_age=True, translate=None):
240 """
242 """
241 Creates a human readable description based on properties
243 Creates a human readable description based on properties
242 of notification object
244 of notification object
243 """
245 """
244
246
245 _map = {
247 _map = {
246 notification.TYPE_CHANGESET_COMMENT: [
248 notification.TYPE_CHANGESET_COMMENT: [
247 _('%(user)s commented on commit %(date_or_age)s'),
249 _('%(user)s commented on commit %(date_or_age)s'),
248 _('%(user)s commented on commit at %(date_or_age)s'),
250 _('%(user)s commented on commit at %(date_or_age)s'),
249 ],
251 ],
250 notification.TYPE_MESSAGE: [
252 notification.TYPE_MESSAGE: [
251 _('%(user)s sent message %(date_or_age)s'),
253 _('%(user)s sent message %(date_or_age)s'),
252 _('%(user)s sent message at %(date_or_age)s'),
254 _('%(user)s sent message at %(date_or_age)s'),
253 ],
255 ],
254 notification.TYPE_MENTION: [
256 notification.TYPE_MENTION: [
255 _('%(user)s mentioned you %(date_or_age)s'),
257 _('%(user)s mentioned you %(date_or_age)s'),
256 _('%(user)s mentioned you at %(date_or_age)s'),
258 _('%(user)s mentioned you at %(date_or_age)s'),
257 ],
259 ],
258 notification.TYPE_REGISTRATION: [
260 notification.TYPE_REGISTRATION: [
259 _('%(user)s registered in RhodeCode %(date_or_age)s'),
261 _('%(user)s registered in RhodeCode %(date_or_age)s'),
260 _('%(user)s registered in RhodeCode at %(date_or_age)s'),
262 _('%(user)s registered in RhodeCode at %(date_or_age)s'),
261 ],
263 ],
262 notification.TYPE_PULL_REQUEST: [
264 notification.TYPE_PULL_REQUEST: [
263 _('%(user)s opened new pull request %(date_or_age)s'),
265 _('%(user)s opened new pull request %(date_or_age)s'),
264 _('%(user)s opened new pull request at %(date_or_age)s'),
266 _('%(user)s opened new pull request at %(date_or_age)s'),
265 ],
267 ],
266 notification.TYPE_PULL_REQUEST_COMMENT: [
268 notification.TYPE_PULL_REQUEST_COMMENT: [
267 _('%(user)s commented on pull request %(date_or_age)s'),
269 _('%(user)s commented on pull request %(date_or_age)s'),
268 _('%(user)s commented on pull request at %(date_or_age)s'),
270 _('%(user)s commented on pull request at %(date_or_age)s'),
269 ],
271 ],
270 }
272 }
271
273
272 templates = _map[notification.type_]
274 templates = _map[notification.type_]
273
275
274 if show_age:
276 if show_age:
275 template = templates[0]
277 template = templates[0]
276 date_or_age = h.age(notification.created_on)
278 date_or_age = h.age(notification.created_on)
277 if translate:
279 if translate:
278 date_or_age = translate(date_or_age)
280 date_or_age = translate(date_or_age)
279
281
280 if isinstance(date_or_age, TranslationString):
282 if isinstance(date_or_age, TranslationString):
281 date_or_age = date_or_age.interpolate()
283 date_or_age = date_or_age.interpolate()
282
284
283 else:
285 else:
284 template = templates[1]
286 template = templates[1]
285 date_or_age = h.format_date(notification.created_on)
287 date_or_age = h.format_date(notification.created_on)
286
288
287 return template % {
289 return template % {
288 'user': notification.created_by_user.username,
290 'user': notification.created_by_user.username,
289 'date_or_age': date_or_age,
291 'date_or_age': date_or_age,
290 }
292 }
291
293
292
294
293 class EmailNotificationModel(BaseModel):
295 class EmailNotificationModel(BaseModel):
294 TYPE_COMMIT_COMMENT = Notification.TYPE_CHANGESET_COMMENT
296 TYPE_COMMIT_COMMENT = Notification.TYPE_CHANGESET_COMMENT
295 TYPE_REGISTRATION = Notification.TYPE_REGISTRATION
297 TYPE_REGISTRATION = Notification.TYPE_REGISTRATION
296 TYPE_PULL_REQUEST = Notification.TYPE_PULL_REQUEST
298 TYPE_PULL_REQUEST = Notification.TYPE_PULL_REQUEST
297 TYPE_PULL_REQUEST_COMMENT = Notification.TYPE_PULL_REQUEST_COMMENT
299 TYPE_PULL_REQUEST_COMMENT = Notification.TYPE_PULL_REQUEST_COMMENT
298 TYPE_MAIN = Notification.TYPE_MESSAGE
300 TYPE_MAIN = Notification.TYPE_MESSAGE
299
301
300 TYPE_PASSWORD_RESET = 'password_reset'
302 TYPE_PASSWORD_RESET = 'password_reset'
301 TYPE_PASSWORD_RESET_CONFIRMATION = 'password_reset_confirmation'
303 TYPE_PASSWORD_RESET_CONFIRMATION = 'password_reset_confirmation'
302 TYPE_EMAIL_TEST = 'email_test'
304 TYPE_EMAIL_TEST = 'email_test'
303 TYPE_TEST = 'test'
305 TYPE_TEST = 'test'
304
306
305 email_types = {
307 email_types = {
306 TYPE_MAIN: 'email_templates/main.mako',
308 TYPE_MAIN: 'email_templates/main.mako',
307 TYPE_TEST: 'email_templates/test.mako',
309 TYPE_TEST: 'email_templates/test.mako',
308 TYPE_EMAIL_TEST: 'email_templates/email_test.mako',
310 TYPE_EMAIL_TEST: 'email_templates/email_test.mako',
309 TYPE_REGISTRATION: 'email_templates/user_registration.mako',
311 TYPE_REGISTRATION: 'email_templates/user_registration.mako',
310 TYPE_PASSWORD_RESET: 'email_templates/password_reset.mako',
312 TYPE_PASSWORD_RESET: 'email_templates/password_reset.mako',
311 TYPE_PASSWORD_RESET_CONFIRMATION: 'email_templates/password_reset_confirmation.mako',
313 TYPE_PASSWORD_RESET_CONFIRMATION: 'email_templates/password_reset_confirmation.mako',
312 TYPE_COMMIT_COMMENT: 'email_templates/commit_comment.mako',
314 TYPE_COMMIT_COMMENT: 'email_templates/commit_comment.mako',
313 TYPE_PULL_REQUEST: 'email_templates/pull_request_review.mako',
315 TYPE_PULL_REQUEST: 'email_templates/pull_request_review.mako',
314 TYPE_PULL_REQUEST_COMMENT: 'email_templates/pull_request_comment.mako',
316 TYPE_PULL_REQUEST_COMMENT: 'email_templates/pull_request_comment.mako',
315 }
317 }
316
318
317 def __init__(self):
319 def __init__(self):
318 """
320 """
319 Example usage::
321 Example usage::
320
322
321 (subject, headers, email_body,
323 (subject, headers, email_body,
322 email_body_plaintext) = EmailNotificationModel().render_email(
324 email_body_plaintext) = EmailNotificationModel().render_email(
323 EmailNotificationModel.TYPE_TEST, **email_kwargs)
325 EmailNotificationModel.TYPE_TEST, **email_kwargs)
324
326
325 """
327 """
326 super(EmailNotificationModel, self).__init__()
328 super(EmailNotificationModel, self).__init__()
327 self.rhodecode_instance_name = rhodecode.CONFIG.get('rhodecode_title')
329 self.rhodecode_instance_name = rhodecode.CONFIG.get('rhodecode_title')
328
330
329 def _update_kwargs_for_render(self, kwargs):
331 def _update_kwargs_for_render(self, kwargs):
330 """
332 """
331 Inject params required for Mako rendering
333 Inject params required for Mako rendering
332
334
333 :param kwargs:
335 :param kwargs:
334 """
336 """
335
337
336 kwargs['rhodecode_instance_name'] = self.rhodecode_instance_name
338 kwargs['rhodecode_instance_name'] = self.rhodecode_instance_name
337 instance_url = h.route_url('home')
339 instance_url = h.route_url('home')
338 _kwargs = {
340 _kwargs = {
339 'instance_url': instance_url,
341 'instance_url': instance_url,
340 'whitespace_filter': self.whitespace_filter
342 'whitespace_filter': self.whitespace_filter
341 }
343 }
342 _kwargs.update(kwargs)
344 _kwargs.update(kwargs)
343 return _kwargs
345 return _kwargs
344
346
345 def whitespace_filter(self, text):
347 def whitespace_filter(self, text):
346 return text.replace('\n', '').replace('\t', '')
348 return text.replace('\n', '').replace('\t', '')
347
349
348 def get_renderer(self, type_):
350 def get_renderer(self, type_):
349 template_name = self.email_types[type_]
351 template_name = self.email_types[type_]
350 return PartialRenderer(template_name)
352 return PartialRenderer(template_name)
351
353
352 def render_email(self, type_, **kwargs):
354 def render_email(self, type_, **kwargs):
353 """
355 """
354 renders template for email, and returns a tuple of
356 renders template for email, and returns a tuple of
355 (subject, email_headers, email_html_body, email_plaintext_body)
357 (subject, email_headers, email_html_body, email_plaintext_body)
356 """
358 """
357 # translator and helpers inject
359 # translator and helpers inject
358 _kwargs = self._update_kwargs_for_render(kwargs)
360 _kwargs = self._update_kwargs_for_render(kwargs)
359
361
360 email_template = self.get_renderer(type_)
362 email_template = self.get_renderer(type_)
361
363
362 subject = email_template.render('subject', **_kwargs)
364 subject = email_template.render('subject', **_kwargs)
363
365
364 try:
366 try:
365 headers = email_template.render('headers', **_kwargs)
367 headers = email_template.render('headers', **_kwargs)
366 except AttributeError:
368 except AttributeError:
367 # it's not defined in template, ok we can skip it
369 # it's not defined in template, ok we can skip it
368 headers = ''
370 headers = ''
369
371
370 try:
372 try:
371 body_plaintext = email_template.render('body_plaintext', **_kwargs)
373 body_plaintext = email_template.render('body_plaintext', **_kwargs)
372 except AttributeError:
374 except AttributeError:
373 # it's not defined in template, ok we can skip it
375 # it's not defined in template, ok we can skip it
374 body_plaintext = ''
376 body_plaintext = ''
375
377
376 # render WHOLE template
378 # render WHOLE template
377 body = email_template.render(None, **_kwargs)
379 body = email_template.render(None, **_kwargs)
378
380
379 return subject, headers, body, body_plaintext
381 return subject, headers, body, body_plaintext
@@ -1,178 +1,183 b''
1
1
2 /******************************************************************************
2 /******************************************************************************
3 * *
3 * *
4 * DO NOT CHANGE THIS FILE MANUALLY *
4 * DO NOT CHANGE THIS FILE MANUALLY *
5 * *
5 * *
6 * *
6 * *
7 * This file is automatically generated when the app starts up with *
7 * This file is automatically generated when the app starts up with *
8 * generate_js_files = true *
8 * generate_js_files = true *
9 * *
9 * *
10 * To add a route here pass jsroute=True to the route definition in the app *
10 * To add a route here pass jsroute=True to the route definition in the app *
11 * *
11 * *
12 ******************************************************************************/
12 ******************************************************************************/
13 function registerRCRoutes() {
13 function registerRCRoutes() {
14 // routes registration
14 // routes registration
15 pyroutes.register('new_repo', '/_admin/create_repository', []);
15 pyroutes.register('new_repo', '/_admin/create_repository', []);
16 pyroutes.register('edit_user', '/_admin/users/%(user_id)s/edit', ['user_id']);
16 pyroutes.register('edit_user', '/_admin/users/%(user_id)s/edit', ['user_id']);
17 pyroutes.register('edit_user_group_members', '/_admin/user_groups/%(user_group_id)s/edit/members', ['user_group_id']);
17 pyroutes.register('edit_user_group_members', '/_admin/user_groups/%(user_group_id)s/edit/members', ['user_group_id']);
18 pyroutes.register('toggle_following', '/_admin/toggle_following', []);
18 pyroutes.register('toggle_following', '/_admin/toggle_following', []);
19 pyroutes.register('changeset_home', '/%(repo_name)s/changeset/%(revision)s', ['repo_name', 'revision']);
19 pyroutes.register('changeset_home', '/%(repo_name)s/changeset/%(revision)s', ['repo_name', 'revision']);
20 pyroutes.register('changeset_comment', '/%(repo_name)s/changeset/%(revision)s/comment', ['repo_name', 'revision']);
20 pyroutes.register('changeset_comment', '/%(repo_name)s/changeset/%(revision)s/comment', ['repo_name', 'revision']);
21 pyroutes.register('changeset_comment_preview', '/%(repo_name)s/changeset/comment/preview', ['repo_name']);
21 pyroutes.register('changeset_comment_preview', '/%(repo_name)s/changeset/comment/preview', ['repo_name']);
22 pyroutes.register('changeset_comment_delete', '/%(repo_name)s/changeset/comment/%(comment_id)s/delete', ['repo_name', 'comment_id']);
22 pyroutes.register('changeset_comment_delete', '/%(repo_name)s/changeset/comment/%(comment_id)s/delete', ['repo_name', 'comment_id']);
23 pyroutes.register('changeset_info', '/%(repo_name)s/changeset_info/%(revision)s', ['repo_name', 'revision']);
23 pyroutes.register('changeset_info', '/%(repo_name)s/changeset_info/%(revision)s', ['repo_name', 'revision']);
24 pyroutes.register('compare_url', '/%(repo_name)s/compare/%(source_ref_type)s@%(source_ref)s...%(target_ref_type)s@%(target_ref)s', ['repo_name', 'source_ref_type', 'source_ref', 'target_ref_type', 'target_ref']);
24 pyroutes.register('compare_url', '/%(repo_name)s/compare/%(source_ref_type)s@%(source_ref)s...%(target_ref_type)s@%(target_ref)s', ['repo_name', 'source_ref_type', 'source_ref', 'target_ref_type', 'target_ref']);
25 pyroutes.register('pullrequest_home', '/%(repo_name)s/pull-request/new', ['repo_name']);
25 pyroutes.register('pullrequest_home', '/%(repo_name)s/pull-request/new', ['repo_name']);
26 pyroutes.register('pullrequest', '/%(repo_name)s/pull-request/new', ['repo_name']);
26 pyroutes.register('pullrequest', '/%(repo_name)s/pull-request/new', ['repo_name']);
27 pyroutes.register('pullrequest_repo_refs', '/%(repo_name)s/pull-request/refs/%(target_repo_name)s', ['repo_name', 'target_repo_name']);
27 pyroutes.register('pullrequest_repo_refs', '/%(repo_name)s/pull-request/refs/%(target_repo_name)s', ['repo_name', 'target_repo_name']);
28 pyroutes.register('pullrequest_repo_destinations', '/%(repo_name)s/pull-request/repo-destinations', ['repo_name']);
28 pyroutes.register('pullrequest_repo_destinations', '/%(repo_name)s/pull-request/repo-destinations', ['repo_name']);
29 pyroutes.register('pullrequest_show', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
29 pyroutes.register('pullrequest_show', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
30 pyroutes.register('pullrequest_update', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
30 pyroutes.register('pullrequest_update', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
31 pyroutes.register('pullrequest_comment', '/%(repo_name)s/pull-request-comment/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
31 pyroutes.register('pullrequest_comment', '/%(repo_name)s/pull-request-comment/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
32 pyroutes.register('pullrequest_comment_delete', '/%(repo_name)s/pull-request-comment/%(comment_id)s/delete', ['repo_name', 'comment_id']);
32 pyroutes.register('pullrequest_comment_delete', '/%(repo_name)s/pull-request-comment/%(comment_id)s/delete', ['repo_name', 'comment_id']);
33 pyroutes.register('changelog_home', '/%(repo_name)s/changelog', ['repo_name']);
33 pyroutes.register('changelog_home', '/%(repo_name)s/changelog', ['repo_name']);
34 pyroutes.register('changelog_file_home', '/%(repo_name)s/changelog/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
34 pyroutes.register('changelog_file_home', '/%(repo_name)s/changelog/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
35 pyroutes.register('changelog_elements', '/%(repo_name)s/changelog_details', ['repo_name']);
35 pyroutes.register('changelog_elements', '/%(repo_name)s/changelog_details', ['repo_name']);
36 pyroutes.register('files_home', '/%(repo_name)s/files/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
36 pyroutes.register('files_home', '/%(repo_name)s/files/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
37 pyroutes.register('files_history_home', '/%(repo_name)s/history/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
37 pyroutes.register('files_history_home', '/%(repo_name)s/history/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
38 pyroutes.register('files_authors_home', '/%(repo_name)s/authors/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
38 pyroutes.register('files_authors_home', '/%(repo_name)s/authors/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
39 pyroutes.register('files_annotate_home', '/%(repo_name)s/annotate/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
39 pyroutes.register('files_annotate_home', '/%(repo_name)s/annotate/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
40 pyroutes.register('files_annotate_previous', '/%(repo_name)s/annotate-previous/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
40 pyroutes.register('files_annotate_previous', '/%(repo_name)s/annotate-previous/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
41 pyroutes.register('files_archive_home', '/%(repo_name)s/archive/%(fname)s', ['repo_name', 'fname']);
41 pyroutes.register('files_archive_home', '/%(repo_name)s/archive/%(fname)s', ['repo_name', 'fname']);
42 pyroutes.register('files_nodelist_home', '/%(repo_name)s/nodelist/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
42 pyroutes.register('files_nodelist_home', '/%(repo_name)s/nodelist/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
43 pyroutes.register('files_nodetree_full', '/%(repo_name)s/nodetree_full/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
43 pyroutes.register('files_nodetree_full', '/%(repo_name)s/nodetree_full/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
44 pyroutes.register('favicon', '/favicon.ico', []);
44 pyroutes.register('favicon', '/favicon.ico', []);
45 pyroutes.register('robots', '/robots.txt', []);
45 pyroutes.register('robots', '/robots.txt', []);
46 pyroutes.register('auth_home', '/_admin/auth*traverse', []);
46 pyroutes.register('auth_home', '/_admin/auth*traverse', []);
47 pyroutes.register('global_integrations_new', '/_admin/integrations/new', []);
47 pyroutes.register('global_integrations_new', '/_admin/integrations/new', []);
48 pyroutes.register('global_integrations_home', '/_admin/integrations', []);
48 pyroutes.register('global_integrations_home', '/_admin/integrations', []);
49 pyroutes.register('global_integrations_list', '/_admin/integrations/%(integration)s', ['integration']);
49 pyroutes.register('global_integrations_list', '/_admin/integrations/%(integration)s', ['integration']);
50 pyroutes.register('global_integrations_create', '/_admin/integrations/%(integration)s/new', ['integration']);
50 pyroutes.register('global_integrations_create', '/_admin/integrations/%(integration)s/new', ['integration']);
51 pyroutes.register('global_integrations_edit', '/_admin/integrations/%(integration)s/%(integration_id)s', ['integration', 'integration_id']);
51 pyroutes.register('global_integrations_edit', '/_admin/integrations/%(integration)s/%(integration_id)s', ['integration', 'integration_id']);
52 pyroutes.register('repo_group_integrations_home', '/%(repo_group_name)s/settings/integrations', ['repo_group_name']);
52 pyroutes.register('repo_group_integrations_home', '/%(repo_group_name)s/settings/integrations', ['repo_group_name']);
53 pyroutes.register('repo_group_integrations_list', '/%(repo_group_name)s/settings/integrations/%(integration)s', ['repo_group_name', 'integration']);
53 pyroutes.register('repo_group_integrations_list', '/%(repo_group_name)s/settings/integrations/%(integration)s', ['repo_group_name', 'integration']);
54 pyroutes.register('repo_group_integrations_new', '/%(repo_group_name)s/settings/integrations/new', ['repo_group_name']);
54 pyroutes.register('repo_group_integrations_new', '/%(repo_group_name)s/settings/integrations/new', ['repo_group_name']);
55 pyroutes.register('repo_group_integrations_create', '/%(repo_group_name)s/settings/integrations/%(integration)s/new', ['repo_group_name', 'integration']);
55 pyroutes.register('repo_group_integrations_create', '/%(repo_group_name)s/settings/integrations/%(integration)s/new', ['repo_group_name', 'integration']);
56 pyroutes.register('repo_group_integrations_edit', '/%(repo_group_name)s/settings/integrations/%(integration)s/%(integration_id)s', ['repo_group_name', 'integration', 'integration_id']);
56 pyroutes.register('repo_group_integrations_edit', '/%(repo_group_name)s/settings/integrations/%(integration)s/%(integration_id)s', ['repo_group_name', 'integration', 'integration_id']);
57 pyroutes.register('repo_integrations_home', '/%(repo_name)s/settings/integrations', ['repo_name']);
57 pyroutes.register('repo_integrations_home', '/%(repo_name)s/settings/integrations', ['repo_name']);
58 pyroutes.register('repo_integrations_list', '/%(repo_name)s/settings/integrations/%(integration)s', ['repo_name', 'integration']);
58 pyroutes.register('repo_integrations_list', '/%(repo_name)s/settings/integrations/%(integration)s', ['repo_name', 'integration']);
59 pyroutes.register('repo_integrations_new', '/%(repo_name)s/settings/integrations/new', ['repo_name']);
59 pyroutes.register('repo_integrations_new', '/%(repo_name)s/settings/integrations/new', ['repo_name']);
60 pyroutes.register('repo_integrations_create', '/%(repo_name)s/settings/integrations/%(integration)s/new', ['repo_name', 'integration']);
60 pyroutes.register('repo_integrations_create', '/%(repo_name)s/settings/integrations/%(integration)s/new', ['repo_name', 'integration']);
61 pyroutes.register('repo_integrations_edit', '/%(repo_name)s/settings/integrations/%(integration)s/%(integration_id)s', ['repo_name', 'integration', 'integration_id']);
61 pyroutes.register('repo_integrations_edit', '/%(repo_name)s/settings/integrations/%(integration)s/%(integration_id)s', ['repo_name', 'integration', 'integration_id']);
62 pyroutes.register('ops_ping', '/_admin/ops/ping', []);
62 pyroutes.register('ops_ping', '/_admin/ops/ping', []);
63 pyroutes.register('ops_error_test', '/_admin/ops/error', []);
63 pyroutes.register('ops_error_test', '/_admin/ops/error', []);
64 pyroutes.register('ops_redirect_test', '/_admin/ops/redirect', []);
64 pyroutes.register('ops_redirect_test', '/_admin/ops/redirect', []);
65 pyroutes.register('admin_home', '/_admin', []);
65 pyroutes.register('admin_home', '/_admin', []);
66 pyroutes.register('admin_audit_logs', '/_admin/audit_logs', []);
66 pyroutes.register('admin_audit_logs', '/_admin/audit_logs', []);
67 pyroutes.register('pull_requests_global_0', '/_admin/pull_requests/%(pull_request_id)s', ['pull_request_id']);
67 pyroutes.register('pull_requests_global_0', '/_admin/pull_requests/%(pull_request_id)s', ['pull_request_id']);
68 pyroutes.register('pull_requests_global_1', '/_admin/pull-requests/%(pull_request_id)s', ['pull_request_id']);
68 pyroutes.register('pull_requests_global_1', '/_admin/pull-requests/%(pull_request_id)s', ['pull_request_id']);
69 pyroutes.register('pull_requests_global', '/_admin/pull-request/%(pull_request_id)s', ['pull_request_id']);
69 pyroutes.register('pull_requests_global', '/_admin/pull-request/%(pull_request_id)s', ['pull_request_id']);
70 pyroutes.register('admin_settings_open_source', '/_admin/settings/open_source', []);
70 pyroutes.register('admin_settings_open_source', '/_admin/settings/open_source', []);
71 pyroutes.register('admin_settings_vcs_svn_generate_cfg', '/_admin/settings/vcs/svn_generate_cfg', []);
71 pyroutes.register('admin_settings_vcs_svn_generate_cfg', '/_admin/settings/vcs/svn_generate_cfg', []);
72 pyroutes.register('admin_settings_system', '/_admin/settings/system', []);
72 pyroutes.register('admin_settings_system', '/_admin/settings/system', []);
73 pyroutes.register('admin_settings_system_update', '/_admin/settings/system/updates', []);
73 pyroutes.register('admin_settings_system_update', '/_admin/settings/system/updates', []);
74 pyroutes.register('admin_settings_sessions', '/_admin/settings/sessions', []);
74 pyroutes.register('admin_settings_sessions', '/_admin/settings/sessions', []);
75 pyroutes.register('admin_settings_sessions_cleanup', '/_admin/settings/sessions/cleanup', []);
75 pyroutes.register('admin_settings_sessions_cleanup', '/_admin/settings/sessions/cleanup', []);
76 pyroutes.register('admin_settings_process_management', '/_admin/settings/process_management', []);
76 pyroutes.register('admin_settings_process_management', '/_admin/settings/process_management', []);
77 pyroutes.register('admin_settings_process_management_signal', '/_admin/settings/process_management/signal', []);
77 pyroutes.register('admin_settings_process_management_signal', '/_admin/settings/process_management/signal', []);
78 pyroutes.register('admin_permissions_ips', '/_admin/permissions/ips', []);
78 pyroutes.register('admin_permissions_ips', '/_admin/permissions/ips', []);
79 pyroutes.register('users', '/_admin/users', []);
79 pyroutes.register('users', '/_admin/users', []);
80 pyroutes.register('users_data', '/_admin/users_data', []);
80 pyroutes.register('users_data', '/_admin/users_data', []);
81 pyroutes.register('edit_user_auth_tokens', '/_admin/users/%(user_id)s/edit/auth_tokens', ['user_id']);
81 pyroutes.register('edit_user_auth_tokens', '/_admin/users/%(user_id)s/edit/auth_tokens', ['user_id']);
82 pyroutes.register('edit_user_auth_tokens_add', '/_admin/users/%(user_id)s/edit/auth_tokens/new', ['user_id']);
82 pyroutes.register('edit_user_auth_tokens_add', '/_admin/users/%(user_id)s/edit/auth_tokens/new', ['user_id']);
83 pyroutes.register('edit_user_auth_tokens_delete', '/_admin/users/%(user_id)s/edit/auth_tokens/delete', ['user_id']);
83 pyroutes.register('edit_user_auth_tokens_delete', '/_admin/users/%(user_id)s/edit/auth_tokens/delete', ['user_id']);
84 pyroutes.register('edit_user_emails', '/_admin/users/%(user_id)s/edit/emails', ['user_id']);
84 pyroutes.register('edit_user_emails', '/_admin/users/%(user_id)s/edit/emails', ['user_id']);
85 pyroutes.register('edit_user_emails_add', '/_admin/users/%(user_id)s/edit/emails/new', ['user_id']);
85 pyroutes.register('edit_user_emails_add', '/_admin/users/%(user_id)s/edit/emails/new', ['user_id']);
86 pyroutes.register('edit_user_emails_delete', '/_admin/users/%(user_id)s/edit/emails/delete', ['user_id']);
86 pyroutes.register('edit_user_emails_delete', '/_admin/users/%(user_id)s/edit/emails/delete', ['user_id']);
87 pyroutes.register('edit_user_ips', '/_admin/users/%(user_id)s/edit/ips', ['user_id']);
87 pyroutes.register('edit_user_ips', '/_admin/users/%(user_id)s/edit/ips', ['user_id']);
88 pyroutes.register('edit_user_ips_add', '/_admin/users/%(user_id)s/edit/ips/new', ['user_id']);
88 pyroutes.register('edit_user_ips_add', '/_admin/users/%(user_id)s/edit/ips/new', ['user_id']);
89 pyroutes.register('edit_user_ips_delete', '/_admin/users/%(user_id)s/edit/ips/delete', ['user_id']);
89 pyroutes.register('edit_user_ips_delete', '/_admin/users/%(user_id)s/edit/ips/delete', ['user_id']);
90 pyroutes.register('edit_user_groups_management', '/_admin/users/%(user_id)s/edit/groups_management', ['user_id']);
90 pyroutes.register('edit_user_groups_management', '/_admin/users/%(user_id)s/edit/groups_management', ['user_id']);
91 pyroutes.register('edit_user_groups_management_updates', '/_admin/users/%(user_id)s/edit/edit_user_groups_management/updates', ['user_id']);
91 pyroutes.register('edit_user_groups_management_updates', '/_admin/users/%(user_id)s/edit/edit_user_groups_management/updates', ['user_id']);
92 pyroutes.register('edit_user_audit_logs', '/_admin/users/%(user_id)s/edit/audit', ['user_id']);
92 pyroutes.register('edit_user_audit_logs', '/_admin/users/%(user_id)s/edit/audit', ['user_id']);
93 pyroutes.register('channelstream_connect', '/_admin/channelstream/connect', []);
93 pyroutes.register('channelstream_connect', '/_admin/channelstream/connect', []);
94 pyroutes.register('channelstream_subscribe', '/_admin/channelstream/subscribe', []);
94 pyroutes.register('channelstream_subscribe', '/_admin/channelstream/subscribe', []);
95 pyroutes.register('channelstream_proxy', '/_channelstream', []);
95 pyroutes.register('channelstream_proxy', '/_channelstream', []);
96 pyroutes.register('login', '/_admin/login', []);
96 pyroutes.register('login', '/_admin/login', []);
97 pyroutes.register('logout', '/_admin/logout', []);
97 pyroutes.register('logout', '/_admin/logout', []);
98 pyroutes.register('register', '/_admin/register', []);
98 pyroutes.register('register', '/_admin/register', []);
99 pyroutes.register('reset_password', '/_admin/password_reset', []);
99 pyroutes.register('reset_password', '/_admin/password_reset', []);
100 pyroutes.register('reset_password_confirmation', '/_admin/password_reset_confirmation', []);
100 pyroutes.register('reset_password_confirmation', '/_admin/password_reset_confirmation', []);
101 pyroutes.register('home', '/', []);
101 pyroutes.register('home', '/', []);
102 pyroutes.register('user_autocomplete_data', '/_users', []);
102 pyroutes.register('user_autocomplete_data', '/_users', []);
103 pyroutes.register('user_group_autocomplete_data', '/_user_groups', []);
103 pyroutes.register('user_group_autocomplete_data', '/_user_groups', []);
104 pyroutes.register('repo_list_data', '/_repos', []);
104 pyroutes.register('repo_list_data', '/_repos', []);
105 pyroutes.register('goto_switcher_data', '/_goto_data', []);
105 pyroutes.register('goto_switcher_data', '/_goto_data', []);
106 pyroutes.register('repo_summary_explicit', '/%(repo_name)s/summary', ['repo_name']);
106 pyroutes.register('repo_summary_explicit', '/%(repo_name)s/summary', ['repo_name']);
107 pyroutes.register('repo_summary_commits', '/%(repo_name)s/summary-commits', ['repo_name']);
107 pyroutes.register('repo_summary_commits', '/%(repo_name)s/summary-commits', ['repo_name']);
108 pyroutes.register('repo_commit', '/%(repo_name)s/changeset/%(commit_id)s', ['repo_name', 'commit_id']);
108 pyroutes.register('repo_commit', '/%(repo_name)s/changeset/%(commit_id)s', ['repo_name', 'commit_id']);
109 pyroutes.register('repo_refs_data', '/%(repo_name)s/refs-data', ['repo_name']);
109 pyroutes.register('repo_refs_data', '/%(repo_name)s/refs-data', ['repo_name']);
110 pyroutes.register('repo_refs_changelog_data', '/%(repo_name)s/refs-data-changelog', ['repo_name']);
110 pyroutes.register('repo_refs_changelog_data', '/%(repo_name)s/refs-data-changelog', ['repo_name']);
111 pyroutes.register('repo_stats', '/%(repo_name)s/repo_stats/%(commit_id)s', ['repo_name', 'commit_id']);
111 pyroutes.register('repo_stats', '/%(repo_name)s/repo_stats/%(commit_id)s', ['repo_name', 'commit_id']);
112 pyroutes.register('tags_home', '/%(repo_name)s/tags', ['repo_name']);
112 pyroutes.register('tags_home', '/%(repo_name)s/tags', ['repo_name']);
113 pyroutes.register('branches_home', '/%(repo_name)s/branches', ['repo_name']);
113 pyroutes.register('branches_home', '/%(repo_name)s/branches', ['repo_name']);
114 pyroutes.register('bookmarks_home', '/%(repo_name)s/bookmarks', ['repo_name']);
114 pyroutes.register('bookmarks_home', '/%(repo_name)s/bookmarks', ['repo_name']);
115 pyroutes.register('pullrequest_show', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
115 pyroutes.register('pullrequest_show', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
116 pyroutes.register('pullrequest_show_all', '/%(repo_name)s/pull-request', ['repo_name']);
116 pyroutes.register('pullrequest_show_all', '/%(repo_name)s/pull-request', ['repo_name']);
117 pyroutes.register('pullrequest_show_all_data', '/%(repo_name)s/pull-request-data', ['repo_name']);
117 pyroutes.register('pullrequest_show_all_data', '/%(repo_name)s/pull-request-data', ['repo_name']);
118 pyroutes.register('changeset_home', '/%(repo_name)s/changeset/%(revision)s', ['repo_name', 'revision']);
118 pyroutes.register('changeset_home', '/%(repo_name)s/changeset/%(revision)s', ['repo_name', 'revision']);
119 pyroutes.register('changeset_children', '/%(repo_name)s/changeset_children/%(revision)s', ['repo_name', 'revision']);
119 pyroutes.register('changeset_children', '/%(repo_name)s/changeset_children/%(revision)s', ['repo_name', 'revision']);
120 pyroutes.register('changeset_parents', '/%(repo_name)s/changeset_parents/%(revision)s', ['repo_name', 'revision']);
120 pyroutes.register('changeset_parents', '/%(repo_name)s/changeset_parents/%(revision)s', ['repo_name', 'revision']);
121 pyroutes.register('edit_repo', '/%(repo_name)s/settings', ['repo_name']);
121 pyroutes.register('edit_repo', '/%(repo_name)s/settings', ['repo_name']);
122 pyroutes.register('edit_repo_advanced', '/%(repo_name)s/settings/advanced', ['repo_name']);
122 pyroutes.register('edit_repo_advanced', '/%(repo_name)s/settings/advanced', ['repo_name']);
123 pyroutes.register('edit_repo_advanced_delete', '/%(repo_name)s/settings/advanced/delete', ['repo_name']);
123 pyroutes.register('edit_repo_advanced_delete', '/%(repo_name)s/settings/advanced/delete', ['repo_name']);
124 pyroutes.register('edit_repo_advanced_locking', '/%(repo_name)s/settings/advanced/locking', ['repo_name']);
124 pyroutes.register('edit_repo_advanced_locking', '/%(repo_name)s/settings/advanced/locking', ['repo_name']);
125 pyroutes.register('edit_repo_advanced_journal', '/%(repo_name)s/settings/advanced/journal', ['repo_name']);
125 pyroutes.register('edit_repo_advanced_journal', '/%(repo_name)s/settings/advanced/journal', ['repo_name']);
126 pyroutes.register('edit_repo_advanced_fork', '/%(repo_name)s/settings/advanced/fork', ['repo_name']);
126 pyroutes.register('edit_repo_advanced_fork', '/%(repo_name)s/settings/advanced/fork', ['repo_name']);
127 pyroutes.register('edit_repo_caches', '/%(repo_name)s/settings/caches', ['repo_name']);
127 pyroutes.register('edit_repo_caches', '/%(repo_name)s/settings/caches', ['repo_name']);
128 pyroutes.register('edit_repo_perms', '/%(repo_name)s/settings/permissions', ['repo_name']);
128 pyroutes.register('edit_repo_perms', '/%(repo_name)s/settings/permissions', ['repo_name']);
129 pyroutes.register('repo_reviewers', '/%(repo_name)s/settings/review/rules', ['repo_name']);
129 pyroutes.register('repo_reviewers', '/%(repo_name)s/settings/review/rules', ['repo_name']);
130 pyroutes.register('repo_default_reviewers_data', '/%(repo_name)s/settings/review/default-reviewers', ['repo_name']);
130 pyroutes.register('repo_default_reviewers_data', '/%(repo_name)s/settings/review/default-reviewers', ['repo_name']);
131 pyroutes.register('repo_maintenance', '/%(repo_name)s/settings/maintenance', ['repo_name']);
131 pyroutes.register('repo_maintenance', '/%(repo_name)s/settings/maintenance', ['repo_name']);
132 pyroutes.register('repo_maintenance_execute', '/%(repo_name)s/settings/maintenance/execute', ['repo_name']);
132 pyroutes.register('repo_maintenance_execute', '/%(repo_name)s/settings/maintenance/execute', ['repo_name']);
133 pyroutes.register('strip', '/%(repo_name)s/settings/strip', ['repo_name']);
133 pyroutes.register('strip', '/%(repo_name)s/settings/strip', ['repo_name']);
134 pyroutes.register('strip_check', '/%(repo_name)s/settings/strip_check', ['repo_name']);
134 pyroutes.register('strip_check', '/%(repo_name)s/settings/strip_check', ['repo_name']);
135 pyroutes.register('strip_execute', '/%(repo_name)s/settings/strip_execute', ['repo_name']);
135 pyroutes.register('strip_execute', '/%(repo_name)s/settings/strip_execute', ['repo_name']);
136 pyroutes.register('rss_feed_home', '/%(repo_name)s/feed/rss', ['repo_name']);
136 pyroutes.register('rss_feed_home', '/%(repo_name)s/feed/rss', ['repo_name']);
137 pyroutes.register('atom_feed_home', '/%(repo_name)s/feed/atom', ['repo_name']);
137 pyroutes.register('atom_feed_home', '/%(repo_name)s/feed/atom', ['repo_name']);
138 pyroutes.register('repo_summary', '/%(repo_name)s', ['repo_name']);
138 pyroutes.register('repo_summary', '/%(repo_name)s', ['repo_name']);
139 pyroutes.register('repo_summary_slash', '/%(repo_name)s/', ['repo_name']);
139 pyroutes.register('repo_summary_slash', '/%(repo_name)s/', ['repo_name']);
140 pyroutes.register('repo_group_home', '/%(repo_group_name)s', ['repo_group_name']);
140 pyroutes.register('repo_group_home', '/%(repo_group_name)s', ['repo_group_name']);
141 pyroutes.register('repo_group_home_slash', '/%(repo_group_name)s/', ['repo_group_name']);
141 pyroutes.register('repo_group_home_slash', '/%(repo_group_name)s/', ['repo_group_name']);
142 pyroutes.register('search', '/_admin/search', []);
142 pyroutes.register('search', '/_admin/search', []);
143 pyroutes.register('search_repo', '/%(repo_name)s/search', ['repo_name']);
143 pyroutes.register('search_repo', '/%(repo_name)s/search', ['repo_name']);
144 pyroutes.register('user_profile', '/_profiles/%(username)s', ['username']);
144 pyroutes.register('user_profile', '/_profiles/%(username)s', ['username']);
145 pyroutes.register('my_account_profile', '/_admin/my_account/profile', []);
145 pyroutes.register('my_account_profile', '/_admin/my_account/profile', []);
146 pyroutes.register('my_account_edit', '/_admin/my_account/edit', []);
146 pyroutes.register('my_account_edit', '/_admin/my_account/edit', []);
147 pyroutes.register('my_account_update', '/_admin/my_account/update', []);
147 pyroutes.register('my_account_update', '/_admin/my_account/update', []);
148 pyroutes.register('my_account_password', '/_admin/my_account/password', []);
148 pyroutes.register('my_account_password', '/_admin/my_account/password', []);
149 pyroutes.register('my_account_password_update', '/_admin/my_account/password', []);
149 pyroutes.register('my_account_password_update', '/_admin/my_account/password', []);
150 pyroutes.register('my_account_auth_tokens', '/_admin/my_account/auth_tokens', []);
150 pyroutes.register('my_account_auth_tokens', '/_admin/my_account/auth_tokens', []);
151 pyroutes.register('my_account_auth_tokens_add', '/_admin/my_account/auth_tokens/new', []);
151 pyroutes.register('my_account_auth_tokens_add', '/_admin/my_account/auth_tokens/new', []);
152 pyroutes.register('my_account_auth_tokens_delete', '/_admin/my_account/auth_tokens/delete', []);
152 pyroutes.register('my_account_auth_tokens_delete', '/_admin/my_account/auth_tokens/delete', []);
153 pyroutes.register('my_account_emails', '/_admin/my_account/emails', []);
153 pyroutes.register('my_account_emails', '/_admin/my_account/emails', []);
154 pyroutes.register('my_account_emails_add', '/_admin/my_account/emails/new', []);
154 pyroutes.register('my_account_emails_add', '/_admin/my_account/emails/new', []);
155 pyroutes.register('my_account_emails_delete', '/_admin/my_account/emails/delete', []);
155 pyroutes.register('my_account_emails_delete', '/_admin/my_account/emails/delete', []);
156 pyroutes.register('my_account_repos', '/_admin/my_account/repos', []);
156 pyroutes.register('my_account_repos', '/_admin/my_account/repos', []);
157 pyroutes.register('my_account_watched', '/_admin/my_account/watched', []);
157 pyroutes.register('my_account_watched', '/_admin/my_account/watched', []);
158 pyroutes.register('my_account_perms', '/_admin/my_account/perms', []);
158 pyroutes.register('my_account_perms', '/_admin/my_account/perms', []);
159 pyroutes.register('my_account_notifications', '/_admin/my_account/notifications', []);
159 pyroutes.register('my_account_notifications', '/_admin/my_account/notifications', []);
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', []);
166 pyroutes.register('gists_create', '/_admin/gists/create', []);
171 pyroutes.register('gists_create', '/_admin/gists/create', []);
167 pyroutes.register('gist_show', '/_admin/gists/%(gist_id)s', ['gist_id']);
172 pyroutes.register('gist_show', '/_admin/gists/%(gist_id)s', ['gist_id']);
168 pyroutes.register('gist_delete', '/_admin/gists/%(gist_id)s/delete', ['gist_id']);
173 pyroutes.register('gist_delete', '/_admin/gists/%(gist_id)s/delete', ['gist_id']);
169 pyroutes.register('gist_edit', '/_admin/gists/%(gist_id)s/edit', ['gist_id']);
174 pyroutes.register('gist_edit', '/_admin/gists/%(gist_id)s/edit', ['gist_id']);
170 pyroutes.register('gist_edit_check_revision', '/_admin/gists/%(gist_id)s/edit/check_revision', ['gist_id']);
175 pyroutes.register('gist_edit_check_revision', '/_admin/gists/%(gist_id)s/edit/check_revision', ['gist_id']);
171 pyroutes.register('gist_update', '/_admin/gists/%(gist_id)s/update', ['gist_id']);
176 pyroutes.register('gist_update', '/_admin/gists/%(gist_id)s/update', ['gist_id']);
172 pyroutes.register('gist_show_rev', '/_admin/gists/%(gist_id)s/%(revision)s', ['gist_id', 'revision']);
177 pyroutes.register('gist_show_rev', '/_admin/gists/%(gist_id)s/%(revision)s', ['gist_id', 'revision']);
173 pyroutes.register('gist_show_formatted', '/_admin/gists/%(gist_id)s/%(revision)s/%(format)s', ['gist_id', 'revision', 'format']);
178 pyroutes.register('gist_show_formatted', '/_admin/gists/%(gist_id)s/%(revision)s/%(format)s', ['gist_id', 'revision', 'format']);
174 pyroutes.register('gist_show_formatted_path', '/_admin/gists/%(gist_id)s/%(revision)s/%(format)s/%(f_path)s', ['gist_id', 'revision', 'format', 'f_path']);
179 pyroutes.register('gist_show_formatted_path', '/_admin/gists/%(gist_id)s/%(revision)s/%(format)s/%(f_path)s', ['gist_id', 'revision', 'format', 'f_path']);
175 pyroutes.register('debug_style_home', '/_admin/debug_style', []);
180 pyroutes.register('debug_style_home', '/_admin/debug_style', []);
176 pyroutes.register('debug_style_template', '/_admin/debug_style/t/%(t_path)s', ['t_path']);
181 pyroutes.register('debug_style_template', '/_admin/debug_style/t/%(t_path)s', ['t_path']);
177 pyroutes.register('apiv2', '/_admin/api', []);
182 pyroutes.register('apiv2', '/_admin/api', []);
178 }
183 }
@@ -1,64 +1,66 b''
1 // # Copyright (C) 2010-2017 RhodeCode GmbH
1 // # Copyright (C) 2010-2017 RhodeCode GmbH
2 // #
2 // #
3 // # This program is free software: you can redistribute it and/or modify
3 // # This program is free software: you can redistribute it and/or modify
4 // # it under the terms of the GNU Affero General Public License, version 3
4 // # it under the terms of the GNU Affero General Public License, version 3
5 // # (only), as published by the Free Software Foundation.
5 // # (only), as published by the Free Software Foundation.
6 // #
6 // #
7 // # This program is distributed in the hope that it will be useful,
7 // # This program is distributed in the hope that it will be useful,
8 // # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 // # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 // # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 // # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 // # GNU General Public License for more details.
10 // # GNU General Public License for more details.
11 // #
11 // #
12 // # You should have received a copy of the GNU Affero General Public License
12 // # You should have received a copy of the GNU Affero General Public License
13 // # along with this program. If not, see <http://www.gnu.org/licenses/>.
13 // # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 // #
14 // #
15 // # This program is dual-licensed. If you wish to learn more about the
15 // # This program is dual-licensed. If you wish to learn more about the
16 // # RhodeCode Enterprise Edition, including its added features, Support services,
16 // # RhodeCode Enterprise Edition, including its added features, Support services,
17 // # and proprietary license terms, please see https://rhodecode.com/licenses/
17 // # and proprietary license terms, please see https://rhodecode.com/licenses/
18
18
19 var _run_callbacks = function(callbacks){
19 var _run_callbacks = function(callbacks){
20 if (callbacks !== undefined){
20 if (callbacks !== undefined){
21 var _l = callbacks.length;
21 var _l = callbacks.length;
22 for (var i=0;i<_l;i++){
22 for (var i=0;i<_l;i++){
23 var func = callbacks[i];
23 var func = callbacks[i];
24 if(typeof(func)=='function'){
24 if(typeof(func)=='function'){
25 try{
25 try{
26 func();
26 func();
27 }catch (err){};
27 }catch (err){};
28 }
28 }
29 }
29 }
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
@@ -1,57 +1,50 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.mako"/>
2 <%inherit file="/base/base.mako"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 ${_('Show notification')} ${c.rhodecode_user.username}
5 ${_('Show notification')} ${c.rhodecode_user.username}
6 %if c.rhodecode_name:
6 %if c.rhodecode_name:
7 &middot; ${h.branding(c.rhodecode_name)}
7 &middot; ${h.branding(c.rhodecode_name)}
8 %endif
8 %endif
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>
16
16
17 <%def name="menu_bar_nav()">
17 <%def name="menu_bar_nav()">
18 ${self.menu_items(active='admin')}
18 ${self.menu_items(active='admin')}
19 </%def>
19 </%def>
20
20
21 <%def name="main()">
21 <%def name="main()">
22 <div class="box">
22 <div class="box">
23 <!-- box / title -->
23 <!-- box / title -->
24 <div class="title">
24 <div class="title">
25 ${self.breadcrumbs()}
25 ${self.breadcrumbs()}
26 </div>
26 </div>
27 <div class="table">
27 <div class="table">
28 <div id="notification_${c.notification.notification_id}" class="main-content-full">
28 <div id="notification_${c.notification.notification_id}" class="main-content-full">
29 <div class="notification-header">
29 <div class="notification-header">
30 ${self.gravatar(c.notification.created_by_user.email, 30)}
30 ${self.gravatar(c.notification.created_by_user.email, 30)}
31 <div class="desc">
31 <div class="desc">
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">
39 <div class="notification-subject">
39 <div class="notification-subject">
40 <h3>${_('Subject')}: ${c.notification.subject}</h3>
40 <h3>${_('Subject')}: ${c.notification.subject}</h3>
41 </div>
41 </div>
42 %if c.notification.body:
42 %if c.notification.body:
43 ${h.render(c.notification.body, renderer=c.visual.default_renderer, mentions=True)}
43 ${h.render(c.notification.body, renderer=c.visual.default_renderer, mentions=True)}
44 %endif
44 %endif
45 </div>
45 </div>
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>
@@ -1,78 +1,68 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.mako"/>
2 <%inherit file="/base/base.mako"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 ${_('My Notifications')} ${c.rhodecode_user.username}
5 ${_('My Notifications')} ${c.rhodecode_user.username}
6 %if c.rhodecode_name:
6 %if c.rhodecode_name:
7 &middot; ${h.branding(c.rhodecode_name)}
7 &middot; ${h.branding(c.rhodecode_name)}
8 %endif
8 %endif
9 </%def>
9 </%def>
10
10
11 <%def name="breadcrumbs_links()">
11 <%def name="breadcrumbs_links()">
12 ${_('My Notifications')}
12 ${_('My Notifications')}
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>
@@ -1,604 +1,600 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="root.mako"/>
2 <%inherit file="root.mako"/>
3
3
4 <div class="outerwrapper">
4 <div class="outerwrapper">
5 <!-- HEADER -->
5 <!-- HEADER -->
6 <div class="header">
6 <div class="header">
7 <div id="header-inner" class="wrapper">
7 <div id="header-inner" class="wrapper">
8 <div id="logo">
8 <div id="logo">
9 <div class="logo-wrapper">
9 <div class="logo-wrapper">
10 <a href="${h.route_path('home')}"><img src="${h.asset('images/rhodecode-logo-white-216x60.png')}" alt="RhodeCode"/></a>
10 <a href="${h.route_path('home')}"><img src="${h.asset('images/rhodecode-logo-white-216x60.png')}" alt="RhodeCode"/></a>
11 </div>
11 </div>
12 %if c.rhodecode_name:
12 %if c.rhodecode_name:
13 <div class="branding">- ${h.branding(c.rhodecode_name)}</div>
13 <div class="branding">- ${h.branding(c.rhodecode_name)}</div>
14 %endif
14 %endif
15 </div>
15 </div>
16 <!-- MENU BAR NAV -->
16 <!-- MENU BAR NAV -->
17 ${self.menu_bar_nav()}
17 ${self.menu_bar_nav()}
18 <!-- END MENU BAR NAV -->
18 <!-- END MENU BAR NAV -->
19 </div>
19 </div>
20 </div>
20 </div>
21 ${self.menu_bar_subnav()}
21 ${self.menu_bar_subnav()}
22 <!-- END HEADER -->
22 <!-- END HEADER -->
23
23
24 <!-- CONTENT -->
24 <!-- CONTENT -->
25 <div id="content" class="wrapper">
25 <div id="content" class="wrapper">
26
26
27 <rhodecode-toast id="notifications"></rhodecode-toast>
27 <rhodecode-toast id="notifications"></rhodecode-toast>
28
28
29 <div class="main">
29 <div class="main">
30 ${next.main()}
30 ${next.main()}
31 </div>
31 </div>
32 </div>
32 </div>
33 <!-- END CONTENT -->
33 <!-- END CONTENT -->
34
34
35 </div>
35 </div>
36 <!-- FOOTER -->
36 <!-- FOOTER -->
37 <div id="footer">
37 <div id="footer">
38 <div id="footer-inner" class="title wrapper">
38 <div id="footer-inner" class="title wrapper">
39 <div>
39 <div>
40 <p class="footer-link-right">
40 <p class="footer-link-right">
41 % if c.visual.show_version:
41 % if c.visual.show_version:
42 RhodeCode Enterprise ${c.rhodecode_version} ${c.rhodecode_edition}
42 RhodeCode Enterprise ${c.rhodecode_version} ${c.rhodecode_edition}
43 % endif
43 % endif
44 &copy; 2010-${h.datetime.today().year}, <a href="${h.route_url('rhodecode_official')}" target="_blank">RhodeCode GmbH</a>. All rights reserved.
44 &copy; 2010-${h.datetime.today().year}, <a href="${h.route_url('rhodecode_official')}" target="_blank">RhodeCode GmbH</a>. All rights reserved.
45 % if c.visual.rhodecode_support_url:
45 % if c.visual.rhodecode_support_url:
46 <a href="${c.visual.rhodecode_support_url}" target="_blank">${_('Support')}</a>
46 <a href="${c.visual.rhodecode_support_url}" target="_blank">${_('Support')}</a>
47 % endif
47 % endif
48 </p>
48 </p>
49 <% sid = 'block' if request.GET.get('showrcid') else 'none' %>
49 <% sid = 'block' if request.GET.get('showrcid') else 'none' %>
50 <p class="server-instance" style="display:${sid}">
50 <p class="server-instance" style="display:${sid}">
51 ## display hidden instance ID if specially defined
51 ## display hidden instance ID if specially defined
52 % if c.rhodecode_instanceid:
52 % if c.rhodecode_instanceid:
53 ${_('RhodeCode instance id: %s') % c.rhodecode_instanceid}
53 ${_('RhodeCode instance id: %s') % c.rhodecode_instanceid}
54 % endif
54 % endif
55 </p>
55 </p>
56 </div>
56 </div>
57 </div>
57 </div>
58 </div>
58 </div>
59
59
60 <!-- END FOOTER -->
60 <!-- END FOOTER -->
61
61
62 ### MAKO DEFS ###
62 ### MAKO DEFS ###
63
63
64 <%def name="menu_bar_subnav()">
64 <%def name="menu_bar_subnav()">
65 </%def>
65 </%def>
66
66
67 <%def name="breadcrumbs(class_='breadcrumbs')">
67 <%def name="breadcrumbs(class_='breadcrumbs')">
68 <div class="${class_}">
68 <div class="${class_}">
69 ${self.breadcrumbs_links()}
69 ${self.breadcrumbs_links()}
70 </div>
70 </div>
71 </%def>
71 </%def>
72
72
73 <%def name="admin_menu()">
73 <%def name="admin_menu()">
74 <ul class="admin_menu submenu">
74 <ul class="admin_menu submenu">
75 <li><a href="${h.route_path('admin_audit_logs')}">${_('Admin audit logs')}</a></li>
75 <li><a href="${h.route_path('admin_audit_logs')}">${_('Admin audit logs')}</a></li>
76 <li><a href="${h.url('repos')}">${_('Repositories')}</a></li>
76 <li><a href="${h.url('repos')}">${_('Repositories')}</a></li>
77 <li><a href="${h.url('repo_groups')}">${_('Repository groups')}</a></li>
77 <li><a href="${h.url('repo_groups')}">${_('Repository groups')}</a></li>
78 <li><a href="${h.route_path('users')}">${_('Users')}</a></li>
78 <li><a href="${h.route_path('users')}">${_('Users')}</a></li>
79 <li><a href="${h.url('users_groups')}">${_('User groups')}</a></li>
79 <li><a href="${h.url('users_groups')}">${_('User groups')}</a></li>
80 <li><a href="${h.url('admin_permissions_application')}">${_('Permissions')}</a></li>
80 <li><a href="${h.url('admin_permissions_application')}">${_('Permissions')}</a></li>
81 <li><a href="${h.route_path('auth_home', traverse='')}">${_('Authentication')}</a></li>
81 <li><a href="${h.route_path('auth_home', traverse='')}">${_('Authentication')}</a></li>
82 <li><a href="${h.route_path('global_integrations_home')}">${_('Integrations')}</a></li>
82 <li><a href="${h.route_path('global_integrations_home')}">${_('Integrations')}</a></li>
83 <li><a href="${h.url('admin_defaults_repositories')}">${_('Defaults')}</a></li>
83 <li><a href="${h.url('admin_defaults_repositories')}">${_('Defaults')}</a></li>
84 <li class="last"><a href="${h.url('admin_settings')}">${_('Settings')}</a></li>
84 <li class="last"><a href="${h.url('admin_settings')}">${_('Settings')}</a></li>
85 </ul>
85 </ul>
86 </%def>
86 </%def>
87
87
88
88
89 <%def name="dt_info_panel(elements)">
89 <%def name="dt_info_panel(elements)">
90 <dl class="dl-horizontal">
90 <dl class="dl-horizontal">
91 %for dt, dd, title, show_items in elements:
91 %for dt, dd, title, show_items in elements:
92 <dt>${dt}:</dt>
92 <dt>${dt}:</dt>
93 <dd title="${h.tooltip(title)}">
93 <dd title="${h.tooltip(title)}">
94 %if callable(dd):
94 %if callable(dd):
95 ## allow lazy evaluation of elements
95 ## allow lazy evaluation of elements
96 ${dd()}
96 ${dd()}
97 %else:
97 %else:
98 ${dd}
98 ${dd}
99 %endif
99 %endif
100 %if show_items:
100 %if show_items:
101 <span class="btn-collapse" data-toggle="item-${h.md5_safe(dt)[:6]}-details">${_('Show More')} </span>
101 <span class="btn-collapse" data-toggle="item-${h.md5_safe(dt)[:6]}-details">${_('Show More')} </span>
102 %endif
102 %endif
103 </dd>
103 </dd>
104
104
105 %if show_items:
105 %if show_items:
106 <div class="collapsable-content" data-toggle="item-${h.md5_safe(dt)[:6]}-details" style="display: none">
106 <div class="collapsable-content" data-toggle="item-${h.md5_safe(dt)[:6]}-details" style="display: none">
107 %for item in show_items:
107 %for item in show_items:
108 <dt></dt>
108 <dt></dt>
109 <dd>${item}</dd>
109 <dd>${item}</dd>
110 %endfor
110 %endfor
111 </div>
111 </div>
112 %endif
112 %endif
113
113
114 %endfor
114 %endfor
115 </dl>
115 </dl>
116 </%def>
116 </%def>
117
117
118
118
119 <%def name="gravatar(email, size=16)">
119 <%def name="gravatar(email, size=16)">
120 <%
120 <%
121 if (size > 16):
121 if (size > 16):
122 gravatar_class = 'gravatar gravatar-large'
122 gravatar_class = 'gravatar gravatar-large'
123 else:
123 else:
124 gravatar_class = 'gravatar'
124 gravatar_class = 'gravatar'
125 %>
125 %>
126 <%doc>
126 <%doc>
127 TODO: johbo: For now we serve double size images to make it smooth
127 TODO: johbo: For now we serve double size images to make it smooth
128 for retina. This is how it worked until now. Should be replaced
128 for retina. This is how it worked until now. Should be replaced
129 with a better solution at some point.
129 with a better solution at some point.
130 </%doc>
130 </%doc>
131 <img class="${gravatar_class}" src="${h.gravatar_url(email, size * 2)}" height="${size}" width="${size}">
131 <img class="${gravatar_class}" src="${h.gravatar_url(email, size * 2)}" height="${size}" width="${size}">
132 </%def>
132 </%def>
133
133
134
134
135 <%def name="gravatar_with_user(contact, size=16, show_disabled=False)">
135 <%def name="gravatar_with_user(contact, size=16, show_disabled=False)">
136 <% email = h.email_or_none(contact) %>
136 <% email = h.email_or_none(contact) %>
137 <div class="rc-user tooltip" title="${h.tooltip(h.author_string(email))}">
137 <div class="rc-user tooltip" title="${h.tooltip(h.author_string(email))}">
138 ${self.gravatar(email, size)}
138 ${self.gravatar(email, size)}
139 <span class="${'user user-disabled' if show_disabled else 'user'}"> ${h.link_to_user(contact)}</span>
139 <span class="${'user user-disabled' if show_disabled else 'user'}"> ${h.link_to_user(contact)}</span>
140 </div>
140 </div>
141 </%def>
141 </%def>
142
142
143
143
144 ## admin menu used for people that have some admin resources
144 ## admin menu used for people that have some admin resources
145 <%def name="admin_menu_simple(repositories=None, repository_groups=None, user_groups=None)">
145 <%def name="admin_menu_simple(repositories=None, repository_groups=None, user_groups=None)">
146 <ul class="submenu">
146 <ul class="submenu">
147 %if repositories:
147 %if repositories:
148 <li class="local-admin-repos"><a href="${h.url('repos')}">${_('Repositories')}</a></li>
148 <li class="local-admin-repos"><a href="${h.url('repos')}">${_('Repositories')}</a></li>
149 %endif
149 %endif
150 %if repository_groups:
150 %if repository_groups:
151 <li class="local-admin-repo-groups"><a href="${h.url('repo_groups')}">${_('Repository groups')}</a></li>
151 <li class="local-admin-repo-groups"><a href="${h.url('repo_groups')}">${_('Repository groups')}</a></li>
152 %endif
152 %endif
153 %if user_groups:
153 %if user_groups:
154 <li class="local-admin-user-groups"><a href="${h.url('users_groups')}">${_('User groups')}</a></li>
154 <li class="local-admin-user-groups"><a href="${h.url('users_groups')}">${_('User groups')}</a></li>
155 %endif
155 %endif
156 </ul>
156 </ul>
157 </%def>
157 </%def>
158
158
159 <%def name="repo_page_title(repo_instance)">
159 <%def name="repo_page_title(repo_instance)">
160 <div class="title-content">
160 <div class="title-content">
161 <div class="title-main">
161 <div class="title-main">
162 ## SVN/HG/GIT icons
162 ## SVN/HG/GIT icons
163 %if h.is_hg(repo_instance):
163 %if h.is_hg(repo_instance):
164 <i class="icon-hg"></i>
164 <i class="icon-hg"></i>
165 %endif
165 %endif
166 %if h.is_git(repo_instance):
166 %if h.is_git(repo_instance):
167 <i class="icon-git"></i>
167 <i class="icon-git"></i>
168 %endif
168 %endif
169 %if h.is_svn(repo_instance):
169 %if h.is_svn(repo_instance):
170 <i class="icon-svn"></i>
170 <i class="icon-svn"></i>
171 %endif
171 %endif
172
172
173 ## public/private
173 ## public/private
174 %if repo_instance.private:
174 %if repo_instance.private:
175 <i class="icon-repo-private"></i>
175 <i class="icon-repo-private"></i>
176 %else:
176 %else:
177 <i class="icon-repo-public"></i>
177 <i class="icon-repo-public"></i>
178 %endif
178 %endif
179
179
180 ## repo name with group name
180 ## repo name with group name
181 ${h.breadcrumb_repo_link(c.rhodecode_db_repo)}
181 ${h.breadcrumb_repo_link(c.rhodecode_db_repo)}
182
182
183 </div>
183 </div>
184
184
185 ## FORKED
185 ## FORKED
186 %if repo_instance.fork:
186 %if repo_instance.fork:
187 <p>
187 <p>
188 <i class="icon-code-fork"></i> ${_('Fork of')}
188 <i class="icon-code-fork"></i> ${_('Fork of')}
189 <a href="${h.route_path('repo_summary',repo_name=repo_instance.fork.repo_name)}">${repo_instance.fork.repo_name}</a>
189 <a href="${h.route_path('repo_summary',repo_name=repo_instance.fork.repo_name)}">${repo_instance.fork.repo_name}</a>
190 </p>
190 </p>
191 %endif
191 %endif
192
192
193 ## IMPORTED FROM REMOTE
193 ## IMPORTED FROM REMOTE
194 %if repo_instance.clone_uri:
194 %if repo_instance.clone_uri:
195 <p>
195 <p>
196 <i class="icon-code-fork"></i> ${_('Clone from')}
196 <i class="icon-code-fork"></i> ${_('Clone from')}
197 <a href="${h.url(h.safe_str(h.hide_credentials(repo_instance.clone_uri)))}">${h.hide_credentials(repo_instance.clone_uri)}</a>
197 <a href="${h.url(h.safe_str(h.hide_credentials(repo_instance.clone_uri)))}">${h.hide_credentials(repo_instance.clone_uri)}</a>
198 </p>
198 </p>
199 %endif
199 %endif
200
200
201 ## LOCKING STATUS
201 ## LOCKING STATUS
202 %if repo_instance.locked[0]:
202 %if repo_instance.locked[0]:
203 <p class="locking_locked">
203 <p class="locking_locked">
204 <i class="icon-repo-lock"></i>
204 <i class="icon-repo-lock"></i>
205 ${_('Repository locked by %(user)s') % {'user': h.person_by_id(repo_instance.locked[0])}}
205 ${_('Repository locked by %(user)s') % {'user': h.person_by_id(repo_instance.locked[0])}}
206 </p>
206 </p>
207 %elif repo_instance.enable_locking:
207 %elif repo_instance.enable_locking:
208 <p class="locking_unlocked">
208 <p class="locking_unlocked">
209 <i class="icon-repo-unlock"></i>
209 <i class="icon-repo-unlock"></i>
210 ${_('Repository not locked. Pull repository to lock it.')}
210 ${_('Repository not locked. Pull repository to lock it.')}
211 </p>
211 </p>
212 %endif
212 %endif
213
213
214 </div>
214 </div>
215 </%def>
215 </%def>
216
216
217 <%def name="repo_menu(active=None)">
217 <%def name="repo_menu(active=None)">
218 <%
218 <%
219 def is_active(selected):
219 def is_active(selected):
220 if selected == active:
220 if selected == active:
221 return "active"
221 return "active"
222 %>
222 %>
223
223
224 <!--- CONTEXT BAR -->
224 <!--- CONTEXT BAR -->
225 <div id="context-bar">
225 <div id="context-bar">
226 <div class="wrapper">
226 <div class="wrapper">
227 <ul id="context-pages" class="horizontal-list navigation">
227 <ul id="context-pages" class="horizontal-list navigation">
228 <li class="${is_active('summary')}"><a class="menulink" href="${h.route_path('repo_summary', repo_name=c.repo_name)}"><div class="menulabel">${_('Summary')}</div></a></li>
228 <li class="${is_active('summary')}"><a class="menulink" href="${h.route_path('repo_summary', repo_name=c.repo_name)}"><div class="menulabel">${_('Summary')}</div></a></li>
229 <li class="${is_active('changelog')}"><a class="menulink" href="${h.url('changelog_home', repo_name=c.repo_name)}"><div class="menulabel">${_('Changelog')}</div></a></li>
229 <li class="${is_active('changelog')}"><a class="menulink" href="${h.url('changelog_home', repo_name=c.repo_name)}"><div class="menulabel">${_('Changelog')}</div></a></li>
230 <li class="${is_active('files')}"><a class="menulink" href="${h.url('files_home', repo_name=c.repo_name, revision=c.rhodecode_db_repo.landing_rev[1])}"><div class="menulabel">${_('Files')}</div></a></li>
230 <li class="${is_active('files')}"><a class="menulink" href="${h.url('files_home', repo_name=c.repo_name, revision=c.rhodecode_db_repo.landing_rev[1])}"><div class="menulabel">${_('Files')}</div></a></li>
231 <li class="${is_active('compare')}">
231 <li class="${is_active('compare')}">
232 <a class="menulink" href="${h.url('compare_home',repo_name=c.repo_name)}"><div class="menulabel">${_('Compare')}</div></a>
232 <a class="menulink" href="${h.url('compare_home',repo_name=c.repo_name)}"><div class="menulabel">${_('Compare')}</div></a>
233 </li>
233 </li>
234 ## TODO: anderson: ideally it would have a function on the scm_instance "enable_pullrequest() and enable_fork()"
234 ## TODO: anderson: ideally it would have a function on the scm_instance "enable_pullrequest() and enable_fork()"
235 %if c.rhodecode_db_repo.repo_type in ['git','hg']:
235 %if c.rhodecode_db_repo.repo_type in ['git','hg']:
236 <li class="${is_active('showpullrequest')}">
236 <li class="${is_active('showpullrequest')}">
237 <a class="menulink" href="${h.route_path('pullrequest_show_all', repo_name=c.repo_name)}" title="${h.tooltip(_('Show Pull Requests for %s') % c.repo_name)}">
237 <a class="menulink" href="${h.route_path('pullrequest_show_all', repo_name=c.repo_name)}" title="${h.tooltip(_('Show Pull Requests for %s') % c.repo_name)}">
238 %if c.repository_pull_requests:
238 %if c.repository_pull_requests:
239 <span class="pr_notifications">${c.repository_pull_requests}</span>
239 <span class="pr_notifications">${c.repository_pull_requests}</span>
240 %endif
240 %endif
241 <div class="menulabel">${_('Pull Requests')}</div>
241 <div class="menulabel">${_('Pull Requests')}</div>
242 </a>
242 </a>
243 </li>
243 </li>
244 %endif
244 %endif
245 <li class="${is_active('options')}">
245 <li class="${is_active('options')}">
246 <a class="menulink dropdown">
246 <a class="menulink dropdown">
247 <div class="menulabel">${_('Options')} <div class="show_more"></div></div>
247 <div class="menulabel">${_('Options')} <div class="show_more"></div></div>
248 </a>
248 </a>
249 <ul class="submenu">
249 <ul class="submenu">
250 %if h.HasRepoPermissionAll('repository.admin')(c.repo_name):
250 %if h.HasRepoPermissionAll('repository.admin')(c.repo_name):
251 <li><a href="${h.route_path('edit_repo',repo_name=c.repo_name)}">${_('Settings')}</a></li>
251 <li><a href="${h.route_path('edit_repo',repo_name=c.repo_name)}">${_('Settings')}</a></li>
252 %endif
252 %endif
253 %if c.rhodecode_db_repo.fork:
253 %if c.rhodecode_db_repo.fork:
254 <li><a href="${h.url('compare_url',repo_name=c.rhodecode_db_repo.fork.repo_name,source_ref_type=c.rhodecode_db_repo.landing_rev[0],source_ref=c.rhodecode_db_repo.landing_rev[1], target_repo=c.repo_name,target_ref_type='branch' if request.GET.get('branch') else c.rhodecode_db_repo.landing_rev[0],target_ref=request.GET.get('branch') or c.rhodecode_db_repo.landing_rev[1], merge=1)}">
254 <li><a href="${h.url('compare_url',repo_name=c.rhodecode_db_repo.fork.repo_name,source_ref_type=c.rhodecode_db_repo.landing_rev[0],source_ref=c.rhodecode_db_repo.landing_rev[1], target_repo=c.repo_name,target_ref_type='branch' if request.GET.get('branch') else c.rhodecode_db_repo.landing_rev[0],target_ref=request.GET.get('branch') or c.rhodecode_db_repo.landing_rev[1], merge=1)}">
255 ${_('Compare fork')}</a></li>
255 ${_('Compare fork')}</a></li>
256 %endif
256 %endif
257
257
258 <li><a href="${h.route_path('search_repo',repo_name=c.repo_name)}">${_('Search')}</a></li>
258 <li><a href="${h.route_path('search_repo',repo_name=c.repo_name)}">${_('Search')}</a></li>
259
259
260 %if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name) and c.rhodecode_db_repo.enable_locking:
260 %if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name) and c.rhodecode_db_repo.enable_locking:
261 %if c.rhodecode_db_repo.locked[0]:
261 %if c.rhodecode_db_repo.locked[0]:
262 <li><a class="locking_del" href="${h.url('toggle_locking',repo_name=c.repo_name)}">${_('Unlock')}</a></li>
262 <li><a class="locking_del" href="${h.url('toggle_locking',repo_name=c.repo_name)}">${_('Unlock')}</a></li>
263 %else:
263 %else:
264 <li><a class="locking_add" href="${h.url('toggle_locking',repo_name=c.repo_name)}">${_('Lock')}</a></li>
264 <li><a class="locking_add" href="${h.url('toggle_locking',repo_name=c.repo_name)}">${_('Lock')}</a></li>
265 %endif
265 %endif
266 %endif
266 %endif
267 %if c.rhodecode_user.username != h.DEFAULT_USER:
267 %if c.rhodecode_user.username != h.DEFAULT_USER:
268 %if c.rhodecode_db_repo.repo_type in ['git','hg']:
268 %if c.rhodecode_db_repo.repo_type in ['git','hg']:
269 <li><a href="${h.url('repo_fork_home',repo_name=c.repo_name)}">${_('Fork')}</a></li>
269 <li><a href="${h.url('repo_fork_home',repo_name=c.repo_name)}">${_('Fork')}</a></li>
270 <li><a href="${h.url('pullrequest_home',repo_name=c.repo_name)}">${_('Create Pull Request')}</a></li>
270 <li><a href="${h.url('pullrequest_home',repo_name=c.repo_name)}">${_('Create Pull Request')}</a></li>
271 %endif
271 %endif
272 %endif
272 %endif
273 </ul>
273 </ul>
274 </li>
274 </li>
275 </ul>
275 </ul>
276 </div>
276 </div>
277 <div class="clear"></div>
277 <div class="clear"></div>
278 </div>
278 </div>
279 <!--- END CONTEXT BAR -->
279 <!--- END CONTEXT BAR -->
280
280
281 </%def>
281 </%def>
282
282
283 <%def name="usermenu(active=False)">
283 <%def name="usermenu(active=False)">
284 ## USER MENU
284 ## USER MENU
285 <li id="quick_login_li" class="${'active' if active else ''}">
285 <li id="quick_login_li" class="${'active' if active else ''}">
286 <a id="quick_login_link" class="menulink childs">
286 <a id="quick_login_link" class="menulink childs">
287 ${gravatar(c.rhodecode_user.email, 20)}
287 ${gravatar(c.rhodecode_user.email, 20)}
288 <span class="user">
288 <span class="user">
289 %if c.rhodecode_user.username != h.DEFAULT_USER:
289 %if c.rhodecode_user.username != h.DEFAULT_USER:
290 <span class="menu_link_user">${c.rhodecode_user.username}</span><div class="show_more"></div>
290 <span class="menu_link_user">${c.rhodecode_user.username}</span><div class="show_more"></div>
291 %else:
291 %else:
292 <span>${_('Sign in')}</span>
292 <span>${_('Sign in')}</span>
293 %endif
293 %endif
294 </span>
294 </span>
295 </a>
295 </a>
296
296
297 <div class="user-menu submenu">
297 <div class="user-menu submenu">
298 <div id="quick_login">
298 <div id="quick_login">
299 %if c.rhodecode_user.username == h.DEFAULT_USER:
299 %if c.rhodecode_user.username == h.DEFAULT_USER:
300 <h4>${_('Sign in to your account')}</h4>
300 <h4>${_('Sign in to your account')}</h4>
301 ${h.form(h.route_path('login', _query={'came_from': h.url.current()}), needs_csrf_token=False)}
301 ${h.form(h.route_path('login', _query={'came_from': h.url.current()}), needs_csrf_token=False)}
302 <div class="form form-vertical">
302 <div class="form form-vertical">
303 <div class="fields">
303 <div class="fields">
304 <div class="field">
304 <div class="field">
305 <div class="label">
305 <div class="label">
306 <label for="username">${_('Username')}:</label>
306 <label for="username">${_('Username')}:</label>
307 </div>
307 </div>
308 <div class="input">
308 <div class="input">
309 ${h.text('username',class_='focus',tabindex=1)}
309 ${h.text('username',class_='focus',tabindex=1)}
310 </div>
310 </div>
311
311
312 </div>
312 </div>
313 <div class="field">
313 <div class="field">
314 <div class="label">
314 <div class="label">
315 <label for="password">${_('Password')}:</label>
315 <label for="password">${_('Password')}:</label>
316 %if h.HasPermissionAny('hg.password_reset.enabled')():
316 %if h.HasPermissionAny('hg.password_reset.enabled')():
317 <span class="forgot_password">${h.link_to(_('(Forgot password?)'),h.route_path('reset_password'), class_='pwd_reset')}</span>
317 <span class="forgot_password">${h.link_to(_('(Forgot password?)'),h.route_path('reset_password'), class_='pwd_reset')}</span>
318 %endif
318 %endif
319 </div>
319 </div>
320 <div class="input">
320 <div class="input">
321 ${h.password('password',class_='focus',tabindex=2)}
321 ${h.password('password',class_='focus',tabindex=2)}
322 </div>
322 </div>
323 </div>
323 </div>
324 <div class="buttons">
324 <div class="buttons">
325 <div class="register">
325 <div class="register">
326 %if h.HasPermissionAny('hg.admin', 'hg.register.auto_activate', 'hg.register.manual_activate')():
326 %if h.HasPermissionAny('hg.admin', 'hg.register.auto_activate', 'hg.register.manual_activate')():
327 ${h.link_to(_("Don't have an account?"),h.route_path('register'))} <br/>
327 ${h.link_to(_("Don't have an account?"),h.route_path('register'))} <br/>
328 %endif
328 %endif
329 ${h.link_to(_("Using external auth? Sign In here."),h.route_path('login'))}
329 ${h.link_to(_("Using external auth? Sign In here."),h.route_path('login'))}
330 </div>
330 </div>
331 <div class="submit">
331 <div class="submit">
332 ${h.submit('sign_in',_('Sign In'),class_="btn btn-small",tabindex=3)}
332 ${h.submit('sign_in',_('Sign In'),class_="btn btn-small",tabindex=3)}
333 </div>
333 </div>
334 </div>
334 </div>
335 </div>
335 </div>
336 </div>
336 </div>
337 ${h.end_form()}
337 ${h.end_form()}
338 %else:
338 %else:
339 <div class="">
339 <div class="">
340 <div class="big_gravatar">${gravatar(c.rhodecode_user.email, 48)}</div>
340 <div class="big_gravatar">${gravatar(c.rhodecode_user.email, 48)}</div>
341 <div class="full_name">${c.rhodecode_user.full_name_or_username}</div>
341 <div class="full_name">${c.rhodecode_user.full_name_or_username}</div>
342 <div class="email">${c.rhodecode_user.email}</div>
342 <div class="email">${c.rhodecode_user.email}</div>
343 </div>
343 </div>
344 <div class="">
344 <div class="">
345 <ol class="links">
345 <ol class="links">
346 <li>${h.link_to(_(u'My account'),h.route_path('my_account_profile'))}</li>
346 <li>${h.link_to(_(u'My account'),h.route_path('my_account_profile'))}</li>
347 % if c.rhodecode_user.personal_repo_group:
347 % if c.rhodecode_user.personal_repo_group:
348 <li>${h.link_to(_(u'My personal group'), h.route_path('repo_group_home', repo_group_name=c.rhodecode_user.personal_repo_group.group_name))}</li>
348 <li>${h.link_to(_(u'My personal group'), h.route_path('repo_group_home', repo_group_name=c.rhodecode_user.personal_repo_group.group_name))}</li>
349 % endif
349 % endif
350 <li class="logout">
350 <li class="logout">
351 ${h.secure_form(h.route_path('logout'), request=request)}
351 ${h.secure_form(h.route_path('logout'), request=request)}
352 ${h.submit('log_out', _(u'Sign Out'),class_="btn btn-primary")}
352 ${h.submit('log_out', _(u'Sign Out'),class_="btn btn-primary")}
353 ${h.end_form()}
353 ${h.end_form()}
354 </li>
354 </li>
355 </ol>
355 </ol>
356 </div>
356 </div>
357 %endif
357 %endif
358 </div>
358 </div>
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>
370 </%def>
366 </%def>
371
367
372 <%def name="menu_items(active=None)">
368 <%def name="menu_items(active=None)">
373 <%
369 <%
374 def is_active(selected):
370 def is_active(selected):
375 if selected == active:
371 if selected == active:
376 return "active"
372 return "active"
377 return ""
373 return ""
378 %>
374 %>
379 <ul id="quick" class="main_nav navigation horizontal-list">
375 <ul id="quick" class="main_nav navigation horizontal-list">
380 <!-- repo switcher -->
376 <!-- repo switcher -->
381 <li class="${is_active('repositories')} repo_switcher_li has_select2">
377 <li class="${is_active('repositories')} repo_switcher_li has_select2">
382 <input id="repo_switcher" name="repo_switcher" type="hidden">
378 <input id="repo_switcher" name="repo_switcher" type="hidden">
383 </li>
379 </li>
384
380
385 ## ROOT MENU
381 ## ROOT MENU
386 %if c.rhodecode_user.username != h.DEFAULT_USER:
382 %if c.rhodecode_user.username != h.DEFAULT_USER:
387 <li class="${is_active('journal')}">
383 <li class="${is_active('journal')}">
388 <a class="menulink" title="${_('Show activity journal')}" href="${h.url('journal')}">
384 <a class="menulink" title="${_('Show activity journal')}" href="${h.url('journal')}">
389 <div class="menulabel">${_('Journal')}</div>
385 <div class="menulabel">${_('Journal')}</div>
390 </a>
386 </a>
391 </li>
387 </li>
392 %else:
388 %else:
393 <li class="${is_active('journal')}">
389 <li class="${is_active('journal')}">
394 <a class="menulink" title="${_('Show Public activity journal')}" href="${h.url('public_journal')}">
390 <a class="menulink" title="${_('Show Public activity journal')}" href="${h.url('public_journal')}">
395 <div class="menulabel">${_('Public journal')}</div>
391 <div class="menulabel">${_('Public journal')}</div>
396 </a>
392 </a>
397 </li>
393 </li>
398 %endif
394 %endif
399 <li class="${is_active('gists')}">
395 <li class="${is_active('gists')}">
400 <a class="menulink childs" title="${_('Show Gists')}" href="${h.route_path('gists_show')}">
396 <a class="menulink childs" title="${_('Show Gists')}" href="${h.route_path('gists_show')}">
401 <div class="menulabel">${_('Gists')}</div>
397 <div class="menulabel">${_('Gists')}</div>
402 </a>
398 </a>
403 </li>
399 </li>
404 <li class="${is_active('search')}">
400 <li class="${is_active('search')}">
405 <a class="menulink" title="${_('Search in repositories you have access to')}" href="${h.route_path('search')}">
401 <a class="menulink" title="${_('Search in repositories you have access to')}" href="${h.route_path('search')}">
406 <div class="menulabel">${_('Search')}</div>
402 <div class="menulabel">${_('Search')}</div>
407 </a>
403 </a>
408 </li>
404 </li>
409 % if h.HasPermissionAll('hg.admin')('access admin main page'):
405 % if h.HasPermissionAll('hg.admin')('access admin main page'):
410 <li class="${is_active('admin')}">
406 <li class="${is_active('admin')}">
411 <a class="menulink childs" title="${_('Admin settings')}" href="#" onclick="return false;">
407 <a class="menulink childs" title="${_('Admin settings')}" href="#" onclick="return false;">
412 <div class="menulabel">${_('Admin')} <div class="show_more"></div></div>
408 <div class="menulabel">${_('Admin')} <div class="show_more"></div></div>
413 </a>
409 </a>
414 ${admin_menu()}
410 ${admin_menu()}
415 </li>
411 </li>
416 % elif c.rhodecode_user.repositories_admin or c.rhodecode_user.repository_groups_admin or c.rhodecode_user.user_groups_admin:
412 % elif c.rhodecode_user.repositories_admin or c.rhodecode_user.repository_groups_admin or c.rhodecode_user.user_groups_admin:
417 <li class="${is_active('admin')}">
413 <li class="${is_active('admin')}">
418 <a class="menulink childs" title="${_('Delegated Admin settings')}">
414 <a class="menulink childs" title="${_('Delegated Admin settings')}">
419 <div class="menulabel">${_('Admin')} <div class="show_more"></div></div>
415 <div class="menulabel">${_('Admin')} <div class="show_more"></div></div>
420 </a>
416 </a>
421 ${admin_menu_simple(c.rhodecode_user.repositories_admin,
417 ${admin_menu_simple(c.rhodecode_user.repositories_admin,
422 c.rhodecode_user.repository_groups_admin,
418 c.rhodecode_user.repository_groups_admin,
423 c.rhodecode_user.user_groups_admin or h.HasPermissionAny('hg.usergroup.create.true')())}
419 c.rhodecode_user.user_groups_admin or h.HasPermissionAny('hg.usergroup.create.true')())}
424 </li>
420 </li>
425 % endif
421 % endif
426 % if c.debug_style:
422 % if c.debug_style:
427 <li class="${is_active('debug_style')}">
423 <li class="${is_active('debug_style')}">
428 <a class="menulink" title="${_('Style')}" href="${h.route_path('debug_style_home')}">
424 <a class="menulink" title="${_('Style')}" href="${h.route_path('debug_style_home')}">
429 <div class="menulabel">${_('Style')}</div>
425 <div class="menulabel">${_('Style')}</div>
430 </a>
426 </a>
431 </li>
427 </li>
432 % endif
428 % endif
433 ## render extra user menu
429 ## render extra user menu
434 ${usermenu(active=(active=='my_account'))}
430 ${usermenu(active=(active=='my_account'))}
435 </ul>
431 </ul>
436
432
437 <script type="text/javascript">
433 <script type="text/javascript">
438 var visual_show_public_icon = "${c.visual.show_public_icon}" == "True";
434 var visual_show_public_icon = "${c.visual.show_public_icon}" == "True";
439
435
440 /*format the look of items in the list*/
436 /*format the look of items in the list*/
441 var format = function(state, escapeMarkup){
437 var format = function(state, escapeMarkup){
442 if (!state.id){
438 if (!state.id){
443 return state.text; // optgroup
439 return state.text; // optgroup
444 }
440 }
445 var obj_dict = state.obj;
441 var obj_dict = state.obj;
446 var tmpl = '';
442 var tmpl = '';
447
443
448 if(obj_dict && state.type == 'repo'){
444 if(obj_dict && state.type == 'repo'){
449 if(obj_dict['repo_type'] === 'hg'){
445 if(obj_dict['repo_type'] === 'hg'){
450 tmpl += '<i class="icon-hg"></i> ';
446 tmpl += '<i class="icon-hg"></i> ';
451 }
447 }
452 else if(obj_dict['repo_type'] === 'git'){
448 else if(obj_dict['repo_type'] === 'git'){
453 tmpl += '<i class="icon-git"></i> ';
449 tmpl += '<i class="icon-git"></i> ';
454 }
450 }
455 else if(obj_dict['repo_type'] === 'svn'){
451 else if(obj_dict['repo_type'] === 'svn'){
456 tmpl += '<i class="icon-svn"></i> ';
452 tmpl += '<i class="icon-svn"></i> ';
457 }
453 }
458 if(obj_dict['private']){
454 if(obj_dict['private']){
459 tmpl += '<i class="icon-lock" ></i> ';
455 tmpl += '<i class="icon-lock" ></i> ';
460 }
456 }
461 else if(visual_show_public_icon){
457 else if(visual_show_public_icon){
462 tmpl += '<i class="icon-unlock-alt"></i> ';
458 tmpl += '<i class="icon-unlock-alt"></i> ';
463 }
459 }
464 }
460 }
465 if(obj_dict && state.type == 'commit') {
461 if(obj_dict && state.type == 'commit') {
466 tmpl += '<i class="icon-tag"></i>';
462 tmpl += '<i class="icon-tag"></i>';
467 }
463 }
468 if(obj_dict && state.type == 'group'){
464 if(obj_dict && state.type == 'group'){
469 tmpl += '<i class="icon-folder-close"></i> ';
465 tmpl += '<i class="icon-folder-close"></i> ';
470 }
466 }
471 tmpl += escapeMarkup(state.text);
467 tmpl += escapeMarkup(state.text);
472 return tmpl;
468 return tmpl;
473 };
469 };
474
470
475 var formatResult = function(result, container, query, escapeMarkup) {
471 var formatResult = function(result, container, query, escapeMarkup) {
476 return format(result, escapeMarkup);
472 return format(result, escapeMarkup);
477 };
473 };
478
474
479 var formatSelection = function(data, container, escapeMarkup) {
475 var formatSelection = function(data, container, escapeMarkup) {
480 return format(data, escapeMarkup);
476 return format(data, escapeMarkup);
481 };
477 };
482
478
483 $("#repo_switcher").select2({
479 $("#repo_switcher").select2({
484 cachedDataSource: {},
480 cachedDataSource: {},
485 minimumInputLength: 2,
481 minimumInputLength: 2,
486 placeholder: '<div class="menulabel">${_('Go to')} <div class="show_more"></div></div>',
482 placeholder: '<div class="menulabel">${_('Go to')} <div class="show_more"></div></div>',
487 dropdownAutoWidth: true,
483 dropdownAutoWidth: true,
488 formatResult: formatResult,
484 formatResult: formatResult,
489 formatSelection: formatSelection,
485 formatSelection: formatSelection,
490 containerCssClass: "repo-switcher",
486 containerCssClass: "repo-switcher",
491 dropdownCssClass: "repo-switcher-dropdown",
487 dropdownCssClass: "repo-switcher-dropdown",
492 escapeMarkup: function(m){
488 escapeMarkup: function(m){
493 // don't escape our custom placeholder
489 // don't escape our custom placeholder
494 if(m.substr(0,23) == '<div class="menulabel">'){
490 if(m.substr(0,23) == '<div class="menulabel">'){
495 return m;
491 return m;
496 }
492 }
497
493
498 return Select2.util.escapeMarkup(m);
494 return Select2.util.escapeMarkup(m);
499 },
495 },
500 query: $.debounce(250, function(query){
496 query: $.debounce(250, function(query){
501 self = this;
497 self = this;
502 var cacheKey = query.term;
498 var cacheKey = query.term;
503 var cachedData = self.cachedDataSource[cacheKey];
499 var cachedData = self.cachedDataSource[cacheKey];
504
500
505 if (cachedData) {
501 if (cachedData) {
506 query.callback({results: cachedData.results});
502 query.callback({results: cachedData.results});
507 } else {
503 } else {
508 $.ajax({
504 $.ajax({
509 url: pyroutes.url('goto_switcher_data'),
505 url: pyroutes.url('goto_switcher_data'),
510 data: {'query': query.term},
506 data: {'query': query.term},
511 dataType: 'json',
507 dataType: 'json',
512 type: 'GET',
508 type: 'GET',
513 success: function(data) {
509 success: function(data) {
514 self.cachedDataSource[cacheKey] = data;
510 self.cachedDataSource[cacheKey] = data;
515 query.callback({results: data.results});
511 query.callback({results: data.results});
516 },
512 },
517 error: function(data, textStatus, errorThrown) {
513 error: function(data, textStatus, errorThrown) {
518 alert("Error while fetching entries.\nError code {0} ({1}).".format(data.status, data.statusText));
514 alert("Error while fetching entries.\nError code {0} ({1}).".format(data.status, data.statusText));
519 }
515 }
520 })
516 })
521 }
517 }
522 })
518 })
523 });
519 });
524
520
525 $("#repo_switcher").on('select2-selecting', function(e){
521 $("#repo_switcher").on('select2-selecting', function(e){
526 e.preventDefault();
522 e.preventDefault();
527 window.location = e.choice.url;
523 window.location = e.choice.url;
528 });
524 });
529
525
530 </script>
526 </script>
531 <script src="${h.asset('js/rhodecode/base/keyboard-bindings.js', ver=c.rhodecode_version_hash)}"></script>
527 <script src="${h.asset('js/rhodecode/base/keyboard-bindings.js', ver=c.rhodecode_version_hash)}"></script>
532 </%def>
528 </%def>
533
529
534 <div class="modal" id="help_kb" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
530 <div class="modal" id="help_kb" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
535 <div class="modal-dialog">
531 <div class="modal-dialog">
536 <div class="modal-content">
532 <div class="modal-content">
537 <div class="modal-header">
533 <div class="modal-header">
538 <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
534 <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
539 <h4 class="modal-title" id="myModalLabel">${_('Keyboard shortcuts')}</h4>
535 <h4 class="modal-title" id="myModalLabel">${_('Keyboard shortcuts')}</h4>
540 </div>
536 </div>
541 <div class="modal-body">
537 <div class="modal-body">
542 <div class="block-left">
538 <div class="block-left">
543 <table class="keyboard-mappings">
539 <table class="keyboard-mappings">
544 <tbody>
540 <tbody>
545 <tr>
541 <tr>
546 <th></th>
542 <th></th>
547 <th>${_('Site-wide shortcuts')}</th>
543 <th>${_('Site-wide shortcuts')}</th>
548 </tr>
544 </tr>
549 <%
545 <%
550 elems = [
546 elems = [
551 ('/', 'Open quick search box'),
547 ('/', 'Open quick search box'),
552 ('g h', 'Goto home page'),
548 ('g h', 'Goto home page'),
553 ('g g', 'Goto my private gists page'),
549 ('g g', 'Goto my private gists page'),
554 ('g G', 'Goto my public gists page'),
550 ('g G', 'Goto my public gists page'),
555 ('n r', 'New repository page'),
551 ('n r', 'New repository page'),
556 ('n g', 'New gist page'),
552 ('n g', 'New gist page'),
557 ]
553 ]
558 %>
554 %>
559 %for key, desc in elems:
555 %for key, desc in elems:
560 <tr>
556 <tr>
561 <td class="keys">
557 <td class="keys">
562 <span class="key tag">${key}</span>
558 <span class="key tag">${key}</span>
563 </td>
559 </td>
564 <td>${desc}</td>
560 <td>${desc}</td>
565 </tr>
561 </tr>
566 %endfor
562 %endfor
567 </tbody>
563 </tbody>
568 </table>
564 </table>
569 </div>
565 </div>
570 <div class="block-left">
566 <div class="block-left">
571 <table class="keyboard-mappings">
567 <table class="keyboard-mappings">
572 <tbody>
568 <tbody>
573 <tr>
569 <tr>
574 <th></th>
570 <th></th>
575 <th>${_('Repositories')}</th>
571 <th>${_('Repositories')}</th>
576 </tr>
572 </tr>
577 <%
573 <%
578 elems = [
574 elems = [
579 ('g s', 'Goto summary page'),
575 ('g s', 'Goto summary page'),
580 ('g c', 'Goto changelog page'),
576 ('g c', 'Goto changelog page'),
581 ('g f', 'Goto files page'),
577 ('g f', 'Goto files page'),
582 ('g F', 'Goto files page with file search activated'),
578 ('g F', 'Goto files page with file search activated'),
583 ('g p', 'Goto pull requests page'),
579 ('g p', 'Goto pull requests page'),
584 ('g o', 'Goto repository settings'),
580 ('g o', 'Goto repository settings'),
585 ('g O', 'Goto repository permissions settings'),
581 ('g O', 'Goto repository permissions settings'),
586 ]
582 ]
587 %>
583 %>
588 %for key, desc in elems:
584 %for key, desc in elems:
589 <tr>
585 <tr>
590 <td class="keys">
586 <td class="keys">
591 <span class="key tag">${key}</span>
587 <span class="key tag">${key}</span>
592 </td>
588 </td>
593 <td>${desc}</td>
589 <td>${desc}</td>
594 </tr>
590 </tr>
595 %endfor
591 %endfor
596 </tbody>
592 </tbody>
597 </table>
593 </table>
598 </div>
594 </div>
599 </div>
595 </div>
600 <div class="modal-footer">
596 <div class="modal-footer">
601 </div>
597 </div>
602 </div><!-- /.modal-content -->
598 </div><!-- /.modal-content -->
603 </div><!-- /.modal-dialog -->
599 </div><!-- /.modal-dialog -->
604 </div><!-- /.modal -->
600 </div><!-- /.modal -->
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