##// END OF EJS Templates
my-account: moved few my account views into pyramid.
marcink -
r1819:956c5cda default
parent child Browse files
Show More
@@ -0,0 +1,76 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 pytest
22
23 from rhodecode.apps._base import ADMIN_PREFIX
24 from rhodecode.model.db import User, UserEmailMap, Repository, UserFollowing
25 from rhodecode.tests import (
26 TestController, TEST_USER_ADMIN_LOGIN, TEST_USER_REGULAR_EMAIL,
27 assert_session_flash)
28 from rhodecode.tests.fixture import Fixture
29
30 fixture = Fixture()
31
32
33 def route_path(name, **kwargs):
34 return {
35 'my_account_repos':
36 ADMIN_PREFIX + '/my_account/repos',
37 'my_account_watched':
38 ADMIN_PREFIX + '/my_account/watched',
39 'my_account_perms':
40 ADMIN_PREFIX + '/my_account/perms',
41 'my_account_notifications':
42 ADMIN_PREFIX + '/my_account/notifications',
43 }[name].format(**kwargs)
44
45
46 class TestMyAccountSimpleViews(TestController):
47
48 def test_my_account_my_repos(self, autologin_user):
49 response = self.app.get(route_path('my_account_repos'))
50 repos = Repository.query().filter(
51 Repository.user == User.get_by_username(
52 TEST_USER_ADMIN_LOGIN)).all()
53 for repo in repos:
54 response.mustcontain('"name_raw": "%s"' % repo.repo_name)
55
56 def test_my_account_my_watched(self, autologin_user):
57 response = self.app.get(route_path('my_account_watched'))
58
59 repos = UserFollowing.query().filter(
60 UserFollowing.user == User.get_by_username(
61 TEST_USER_ADMIN_LOGIN)).all()
62 for repo in repos:
63 response.mustcontain(
64 '"name_raw": "%s"' % repo.follows_repository.repo_name)
65
66 def test_my_account_perms(self, autologin_user):
67 response = self.app.get(route_path('my_account_perms'))
68 assert_response = response.assert_response()
69 assert assert_response.get_elements('.perm_tag.none')
70 assert assert_response.get_elements('.perm_tag.read')
71 assert assert_response.get_elements('.perm_tag.write')
72 assert assert_response.get_elements('.perm_tag.admin')
73
74 def test_my_account_notifications(self, autologin_user):
75 response = self.app.get(route_path('my_account_notifications'))
76 response.mustcontain('Test flash message')
@@ -1,65 +1,85 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 config.add_route(
31 config.add_route(
32 name='my_account_password',
32 name='my_account_password',
33 pattern=ADMIN_PREFIX + '/my_account/password')
33 pattern=ADMIN_PREFIX + '/my_account/password')
34
34
35 config.add_route(
35 config.add_route(
36 name='my_account_password_update',
36 name='my_account_password_update',
37 pattern=ADMIN_PREFIX + '/my_account/password')
37 pattern=ADMIN_PREFIX + '/my_account/password')
38
38
39 config.add_route(
39 config.add_route(
40 name='my_account_auth_tokens',
40 name='my_account_auth_tokens',
41 pattern=ADMIN_PREFIX + '/my_account/auth_tokens')
41 pattern=ADMIN_PREFIX + '/my_account/auth_tokens')
42 config.add_route(
42 config.add_route(
43 name='my_account_auth_tokens_add',
43 name='my_account_auth_tokens_add',
44 pattern=ADMIN_PREFIX + '/my_account/auth_tokens/new')
44 pattern=ADMIN_PREFIX + '/my_account/auth_tokens/new')
45 config.add_route(
45 config.add_route(
46 name='my_account_auth_tokens_delete',
46 name='my_account_auth_tokens_delete',
47 pattern=ADMIN_PREFIX + '/my_account/auth_tokens/delete')
47 pattern=ADMIN_PREFIX + '/my_account/auth_tokens/delete')
48
48
49 config.add_route(
49 config.add_route(
50 name='my_account_emails',
50 name='my_account_emails',
51 pattern=ADMIN_PREFIX + '/my_account/emails')
51 pattern=ADMIN_PREFIX + '/my_account/emails')
52 config.add_route(
52 config.add_route(
53 name='my_account_emails_add',
53 name='my_account_emails_add',
54 pattern=ADMIN_PREFIX + '/my_account/emails/new')
54 pattern=ADMIN_PREFIX + '/my_account/emails/new')
55 config.add_route(
55 config.add_route(
56 name='my_account_emails_delete',
56 name='my_account_emails_delete',
57 pattern=ADMIN_PREFIX + '/my_account/emails/delete')
57 pattern=ADMIN_PREFIX + '/my_account/emails/delete')
58
58
59 config.add_route(
60 name='my_account_repos',
61 pattern=ADMIN_PREFIX + '/my_account/repos')
62
63 config.add_route(
64 name='my_account_watched',
65 pattern=ADMIN_PREFIX + '/my_account/watched')
66
67 config.add_route(
68 name='my_account_perms',
69 pattern=ADMIN_PREFIX + '/my_account/perms')
70
71 config.add_route(
72 name='my_account_notifications',
73 pattern=ADMIN_PREFIX + '/my_account/notifications')
74
75 config.add_route(
76 name='my_account_notifications_toggle_visibility',
77 pattern=ADMIN_PREFIX + '/my_account/toggle_visibility')
78
59 # channelstream test
79 # channelstream test
60 config.add_route(
80 config.add_route(
61 name='my_account_notifications_test_channelstream',
81 name='my_account_notifications_test_channelstream',
62 pattern=ADMIN_PREFIX + '/my_account/test_channelstream')
82 pattern=ADMIN_PREFIX + '/my_account/test_channelstream')
63
83
64 # Scan module for configuration decorators.
84 # Scan module for configuration decorators.
65 config.scan()
85 config.scan()
@@ -1,292 +1,378 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 import logging
21 import logging
22 import datetime
22 import datetime
23
23
24 import formencode
24 import formencode
25 from pyramid.httpexceptions import HTTPFound
25 from pyramid.httpexceptions import HTTPFound
26 from pyramid.view import view_config
26 from pyramid.view import view_config
27
27
28 from rhodecode.apps._base import BaseAppView
28 from rhodecode.apps._base import BaseAppView
29 from rhodecode import forms
29 from rhodecode import forms
30 from rhodecode.lib import helpers as h
30 from rhodecode.lib import helpers as h
31 from rhodecode.lib.ext_json import json
31 from rhodecode.lib.auth import LoginRequired, NotAnonymous, CSRFRequired
32 from rhodecode.lib.auth import LoginRequired, NotAnonymous, CSRFRequired
32 from rhodecode.lib.channelstream import channelstream_request, \
33 from rhodecode.lib.channelstream import channelstream_request, \
33 ChannelstreamException
34 ChannelstreamException
34 from rhodecode.lib.utils2 import safe_int, md5
35 from rhodecode.lib.utils2 import safe_int, md5
35 from rhodecode.model.auth_token import AuthTokenModel
36 from rhodecode.model.auth_token import AuthTokenModel
36 from rhodecode.model.db import UserEmailMap
37 from rhodecode.model.db import (
38 Repository, PullRequest, UserEmailMap, User, UserFollowing, joinedload)
37 from rhodecode.model.meta import Session
39 from rhodecode.model.meta import Session
40 from rhodecode.model.scm import RepoList
38 from rhodecode.model.user import UserModel
41 from rhodecode.model.user import UserModel
42 from rhodecode.model.repo import RepoModel
39 from rhodecode.model.validation_schema.schemas import user_schema
43 from rhodecode.model.validation_schema.schemas import user_schema
40
44
41 log = logging.getLogger(__name__)
45 log = logging.getLogger(__name__)
42
46
43
47
44 class MyAccountView(BaseAppView):
48 class MyAccountView(BaseAppView):
45 ALLOW_SCOPED_TOKENS = False
49 ALLOW_SCOPED_TOKENS = False
46 """
50 """
47 This view has alternative version inside EE, if modified please take a look
51 This view has alternative version inside EE, if modified please take a look
48 in there as well.
52 in there as well.
49 """
53 """
50
54
51 def load_default_context(self):
55 def load_default_context(self):
52 c = self._get_local_tmpl_context()
56 c = self._get_local_tmpl_context()
53 c.user = c.auth_user.get_instance()
57 c.user = c.auth_user.get_instance()
54 c.allow_scoped_tokens = self.ALLOW_SCOPED_TOKENS
58 c.allow_scoped_tokens = self.ALLOW_SCOPED_TOKENS
55 self._register_global_c(c)
59 self._register_global_c(c)
56 return c
60 return c
57
61
58 @LoginRequired()
62 @LoginRequired()
59 @NotAnonymous()
63 @NotAnonymous()
60 @view_config(
64 @view_config(
61 route_name='my_account_profile', request_method='GET',
65 route_name='my_account_profile', request_method='GET',
62 renderer='rhodecode:templates/admin/my_account/my_account.mako')
66 renderer='rhodecode:templates/admin/my_account/my_account.mako')
63 def my_account_profile(self):
67 def my_account_profile(self):
64 c = self.load_default_context()
68 c = self.load_default_context()
65 c.active = 'profile'
69 c.active = 'profile'
66 return self._get_template_context(c)
70 return self._get_template_context(c)
67
71
68 @LoginRequired()
72 @LoginRequired()
69 @NotAnonymous()
73 @NotAnonymous()
70 @view_config(
74 @view_config(
71 route_name='my_account_password', request_method='GET',
75 route_name='my_account_password', request_method='GET',
72 renderer='rhodecode:templates/admin/my_account/my_account.mako')
76 renderer='rhodecode:templates/admin/my_account/my_account.mako')
73 def my_account_password(self):
77 def my_account_password(self):
74 c = self.load_default_context()
78 c = self.load_default_context()
75 c.active = 'password'
79 c.active = 'password'
76 c.extern_type = c.user.extern_type
80 c.extern_type = c.user.extern_type
77
81
78 schema = user_schema.ChangePasswordSchema().bind(
82 schema = user_schema.ChangePasswordSchema().bind(
79 username=c.user.username)
83 username=c.user.username)
80
84
81 form = forms.Form(
85 form = forms.Form(
82 schema, buttons=(forms.buttons.save, forms.buttons.reset))
86 schema, buttons=(forms.buttons.save, forms.buttons.reset))
83
87
84 c.form = form
88 c.form = form
85 return self._get_template_context(c)
89 return self._get_template_context(c)
86
90
87 @LoginRequired()
91 @LoginRequired()
88 @NotAnonymous()
92 @NotAnonymous()
89 @CSRFRequired()
93 @CSRFRequired()
90 @view_config(
94 @view_config(
91 route_name='my_account_password', request_method='POST',
95 route_name='my_account_password', request_method='POST',
92 renderer='rhodecode:templates/admin/my_account/my_account.mako')
96 renderer='rhodecode:templates/admin/my_account/my_account.mako')
93 def my_account_password_update(self):
97 def my_account_password_update(self):
94 _ = self.request.translate
98 _ = self.request.translate
95 c = self.load_default_context()
99 c = self.load_default_context()
96 c.active = 'password'
100 c.active = 'password'
97 c.extern_type = c.user.extern_type
101 c.extern_type = c.user.extern_type
98
102
99 schema = user_schema.ChangePasswordSchema().bind(
103 schema = user_schema.ChangePasswordSchema().bind(
100 username=c.user.username)
104 username=c.user.username)
101
105
102 form = forms.Form(
106 form = forms.Form(
103 schema, buttons=(forms.buttons.save, forms.buttons.reset))
107 schema, buttons=(forms.buttons.save, forms.buttons.reset))
104
108
105 if c.extern_type != 'rhodecode':
109 if c.extern_type != 'rhodecode':
106 raise HTTPFound(self.request.route_path('my_account_password'))
110 raise HTTPFound(self.request.route_path('my_account_password'))
107
111
108 controls = self.request.POST.items()
112 controls = self.request.POST.items()
109 try:
113 try:
110 valid_data = form.validate(controls)
114 valid_data = form.validate(controls)
111 UserModel().update_user(c.user.user_id, **valid_data)
115 UserModel().update_user(c.user.user_id, **valid_data)
112 c.user.update_userdata(force_password_change=False)
116 c.user.update_userdata(force_password_change=False)
113 Session().commit()
117 Session().commit()
114 except forms.ValidationFailure as e:
118 except forms.ValidationFailure as e:
115 c.form = e
119 c.form = e
116 return self._get_template_context(c)
120 return self._get_template_context(c)
117
121
118 except Exception:
122 except Exception:
119 log.exception("Exception updating password")
123 log.exception("Exception updating password")
120 h.flash(_('Error occurred during update of user password'),
124 h.flash(_('Error occurred during update of user password'),
121 category='error')
125 category='error')
122 else:
126 else:
123 instance = c.auth_user.get_instance()
127 instance = c.auth_user.get_instance()
124 self.session.setdefault('rhodecode_user', {}).update(
128 self.session.setdefault('rhodecode_user', {}).update(
125 {'password': md5(instance.password)})
129 {'password': md5(instance.password)})
126 self.session.save()
130 self.session.save()
127 h.flash(_("Successfully updated password"), category='success')
131 h.flash(_("Successfully updated password"), category='success')
128
132
129 raise HTTPFound(self.request.route_path('my_account_password'))
133 raise HTTPFound(self.request.route_path('my_account_password'))
130
134
131 @LoginRequired()
135 @LoginRequired()
132 @NotAnonymous()
136 @NotAnonymous()
133 @view_config(
137 @view_config(
134 route_name='my_account_auth_tokens', request_method='GET',
138 route_name='my_account_auth_tokens', request_method='GET',
135 renderer='rhodecode:templates/admin/my_account/my_account.mako')
139 renderer='rhodecode:templates/admin/my_account/my_account.mako')
136 def my_account_auth_tokens(self):
140 def my_account_auth_tokens(self):
137 _ = self.request.translate
141 _ = self.request.translate
138
142
139 c = self.load_default_context()
143 c = self.load_default_context()
140 c.active = 'auth_tokens'
144 c.active = 'auth_tokens'
141
145
142 c.lifetime_values = [
146 c.lifetime_values = [
143 (str(-1), _('forever')),
147 (str(-1), _('forever')),
144 (str(5), _('5 minutes')),
148 (str(5), _('5 minutes')),
145 (str(60), _('1 hour')),
149 (str(60), _('1 hour')),
146 (str(60 * 24), _('1 day')),
150 (str(60 * 24), _('1 day')),
147 (str(60 * 24 * 30), _('1 month')),
151 (str(60 * 24 * 30), _('1 month')),
148 ]
152 ]
149 c.lifetime_options = [(c.lifetime_values, _("Lifetime"))]
153 c.lifetime_options = [(c.lifetime_values, _("Lifetime"))]
150 c.role_values = [
154 c.role_values = [
151 (x, AuthTokenModel.cls._get_role_name(x))
155 (x, AuthTokenModel.cls._get_role_name(x))
152 for x in AuthTokenModel.cls.ROLES]
156 for x in AuthTokenModel.cls.ROLES]
153 c.role_options = [(c.role_values, _("Role"))]
157 c.role_options = [(c.role_values, _("Role"))]
154 c.user_auth_tokens = AuthTokenModel().get_auth_tokens(
158 c.user_auth_tokens = AuthTokenModel().get_auth_tokens(
155 c.user.user_id, show_expired=True)
159 c.user.user_id, show_expired=True)
156 return self._get_template_context(c)
160 return self._get_template_context(c)
157
161
158 def maybe_attach_token_scope(self, token):
162 def maybe_attach_token_scope(self, token):
159 # implemented in EE edition
163 # implemented in EE edition
160 pass
164 pass
161
165
162 @LoginRequired()
166 @LoginRequired()
163 @NotAnonymous()
167 @NotAnonymous()
164 @CSRFRequired()
168 @CSRFRequired()
165 @view_config(
169 @view_config(
166 route_name='my_account_auth_tokens_add', request_method='POST',)
170 route_name='my_account_auth_tokens_add', request_method='POST',)
167 def my_account_auth_tokens_add(self):
171 def my_account_auth_tokens_add(self):
168 _ = self.request.translate
172 _ = self.request.translate
169 c = self.load_default_context()
173 c = self.load_default_context()
170
174
171 lifetime = safe_int(self.request.POST.get('lifetime'), -1)
175 lifetime = safe_int(self.request.POST.get('lifetime'), -1)
172 description = self.request.POST.get('description')
176 description = self.request.POST.get('description')
173 role = self.request.POST.get('role')
177 role = self.request.POST.get('role')
174
178
175 token = AuthTokenModel().create(
179 token = AuthTokenModel().create(
176 c.user.user_id, description, lifetime, role)
180 c.user.user_id, description, lifetime, role)
177 self.maybe_attach_token_scope(token)
181 self.maybe_attach_token_scope(token)
178 Session().commit()
182 Session().commit()
179
183
180 h.flash(_("Auth token successfully created"), category='success')
184 h.flash(_("Auth token successfully created"), category='success')
181 return HTTPFound(h.route_path('my_account_auth_tokens'))
185 return HTTPFound(h.route_path('my_account_auth_tokens'))
182
186
183 @LoginRequired()
187 @LoginRequired()
184 @NotAnonymous()
188 @NotAnonymous()
185 @CSRFRequired()
189 @CSRFRequired()
186 @view_config(
190 @view_config(
187 route_name='my_account_auth_tokens_delete', request_method='POST')
191 route_name='my_account_auth_tokens_delete', request_method='POST')
188 def my_account_auth_tokens_delete(self):
192 def my_account_auth_tokens_delete(self):
189 _ = self.request.translate
193 _ = self.request.translate
190 c = self.load_default_context()
194 c = self.load_default_context()
191
195
192 del_auth_token = self.request.POST.get('del_auth_token')
196 del_auth_token = self.request.POST.get('del_auth_token')
193
197
194 if del_auth_token:
198 if del_auth_token:
195 AuthTokenModel().delete(del_auth_token, c.user.user_id)
199 AuthTokenModel().delete(del_auth_token, c.user.user_id)
196 Session().commit()
200 Session().commit()
197 h.flash(_("Auth token successfully deleted"), category='success')
201 h.flash(_("Auth token successfully deleted"), category='success')
198
202
199 return HTTPFound(h.route_path('my_account_auth_tokens'))
203 return HTTPFound(h.route_path('my_account_auth_tokens'))
200
204
201 @LoginRequired()
205 @LoginRequired()
202 @NotAnonymous()
206 @NotAnonymous()
203 @view_config(
207 @view_config(
204 route_name='my_account_emails', request_method='GET',
208 route_name='my_account_emails', request_method='GET',
205 renderer='rhodecode:templates/admin/my_account/my_account.mako')
209 renderer='rhodecode:templates/admin/my_account/my_account.mako')
206 def my_account_emails(self):
210 def my_account_emails(self):
207 _ = self.request.translate
211 _ = self.request.translate
208
212
209 c = self.load_default_context()
213 c = self.load_default_context()
210 c.active = 'emails'
214 c.active = 'emails'
211
215
212 c.user_email_map = UserEmailMap.query()\
216 c.user_email_map = UserEmailMap.query()\
213 .filter(UserEmailMap.user == c.user).all()
217 .filter(UserEmailMap.user == c.user).all()
214 return self._get_template_context(c)
218 return self._get_template_context(c)
215
219
216 @LoginRequired()
220 @LoginRequired()
217 @NotAnonymous()
221 @NotAnonymous()
218 @CSRFRequired()
222 @CSRFRequired()
219 @view_config(
223 @view_config(
220 route_name='my_account_emails_add', request_method='POST')
224 route_name='my_account_emails_add', request_method='POST')
221 def my_account_emails_add(self):
225 def my_account_emails_add(self):
222 _ = self.request.translate
226 _ = self.request.translate
223 c = self.load_default_context()
227 c = self.load_default_context()
224
228
225 email = self.request.POST.get('new_email')
229 email = self.request.POST.get('new_email')
226
230
227 try:
231 try:
228 UserModel().add_extra_email(c.user.user_id, email)
232 UserModel().add_extra_email(c.user.user_id, email)
229 Session().commit()
233 Session().commit()
230 h.flash(_("Added new email address `%s` for user account") % email,
234 h.flash(_("Added new email address `%s` for user account") % email,
231 category='success')
235 category='success')
232 except formencode.Invalid as error:
236 except formencode.Invalid as error:
233 msg = error.error_dict['email']
237 msg = error.error_dict['email']
234 h.flash(msg, category='error')
238 h.flash(msg, category='error')
235 except Exception:
239 except Exception:
236 log.exception("Exception in my_account_emails")
240 log.exception("Exception in my_account_emails")
237 h.flash(_('An error occurred during email saving'),
241 h.flash(_('An error occurred during email saving'),
238 category='error')
242 category='error')
239 return HTTPFound(h.route_path('my_account_emails'))
243 return HTTPFound(h.route_path('my_account_emails'))
240
244
241 @LoginRequired()
245 @LoginRequired()
242 @NotAnonymous()
246 @NotAnonymous()
243 @CSRFRequired()
247 @CSRFRequired()
244 @view_config(
248 @view_config(
245 route_name='my_account_emails_delete', request_method='POST')
249 route_name='my_account_emails_delete', request_method='POST')
246 def my_account_emails_delete(self):
250 def my_account_emails_delete(self):
247 _ = self.request.translate
251 _ = self.request.translate
248 c = self.load_default_context()
252 c = self.load_default_context()
249
253
250 del_email_id = self.request.POST.get('del_email_id')
254 del_email_id = self.request.POST.get('del_email_id')
251 if del_email_id:
255 if del_email_id:
252
256
253 UserModel().delete_extra_email(
257 UserModel().delete_extra_email(
254 c.user.user_id, del_email_id)
258 c.user.user_id, del_email_id)
255 Session().commit()
259 Session().commit()
256 h.flash(_("Email successfully deleted"),
260 h.flash(_("Email successfully deleted"),
257 category='success')
261 category='success')
258 return HTTPFound(h.route_path('my_account_emails'))
262 return HTTPFound(h.route_path('my_account_emails'))
259
263
260 @LoginRequired()
264 @LoginRequired()
261 @NotAnonymous()
265 @NotAnonymous()
262 @CSRFRequired()
266 @CSRFRequired()
263 @view_config(
267 @view_config(
264 route_name='my_account_notifications_test_channelstream',
268 route_name='my_account_notifications_test_channelstream',
265 request_method='POST', renderer='json_ext')
269 request_method='POST', renderer='json_ext')
266 def my_account_notifications_test_channelstream(self):
270 def my_account_notifications_test_channelstream(self):
267 message = 'Test message sent via Channelstream by user: {}, on {}'.format(
271 message = 'Test message sent via Channelstream by user: {}, on {}'.format(
268 self._rhodecode_user.username, datetime.datetime.now())
272 self._rhodecode_user.username, datetime.datetime.now())
269 payload = {
273 payload = {
270 # 'channel': 'broadcast',
274 # 'channel': 'broadcast',
271 'type': 'message',
275 'type': 'message',
272 'timestamp': datetime.datetime.utcnow(),
276 'timestamp': datetime.datetime.utcnow(),
273 'user': 'system',
277 'user': 'system',
274 'pm_users': [self._rhodecode_user.username],
278 'pm_users': [self._rhodecode_user.username],
275 'message': {
279 'message': {
276 'message': message,
280 'message': message,
277 'level': 'info',
281 'level': 'info',
278 'topic': '/notifications'
282 'topic': '/notifications'
279 }
283 }
280 }
284 }
281
285
282 registry = self.request.registry
286 registry = self.request.registry
283 rhodecode_plugins = getattr(registry, 'rhodecode_plugins', {})
287 rhodecode_plugins = getattr(registry, 'rhodecode_plugins', {})
284 channelstream_config = rhodecode_plugins.get('channelstream', {})
288 channelstream_config = rhodecode_plugins.get('channelstream', {})
285
289
286 try:
290 try:
287 channelstream_request(channelstream_config, [payload], '/message')
291 channelstream_request(channelstream_config, [payload], '/message')
288 except ChannelstreamException as e:
292 except ChannelstreamException as e:
289 log.exception('Failed to send channelstream data')
293 log.exception('Failed to send channelstream data')
290 return {"response": 'ERROR: {}'.format(e.__class__.__name__)}
294 return {"response": 'ERROR: {}'.format(e.__class__.__name__)}
291 return {"response": 'Channelstream data sent. '
295 return {"response": 'Channelstream data sent. '
292 'You should see a new live message now.'}
296 'You should see a new live message now.'}
297
298 def _load_my_repos_data(self, watched=False):
299 if watched:
300 admin = False
301 follows_repos = Session().query(UserFollowing)\
302 .filter(UserFollowing.user_id == self._rhodecode_user.user_id)\
303 .options(joinedload(UserFollowing.follows_repository))\
304 .all()
305 repo_list = [x.follows_repository for x in follows_repos]
306 else:
307 admin = True
308 repo_list = Repository.get_all_repos(
309 user_id=self._rhodecode_user.user_id)
310 repo_list = RepoList(repo_list, perm_set=[
311 'repository.read', 'repository.write', 'repository.admin'])
312
313 repos_data = RepoModel().get_repos_as_dict(
314 repo_list=repo_list, admin=admin)
315 # json used to render the grid
316 return json.dumps(repos_data)
317
318 @LoginRequired()
319 @NotAnonymous()
320 @view_config(
321 route_name='my_account_repos', request_method='GET',
322 renderer='rhodecode:templates/admin/my_account/my_account.mako')
323 def my_account_repos(self):
324 c = self.load_default_context()
325 c.active = 'repos'
326
327 # json used to render the grid
328 c.data = self._load_my_repos_data()
329 return self._get_template_context(c)
330
331 @LoginRequired()
332 @NotAnonymous()
333 @view_config(
334 route_name='my_account_watched', request_method='GET',
335 renderer='rhodecode:templates/admin/my_account/my_account.mako')
336 def my_account_watched(self):
337 c = self.load_default_context()
338 c.active = 'watched'
339
340 # json used to render the grid
341 c.data = self._load_my_repos_data(watched=True)
342 return self._get_template_context(c)
343
344 @LoginRequired()
345 @NotAnonymous()
346 @view_config(
347 route_name='my_account_perms', request_method='GET',
348 renderer='rhodecode:templates/admin/my_account/my_account.mako')
349 def my_account_perms(self):
350 c = self.load_default_context()
351 c.active = 'perms'
352
353 c.perm_user = c.auth_user
354 return self._get_template_context(c)
355
356 @LoginRequired()
357 @NotAnonymous()
358 @view_config(
359 route_name='my_account_notifications', request_method='GET',
360 renderer='rhodecode:templates/admin/my_account/my_account.mako')
361 def my_notifications(self):
362 c = self.load_default_context()
363 c.active = 'notifications'
364
365 return self._get_template_context(c)
366
367 @LoginRequired()
368 @NotAnonymous()
369 @CSRFRequired()
370 @view_config(
371 route_name='my_account_notifications_toggle_visibility',
372 request_method='POST', renderer='json_ext')
373 def my_notifications_toggle_visibility(self):
374 user = self._rhodecode_db_user
375 new_status = not user.user_data.get('notification_status', True)
376 user.update_userdata(notification_status=new_status)
377 Session().commit()
378 return user.user_data['notification_status'] No newline at end of file
@@ -1,975 +1,958 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 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
290 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
291 action='edit_emails', conditions={'method': ['GET']})
291 action='edit_emails', conditions={'method': ['GET']})
292 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
292 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
293 action='add_email', conditions={'method': ['PUT']})
293 action='add_email', conditions={'method': ['PUT']})
294 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
294 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
295 action='delete_email', conditions={'method': ['DELETE']})
295 action='delete_email', conditions={'method': ['DELETE']})
296
296
297 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
297 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
298 action='edit_ips', conditions={'method': ['GET']})
298 action='edit_ips', conditions={'method': ['GET']})
299 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
299 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
300 action='add_ip', conditions={'method': ['PUT']})
300 action='add_ip', conditions={'method': ['PUT']})
301 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
301 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
302 action='delete_ip', conditions={'method': ['DELETE']})
302 action='delete_ip', conditions={'method': ['DELETE']})
303
303
304 # ADMIN USER GROUPS REST ROUTES
304 # ADMIN USER GROUPS REST ROUTES
305 with rmap.submapper(path_prefix=ADMIN_PREFIX,
305 with rmap.submapper(path_prefix=ADMIN_PREFIX,
306 controller='admin/user_groups') as m:
306 controller='admin/user_groups') as m:
307 m.connect('users_groups', '/user_groups',
307 m.connect('users_groups', '/user_groups',
308 action='create', conditions={'method': ['POST']})
308 action='create', conditions={'method': ['POST']})
309 m.connect('users_groups', '/user_groups',
309 m.connect('users_groups', '/user_groups',
310 action='index', conditions={'method': ['GET']})
310 action='index', conditions={'method': ['GET']})
311 m.connect('new_users_group', '/user_groups/new',
311 m.connect('new_users_group', '/user_groups/new',
312 action='new', conditions={'method': ['GET']})
312 action='new', conditions={'method': ['GET']})
313 m.connect('update_users_group', '/user_groups/{user_group_id}',
313 m.connect('update_users_group', '/user_groups/{user_group_id}',
314 action='update', conditions={'method': ['PUT']})
314 action='update', conditions={'method': ['PUT']})
315 m.connect('delete_users_group', '/user_groups/{user_group_id}',
315 m.connect('delete_users_group', '/user_groups/{user_group_id}',
316 action='delete', conditions={'method': ['DELETE']})
316 action='delete', conditions={'method': ['DELETE']})
317 m.connect('edit_users_group', '/user_groups/{user_group_id}/edit',
317 m.connect('edit_users_group', '/user_groups/{user_group_id}/edit',
318 action='edit', conditions={'method': ['GET']},
318 action='edit', conditions={'method': ['GET']},
319 function=check_user_group)
319 function=check_user_group)
320
320
321 # EXTRAS USER GROUP ROUTES
321 # EXTRAS USER GROUP ROUTES
322 m.connect('edit_user_group_global_perms',
322 m.connect('edit_user_group_global_perms',
323 '/user_groups/{user_group_id}/edit/global_permissions',
323 '/user_groups/{user_group_id}/edit/global_permissions',
324 action='edit_global_perms', conditions={'method': ['GET']})
324 action='edit_global_perms', conditions={'method': ['GET']})
325 m.connect('edit_user_group_global_perms',
325 m.connect('edit_user_group_global_perms',
326 '/user_groups/{user_group_id}/edit/global_permissions',
326 '/user_groups/{user_group_id}/edit/global_permissions',
327 action='update_global_perms', conditions={'method': ['PUT']})
327 action='update_global_perms', conditions={'method': ['PUT']})
328 m.connect('edit_user_group_perms_summary',
328 m.connect('edit_user_group_perms_summary',
329 '/user_groups/{user_group_id}/edit/permissions_summary',
329 '/user_groups/{user_group_id}/edit/permissions_summary',
330 action='edit_perms_summary', conditions={'method': ['GET']})
330 action='edit_perms_summary', conditions={'method': ['GET']})
331
331
332 m.connect('edit_user_group_perms',
332 m.connect('edit_user_group_perms',
333 '/user_groups/{user_group_id}/edit/permissions',
333 '/user_groups/{user_group_id}/edit/permissions',
334 action='edit_perms', conditions={'method': ['GET']})
334 action='edit_perms', conditions={'method': ['GET']})
335 m.connect('edit_user_group_perms',
335 m.connect('edit_user_group_perms',
336 '/user_groups/{user_group_id}/edit/permissions',
336 '/user_groups/{user_group_id}/edit/permissions',
337 action='update_perms', conditions={'method': ['PUT']})
337 action='update_perms', conditions={'method': ['PUT']})
338
338
339 m.connect('edit_user_group_advanced',
339 m.connect('edit_user_group_advanced',
340 '/user_groups/{user_group_id}/edit/advanced',
340 '/user_groups/{user_group_id}/edit/advanced',
341 action='edit_advanced', conditions={'method': ['GET']})
341 action='edit_advanced', conditions={'method': ['GET']})
342
342
343 m.connect('edit_user_group_advanced_sync',
343 m.connect('edit_user_group_advanced_sync',
344 '/user_groups/{user_group_id}/edit/advanced/sync',
344 '/user_groups/{user_group_id}/edit/advanced/sync',
345 action='edit_advanced_set_synchronization', conditions={'method': ['POST']})
345 action='edit_advanced_set_synchronization', conditions={'method': ['POST']})
346
346
347 m.connect('edit_user_group_members',
347 m.connect('edit_user_group_members',
348 '/user_groups/{user_group_id}/edit/members', jsroute=True,
348 '/user_groups/{user_group_id}/edit/members', jsroute=True,
349 action='user_group_members', conditions={'method': ['GET']})
349 action='user_group_members', conditions={'method': ['GET']})
350
350
351 # ADMIN PERMISSIONS ROUTES
351 # ADMIN PERMISSIONS ROUTES
352 with rmap.submapper(path_prefix=ADMIN_PREFIX,
352 with rmap.submapper(path_prefix=ADMIN_PREFIX,
353 controller='admin/permissions') as m:
353 controller='admin/permissions') as m:
354 m.connect('admin_permissions_application', '/permissions/application',
354 m.connect('admin_permissions_application', '/permissions/application',
355 action='permission_application_update', conditions={'method': ['POST']})
355 action='permission_application_update', conditions={'method': ['POST']})
356 m.connect('admin_permissions_application', '/permissions/application',
356 m.connect('admin_permissions_application', '/permissions/application',
357 action='permission_application', conditions={'method': ['GET']})
357 action='permission_application', conditions={'method': ['GET']})
358
358
359 m.connect('admin_permissions_global', '/permissions/global',
359 m.connect('admin_permissions_global', '/permissions/global',
360 action='permission_global_update', conditions={'method': ['POST']})
360 action='permission_global_update', conditions={'method': ['POST']})
361 m.connect('admin_permissions_global', '/permissions/global',
361 m.connect('admin_permissions_global', '/permissions/global',
362 action='permission_global', conditions={'method': ['GET']})
362 action='permission_global', conditions={'method': ['GET']})
363
363
364 m.connect('admin_permissions_object', '/permissions/object',
364 m.connect('admin_permissions_object', '/permissions/object',
365 action='permission_objects_update', conditions={'method': ['POST']})
365 action='permission_objects_update', conditions={'method': ['POST']})
366 m.connect('admin_permissions_object', '/permissions/object',
366 m.connect('admin_permissions_object', '/permissions/object',
367 action='permission_objects', conditions={'method': ['GET']})
367 action='permission_objects', conditions={'method': ['GET']})
368
368
369 m.connect('admin_permissions_ips', '/permissions/ips',
369 m.connect('admin_permissions_ips', '/permissions/ips',
370 action='permission_ips', conditions={'method': ['POST']})
370 action='permission_ips', conditions={'method': ['POST']})
371 m.connect('admin_permissions_ips', '/permissions/ips',
371 m.connect('admin_permissions_ips', '/permissions/ips',
372 action='permission_ips', conditions={'method': ['GET']})
372 action='permission_ips', conditions={'method': ['GET']})
373
373
374 m.connect('admin_permissions_overview', '/permissions/overview',
374 m.connect('admin_permissions_overview', '/permissions/overview',
375 action='permission_perms', conditions={'method': ['GET']})
375 action='permission_perms', conditions={'method': ['GET']})
376
376
377 # ADMIN DEFAULTS REST ROUTES
377 # ADMIN DEFAULTS REST ROUTES
378 with rmap.submapper(path_prefix=ADMIN_PREFIX,
378 with rmap.submapper(path_prefix=ADMIN_PREFIX,
379 controller='admin/defaults') as m:
379 controller='admin/defaults') as m:
380 m.connect('admin_defaults_repositories', '/defaults/repositories',
380 m.connect('admin_defaults_repositories', '/defaults/repositories',
381 action='update_repository_defaults', conditions={'method': ['POST']})
381 action='update_repository_defaults', conditions={'method': ['POST']})
382 m.connect('admin_defaults_repositories', '/defaults/repositories',
382 m.connect('admin_defaults_repositories', '/defaults/repositories',
383 action='index', conditions={'method': ['GET']})
383 action='index', conditions={'method': ['GET']})
384
384
385 # ADMIN DEBUG STYLE ROUTES
385 # ADMIN DEBUG STYLE ROUTES
386 if str2bool(config.get('debug_style')):
386 if str2bool(config.get('debug_style')):
387 with rmap.submapper(path_prefix=ADMIN_PREFIX + '/debug_style',
387 with rmap.submapper(path_prefix=ADMIN_PREFIX + '/debug_style',
388 controller='debug_style') as m:
388 controller='debug_style') as m:
389 m.connect('debug_style_home', '',
389 m.connect('debug_style_home', '',
390 action='index', conditions={'method': ['GET']})
390 action='index', conditions={'method': ['GET']})
391 m.connect('debug_style_template', '/t/{t_path}',
391 m.connect('debug_style_template', '/t/{t_path}',
392 action='template', conditions={'method': ['GET']})
392 action='template', conditions={'method': ['GET']})
393
393
394 # ADMIN SETTINGS ROUTES
394 # ADMIN SETTINGS ROUTES
395 with rmap.submapper(path_prefix=ADMIN_PREFIX,
395 with rmap.submapper(path_prefix=ADMIN_PREFIX,
396 controller='admin/settings') as m:
396 controller='admin/settings') as m:
397
397
398 # default
398 # default
399 m.connect('admin_settings', '/settings',
399 m.connect('admin_settings', '/settings',
400 action='settings_global_update',
400 action='settings_global_update',
401 conditions={'method': ['POST']})
401 conditions={'method': ['POST']})
402 m.connect('admin_settings', '/settings',
402 m.connect('admin_settings', '/settings',
403 action='settings_global', conditions={'method': ['GET']})
403 action='settings_global', conditions={'method': ['GET']})
404
404
405 m.connect('admin_settings_vcs', '/settings/vcs',
405 m.connect('admin_settings_vcs', '/settings/vcs',
406 action='settings_vcs_update',
406 action='settings_vcs_update',
407 conditions={'method': ['POST']})
407 conditions={'method': ['POST']})
408 m.connect('admin_settings_vcs', '/settings/vcs',
408 m.connect('admin_settings_vcs', '/settings/vcs',
409 action='settings_vcs',
409 action='settings_vcs',
410 conditions={'method': ['GET']})
410 conditions={'method': ['GET']})
411 m.connect('admin_settings_vcs', '/settings/vcs',
411 m.connect('admin_settings_vcs', '/settings/vcs',
412 action='delete_svn_pattern',
412 action='delete_svn_pattern',
413 conditions={'method': ['DELETE']})
413 conditions={'method': ['DELETE']})
414
414
415 m.connect('admin_settings_mapping', '/settings/mapping',
415 m.connect('admin_settings_mapping', '/settings/mapping',
416 action='settings_mapping_update',
416 action='settings_mapping_update',
417 conditions={'method': ['POST']})
417 conditions={'method': ['POST']})
418 m.connect('admin_settings_mapping', '/settings/mapping',
418 m.connect('admin_settings_mapping', '/settings/mapping',
419 action='settings_mapping', conditions={'method': ['GET']})
419 action='settings_mapping', conditions={'method': ['GET']})
420
420
421 m.connect('admin_settings_global', '/settings/global',
421 m.connect('admin_settings_global', '/settings/global',
422 action='settings_global_update',
422 action='settings_global_update',
423 conditions={'method': ['POST']})
423 conditions={'method': ['POST']})
424 m.connect('admin_settings_global', '/settings/global',
424 m.connect('admin_settings_global', '/settings/global',
425 action='settings_global', conditions={'method': ['GET']})
425 action='settings_global', conditions={'method': ['GET']})
426
426
427 m.connect('admin_settings_visual', '/settings/visual',
427 m.connect('admin_settings_visual', '/settings/visual',
428 action='settings_visual_update',
428 action='settings_visual_update',
429 conditions={'method': ['POST']})
429 conditions={'method': ['POST']})
430 m.connect('admin_settings_visual', '/settings/visual',
430 m.connect('admin_settings_visual', '/settings/visual',
431 action='settings_visual', conditions={'method': ['GET']})
431 action='settings_visual', conditions={'method': ['GET']})
432
432
433 m.connect('admin_settings_issuetracker',
433 m.connect('admin_settings_issuetracker',
434 '/settings/issue-tracker', action='settings_issuetracker',
434 '/settings/issue-tracker', action='settings_issuetracker',
435 conditions={'method': ['GET']})
435 conditions={'method': ['GET']})
436 m.connect('admin_settings_issuetracker_save',
436 m.connect('admin_settings_issuetracker_save',
437 '/settings/issue-tracker/save',
437 '/settings/issue-tracker/save',
438 action='settings_issuetracker_save',
438 action='settings_issuetracker_save',
439 conditions={'method': ['POST']})
439 conditions={'method': ['POST']})
440 m.connect('admin_issuetracker_test', '/settings/issue-tracker/test',
440 m.connect('admin_issuetracker_test', '/settings/issue-tracker/test',
441 action='settings_issuetracker_test',
441 action='settings_issuetracker_test',
442 conditions={'method': ['POST']})
442 conditions={'method': ['POST']})
443 m.connect('admin_issuetracker_delete',
443 m.connect('admin_issuetracker_delete',
444 '/settings/issue-tracker/delete',
444 '/settings/issue-tracker/delete',
445 action='settings_issuetracker_delete',
445 action='settings_issuetracker_delete',
446 conditions={'method': ['DELETE']})
446 conditions={'method': ['DELETE']})
447
447
448 m.connect('admin_settings_email', '/settings/email',
448 m.connect('admin_settings_email', '/settings/email',
449 action='settings_email_update',
449 action='settings_email_update',
450 conditions={'method': ['POST']})
450 conditions={'method': ['POST']})
451 m.connect('admin_settings_email', '/settings/email',
451 m.connect('admin_settings_email', '/settings/email',
452 action='settings_email', conditions={'method': ['GET']})
452 action='settings_email', conditions={'method': ['GET']})
453
453
454 m.connect('admin_settings_hooks', '/settings/hooks',
454 m.connect('admin_settings_hooks', '/settings/hooks',
455 action='settings_hooks_update',
455 action='settings_hooks_update',
456 conditions={'method': ['POST', 'DELETE']})
456 conditions={'method': ['POST', 'DELETE']})
457 m.connect('admin_settings_hooks', '/settings/hooks',
457 m.connect('admin_settings_hooks', '/settings/hooks',
458 action='settings_hooks', conditions={'method': ['GET']})
458 action='settings_hooks', conditions={'method': ['GET']})
459
459
460 m.connect('admin_settings_search', '/settings/search',
460 m.connect('admin_settings_search', '/settings/search',
461 action='settings_search', conditions={'method': ['GET']})
461 action='settings_search', conditions={'method': ['GET']})
462
462
463 m.connect('admin_settings_supervisor', '/settings/supervisor',
463 m.connect('admin_settings_supervisor', '/settings/supervisor',
464 action='settings_supervisor', conditions={'method': ['GET']})
464 action='settings_supervisor', conditions={'method': ['GET']})
465 m.connect('admin_settings_supervisor_log', '/settings/supervisor/{procid}/log',
465 m.connect('admin_settings_supervisor_log', '/settings/supervisor/{procid}/log',
466 action='settings_supervisor_log', conditions={'method': ['GET']})
466 action='settings_supervisor_log', conditions={'method': ['GET']})
467
467
468 m.connect('admin_settings_labs', '/settings/labs',
468 m.connect('admin_settings_labs', '/settings/labs',
469 action='settings_labs_update',
469 action='settings_labs_update',
470 conditions={'method': ['POST']})
470 conditions={'method': ['POST']})
471 m.connect('admin_settings_labs', '/settings/labs',
471 m.connect('admin_settings_labs', '/settings/labs',
472 action='settings_labs', conditions={'method': ['GET']})
472 action='settings_labs', conditions={'method': ['GET']})
473
473
474 # ADMIN MY ACCOUNT
474 # ADMIN MY ACCOUNT
475 with rmap.submapper(path_prefix=ADMIN_PREFIX,
475 with rmap.submapper(path_prefix=ADMIN_PREFIX,
476 controller='admin/my_account') as m:
476 controller='admin/my_account') as m:
477
477
478 m.connect('my_account_edit', '/my_account/edit',
478 m.connect('my_account_edit', '/my_account/edit',
479 action='my_account_edit', conditions={'method': ['GET']})
479 action='my_account_edit', conditions={'method': ['GET']})
480 m.connect('my_account', '/my_account/update',
480 m.connect('my_account', '/my_account/update',
481 action='my_account_update', conditions={'method': ['POST']})
481 action='my_account_update', conditions={'method': ['POST']})
482
482
483 # NOTE(marcink): this needs to be kept for password force flag to be
483 # NOTE(marcink): this needs to be kept for password force flag to be
484 # handler, remove after migration to pyramid
484 # handler, remove after migration to pyramid
485 m.connect('my_account_password', '/my_account/password',
485 m.connect('my_account_password', '/my_account/password',
486 action='my_account_password', conditions={'method': ['GET']})
486 action='my_account_password', conditions={'method': ['GET']})
487
487
488 m.connect('my_account_repos', '/my_account/repos',
489 action='my_account_repos', conditions={'method': ['GET']})
490
491 m.connect('my_account_watched', '/my_account/watched',
492 action='my_account_watched', conditions={'method': ['GET']})
493
494 m.connect('my_account_pullrequests', '/my_account/pull_requests',
488 m.connect('my_account_pullrequests', '/my_account/pull_requests',
495 action='my_account_pullrequests', conditions={'method': ['GET']})
489 action='my_account_pullrequests', conditions={'method': ['GET']})
496
490
497 m.connect('my_account_perms', '/my_account/perms',
498 action='my_account_perms', conditions={'method': ['GET']})
499
500 m.connect('my_account_notifications', '/my_account/notifications',
501 action='my_notifications',
502 conditions={'method': ['GET']})
503 m.connect('my_account_notifications_toggle_visibility',
504 '/my_account/toggle_visibility',
505 action='my_notifications_toggle_visibility',
506 conditions={'method': ['POST']})
507
508 # NOTIFICATION REST ROUTES
491 # NOTIFICATION REST ROUTES
509 with rmap.submapper(path_prefix=ADMIN_PREFIX,
492 with rmap.submapper(path_prefix=ADMIN_PREFIX,
510 controller='admin/notifications') as m:
493 controller='admin/notifications') as m:
511 m.connect('notifications', '/notifications',
494 m.connect('notifications', '/notifications',
512 action='index', conditions={'method': ['GET']})
495 action='index', conditions={'method': ['GET']})
513 m.connect('notifications_mark_all_read', '/notifications/mark_all_read',
496 m.connect('notifications_mark_all_read', '/notifications/mark_all_read',
514 action='mark_all_read', conditions={'method': ['POST']})
497 action='mark_all_read', conditions={'method': ['POST']})
515 m.connect('/notifications/{notification_id}',
498 m.connect('/notifications/{notification_id}',
516 action='update', conditions={'method': ['PUT']})
499 action='update', conditions={'method': ['PUT']})
517 m.connect('/notifications/{notification_id}',
500 m.connect('/notifications/{notification_id}',
518 action='delete', conditions={'method': ['DELETE']})
501 action='delete', conditions={'method': ['DELETE']})
519 m.connect('notification', '/notifications/{notification_id}',
502 m.connect('notification', '/notifications/{notification_id}',
520 action='show', conditions={'method': ['GET']})
503 action='show', conditions={'method': ['GET']})
521
504
522 # ADMIN GIST
505 # ADMIN GIST
523 with rmap.submapper(path_prefix=ADMIN_PREFIX,
506 with rmap.submapper(path_prefix=ADMIN_PREFIX,
524 controller='admin/gists') as m:
507 controller='admin/gists') as m:
525 m.connect('gists', '/gists',
508 m.connect('gists', '/gists',
526 action='create', conditions={'method': ['POST']})
509 action='create', conditions={'method': ['POST']})
527 m.connect('gists', '/gists', jsroute=True,
510 m.connect('gists', '/gists', jsroute=True,
528 action='index', conditions={'method': ['GET']})
511 action='index', conditions={'method': ['GET']})
529 m.connect('new_gist', '/gists/new', jsroute=True,
512 m.connect('new_gist', '/gists/new', jsroute=True,
530 action='new', conditions={'method': ['GET']})
513 action='new', conditions={'method': ['GET']})
531
514
532 m.connect('/gists/{gist_id}',
515 m.connect('/gists/{gist_id}',
533 action='delete', conditions={'method': ['DELETE']})
516 action='delete', conditions={'method': ['DELETE']})
534 m.connect('edit_gist', '/gists/{gist_id}/edit',
517 m.connect('edit_gist', '/gists/{gist_id}/edit',
535 action='edit_form', conditions={'method': ['GET']})
518 action='edit_form', conditions={'method': ['GET']})
536 m.connect('edit_gist', '/gists/{gist_id}/edit',
519 m.connect('edit_gist', '/gists/{gist_id}/edit',
537 action='edit', conditions={'method': ['POST']})
520 action='edit', conditions={'method': ['POST']})
538 m.connect(
521 m.connect(
539 'edit_gist_check_revision', '/gists/{gist_id}/edit/check_revision',
522 'edit_gist_check_revision', '/gists/{gist_id}/edit/check_revision',
540 action='check_revision', conditions={'method': ['GET']})
523 action='check_revision', conditions={'method': ['GET']})
541
524
542 m.connect('gist', '/gists/{gist_id}',
525 m.connect('gist', '/gists/{gist_id}',
543 action='show', conditions={'method': ['GET']})
526 action='show', conditions={'method': ['GET']})
544 m.connect('gist_rev', '/gists/{gist_id}/{revision}',
527 m.connect('gist_rev', '/gists/{gist_id}/{revision}',
545 revision='tip',
528 revision='tip',
546 action='show', conditions={'method': ['GET']})
529 action='show', conditions={'method': ['GET']})
547 m.connect('formatted_gist', '/gists/{gist_id}/{revision}/{format}',
530 m.connect('formatted_gist', '/gists/{gist_id}/{revision}/{format}',
548 revision='tip',
531 revision='tip',
549 action='show', conditions={'method': ['GET']})
532 action='show', conditions={'method': ['GET']})
550 m.connect('formatted_gist_file', '/gists/{gist_id}/{revision}/{format}/{f_path}',
533 m.connect('formatted_gist_file', '/gists/{gist_id}/{revision}/{format}/{f_path}',
551 revision='tip',
534 revision='tip',
552 action='show', conditions={'method': ['GET']},
535 action='show', conditions={'method': ['GET']},
553 requirements=URL_NAME_REQUIREMENTS)
536 requirements=URL_NAME_REQUIREMENTS)
554
537
555 # USER JOURNAL
538 # USER JOURNAL
556 rmap.connect('journal', '%s/journal' % (ADMIN_PREFIX,),
539 rmap.connect('journal', '%s/journal' % (ADMIN_PREFIX,),
557 controller='journal', action='index')
540 controller='journal', action='index')
558 rmap.connect('journal_rss', '%s/journal/rss' % (ADMIN_PREFIX,),
541 rmap.connect('journal_rss', '%s/journal/rss' % (ADMIN_PREFIX,),
559 controller='journal', action='journal_rss')
542 controller='journal', action='journal_rss')
560 rmap.connect('journal_atom', '%s/journal/atom' % (ADMIN_PREFIX,),
543 rmap.connect('journal_atom', '%s/journal/atom' % (ADMIN_PREFIX,),
561 controller='journal', action='journal_atom')
544 controller='journal', action='journal_atom')
562
545
563 rmap.connect('public_journal', '%s/public_journal' % (ADMIN_PREFIX,),
546 rmap.connect('public_journal', '%s/public_journal' % (ADMIN_PREFIX,),
564 controller='journal', action='public_journal')
547 controller='journal', action='public_journal')
565
548
566 rmap.connect('public_journal_rss', '%s/public_journal/rss' % (ADMIN_PREFIX,),
549 rmap.connect('public_journal_rss', '%s/public_journal/rss' % (ADMIN_PREFIX,),
567 controller='journal', action='public_journal_rss')
550 controller='journal', action='public_journal_rss')
568
551
569 rmap.connect('public_journal_rss_old', '%s/public_journal_rss' % (ADMIN_PREFIX,),
552 rmap.connect('public_journal_rss_old', '%s/public_journal_rss' % (ADMIN_PREFIX,),
570 controller='journal', action='public_journal_rss')
553 controller='journal', action='public_journal_rss')
571
554
572 rmap.connect('public_journal_atom',
555 rmap.connect('public_journal_atom',
573 '%s/public_journal/atom' % (ADMIN_PREFIX,), controller='journal',
556 '%s/public_journal/atom' % (ADMIN_PREFIX,), controller='journal',
574 action='public_journal_atom')
557 action='public_journal_atom')
575
558
576 rmap.connect('public_journal_atom_old',
559 rmap.connect('public_journal_atom_old',
577 '%s/public_journal_atom' % (ADMIN_PREFIX,), controller='journal',
560 '%s/public_journal_atom' % (ADMIN_PREFIX,), controller='journal',
578 action='public_journal_atom')
561 action='public_journal_atom')
579
562
580 rmap.connect('toggle_following', '%s/toggle_following' % (ADMIN_PREFIX,),
563 rmap.connect('toggle_following', '%s/toggle_following' % (ADMIN_PREFIX,),
581 controller='journal', action='toggle_following', jsroute=True,
564 controller='journal', action='toggle_following', jsroute=True,
582 conditions={'method': ['POST']})
565 conditions={'method': ['POST']})
583
566
584 # FEEDS
567 # FEEDS
585 rmap.connect('rss_feed_home', '/{repo_name}/feed/rss',
568 rmap.connect('rss_feed_home', '/{repo_name}/feed/rss',
586 controller='feed', action='rss',
569 controller='feed', action='rss',
587 conditions={'function': check_repo},
570 conditions={'function': check_repo},
588 requirements=URL_NAME_REQUIREMENTS)
571 requirements=URL_NAME_REQUIREMENTS)
589
572
590 rmap.connect('atom_feed_home', '/{repo_name}/feed/atom',
573 rmap.connect('atom_feed_home', '/{repo_name}/feed/atom',
591 controller='feed', action='atom',
574 controller='feed', action='atom',
592 conditions={'function': check_repo},
575 conditions={'function': check_repo},
593 requirements=URL_NAME_REQUIREMENTS)
576 requirements=URL_NAME_REQUIREMENTS)
594
577
595 #==========================================================================
578 #==========================================================================
596 # REPOSITORY ROUTES
579 # REPOSITORY ROUTES
597 #==========================================================================
580 #==========================================================================
598
581
599 rmap.connect('repo_creating_home', '/{repo_name}/repo_creating',
582 rmap.connect('repo_creating_home', '/{repo_name}/repo_creating',
600 controller='admin/repos', action='repo_creating',
583 controller='admin/repos', action='repo_creating',
601 requirements=URL_NAME_REQUIREMENTS)
584 requirements=URL_NAME_REQUIREMENTS)
602 rmap.connect('repo_check_home', '/{repo_name}/crepo_check',
585 rmap.connect('repo_check_home', '/{repo_name}/crepo_check',
603 controller='admin/repos', action='repo_check',
586 controller='admin/repos', action='repo_check',
604 requirements=URL_NAME_REQUIREMENTS)
587 requirements=URL_NAME_REQUIREMENTS)
605
588
606 rmap.connect('changeset_home', '/{repo_name}/changeset/{revision}',
589 rmap.connect('changeset_home', '/{repo_name}/changeset/{revision}',
607 controller='changeset', revision='tip',
590 controller='changeset', revision='tip',
608 conditions={'function': check_repo},
591 conditions={'function': check_repo},
609 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
592 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
610 rmap.connect('changeset_children', '/{repo_name}/changeset_children/{revision}',
593 rmap.connect('changeset_children', '/{repo_name}/changeset_children/{revision}',
611 controller='changeset', revision='tip', action='changeset_children',
594 controller='changeset', revision='tip', action='changeset_children',
612 conditions={'function': check_repo},
595 conditions={'function': check_repo},
613 requirements=URL_NAME_REQUIREMENTS)
596 requirements=URL_NAME_REQUIREMENTS)
614 rmap.connect('changeset_parents', '/{repo_name}/changeset_parents/{revision}',
597 rmap.connect('changeset_parents', '/{repo_name}/changeset_parents/{revision}',
615 controller='changeset', revision='tip', action='changeset_parents',
598 controller='changeset', revision='tip', action='changeset_parents',
616 conditions={'function': check_repo},
599 conditions={'function': check_repo},
617 requirements=URL_NAME_REQUIREMENTS)
600 requirements=URL_NAME_REQUIREMENTS)
618
601
619 # repo edit options
602 # repo edit options
620 rmap.connect('edit_repo_fields', '/{repo_name}/settings/fields',
603 rmap.connect('edit_repo_fields', '/{repo_name}/settings/fields',
621 controller='admin/repos', action='edit_fields',
604 controller='admin/repos', action='edit_fields',
622 conditions={'method': ['GET'], 'function': check_repo},
605 conditions={'method': ['GET'], 'function': check_repo},
623 requirements=URL_NAME_REQUIREMENTS)
606 requirements=URL_NAME_REQUIREMENTS)
624 rmap.connect('create_repo_fields', '/{repo_name}/settings/fields/new',
607 rmap.connect('create_repo_fields', '/{repo_name}/settings/fields/new',
625 controller='admin/repos', action='create_repo_field',
608 controller='admin/repos', action='create_repo_field',
626 conditions={'method': ['PUT'], 'function': check_repo},
609 conditions={'method': ['PUT'], 'function': check_repo},
627 requirements=URL_NAME_REQUIREMENTS)
610 requirements=URL_NAME_REQUIREMENTS)
628 rmap.connect('delete_repo_fields', '/{repo_name}/settings/fields/{field_id}',
611 rmap.connect('delete_repo_fields', '/{repo_name}/settings/fields/{field_id}',
629 controller='admin/repos', action='delete_repo_field',
612 controller='admin/repos', action='delete_repo_field',
630 conditions={'method': ['DELETE'], 'function': check_repo},
613 conditions={'method': ['DELETE'], 'function': check_repo},
631 requirements=URL_NAME_REQUIREMENTS)
614 requirements=URL_NAME_REQUIREMENTS)
632
615
633 rmap.connect('toggle_locking', '/{repo_name}/settings/advanced/locking_toggle',
616 rmap.connect('toggle_locking', '/{repo_name}/settings/advanced/locking_toggle',
634 controller='admin/repos', action='toggle_locking',
617 controller='admin/repos', action='toggle_locking',
635 conditions={'method': ['GET'], 'function': check_repo},
618 conditions={'method': ['GET'], 'function': check_repo},
636 requirements=URL_NAME_REQUIREMENTS)
619 requirements=URL_NAME_REQUIREMENTS)
637
620
638 rmap.connect('edit_repo_remote', '/{repo_name}/settings/remote',
621 rmap.connect('edit_repo_remote', '/{repo_name}/settings/remote',
639 controller='admin/repos', action='edit_remote_form',
622 controller='admin/repos', action='edit_remote_form',
640 conditions={'method': ['GET'], 'function': check_repo},
623 conditions={'method': ['GET'], 'function': check_repo},
641 requirements=URL_NAME_REQUIREMENTS)
624 requirements=URL_NAME_REQUIREMENTS)
642 rmap.connect('edit_repo_remote', '/{repo_name}/settings/remote',
625 rmap.connect('edit_repo_remote', '/{repo_name}/settings/remote',
643 controller='admin/repos', action='edit_remote',
626 controller='admin/repos', action='edit_remote',
644 conditions={'method': ['PUT'], 'function': check_repo},
627 conditions={'method': ['PUT'], 'function': check_repo},
645 requirements=URL_NAME_REQUIREMENTS)
628 requirements=URL_NAME_REQUIREMENTS)
646
629
647 rmap.connect('edit_repo_statistics', '/{repo_name}/settings/statistics',
630 rmap.connect('edit_repo_statistics', '/{repo_name}/settings/statistics',
648 controller='admin/repos', action='edit_statistics_form',
631 controller='admin/repos', action='edit_statistics_form',
649 conditions={'method': ['GET'], 'function': check_repo},
632 conditions={'method': ['GET'], 'function': check_repo},
650 requirements=URL_NAME_REQUIREMENTS)
633 requirements=URL_NAME_REQUIREMENTS)
651 rmap.connect('edit_repo_statistics', '/{repo_name}/settings/statistics',
634 rmap.connect('edit_repo_statistics', '/{repo_name}/settings/statistics',
652 controller='admin/repos', action='edit_statistics',
635 controller='admin/repos', action='edit_statistics',
653 conditions={'method': ['PUT'], 'function': check_repo},
636 conditions={'method': ['PUT'], 'function': check_repo},
654 requirements=URL_NAME_REQUIREMENTS)
637 requirements=URL_NAME_REQUIREMENTS)
655 rmap.connect('repo_settings_issuetracker',
638 rmap.connect('repo_settings_issuetracker',
656 '/{repo_name}/settings/issue-tracker',
639 '/{repo_name}/settings/issue-tracker',
657 controller='admin/repos', action='repo_issuetracker',
640 controller='admin/repos', action='repo_issuetracker',
658 conditions={'method': ['GET'], 'function': check_repo},
641 conditions={'method': ['GET'], 'function': check_repo},
659 requirements=URL_NAME_REQUIREMENTS)
642 requirements=URL_NAME_REQUIREMENTS)
660 rmap.connect('repo_issuetracker_test',
643 rmap.connect('repo_issuetracker_test',
661 '/{repo_name}/settings/issue-tracker/test',
644 '/{repo_name}/settings/issue-tracker/test',
662 controller='admin/repos', action='repo_issuetracker_test',
645 controller='admin/repos', action='repo_issuetracker_test',
663 conditions={'method': ['POST'], 'function': check_repo},
646 conditions={'method': ['POST'], 'function': check_repo},
664 requirements=URL_NAME_REQUIREMENTS)
647 requirements=URL_NAME_REQUIREMENTS)
665 rmap.connect('repo_issuetracker_delete',
648 rmap.connect('repo_issuetracker_delete',
666 '/{repo_name}/settings/issue-tracker/delete',
649 '/{repo_name}/settings/issue-tracker/delete',
667 controller='admin/repos', action='repo_issuetracker_delete',
650 controller='admin/repos', action='repo_issuetracker_delete',
668 conditions={'method': ['DELETE'], 'function': check_repo},
651 conditions={'method': ['DELETE'], 'function': check_repo},
669 requirements=URL_NAME_REQUIREMENTS)
652 requirements=URL_NAME_REQUIREMENTS)
670 rmap.connect('repo_issuetracker_save',
653 rmap.connect('repo_issuetracker_save',
671 '/{repo_name}/settings/issue-tracker/save',
654 '/{repo_name}/settings/issue-tracker/save',
672 controller='admin/repos', action='repo_issuetracker_save',
655 controller='admin/repos', action='repo_issuetracker_save',
673 conditions={'method': ['POST'], 'function': check_repo},
656 conditions={'method': ['POST'], 'function': check_repo},
674 requirements=URL_NAME_REQUIREMENTS)
657 requirements=URL_NAME_REQUIREMENTS)
675 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
658 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
676 controller='admin/repos', action='repo_settings_vcs_update',
659 controller='admin/repos', action='repo_settings_vcs_update',
677 conditions={'method': ['POST'], 'function': check_repo},
660 conditions={'method': ['POST'], 'function': check_repo},
678 requirements=URL_NAME_REQUIREMENTS)
661 requirements=URL_NAME_REQUIREMENTS)
679 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
662 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
680 controller='admin/repos', action='repo_settings_vcs',
663 controller='admin/repos', action='repo_settings_vcs',
681 conditions={'method': ['GET'], 'function': check_repo},
664 conditions={'method': ['GET'], 'function': check_repo},
682 requirements=URL_NAME_REQUIREMENTS)
665 requirements=URL_NAME_REQUIREMENTS)
683 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
666 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
684 controller='admin/repos', action='repo_delete_svn_pattern',
667 controller='admin/repos', action='repo_delete_svn_pattern',
685 conditions={'method': ['DELETE'], 'function': check_repo},
668 conditions={'method': ['DELETE'], 'function': check_repo},
686 requirements=URL_NAME_REQUIREMENTS)
669 requirements=URL_NAME_REQUIREMENTS)
687 rmap.connect('repo_pullrequest_settings', '/{repo_name}/settings/pullrequest',
670 rmap.connect('repo_pullrequest_settings', '/{repo_name}/settings/pullrequest',
688 controller='admin/repos', action='repo_settings_pullrequest',
671 controller='admin/repos', action='repo_settings_pullrequest',
689 conditions={'method': ['GET', 'POST'], 'function': check_repo},
672 conditions={'method': ['GET', 'POST'], 'function': check_repo},
690 requirements=URL_NAME_REQUIREMENTS)
673 requirements=URL_NAME_REQUIREMENTS)
691
674
692 # still working url for backward compat.
675 # still working url for backward compat.
693 rmap.connect('raw_changeset_home_depraced',
676 rmap.connect('raw_changeset_home_depraced',
694 '/{repo_name}/raw-changeset/{revision}',
677 '/{repo_name}/raw-changeset/{revision}',
695 controller='changeset', action='changeset_raw',
678 controller='changeset', action='changeset_raw',
696 revision='tip', conditions={'function': check_repo},
679 revision='tip', conditions={'function': check_repo},
697 requirements=URL_NAME_REQUIREMENTS)
680 requirements=URL_NAME_REQUIREMENTS)
698
681
699 # new URLs
682 # new URLs
700 rmap.connect('changeset_raw_home',
683 rmap.connect('changeset_raw_home',
701 '/{repo_name}/changeset-diff/{revision}',
684 '/{repo_name}/changeset-diff/{revision}',
702 controller='changeset', action='changeset_raw',
685 controller='changeset', action='changeset_raw',
703 revision='tip', conditions={'function': check_repo},
686 revision='tip', conditions={'function': check_repo},
704 requirements=URL_NAME_REQUIREMENTS)
687 requirements=URL_NAME_REQUIREMENTS)
705
688
706 rmap.connect('changeset_patch_home',
689 rmap.connect('changeset_patch_home',
707 '/{repo_name}/changeset-patch/{revision}',
690 '/{repo_name}/changeset-patch/{revision}',
708 controller='changeset', action='changeset_patch',
691 controller='changeset', action='changeset_patch',
709 revision='tip', conditions={'function': check_repo},
692 revision='tip', conditions={'function': check_repo},
710 requirements=URL_NAME_REQUIREMENTS)
693 requirements=URL_NAME_REQUIREMENTS)
711
694
712 rmap.connect('changeset_download_home',
695 rmap.connect('changeset_download_home',
713 '/{repo_name}/changeset-download/{revision}',
696 '/{repo_name}/changeset-download/{revision}',
714 controller='changeset', action='changeset_download',
697 controller='changeset', action='changeset_download',
715 revision='tip', conditions={'function': check_repo},
698 revision='tip', conditions={'function': check_repo},
716 requirements=URL_NAME_REQUIREMENTS)
699 requirements=URL_NAME_REQUIREMENTS)
717
700
718 rmap.connect('changeset_comment',
701 rmap.connect('changeset_comment',
719 '/{repo_name}/changeset/{revision}/comment', jsroute=True,
702 '/{repo_name}/changeset/{revision}/comment', jsroute=True,
720 controller='changeset', revision='tip', action='comment',
703 controller='changeset', revision='tip', action='comment',
721 conditions={'function': check_repo},
704 conditions={'function': check_repo},
722 requirements=URL_NAME_REQUIREMENTS)
705 requirements=URL_NAME_REQUIREMENTS)
723
706
724 rmap.connect('changeset_comment_preview',
707 rmap.connect('changeset_comment_preview',
725 '/{repo_name}/changeset/comment/preview', jsroute=True,
708 '/{repo_name}/changeset/comment/preview', jsroute=True,
726 controller='changeset', action='preview_comment',
709 controller='changeset', action='preview_comment',
727 conditions={'function': check_repo, 'method': ['POST']},
710 conditions={'function': check_repo, 'method': ['POST']},
728 requirements=URL_NAME_REQUIREMENTS)
711 requirements=URL_NAME_REQUIREMENTS)
729
712
730 rmap.connect('changeset_comment_delete',
713 rmap.connect('changeset_comment_delete',
731 '/{repo_name}/changeset/comment/{comment_id}/delete',
714 '/{repo_name}/changeset/comment/{comment_id}/delete',
732 controller='changeset', action='delete_comment',
715 controller='changeset', action='delete_comment',
733 conditions={'function': check_repo, 'method': ['DELETE']},
716 conditions={'function': check_repo, 'method': ['DELETE']},
734 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
717 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
735
718
736 rmap.connect('changeset_info', '/{repo_name}/changeset_info/{revision}',
719 rmap.connect('changeset_info', '/{repo_name}/changeset_info/{revision}',
737 controller='changeset', action='changeset_info',
720 controller='changeset', action='changeset_info',
738 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
721 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
739
722
740 rmap.connect('compare_home',
723 rmap.connect('compare_home',
741 '/{repo_name}/compare',
724 '/{repo_name}/compare',
742 controller='compare', action='index',
725 controller='compare', action='index',
743 conditions={'function': check_repo},
726 conditions={'function': check_repo},
744 requirements=URL_NAME_REQUIREMENTS)
727 requirements=URL_NAME_REQUIREMENTS)
745
728
746 rmap.connect('compare_url',
729 rmap.connect('compare_url',
747 '/{repo_name}/compare/{source_ref_type}@{source_ref:.*?}...{target_ref_type}@{target_ref:.*?}',
730 '/{repo_name}/compare/{source_ref_type}@{source_ref:.*?}...{target_ref_type}@{target_ref:.*?}',
748 controller='compare', action='compare',
731 controller='compare', action='compare',
749 conditions={'function': check_repo},
732 conditions={'function': check_repo},
750 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
733 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
751
734
752 rmap.connect('pullrequest_home',
735 rmap.connect('pullrequest_home',
753 '/{repo_name}/pull-request/new', controller='pullrequests',
736 '/{repo_name}/pull-request/new', controller='pullrequests',
754 action='index', conditions={'function': check_repo,
737 action='index', conditions={'function': check_repo,
755 'method': ['GET']},
738 'method': ['GET']},
756 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
739 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
757
740
758 rmap.connect('pullrequest',
741 rmap.connect('pullrequest',
759 '/{repo_name}/pull-request/new', controller='pullrequests',
742 '/{repo_name}/pull-request/new', controller='pullrequests',
760 action='create', conditions={'function': check_repo,
743 action='create', conditions={'function': check_repo,
761 'method': ['POST']},
744 'method': ['POST']},
762 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
745 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
763
746
764 rmap.connect('pullrequest_repo_refs',
747 rmap.connect('pullrequest_repo_refs',
765 '/{repo_name}/pull-request/refs/{target_repo_name:.*?[^/]}',
748 '/{repo_name}/pull-request/refs/{target_repo_name:.*?[^/]}',
766 controller='pullrequests',
749 controller='pullrequests',
767 action='get_repo_refs',
750 action='get_repo_refs',
768 conditions={'function': check_repo, 'method': ['GET']},
751 conditions={'function': check_repo, 'method': ['GET']},
769 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
752 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
770
753
771 rmap.connect('pullrequest_repo_destinations',
754 rmap.connect('pullrequest_repo_destinations',
772 '/{repo_name}/pull-request/repo-destinations',
755 '/{repo_name}/pull-request/repo-destinations',
773 controller='pullrequests',
756 controller='pullrequests',
774 action='get_repo_destinations',
757 action='get_repo_destinations',
775 conditions={'function': check_repo, 'method': ['GET']},
758 conditions={'function': check_repo, 'method': ['GET']},
776 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
759 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
777
760
778 rmap.connect('pullrequest_show',
761 rmap.connect('pullrequest_show',
779 '/{repo_name}/pull-request/{pull_request_id}',
762 '/{repo_name}/pull-request/{pull_request_id}',
780 controller='pullrequests',
763 controller='pullrequests',
781 action='show', conditions={'function': check_repo,
764 action='show', conditions={'function': check_repo,
782 'method': ['GET']},
765 'method': ['GET']},
783 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
766 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
784
767
785 rmap.connect('pullrequest_update',
768 rmap.connect('pullrequest_update',
786 '/{repo_name}/pull-request/{pull_request_id}',
769 '/{repo_name}/pull-request/{pull_request_id}',
787 controller='pullrequests',
770 controller='pullrequests',
788 action='update', conditions={'function': check_repo,
771 action='update', conditions={'function': check_repo,
789 'method': ['PUT']},
772 'method': ['PUT']},
790 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
773 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
791
774
792 rmap.connect('pullrequest_merge',
775 rmap.connect('pullrequest_merge',
793 '/{repo_name}/pull-request/{pull_request_id}',
776 '/{repo_name}/pull-request/{pull_request_id}',
794 controller='pullrequests',
777 controller='pullrequests',
795 action='merge', conditions={'function': check_repo,
778 action='merge', conditions={'function': check_repo,
796 'method': ['POST']},
779 'method': ['POST']},
797 requirements=URL_NAME_REQUIREMENTS)
780 requirements=URL_NAME_REQUIREMENTS)
798
781
799 rmap.connect('pullrequest_delete',
782 rmap.connect('pullrequest_delete',
800 '/{repo_name}/pull-request/{pull_request_id}',
783 '/{repo_name}/pull-request/{pull_request_id}',
801 controller='pullrequests',
784 controller='pullrequests',
802 action='delete', conditions={'function': check_repo,
785 action='delete', conditions={'function': check_repo,
803 'method': ['DELETE']},
786 'method': ['DELETE']},
804 requirements=URL_NAME_REQUIREMENTS)
787 requirements=URL_NAME_REQUIREMENTS)
805
788
806 rmap.connect('pullrequest_comment',
789 rmap.connect('pullrequest_comment',
807 '/{repo_name}/pull-request-comment/{pull_request_id}',
790 '/{repo_name}/pull-request-comment/{pull_request_id}',
808 controller='pullrequests',
791 controller='pullrequests',
809 action='comment', conditions={'function': check_repo,
792 action='comment', conditions={'function': check_repo,
810 'method': ['POST']},
793 'method': ['POST']},
811 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
794 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
812
795
813 rmap.connect('pullrequest_comment_delete',
796 rmap.connect('pullrequest_comment_delete',
814 '/{repo_name}/pull-request-comment/{comment_id}/delete',
797 '/{repo_name}/pull-request-comment/{comment_id}/delete',
815 controller='pullrequests', action='delete_comment',
798 controller='pullrequests', action='delete_comment',
816 conditions={'function': check_repo, 'method': ['DELETE']},
799 conditions={'function': check_repo, 'method': ['DELETE']},
817 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
800 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
818
801
819 rmap.connect('changelog_home', '/{repo_name}/changelog', jsroute=True,
802 rmap.connect('changelog_home', '/{repo_name}/changelog', jsroute=True,
820 controller='changelog', conditions={'function': check_repo},
803 controller='changelog', conditions={'function': check_repo},
821 requirements=URL_NAME_REQUIREMENTS)
804 requirements=URL_NAME_REQUIREMENTS)
822
805
823 rmap.connect('changelog_file_home',
806 rmap.connect('changelog_file_home',
824 '/{repo_name}/changelog/{revision}/{f_path}',
807 '/{repo_name}/changelog/{revision}/{f_path}',
825 controller='changelog', f_path=None,
808 controller='changelog', f_path=None,
826 conditions={'function': check_repo},
809 conditions={'function': check_repo},
827 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
810 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
828
811
829 rmap.connect('changelog_elements', '/{repo_name}/changelog_details',
812 rmap.connect('changelog_elements', '/{repo_name}/changelog_details',
830 controller='changelog', action='changelog_elements',
813 controller='changelog', action='changelog_elements',
831 conditions={'function': check_repo},
814 conditions={'function': check_repo},
832 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
815 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
833
816
834 rmap.connect('files_home', '/{repo_name}/files/{revision}/{f_path}',
817 rmap.connect('files_home', '/{repo_name}/files/{revision}/{f_path}',
835 controller='files', revision='tip', f_path='',
818 controller='files', revision='tip', f_path='',
836 conditions={'function': check_repo},
819 conditions={'function': check_repo},
837 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
820 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
838
821
839 rmap.connect('files_home_simple_catchrev',
822 rmap.connect('files_home_simple_catchrev',
840 '/{repo_name}/files/{revision}',
823 '/{repo_name}/files/{revision}',
841 controller='files', revision='tip', f_path='',
824 controller='files', revision='tip', f_path='',
842 conditions={'function': check_repo},
825 conditions={'function': check_repo},
843 requirements=URL_NAME_REQUIREMENTS)
826 requirements=URL_NAME_REQUIREMENTS)
844
827
845 rmap.connect('files_home_simple_catchall',
828 rmap.connect('files_home_simple_catchall',
846 '/{repo_name}/files',
829 '/{repo_name}/files',
847 controller='files', revision='tip', f_path='',
830 controller='files', revision='tip', f_path='',
848 conditions={'function': check_repo},
831 conditions={'function': check_repo},
849 requirements=URL_NAME_REQUIREMENTS)
832 requirements=URL_NAME_REQUIREMENTS)
850
833
851 rmap.connect('files_history_home',
834 rmap.connect('files_history_home',
852 '/{repo_name}/history/{revision}/{f_path}',
835 '/{repo_name}/history/{revision}/{f_path}',
853 controller='files', action='history', revision='tip', f_path='',
836 controller='files', action='history', revision='tip', f_path='',
854 conditions={'function': check_repo},
837 conditions={'function': check_repo},
855 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
838 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
856
839
857 rmap.connect('files_authors_home',
840 rmap.connect('files_authors_home',
858 '/{repo_name}/authors/{revision}/{f_path}',
841 '/{repo_name}/authors/{revision}/{f_path}',
859 controller='files', action='authors', revision='tip', f_path='',
842 controller='files', action='authors', revision='tip', f_path='',
860 conditions={'function': check_repo},
843 conditions={'function': check_repo},
861 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
844 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
862
845
863 rmap.connect('files_diff_home', '/{repo_name}/diff/{f_path}',
846 rmap.connect('files_diff_home', '/{repo_name}/diff/{f_path}',
864 controller='files', action='diff', f_path='',
847 controller='files', action='diff', f_path='',
865 conditions={'function': check_repo},
848 conditions={'function': check_repo},
866 requirements=URL_NAME_REQUIREMENTS)
849 requirements=URL_NAME_REQUIREMENTS)
867
850
868 rmap.connect('files_diff_2way_home',
851 rmap.connect('files_diff_2way_home',
869 '/{repo_name}/diff-2way/{f_path}',
852 '/{repo_name}/diff-2way/{f_path}',
870 controller='files', action='diff_2way', f_path='',
853 controller='files', action='diff_2way', f_path='',
871 conditions={'function': check_repo},
854 conditions={'function': check_repo},
872 requirements=URL_NAME_REQUIREMENTS)
855 requirements=URL_NAME_REQUIREMENTS)
873
856
874 rmap.connect('files_rawfile_home',
857 rmap.connect('files_rawfile_home',
875 '/{repo_name}/rawfile/{revision}/{f_path}',
858 '/{repo_name}/rawfile/{revision}/{f_path}',
876 controller='files', action='rawfile', revision='tip',
859 controller='files', action='rawfile', revision='tip',
877 f_path='', conditions={'function': check_repo},
860 f_path='', conditions={'function': check_repo},
878 requirements=URL_NAME_REQUIREMENTS)
861 requirements=URL_NAME_REQUIREMENTS)
879
862
880 rmap.connect('files_raw_home',
863 rmap.connect('files_raw_home',
881 '/{repo_name}/raw/{revision}/{f_path}',
864 '/{repo_name}/raw/{revision}/{f_path}',
882 controller='files', action='raw', revision='tip', f_path='',
865 controller='files', action='raw', revision='tip', f_path='',
883 conditions={'function': check_repo},
866 conditions={'function': check_repo},
884 requirements=URL_NAME_REQUIREMENTS)
867 requirements=URL_NAME_REQUIREMENTS)
885
868
886 rmap.connect('files_render_home',
869 rmap.connect('files_render_home',
887 '/{repo_name}/render/{revision}/{f_path}',
870 '/{repo_name}/render/{revision}/{f_path}',
888 controller='files', action='index', revision='tip', f_path='',
871 controller='files', action='index', revision='tip', f_path='',
889 rendered=True, conditions={'function': check_repo},
872 rendered=True, conditions={'function': check_repo},
890 requirements=URL_NAME_REQUIREMENTS)
873 requirements=URL_NAME_REQUIREMENTS)
891
874
892 rmap.connect('files_annotate_home',
875 rmap.connect('files_annotate_home',
893 '/{repo_name}/annotate/{revision}/{f_path}',
876 '/{repo_name}/annotate/{revision}/{f_path}',
894 controller='files', action='index', revision='tip',
877 controller='files', action='index', revision='tip',
895 f_path='', annotate=True, conditions={'function': check_repo},
878 f_path='', annotate=True, conditions={'function': check_repo},
896 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
879 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
897
880
898 rmap.connect('files_annotate_previous',
881 rmap.connect('files_annotate_previous',
899 '/{repo_name}/annotate-previous/{revision}/{f_path}',
882 '/{repo_name}/annotate-previous/{revision}/{f_path}',
900 controller='files', action='annotate_previous', revision='tip',
883 controller='files', action='annotate_previous', revision='tip',
901 f_path='', annotate=True, conditions={'function': check_repo},
884 f_path='', annotate=True, conditions={'function': check_repo},
902 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
885 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
903
886
904 rmap.connect('files_edit',
887 rmap.connect('files_edit',
905 '/{repo_name}/edit/{revision}/{f_path}',
888 '/{repo_name}/edit/{revision}/{f_path}',
906 controller='files', action='edit', revision='tip',
889 controller='files', action='edit', revision='tip',
907 f_path='',
890 f_path='',
908 conditions={'function': check_repo, 'method': ['POST']},
891 conditions={'function': check_repo, 'method': ['POST']},
909 requirements=URL_NAME_REQUIREMENTS)
892 requirements=URL_NAME_REQUIREMENTS)
910
893
911 rmap.connect('files_edit_home',
894 rmap.connect('files_edit_home',
912 '/{repo_name}/edit/{revision}/{f_path}',
895 '/{repo_name}/edit/{revision}/{f_path}',
913 controller='files', action='edit_home', revision='tip',
896 controller='files', action='edit_home', revision='tip',
914 f_path='', conditions={'function': check_repo},
897 f_path='', conditions={'function': check_repo},
915 requirements=URL_NAME_REQUIREMENTS)
898 requirements=URL_NAME_REQUIREMENTS)
916
899
917 rmap.connect('files_add',
900 rmap.connect('files_add',
918 '/{repo_name}/add/{revision}/{f_path}',
901 '/{repo_name}/add/{revision}/{f_path}',
919 controller='files', action='add', revision='tip',
902 controller='files', action='add', revision='tip',
920 f_path='',
903 f_path='',
921 conditions={'function': check_repo, 'method': ['POST']},
904 conditions={'function': check_repo, 'method': ['POST']},
922 requirements=URL_NAME_REQUIREMENTS)
905 requirements=URL_NAME_REQUIREMENTS)
923
906
924 rmap.connect('files_add_home',
907 rmap.connect('files_add_home',
925 '/{repo_name}/add/{revision}/{f_path}',
908 '/{repo_name}/add/{revision}/{f_path}',
926 controller='files', action='add_home', revision='tip',
909 controller='files', action='add_home', revision='tip',
927 f_path='', conditions={'function': check_repo},
910 f_path='', conditions={'function': check_repo},
928 requirements=URL_NAME_REQUIREMENTS)
911 requirements=URL_NAME_REQUIREMENTS)
929
912
930 rmap.connect('files_delete',
913 rmap.connect('files_delete',
931 '/{repo_name}/delete/{revision}/{f_path}',
914 '/{repo_name}/delete/{revision}/{f_path}',
932 controller='files', action='delete', revision='tip',
915 controller='files', action='delete', revision='tip',
933 f_path='',
916 f_path='',
934 conditions={'function': check_repo, 'method': ['POST']},
917 conditions={'function': check_repo, 'method': ['POST']},
935 requirements=URL_NAME_REQUIREMENTS)
918 requirements=URL_NAME_REQUIREMENTS)
936
919
937 rmap.connect('files_delete_home',
920 rmap.connect('files_delete_home',
938 '/{repo_name}/delete/{revision}/{f_path}',
921 '/{repo_name}/delete/{revision}/{f_path}',
939 controller='files', action='delete_home', revision='tip',
922 controller='files', action='delete_home', revision='tip',
940 f_path='', conditions={'function': check_repo},
923 f_path='', conditions={'function': check_repo},
941 requirements=URL_NAME_REQUIREMENTS)
924 requirements=URL_NAME_REQUIREMENTS)
942
925
943 rmap.connect('files_archive_home', '/{repo_name}/archive/{fname}',
926 rmap.connect('files_archive_home', '/{repo_name}/archive/{fname}',
944 controller='files', action='archivefile',
927 controller='files', action='archivefile',
945 conditions={'function': check_repo},
928 conditions={'function': check_repo},
946 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
929 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
947
930
948 rmap.connect('files_nodelist_home',
931 rmap.connect('files_nodelist_home',
949 '/{repo_name}/nodelist/{revision}/{f_path}',
932 '/{repo_name}/nodelist/{revision}/{f_path}',
950 controller='files', action='nodelist',
933 controller='files', action='nodelist',
951 conditions={'function': check_repo},
934 conditions={'function': check_repo},
952 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
935 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
953
936
954 rmap.connect('files_nodetree_full',
937 rmap.connect('files_nodetree_full',
955 '/{repo_name}/nodetree_full/{commit_id}/{f_path}',
938 '/{repo_name}/nodetree_full/{commit_id}/{f_path}',
956 controller='files', action='nodetree_full',
939 controller='files', action='nodetree_full',
957 conditions={'function': check_repo},
940 conditions={'function': check_repo},
958 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
941 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
959
942
960 rmap.connect('repo_fork_create_home', '/{repo_name}/fork',
943 rmap.connect('repo_fork_create_home', '/{repo_name}/fork',
961 controller='forks', action='fork_create',
944 controller='forks', action='fork_create',
962 conditions={'function': check_repo, 'method': ['POST']},
945 conditions={'function': check_repo, 'method': ['POST']},
963 requirements=URL_NAME_REQUIREMENTS)
946 requirements=URL_NAME_REQUIREMENTS)
964
947
965 rmap.connect('repo_fork_home', '/{repo_name}/fork',
948 rmap.connect('repo_fork_home', '/{repo_name}/fork',
966 controller='forks', action='fork',
949 controller='forks', action='fork',
967 conditions={'function': check_repo},
950 conditions={'function': check_repo},
968 requirements=URL_NAME_REQUIREMENTS)
951 requirements=URL_NAME_REQUIREMENTS)
969
952
970 rmap.connect('repo_forks_home', '/{repo_name}/forks',
953 rmap.connect('repo_forks_home', '/{repo_name}/forks',
971 controller='forks', action='forks',
954 controller='forks', action='forks',
972 conditions={'function': check_repo},
955 conditions={'function': check_repo},
973 requirements=URL_NAME_REQUIREMENTS)
956 requirements=URL_NAME_REQUIREMENTS)
974
957
975 return rmap
958 return rmap
@@ -1,294 +1,236 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2013-2017 RhodeCode GmbH
3 # Copyright (C) 2013-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 my account controller for RhodeCode admin
23 my account controller for RhodeCode admin
24 """
24 """
25
25
26 import logging
26 import logging
27
27
28 import formencode
28 import formencode
29 from formencode import htmlfill
29 from formencode import htmlfill
30 from pyramid.httpexceptions import HTTPFound
30 from pyramid.httpexceptions import HTTPFound
31
31
32 from pylons import request, tmpl_context as c, url
32 from pylons import request, tmpl_context as c
33 from pylons.controllers.util import redirect
33 from pylons.controllers.util import redirect
34 from pylons.i18n.translation import _
34 from pylons.i18n.translation import _
35 from sqlalchemy.orm import joinedload
36
35
37 from rhodecode.lib import helpers as h
36 from rhodecode.lib import helpers as h
38 from rhodecode.lib import auth
37 from rhodecode.lib import auth
39 from rhodecode.lib.auth import (
38 from rhodecode.lib.auth import (
40 LoginRequired, NotAnonymous, AuthUser)
39 LoginRequired, NotAnonymous, AuthUser)
41 from rhodecode.lib.base import BaseController, render
40 from rhodecode.lib.base import BaseController, render
42 from rhodecode.lib.utils import jsonify
43 from rhodecode.lib.utils2 import safe_int, str2bool
41 from rhodecode.lib.utils2 import safe_int, str2bool
44 from rhodecode.lib.ext_json import json
42 from rhodecode.lib.ext_json import json
45
43
46 from rhodecode.model.db import (
44 from rhodecode.model.db import (
47 Repository, PullRequest, UserEmailMap, User, UserFollowing)
45 Repository, PullRequest, UserEmailMap, User, UserFollowing)
48 from rhodecode.model.forms import UserForm
46 from rhodecode.model.forms import UserForm
49 from rhodecode.model.scm import RepoList
50 from rhodecode.model.user import UserModel
47 from rhodecode.model.user import UserModel
51 from rhodecode.model.repo import RepoModel
52 from rhodecode.model.meta import Session
48 from rhodecode.model.meta import Session
53 from rhodecode.model.pull_request import PullRequestModel
49 from rhodecode.model.pull_request import PullRequestModel
54 from rhodecode.model.comment import CommentsModel
50 from rhodecode.model.comment import CommentsModel
55
51
56 log = logging.getLogger(__name__)
52 log = logging.getLogger(__name__)
57
53
58
54
59 class MyAccountController(BaseController):
55 class MyAccountController(BaseController):
60 """REST Controller styled on the Atom Publishing Protocol"""
56 """REST Controller styled on the Atom Publishing Protocol"""
61 # To properly map this controller, ensure your config/routing.py
57 # To properly map this controller, ensure your config/routing.py
62 # file has a resource setup:
58 # file has a resource setup:
63 # map.resource('setting', 'settings', controller='admin/settings',
59 # map.resource('setting', 'settings', controller='admin/settings',
64 # path_prefix='/admin', name_prefix='admin_')
60 # path_prefix='/admin', name_prefix='admin_')
65
61
66 @LoginRequired()
62 @LoginRequired()
67 @NotAnonymous()
63 @NotAnonymous()
68 def __before__(self):
64 def __before__(self):
69 super(MyAccountController, self).__before__()
65 super(MyAccountController, self).__before__()
70
66
71 def __load_data(self):
67 def __load_data(self):
72 c.user = User.get(c.rhodecode_user.user_id)
68 c.user = User.get(c.rhodecode_user.user_id)
73 if c.user.username == User.DEFAULT_USER:
69 if c.user.username == User.DEFAULT_USER:
74 h.flash(_("You can't edit this user since it's"
70 h.flash(_("You can't edit this user since it's"
75 " crucial for entire application"), category='warning')
71 " crucial for entire application"), category='warning')
76 return redirect(h.route_path('users'))
72 return redirect(h.route_path('users'))
77
73
78 c.auth_user = AuthUser(
74 c.auth_user = AuthUser(
79 user_id=c.rhodecode_user.user_id, ip_addr=self.ip_addr)
75 user_id=c.rhodecode_user.user_id, ip_addr=self.ip_addr)
80
76
81 def _load_my_repos_data(self, watched=False):
82 if watched:
83 admin = False
84 follows_repos = Session().query(UserFollowing)\
85 .filter(UserFollowing.user_id == c.rhodecode_user.user_id)\
86 .options(joinedload(UserFollowing.follows_repository))\
87 .all()
88 repo_list = [x.follows_repository for x in follows_repos]
89 else:
90 admin = True
91 repo_list = Repository.get_all_repos(
92 user_id=c.rhodecode_user.user_id)
93 repo_list = RepoList(repo_list, perm_set=[
94 'repository.read', 'repository.write', 'repository.admin'])
95
96 repos_data = RepoModel().get_repos_as_dict(
97 repo_list=repo_list, admin=admin)
98 # json used to render the grid
99 return json.dumps(repos_data)
100
101 @auth.CSRFRequired()
77 @auth.CSRFRequired()
102 def my_account_update(self):
78 def my_account_update(self):
103 """
79 """
104 POST /_admin/my_account Updates info of my account
80 POST /_admin/my_account Updates info of my account
105 """
81 """
106 # url('my_account')
82 # url('my_account')
107 c.active = 'profile_edit'
83 c.active = 'profile_edit'
108 self.__load_data()
84 self.__load_data()
109 c.perm_user = c.auth_user
85 c.perm_user = c.auth_user
110 c.extern_type = c.user.extern_type
86 c.extern_type = c.user.extern_type
111 c.extern_name = c.user.extern_name
87 c.extern_name = c.user.extern_name
112
88
113 defaults = c.user.get_dict()
89 defaults = c.user.get_dict()
114 update = False
90 update = False
115 _form = UserForm(edit=True,
91 _form = UserForm(edit=True,
116 old_data={'user_id': c.rhodecode_user.user_id,
92 old_data={'user_id': c.rhodecode_user.user_id,
117 'email': c.rhodecode_user.email})()
93 'email': c.rhodecode_user.email})()
118 form_result = {}
94 form_result = {}
119 try:
95 try:
120 post_data = dict(request.POST)
96 post_data = dict(request.POST)
121 post_data['new_password'] = ''
97 post_data['new_password'] = ''
122 post_data['password_confirmation'] = ''
98 post_data['password_confirmation'] = ''
123 form_result = _form.to_python(post_data)
99 form_result = _form.to_python(post_data)
124 # skip updating those attrs for my account
100 # skip updating those attrs for my account
125 skip_attrs = ['admin', 'active', 'extern_type', 'extern_name',
101 skip_attrs = ['admin', 'active', 'extern_type', 'extern_name',
126 'new_password', 'password_confirmation']
102 'new_password', 'password_confirmation']
127 # TODO: plugin should define if username can be updated
103 # TODO: plugin should define if username can be updated
128 if c.extern_type != "rhodecode":
104 if c.extern_type != "rhodecode":
129 # forbid updating username for external accounts
105 # forbid updating username for external accounts
130 skip_attrs.append('username')
106 skip_attrs.append('username')
131
107
132 UserModel().update_user(
108 UserModel().update_user(
133 c.rhodecode_user.user_id, skip_attrs=skip_attrs, **form_result)
109 c.rhodecode_user.user_id, skip_attrs=skip_attrs, **form_result)
134 h.flash(_('Your account was updated successfully'),
110 h.flash(_('Your account was updated successfully'),
135 category='success')
111 category='success')
136 Session().commit()
112 Session().commit()
137 update = True
113 update = True
138
114
139 except formencode.Invalid as errors:
115 except formencode.Invalid as errors:
140 return htmlfill.render(
116 return htmlfill.render(
141 render('admin/my_account/my_account.mako'),
117 render('admin/my_account/my_account.mako'),
142 defaults=errors.value,
118 defaults=errors.value,
143 errors=errors.error_dict or {},
119 errors=errors.error_dict or {},
144 prefix_error=False,
120 prefix_error=False,
145 encoding="UTF-8",
121 encoding="UTF-8",
146 force_defaults=False)
122 force_defaults=False)
147 except Exception:
123 except Exception:
148 log.exception("Exception updating user")
124 log.exception("Exception updating user")
149 h.flash(_('Error occurred during update of user %s')
125 h.flash(_('Error occurred during update of user %s')
150 % form_result.get('username'), category='error')
126 % form_result.get('username'), category='error')
151
127
152 if update:
128 if update:
153 raise HTTPFound(h.route_path('my_account_profile'))
129 raise HTTPFound(h.route_path('my_account_profile'))
154
130
155 return htmlfill.render(
131 return htmlfill.render(
156 render('admin/my_account/my_account.mako'),
132 render('admin/my_account/my_account.mako'),
157 defaults=defaults,
133 defaults=defaults,
158 encoding="UTF-8",
134 encoding="UTF-8",
159 force_defaults=False
135 force_defaults=False
160 )
136 )
161
137
162 def my_account_edit(self):
138 def my_account_edit(self):
163 """
139 """
164 GET /_admin/my_account/edit Displays edit form of my account
140 GET /_admin/my_account/edit Displays edit form of my account
165 """
141 """
166 c.active = 'profile_edit'
142 c.active = 'profile_edit'
167 self.__load_data()
143 self.__load_data()
168 c.perm_user = c.auth_user
144 c.perm_user = c.auth_user
169 c.extern_type = c.user.extern_type
145 c.extern_type = c.user.extern_type
170 c.extern_name = c.user.extern_name
146 c.extern_name = c.user.extern_name
171
147
172 defaults = c.user.get_dict()
148 defaults = c.user.get_dict()
173 return htmlfill.render(
149 return htmlfill.render(
174 render('admin/my_account/my_account.mako'),
150 render('admin/my_account/my_account.mako'),
175 defaults=defaults,
151 defaults=defaults,
176 encoding="UTF-8",
152 encoding="UTF-8",
177 force_defaults=False
153 force_defaults=False
178 )
154 )
179
155
180 def my_account_repos(self):
181 c.active = 'repos'
182 self.__load_data()
183
184 # json used to render the grid
185 c.data = self._load_my_repos_data()
186 return render('admin/my_account/my_account.mako')
187
188 def my_account_watched(self):
189 c.active = 'watched'
190 self.__load_data()
191
192 # json used to render the grid
193 c.data = self._load_my_repos_data(watched=True)
194 return render('admin/my_account/my_account.mako')
195
196 def my_account_perms(self):
197 c.active = 'perms'
198 self.__load_data()
199 c.perm_user = c.auth_user
200
201 return render('admin/my_account/my_account.mako')
202
203 def _extract_ordering(self, request):
156 def _extract_ordering(self, request):
204 column_index = safe_int(request.GET.get('order[0][column]'))
157 column_index = safe_int(request.GET.get('order[0][column]'))
205 order_dir = request.GET.get('order[0][dir]', 'desc')
158 order_dir = request.GET.get('order[0][dir]', 'desc')
206 order_by = request.GET.get(
159 order_by = request.GET.get(
207 'columns[%s][data][sort]' % column_index, 'name_raw')
160 'columns[%s][data][sort]' % column_index, 'name_raw')
208 return order_by, order_dir
161 return order_by, order_dir
209
162
210 def _get_pull_requests_list(self, statuses):
163 def _get_pull_requests_list(self, statuses):
211 start = safe_int(request.GET.get('start'), 0)
164 start = safe_int(request.GET.get('start'), 0)
212 length = safe_int(request.GET.get('length'), c.visual.dashboard_items)
165 length = safe_int(request.GET.get('length'), c.visual.dashboard_items)
213 order_by, order_dir = self._extract_ordering(request)
166 order_by, order_dir = self._extract_ordering(request)
214
167
215 pull_requests = PullRequestModel().get_im_participating_in(
168 pull_requests = PullRequestModel().get_im_participating_in(
216 user_id=c.rhodecode_user.user_id,
169 user_id=c.rhodecode_user.user_id,
217 statuses=statuses,
170 statuses=statuses,
218 offset=start, length=length, order_by=order_by,
171 offset=start, length=length, order_by=order_by,
219 order_dir=order_dir)
172 order_dir=order_dir)
220
173
221 pull_requests_total_count = PullRequestModel().count_im_participating_in(
174 pull_requests_total_count = PullRequestModel().count_im_participating_in(
222 user_id=c.rhodecode_user.user_id, statuses=statuses)
175 user_id=c.rhodecode_user.user_id, statuses=statuses)
223
176
224 from rhodecode.lib.utils import PartialRenderer
177 from rhodecode.lib.utils import PartialRenderer
225 _render = PartialRenderer('data_table/_dt_elements.mako')
178 _render = PartialRenderer('data_table/_dt_elements.mako')
226 data = []
179 data = []
227 for pr in pull_requests:
180 for pr in pull_requests:
228 repo_id = pr.target_repo_id
181 repo_id = pr.target_repo_id
229 comments = CommentsModel().get_all_comments(
182 comments = CommentsModel().get_all_comments(
230 repo_id, pull_request=pr)
183 repo_id, pull_request=pr)
231 owned = pr.user_id == c.rhodecode_user.user_id
184 owned = pr.user_id == c.rhodecode_user.user_id
232 status = pr.calculated_review_status()
185 status = pr.calculated_review_status()
233
186
234 data.append({
187 data.append({
235 'target_repo': _render('pullrequest_target_repo',
188 'target_repo': _render('pullrequest_target_repo',
236 pr.target_repo.repo_name),
189 pr.target_repo.repo_name),
237 'name': _render('pullrequest_name',
190 'name': _render('pullrequest_name',
238 pr.pull_request_id, pr.target_repo.repo_name,
191 pr.pull_request_id, pr.target_repo.repo_name,
239 short=True),
192 short=True),
240 'name_raw': pr.pull_request_id,
193 'name_raw': pr.pull_request_id,
241 'status': _render('pullrequest_status', status),
194 'status': _render('pullrequest_status', status),
242 'title': _render(
195 'title': _render(
243 'pullrequest_title', pr.title, pr.description),
196 'pullrequest_title', pr.title, pr.description),
244 'description': h.escape(pr.description),
197 'description': h.escape(pr.description),
245 'updated_on': _render('pullrequest_updated_on',
198 'updated_on': _render('pullrequest_updated_on',
246 h.datetime_to_time(pr.updated_on)),
199 h.datetime_to_time(pr.updated_on)),
247 'updated_on_raw': h.datetime_to_time(pr.updated_on),
200 'updated_on_raw': h.datetime_to_time(pr.updated_on),
248 'created_on': _render('pullrequest_updated_on',
201 'created_on': _render('pullrequest_updated_on',
249 h.datetime_to_time(pr.created_on)),
202 h.datetime_to_time(pr.created_on)),
250 'created_on_raw': h.datetime_to_time(pr.created_on),
203 'created_on_raw': h.datetime_to_time(pr.created_on),
251 'author': _render('pullrequest_author',
204 'author': _render('pullrequest_author',
252 pr.author.full_contact, ),
205 pr.author.full_contact, ),
253 'author_raw': pr.author.full_name,
206 'author_raw': pr.author.full_name,
254 'comments': _render('pullrequest_comments', len(comments)),
207 'comments': _render('pullrequest_comments', len(comments)),
255 'comments_raw': len(comments),
208 'comments_raw': len(comments),
256 'closed': pr.is_closed(),
209 'closed': pr.is_closed(),
257 'owned': owned
210 'owned': owned
258 })
211 })
259 # json used to render the grid
212 # json used to render the grid
260 data = ({
213 data = ({
261 'data': data,
214 'data': data,
262 'recordsTotal': pull_requests_total_count,
215 'recordsTotal': pull_requests_total_count,
263 'recordsFiltered': pull_requests_total_count,
216 'recordsFiltered': pull_requests_total_count,
264 })
217 })
265 return data
218 return data
266
219
267 def my_account_pullrequests(self):
220 def my_account_pullrequests(self):
268 c.active = 'pullrequests'
221 c.active = 'pullrequests'
269 self.__load_data()
222 self.__load_data()
270 c.show_closed = str2bool(request.GET.get('pr_show_closed'))
223 c.show_closed = str2bool(request.GET.get('pr_show_closed'))
271
224
272 statuses = [PullRequest.STATUS_NEW, PullRequest.STATUS_OPEN]
225 statuses = [PullRequest.STATUS_NEW, PullRequest.STATUS_OPEN]
273 if c.show_closed:
226 if c.show_closed:
274 statuses += [PullRequest.STATUS_CLOSED]
227 statuses += [PullRequest.STATUS_CLOSED]
275 data = self._get_pull_requests_list(statuses)
228 data = self._get_pull_requests_list(statuses)
276 if not request.is_xhr:
229 if not request.is_xhr:
277 c.data_participate = json.dumps(data['data'])
230 c.data_participate = json.dumps(data['data'])
278 c.records_total_participate = data['recordsTotal']
231 c.records_total_participate = data['recordsTotal']
279 return render('admin/my_account/my_account.mako')
232 return render('admin/my_account/my_account.mako')
280 else:
233 else:
281 return json.dumps(data)
234 return json.dumps(data)
282
235
283 def my_notifications(self):
284 c.active = 'notifications'
285 return render('admin/my_account/my_account.mako')
286
236
287 @auth.CSRFRequired()
288 @jsonify
289 def my_notifications_toggle_visibility(self):
290 user = c.rhodecode_user.get_instance()
291 new_status = not user.user_data.get('notification_status', True)
292 user.update_userdata(notification_status=new_status)
293 Session().commit()
294 return user.user_data['notification_status']
@@ -1,139 +1,147 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('gists', '/_admin/gists', []);
18 pyroutes.register('gists', '/_admin/gists', []);
19 pyroutes.register('new_gist', '/_admin/gists/new', []);
19 pyroutes.register('new_gist', '/_admin/gists/new', []);
20 pyroutes.register('toggle_following', '/_admin/toggle_following', []);
20 pyroutes.register('toggle_following', '/_admin/toggle_following', []);
21 pyroutes.register('changeset_home', '/%(repo_name)s/changeset/%(revision)s', ['repo_name', 'revision']);
21 pyroutes.register('changeset_home', '/%(repo_name)s/changeset/%(revision)s', ['repo_name', 'revision']);
22 pyroutes.register('changeset_comment', '/%(repo_name)s/changeset/%(revision)s/comment', ['repo_name', 'revision']);
22 pyroutes.register('changeset_comment', '/%(repo_name)s/changeset/%(revision)s/comment', ['repo_name', 'revision']);
23 pyroutes.register('changeset_comment_preview', '/%(repo_name)s/changeset/comment/preview', ['repo_name']);
23 pyroutes.register('changeset_comment_preview', '/%(repo_name)s/changeset/comment/preview', ['repo_name']);
24 pyroutes.register('changeset_comment_delete', '/%(repo_name)s/changeset/comment/%(comment_id)s/delete', ['repo_name', 'comment_id']);
24 pyroutes.register('changeset_comment_delete', '/%(repo_name)s/changeset/comment/%(comment_id)s/delete', ['repo_name', 'comment_id']);
25 pyroutes.register('changeset_info', '/%(repo_name)s/changeset_info/%(revision)s', ['repo_name', 'revision']);
25 pyroutes.register('changeset_info', '/%(repo_name)s/changeset_info/%(revision)s', ['repo_name', 'revision']);
26 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']);
26 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']);
27 pyroutes.register('pullrequest_home', '/%(repo_name)s/pull-request/new', ['repo_name']);
27 pyroutes.register('pullrequest_home', '/%(repo_name)s/pull-request/new', ['repo_name']);
28 pyroutes.register('pullrequest', '/%(repo_name)s/pull-request/new', ['repo_name']);
28 pyroutes.register('pullrequest', '/%(repo_name)s/pull-request/new', ['repo_name']);
29 pyroutes.register('pullrequest_repo_refs', '/%(repo_name)s/pull-request/refs/%(target_repo_name)s', ['repo_name', 'target_repo_name']);
29 pyroutes.register('pullrequest_repo_refs', '/%(repo_name)s/pull-request/refs/%(target_repo_name)s', ['repo_name', 'target_repo_name']);
30 pyroutes.register('pullrequest_repo_destinations', '/%(repo_name)s/pull-request/repo-destinations', ['repo_name']);
30 pyroutes.register('pullrequest_repo_destinations', '/%(repo_name)s/pull-request/repo-destinations', ['repo_name']);
31 pyroutes.register('pullrequest_show', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
31 pyroutes.register('pullrequest_show', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
32 pyroutes.register('pullrequest_update', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
32 pyroutes.register('pullrequest_update', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
33 pyroutes.register('pullrequest_comment', '/%(repo_name)s/pull-request-comment/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
33 pyroutes.register('pullrequest_comment', '/%(repo_name)s/pull-request-comment/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
34 pyroutes.register('pullrequest_comment_delete', '/%(repo_name)s/pull-request-comment/%(comment_id)s/delete', ['repo_name', 'comment_id']);
34 pyroutes.register('pullrequest_comment_delete', '/%(repo_name)s/pull-request-comment/%(comment_id)s/delete', ['repo_name', 'comment_id']);
35 pyroutes.register('changelog_home', '/%(repo_name)s/changelog', ['repo_name']);
35 pyroutes.register('changelog_home', '/%(repo_name)s/changelog', ['repo_name']);
36 pyroutes.register('changelog_file_home', '/%(repo_name)s/changelog/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
36 pyroutes.register('changelog_file_home', '/%(repo_name)s/changelog/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
37 pyroutes.register('changelog_elements', '/%(repo_name)s/changelog_details', ['repo_name']);
37 pyroutes.register('changelog_elements', '/%(repo_name)s/changelog_details', ['repo_name']);
38 pyroutes.register('files_home', '/%(repo_name)s/files/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
38 pyroutes.register('files_home', '/%(repo_name)s/files/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
39 pyroutes.register('files_history_home', '/%(repo_name)s/history/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
39 pyroutes.register('files_history_home', '/%(repo_name)s/history/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
40 pyroutes.register('files_authors_home', '/%(repo_name)s/authors/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
40 pyroutes.register('files_authors_home', '/%(repo_name)s/authors/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
41 pyroutes.register('files_annotate_home', '/%(repo_name)s/annotate/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
41 pyroutes.register('files_annotate_home', '/%(repo_name)s/annotate/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
42 pyroutes.register('files_annotate_previous', '/%(repo_name)s/annotate-previous/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
42 pyroutes.register('files_annotate_previous', '/%(repo_name)s/annotate-previous/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
43 pyroutes.register('files_archive_home', '/%(repo_name)s/archive/%(fname)s', ['repo_name', 'fname']);
43 pyroutes.register('files_archive_home', '/%(repo_name)s/archive/%(fname)s', ['repo_name', 'fname']);
44 pyroutes.register('files_nodelist_home', '/%(repo_name)s/nodelist/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
44 pyroutes.register('files_nodelist_home', '/%(repo_name)s/nodelist/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
45 pyroutes.register('files_nodetree_full', '/%(repo_name)s/nodetree_full/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
45 pyroutes.register('files_nodetree_full', '/%(repo_name)s/nodetree_full/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
46 pyroutes.register('favicon', '/favicon.ico', []);
46 pyroutes.register('favicon', '/favicon.ico', []);
47 pyroutes.register('robots', '/robots.txt', []);
47 pyroutes.register('robots', '/robots.txt', []);
48 pyroutes.register('auth_home', '/_admin/auth*traverse', []);
48 pyroutes.register('auth_home', '/_admin/auth*traverse', []);
49 pyroutes.register('global_integrations_new', '/_admin/integrations/new', []);
49 pyroutes.register('global_integrations_new', '/_admin/integrations/new', []);
50 pyroutes.register('global_integrations_home', '/_admin/integrations', []);
50 pyroutes.register('global_integrations_home', '/_admin/integrations', []);
51 pyroutes.register('global_integrations_list', '/_admin/integrations/%(integration)s', ['integration']);
51 pyroutes.register('global_integrations_list', '/_admin/integrations/%(integration)s', ['integration']);
52 pyroutes.register('global_integrations_create', '/_admin/integrations/%(integration)s/new', ['integration']);
52 pyroutes.register('global_integrations_create', '/_admin/integrations/%(integration)s/new', ['integration']);
53 pyroutes.register('global_integrations_edit', '/_admin/integrations/%(integration)s/%(integration_id)s', ['integration', 'integration_id']);
53 pyroutes.register('global_integrations_edit', '/_admin/integrations/%(integration)s/%(integration_id)s', ['integration', 'integration_id']);
54 pyroutes.register('repo_group_integrations_home', '%(repo_group_name)s/settings/integrations', ['repo_group_name']);
54 pyroutes.register('repo_group_integrations_home', '%(repo_group_name)s/settings/integrations', ['repo_group_name']);
55 pyroutes.register('repo_group_integrations_list', '%(repo_group_name)s/settings/integrations/%(integration)s', ['repo_group_name', 'integration']);
55 pyroutes.register('repo_group_integrations_list', '%(repo_group_name)s/settings/integrations/%(integration)s', ['repo_group_name', 'integration']);
56 pyroutes.register('repo_group_integrations_new', '%(repo_group_name)s/settings/integrations/new', ['repo_group_name']);
56 pyroutes.register('repo_group_integrations_new', '%(repo_group_name)s/settings/integrations/new', ['repo_group_name']);
57 pyroutes.register('repo_group_integrations_create', '%(repo_group_name)s/settings/integrations/%(integration)s/new', ['repo_group_name', 'integration']);
57 pyroutes.register('repo_group_integrations_create', '%(repo_group_name)s/settings/integrations/%(integration)s/new', ['repo_group_name', 'integration']);
58 pyroutes.register('repo_group_integrations_edit', '%(repo_group_name)s/settings/integrations/%(integration)s/%(integration_id)s', ['repo_group_name', 'integration', 'integration_id']);
58 pyroutes.register('repo_group_integrations_edit', '%(repo_group_name)s/settings/integrations/%(integration)s/%(integration_id)s', ['repo_group_name', 'integration', 'integration_id']);
59 pyroutes.register('repo_integrations_home', '%(repo_name)s/settings/integrations', ['repo_name']);
59 pyroutes.register('repo_integrations_home', '%(repo_name)s/settings/integrations', ['repo_name']);
60 pyroutes.register('repo_integrations_list', '%(repo_name)s/settings/integrations/%(integration)s', ['repo_name', 'integration']);
60 pyroutes.register('repo_integrations_list', '%(repo_name)s/settings/integrations/%(integration)s', ['repo_name', 'integration']);
61 pyroutes.register('repo_integrations_new', '%(repo_name)s/settings/integrations/new', ['repo_name']);
61 pyroutes.register('repo_integrations_new', '%(repo_name)s/settings/integrations/new', ['repo_name']);
62 pyroutes.register('repo_integrations_create', '%(repo_name)s/settings/integrations/%(integration)s/new', ['repo_name', 'integration']);
62 pyroutes.register('repo_integrations_create', '%(repo_name)s/settings/integrations/%(integration)s/new', ['repo_name', 'integration']);
63 pyroutes.register('repo_integrations_edit', '%(repo_name)s/settings/integrations/%(integration)s/%(integration_id)s', ['repo_name', 'integration', 'integration_id']);
63 pyroutes.register('repo_integrations_edit', '%(repo_name)s/settings/integrations/%(integration)s/%(integration_id)s', ['repo_name', 'integration', 'integration_id']);
64 pyroutes.register('ops_ping', '_admin/ops/ping', []);
64 pyroutes.register('ops_ping', '_admin/ops/ping', []);
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('users', '_admin/users', []);
76 pyroutes.register('users', '_admin/users', []);
77 pyroutes.register('users_data', '_admin/users_data', []);
77 pyroutes.register('users_data', '_admin/users_data', []);
78 pyroutes.register('edit_user_auth_tokens', '_admin/users/%(user_id)s/edit/auth_tokens', ['user_id']);
78 pyroutes.register('edit_user_auth_tokens', '_admin/users/%(user_id)s/edit/auth_tokens', ['user_id']);
79 pyroutes.register('edit_user_auth_tokens_add', '_admin/users/%(user_id)s/edit/auth_tokens/new', ['user_id']);
79 pyroutes.register('edit_user_auth_tokens_add', '_admin/users/%(user_id)s/edit/auth_tokens/new', ['user_id']);
80 pyroutes.register('edit_user_auth_tokens_delete', '_admin/users/%(user_id)s/edit/auth_tokens/delete', ['user_id']);
80 pyroutes.register('edit_user_auth_tokens_delete', '_admin/users/%(user_id)s/edit/auth_tokens/delete', ['user_id']);
81 pyroutes.register('edit_user_groups_management', '_admin/users/%(user_id)s/edit/groups_management', ['user_id']);
81 pyroutes.register('edit_user_groups_management', '_admin/users/%(user_id)s/edit/groups_management', ['user_id']);
82 pyroutes.register('edit_user_groups_management_updates', '_admin/users/%(user_id)s/edit/edit_user_groups_management/updates', ['user_id']);
82 pyroutes.register('edit_user_groups_management_updates', '_admin/users/%(user_id)s/edit/edit_user_groups_management/updates', ['user_id']);
83 pyroutes.register('edit_user_audit_logs', '_admin/users/%(user_id)s/edit/audit', ['user_id']);
83 pyroutes.register('edit_user_audit_logs', '_admin/users/%(user_id)s/edit/audit', ['user_id']);
84 pyroutes.register('channelstream_connect', '/_admin/channelstream/connect', []);
84 pyroutes.register('channelstream_connect', '/_admin/channelstream/connect', []);
85 pyroutes.register('channelstream_subscribe', '/_admin/channelstream/subscribe', []);
85 pyroutes.register('channelstream_subscribe', '/_admin/channelstream/subscribe', []);
86 pyroutes.register('channelstream_proxy', '/_channelstream', []);
86 pyroutes.register('channelstream_proxy', '/_channelstream', []);
87 pyroutes.register('login', '/_admin/login', []);
87 pyroutes.register('login', '/_admin/login', []);
88 pyroutes.register('logout', '/_admin/logout', []);
88 pyroutes.register('logout', '/_admin/logout', []);
89 pyroutes.register('register', '/_admin/register', []);
89 pyroutes.register('register', '/_admin/register', []);
90 pyroutes.register('reset_password', '/_admin/password_reset', []);
90 pyroutes.register('reset_password', '/_admin/password_reset', []);
91 pyroutes.register('reset_password_confirmation', '/_admin/password_reset_confirmation', []);
91 pyroutes.register('reset_password_confirmation', '/_admin/password_reset_confirmation', []);
92 pyroutes.register('home', '/', []);
92 pyroutes.register('home', '/', []);
93 pyroutes.register('user_autocomplete_data', '/_users', []);
93 pyroutes.register('user_autocomplete_data', '/_users', []);
94 pyroutes.register('user_group_autocomplete_data', '/_user_groups', []);
94 pyroutes.register('user_group_autocomplete_data', '/_user_groups', []);
95 pyroutes.register('repo_list_data', '/_repos', []);
95 pyroutes.register('repo_list_data', '/_repos', []);
96 pyroutes.register('goto_switcher_data', '/_goto_data', []);
96 pyroutes.register('goto_switcher_data', '/_goto_data', []);
97 pyroutes.register('repo_summary_explicit', '/%(repo_name)s/summary', ['repo_name']);
97 pyroutes.register('repo_summary_explicit', '/%(repo_name)s/summary', ['repo_name']);
98 pyroutes.register('repo_summary_commits', '/%(repo_name)s/summary-commits', ['repo_name']);
98 pyroutes.register('repo_summary_commits', '/%(repo_name)s/summary-commits', ['repo_name']);
99 pyroutes.register('repo_commit', '/%(repo_name)s/changeset/%(commit_id)s', ['repo_name', 'commit_id']);
99 pyroutes.register('repo_commit', '/%(repo_name)s/changeset/%(commit_id)s', ['repo_name', 'commit_id']);
100 pyroutes.register('repo_refs_data', '/%(repo_name)s/refs-data', ['repo_name']);
100 pyroutes.register('repo_refs_data', '/%(repo_name)s/refs-data', ['repo_name']);
101 pyroutes.register('repo_refs_changelog_data', '/%(repo_name)s/refs-data-changelog', ['repo_name']);
101 pyroutes.register('repo_refs_changelog_data', '/%(repo_name)s/refs-data-changelog', ['repo_name']);
102 pyroutes.register('repo_stats', '/%(repo_name)s/repo_stats/%(commit_id)s', ['repo_name', 'commit_id']);
102 pyroutes.register('repo_stats', '/%(repo_name)s/repo_stats/%(commit_id)s', ['repo_name', 'commit_id']);
103 pyroutes.register('tags_home', '/%(repo_name)s/tags', ['repo_name']);
103 pyroutes.register('tags_home', '/%(repo_name)s/tags', ['repo_name']);
104 pyroutes.register('branches_home', '/%(repo_name)s/branches', ['repo_name']);
104 pyroutes.register('branches_home', '/%(repo_name)s/branches', ['repo_name']);
105 pyroutes.register('bookmarks_home', '/%(repo_name)s/bookmarks', ['repo_name']);
105 pyroutes.register('bookmarks_home', '/%(repo_name)s/bookmarks', ['repo_name']);
106 pyroutes.register('pullrequest_show', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
106 pyroutes.register('pullrequest_show', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
107 pyroutes.register('pullrequest_show_all', '/%(repo_name)s/pull-request', ['repo_name']);
107 pyroutes.register('pullrequest_show_all', '/%(repo_name)s/pull-request', ['repo_name']);
108 pyroutes.register('pullrequest_show_all_data', '/%(repo_name)s/pull-request-data', ['repo_name']);
108 pyroutes.register('pullrequest_show_all_data', '/%(repo_name)s/pull-request-data', ['repo_name']);
109 pyroutes.register('edit_repo', '/%(repo_name)s/settings', ['repo_name']);
109 pyroutes.register('edit_repo', '/%(repo_name)s/settings', ['repo_name']);
110 pyroutes.register('edit_repo_advanced', '/%(repo_name)s/settings/advanced', ['repo_name']);
110 pyroutes.register('edit_repo_advanced', '/%(repo_name)s/settings/advanced', ['repo_name']);
111 pyroutes.register('edit_repo_advanced_delete', '/%(repo_name)s/settings/advanced/delete', ['repo_name']);
111 pyroutes.register('edit_repo_advanced_delete', '/%(repo_name)s/settings/advanced/delete', ['repo_name']);
112 pyroutes.register('edit_repo_advanced_locking', '/%(repo_name)s/settings/advanced/locking', ['repo_name']);
112 pyroutes.register('edit_repo_advanced_locking', '/%(repo_name)s/settings/advanced/locking', ['repo_name']);
113 pyroutes.register('edit_repo_advanced_journal', '/%(repo_name)s/settings/advanced/journal', ['repo_name']);
113 pyroutes.register('edit_repo_advanced_journal', '/%(repo_name)s/settings/advanced/journal', ['repo_name']);
114 pyroutes.register('edit_repo_advanced_fork', '/%(repo_name)s/settings/advanced/fork', ['repo_name']);
114 pyroutes.register('edit_repo_advanced_fork', '/%(repo_name)s/settings/advanced/fork', ['repo_name']);
115 pyroutes.register('edit_repo_caches', '/%(repo_name)s/settings/caches', ['repo_name']);
115 pyroutes.register('edit_repo_caches', '/%(repo_name)s/settings/caches', ['repo_name']);
116 pyroutes.register('edit_repo_perms', '/%(repo_name)s/settings/permissions', ['repo_name']);
116 pyroutes.register('edit_repo_perms', '/%(repo_name)s/settings/permissions', ['repo_name']);
117 pyroutes.register('repo_reviewers', '/%(repo_name)s/settings/review/rules', ['repo_name']);
117 pyroutes.register('repo_reviewers', '/%(repo_name)s/settings/review/rules', ['repo_name']);
118 pyroutes.register('repo_default_reviewers_data', '/%(repo_name)s/settings/review/default-reviewers', ['repo_name']);
118 pyroutes.register('repo_default_reviewers_data', '/%(repo_name)s/settings/review/default-reviewers', ['repo_name']);
119 pyroutes.register('repo_maintenance', '/%(repo_name)s/settings/maintenance', ['repo_name']);
119 pyroutes.register('repo_maintenance', '/%(repo_name)s/settings/maintenance', ['repo_name']);
120 pyroutes.register('repo_maintenance_execute', '/%(repo_name)s/settings/maintenance/execute', ['repo_name']);
120 pyroutes.register('repo_maintenance_execute', '/%(repo_name)s/settings/maintenance/execute', ['repo_name']);
121 pyroutes.register('strip', '/%(repo_name)s/settings/strip', ['repo_name']);
121 pyroutes.register('strip', '/%(repo_name)s/settings/strip', ['repo_name']);
122 pyroutes.register('strip_check', '/%(repo_name)s/settings/strip_check', ['repo_name']);
122 pyroutes.register('strip_check', '/%(repo_name)s/settings/strip_check', ['repo_name']);
123 pyroutes.register('strip_execute', '/%(repo_name)s/settings/strip_execute', ['repo_name']);
123 pyroutes.register('strip_execute', '/%(repo_name)s/settings/strip_execute', ['repo_name']);
124 pyroutes.register('repo_summary', '/%(repo_name)s', ['repo_name']);
124 pyroutes.register('repo_summary', '/%(repo_name)s', ['repo_name']);
125 pyroutes.register('repo_summary_slash', '/%(repo_name)s/', ['repo_name']);
125 pyroutes.register('repo_summary_slash', '/%(repo_name)s/', ['repo_name']);
126 pyroutes.register('repo_group_home', '/%(repo_group_name)s', ['repo_group_name']);
126 pyroutes.register('repo_group_home', '/%(repo_group_name)s', ['repo_group_name']);
127 pyroutes.register('repo_group_home_slash', '/%(repo_group_name)s/', ['repo_group_name']);
127 pyroutes.register('repo_group_home_slash', '/%(repo_group_name)s/', ['repo_group_name']);
128 pyroutes.register('search', '/_admin/search', []);
128 pyroutes.register('search', '/_admin/search', []);
129 pyroutes.register('search_repo', '/%(repo_name)s/search', ['repo_name']);
129 pyroutes.register('search_repo', '/%(repo_name)s/search', ['repo_name']);
130 pyroutes.register('user_profile', '/_profiles/%(username)s', ['username']);
130 pyroutes.register('user_profile', '/_profiles/%(username)s', ['username']);
131 pyroutes.register('my_account_profile', '/_admin/my_account/profile', []);
131 pyroutes.register('my_account_profile', '/_admin/my_account/profile', []);
132 pyroutes.register('my_account_password', '/_admin/my_account/password', []);
132 pyroutes.register('my_account_password', '/_admin/my_account/password', []);
133 pyroutes.register('my_account_password_update', '/_admin/my_account/password', []);
133 pyroutes.register('my_account_password_update', '/_admin/my_account/password', []);
134 pyroutes.register('my_account_auth_tokens', '/_admin/my_account/auth_tokens', []);
134 pyroutes.register('my_account_auth_tokens', '/_admin/my_account/auth_tokens', []);
135 pyroutes.register('my_account_auth_tokens_add', '/_admin/my_account/auth_tokens/new', []);
135 pyroutes.register('my_account_auth_tokens_add', '/_admin/my_account/auth_tokens/new', []);
136 pyroutes.register('my_account_auth_tokens_delete', '/_admin/my_account/auth_tokens/delete', []);
136 pyroutes.register('my_account_auth_tokens_delete', '/_admin/my_account/auth_tokens/delete', []);
137 pyroutes.register('my_account_emails', '/_admin/my_account/emails', []);
138 pyroutes.register('my_account_emails_add', '/_admin/my_account/emails/new', []);
139 pyroutes.register('my_account_emails_delete', '/_admin/my_account/emails/delete', []);
140 pyroutes.register('my_account_repos', '/_admin/my_account/repos', []);
141 pyroutes.register('my_account_watched', '/_admin/my_account/watched', []);
142 pyroutes.register('my_account_perms', '/_admin/my_account/perms', []);
143 pyroutes.register('my_account_notifications', '/_admin/my_account/notifications', []);
144 pyroutes.register('my_account_notifications_toggle_visibility', '/_admin/my_account/toggle_visibility', []);
137 pyroutes.register('my_account_notifications_test_channelstream', '/_admin/my_account/test_channelstream', []);
145 pyroutes.register('my_account_notifications_test_channelstream', '/_admin/my_account/test_channelstream', []);
138 pyroutes.register('apiv2', '/_admin/api', []);
146 pyroutes.register('apiv2', '/_admin/api', []);
139 }
147 }
@@ -1,52 +1,52 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 account')} ${c.rhodecode_user.username}
5 ${_('My account')} ${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 Account')}
12 ${_('My Account')}
13 </%def>
13 </%def>
14
14
15 <%def name="menu_bar_nav()">
15 <%def name="menu_bar_nav()">
16 ${self.menu_items(active='my_account')}
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 <div class="title">
21 <div class="title">
22 ${self.breadcrumbs()}
22 ${self.breadcrumbs()}
23 </div>
23 </div>
24
24
25 <div class="sidebar-col-wrapper scw-small">
25 <div class="sidebar-col-wrapper scw-small">
26 ##main
26 ##main
27 <div class="sidebar">
27 <div class="sidebar">
28 <ul class="nav nav-pills nav-stacked">
28 <ul class="nav nav-pills nav-stacked">
29 <li class="${'active' if c.active=='profile' or c.active=='profile_edit' else ''}"><a href="${h.route_path('my_account_profile')}">${_('Profile')}</a></li>
29 <li class="${'active' if c.active=='profile' or c.active=='profile_edit' else ''}"><a href="${h.route_path('my_account_profile')}">${_('Profile')}</a></li>
30 <li class="${'active' if c.active=='password' else ''}"><a href="${h.route_path('my_account_password')}">${_('Password')}</a></li>
30 <li class="${'active' if c.active=='password' else ''}"><a href="${h.route_path('my_account_password')}">${_('Password')}</a></li>
31 <li class="${'active' if c.active=='auth_tokens' else ''}"><a href="${h.route_path('my_account_auth_tokens')}">${_('Auth Tokens')}</a></li>
31 <li class="${'active' if c.active=='auth_tokens' else ''}"><a href="${h.route_path('my_account_auth_tokens')}">${_('Auth Tokens')}</a></li>
32 ## TODO: Find a better integration of oauth views into navigation.
32 ## TODO: Find a better integration of oauth views into navigation.
33 <% my_account_oauth_url = h.route_path_or_none('my_account_oauth') %>
33 <% my_account_oauth_url = h.route_path_or_none('my_account_oauth') %>
34 % if my_account_oauth_url:
34 % if my_account_oauth_url:
35 <li class="${'active' if c.active=='oauth' else ''}"><a href="${my_account_oauth_url}">${_('OAuth Identities')}</a></li>
35 <li class="${'active' if c.active=='oauth' else ''}"><a href="${my_account_oauth_url}">${_('OAuth Identities')}</a></li>
36 % endif
36 % endif
37 <li class="${'active' if c.active=='emails' else ''}"><a href="${h.route_path('my_account_emails')}">${_('Emails')}</a></li>
37 <li class="${'active' if c.active=='emails' else ''}"><a href="${h.route_path('my_account_emails')}">${_('Emails')}</a></li>
38 <li class="${'active' if c.active=='repos' else ''}"><a href="${h.url('my_account_repos')}">${_('Repositories')}</a></li>
38 <li class="${'active' if c.active=='repos' else ''}"><a href="${h.route_path('my_account_repos')}">${_('Repositories')}</a></li>
39 <li class="${'active' if c.active=='watched' else ''}"><a href="${h.url('my_account_watched')}">${_('Watched')}</a></li>
39 <li class="${'active' if c.active=='watched' else ''}"><a href="${h.route_path('my_account_watched')}">${_('Watched')}</a></li>
40 <li class="${'active' if c.active=='pullrequests' else ''}"><a href="${h.url('my_account_pullrequests')}">${_('Pull Requests')}</a></li>
40 <li class="${'active' if c.active=='pullrequests' else ''}"><a href="${h.url('my_account_pullrequests')}">${_('Pull Requests')}</a></li>
41 <li class="${'active' if c.active=='perms' else ''}"><a href="${h.url('my_account_perms')}">${_('Permissions')}</a></li>
41 <li class="${'active' if c.active=='perms' else ''}"><a href="${h.route_path('my_account_perms')}">${_('Permissions')}</a></li>
42 <li class="${'active' if c.active=='my_notifications' else ''}"><a href="${h.url('my_account_notifications')}">${_('Live Notifications')}</a></li>
42 <li class="${'active' if c.active=='my_notifications' else ''}"><a href="${h.route_path('my_account_notifications')}">${_('Live Notifications')}</a></li>
43 </ul>
43 </ul>
44 </div>
44 </div>
45
45
46 <div class="main-content-full-width">
46 <div class="main-content-full-width">
47 <%include file="/admin/my_account/my_account_${c.active}.mako"/>
47 <%include file="/admin/my_account/my_account_${c.active}.mako"/>
48 </div>
48 </div>
49 </div>
49 </div>
50 </div>
50 </div>
51
51
52 </%def>
52 </%def>
@@ -1,130 +1,130 b''
1 <template is="dom-bind" id="notificationsPage">
1 <template is="dom-bind" id="notificationsPage">
2 <iron-ajax id="toggleNotifications"
2 <iron-ajax id="toggleNotifications"
3 method="post"
3 method="post"
4 url="${url('my_account_notifications_toggle_visibility')}"
4 url="${h.route_path('my_account_notifications_toggle_visibility')}"
5 content-type="application/json"
5 content-type="application/json"
6 loading="{{changeNotificationsLoading}}"
6 loading="{{changeNotificationsLoading}}"
7 on-response="handleNotifications"
7 on-response="handleNotifications"
8 handle-as="json">
8 handle-as="json">
9 </iron-ajax>
9 </iron-ajax>
10
10
11 <iron-ajax id="sendTestNotification"
11 <iron-ajax id="sendTestNotification"
12 method="post"
12 method="post"
13 url="${h.route_path('my_account_notifications_test_channelstream')}"
13 url="${h.route_path('my_account_notifications_test_channelstream')}"
14 content-type="application/json"
14 content-type="application/json"
15 on-response="handleTestNotification"
15 on-response="handleTestNotification"
16 handle-as="json">
16 handle-as="json">
17 </iron-ajax>
17 </iron-ajax>
18
18
19 <div class="panel panel-default">
19 <div class="panel panel-default">
20 <div class="panel-heading">
20 <div class="panel-heading">
21 <h3 class="panel-title">${_('Your Live Notification Settings')}</h3>
21 <h3 class="panel-title">${_('Your Live Notification Settings')}</h3>
22 </div>
22 </div>
23 <div class="panel-body">
23 <div class="panel-body">
24
24
25 <p><strong>IMPORTANT:</strong> This feature requires enabled channelstream websocket server to function correctly.</p>
25 <p><strong>IMPORTANT:</strong> This feature requires enabled channelstream websocket server to function correctly.</p>
26
26
27 <p class="hidden">Status of browser notifications permission: <strong id="browser-notification-status"></strong></p>
27 <p class="hidden">Status of browser notifications permission: <strong id="browser-notification-status"></strong></p>
28
28
29 <div class="form">
29 <div class="form">
30 <div class="fields">
30 <div class="fields">
31 <div class="field">
31 <div class="field">
32 <div class="label">
32 <div class="label">
33 <label for="new_email">${_('Notifications Status')}:</label>
33 <label for="new_email">${_('Notifications Status')}:</label>
34 </div>
34 </div>
35 <div class="checkboxes">
35 <div class="checkboxes">
36 <rhodecode-toggle id="live-notifications" active="[[changeNotificationsLoading]]" on-change="toggleNotifications" ${'checked' if c.rhodecode_user.get_instance().user_data.get('notification_status') else ''}></rhodecode-toggle>
36 <rhodecode-toggle id="live-notifications" active="[[changeNotificationsLoading]]" on-change="toggleNotifications" ${'checked' if c.rhodecode_user.get_instance().user_data.get('notification_status') else ''}></rhodecode-toggle>
37 </div>
37 </div>
38 </div>
38 </div>
39 </div>
39 </div>
40 </div>
40 </div>
41 </div>
41 </div>
42 </div>
42 </div>
43
43
44 <div class="panel panel-default">
44 <div class="panel panel-default">
45 <div class="panel-heading">
45 <div class="panel-heading">
46 <h3 class="panel-title">${_('Test Notifications')}</h3>
46 <h3 class="panel-title">${_('Test Notifications')}</h3>
47 </div>
47 </div>
48 <div class="panel-body">
48 <div class="panel-body">
49
49
50
50
51 <div style="padding: 0px 0px 20px 0px">
51 <div style="padding: 0px 0px 20px 0px">
52 <button class="btn" id="test-notification" on-tap="testNotifications">Test flash message</button>
52 <button class="btn" id="test-notification" on-tap="testNotifications">Test flash message</button>
53 <button class="btn" id="test-notification-live" on-tap="testNotificationsLive">Test live notification</button>
53 <button class="btn" id="test-notification-live" on-tap="testNotificationsLive">Test live notification</button>
54 </div>
54 </div>
55 <h4 id="test-response"></h4>
55 <h4 id="test-response"></h4>
56
56
57 </div>
57 </div>
58
58
59
59
60
60
61 </div>
61 </div>
62
62
63 <script type="text/javascript">
63 <script type="text/javascript">
64 /** because im not creating a custom element for this page
64 /** because im not creating a custom element for this page
65 * we need to push the function onto the dom-template
65 * we need to push the function onto the dom-template
66 * ideally we turn this into notification-settings elements
66 * ideally we turn this into notification-settings elements
67 * then it will be cleaner
67 * then it will be cleaner
68 */
68 */
69 var ctrlr = $('#notificationsPage')[0];
69 var ctrlr = $('#notificationsPage')[0];
70 ctrlr.toggleNotifications = function(event){
70 ctrlr.toggleNotifications = function(event){
71 var ajax = $('#toggleNotifications')[0];
71 var ajax = $('#toggleNotifications')[0];
72 ajax.headers = {"X-CSRF-Token": CSRF_TOKEN};
72 ajax.headers = {"X-CSRF-Token": CSRF_TOKEN};
73 ajax.body = {notification_status:event.target.active};
73 ajax.body = {notification_status:event.target.active};
74 ajax.generateRequest();
74 ajax.generateRequest();
75 };
75 };
76 ctrlr.handleNotifications = function(event){
76 ctrlr.handleNotifications = function(event){
77 $('#live-notifications')[0].checked = event.detail.response;
77 $('#live-notifications')[0].checked = event.detail.response;
78 };
78 };
79
79
80 ctrlr.testNotifications = function(event){
80 ctrlr.testNotifications = function(event){
81 var levels = ['info', 'error', 'warning', 'success'];
81 var levels = ['info', 'error', 'warning', 'success'];
82 var level = levels[Math.floor(Math.random()*levels.length)];
82 var level = levels[Math.floor(Math.random()*levels.length)];
83 function getRandomArbitrary(min, max) {
83 function getRandomArbitrary(min, max) {
84 return parseInt(Math.random() * (max - min) + min);
84 return parseInt(Math.random() * (max - min) + min);
85 }
85 }
86 function shuffle(a) {
86 function shuffle(a) {
87 var j, x, i;
87 var j, x, i;
88 for (i = a.length; i; i--) {
88 for (i = a.length; i; i--) {
89 j = Math.floor(Math.random() * i);
89 j = Math.floor(Math.random() * i);
90 x = a[i - 1];
90 x = a[i - 1];
91 a[i - 1] = a[j];
91 a[i - 1] = a[j];
92 a[j] = x;
92 a[j] = x;
93 }
93 }
94 }
94 }
95 var wordDb = [
95 var wordDb = [
96 "Leela,", "Bender,", "we are", "going", "grave", "robbing.",
96 "Leela,", "Bender,", "we are", "going", "grave", "robbing.",
97 "Oh,", "I", "think", "we", "should", "just", "stay", "friends.",
97 "Oh,", "I", "think", "we", "should", "just", "stay", "friends.",
98 "got", "to", "find", "a", "way", "to", "escape", "the", "horrible",
98 "got", "to", "find", "a", "way", "to", "escape", "the", "horrible",
99 "ravages", "of", "youth.", "Suddenly,", "going", "to",
99 "ravages", "of", "youth.", "Suddenly,", "going", "to",
100 "the", "bathroom", "like", "clockwork,", "every", "three",
100 "the", "bathroom", "like", "clockwork,", "every", "three",
101 "hours.", "And", "those", "jerks", "at", "Social", "Security",
101 "hours.", "And", "those", "jerks", "at", "Social", "Security",
102 "stopped", "sending", "me", "checks.", "Now", "have", "to", "pay"
102 "stopped", "sending", "me", "checks.", "Now", "have", "to", "pay"
103 ];
103 ];
104 shuffle(wordDb);
104 shuffle(wordDb);
105 wordDb = wordDb.slice(0, getRandomArbitrary(3, wordDb.length));
105 wordDb = wordDb.slice(0, getRandomArbitrary(3, wordDb.length));
106 var randomMessage = wordDb.join(" ");
106 var randomMessage = wordDb.join(" ");
107 var payload = {
107 var payload = {
108 message: {
108 message: {
109 message: randomMessage + " " + new Date(),
109 message: randomMessage + " " + new Date(),
110 level: level,
110 level: level,
111 force: true
111 force: true
112 }
112 }
113 };
113 };
114 $.Topic('/notifications').publish(payload);
114 $.Topic('/notifications').publish(payload);
115 };
115 };
116 ctrlr.testNotificationsLive = function(event){
116 ctrlr.testNotificationsLive = function(event){
117 var ajax = $('#sendTestNotification')[0];
117 var ajax = $('#sendTestNotification')[0];
118 ajax.headers = {"X-CSRF-Token": CSRF_TOKEN};
118 ajax.headers = {"X-CSRF-Token": CSRF_TOKEN};
119 ajax.body = {test_msg: 'Hello !'};
119 ajax.body = {test_msg: 'Hello !'};
120 ajax.generateRequest();
120 ajax.generateRequest();
121 };
121 };
122 ctrlr.handleTestNotification = function(event){
122 ctrlr.handleTestNotification = function(event){
123 var reply = event.detail.response.response;
123 var reply = event.detail.response.response;
124 reply = reply || 'no reply form server';
124 reply = reply || 'no reply form server';
125 $('#test-response').html(reply);
125 $('#test-response').html(reply);
126 };
126 };
127
127
128 </script>
128 </script>
129
129
130 </template>
130 </template>
@@ -1,203 +1,181 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.lib import helpers as h
23 from rhodecode.lib import helpers as h
24 from rhodecode.model.db import User, UserFollowing, Repository
24 from rhodecode.model.db import User, UserFollowing, Repository
25 from rhodecode.tests import (
25 from rhodecode.tests import (
26 TestController, url, TEST_USER_ADMIN_LOGIN, assert_session_flash)
26 TestController, url, TEST_USER_ADMIN_LOGIN, assert_session_flash)
27 from rhodecode.tests.fixture import Fixture
27 from rhodecode.tests.fixture import Fixture
28 from rhodecode.tests.utils import AssertResponse
28 from rhodecode.tests.utils import AssertResponse
29
29
30 fixture = Fixture()
30 fixture = Fixture()
31
31
32
32
33 def route_path(name, **kwargs):
33 def route_path(name, **kwargs):
34 return {
34 return {
35 'home': '/',
35 'home': '/',
36 }[name].format(**kwargs)
36 }[name].format(**kwargs)
37
37
38
38
39 class TestMyAccountController(TestController):
39 class TestMyAccountController(TestController):
40 test_user_1 = 'testme'
40 test_user_1 = 'testme'
41 test_user_1_password = '0jd83nHNS/d23n'
41 test_user_1_password = '0jd83nHNS/d23n'
42 destroy_users = set()
42 destroy_users = set()
43
43
44 @classmethod
44 @classmethod
45 def teardown_class(cls):
45 def teardown_class(cls):
46 fixture.destroy_users(cls.destroy_users)
46 fixture.destroy_users(cls.destroy_users)
47
47
48 def test_logout_form_contains_csrf(self, autologin_user, csrf_token):
48 def test_logout_form_contains_csrf(self, autologin_user, csrf_token):
49 response = self.app.get(route_path('home'))
49 response = self.app.get(route_path('home'))
50 assert_response = AssertResponse(response)
50 assert_response = AssertResponse(response)
51 element = assert_response.get_element('.logout #csrf_token')
51 element = assert_response.get_element('.logout #csrf_token')
52 assert element.value == csrf_token
52 assert element.value == csrf_token
53
53
54 def test_my_account_edit(self):
54 def test_my_account_edit(self):
55 self.log_user()
55 self.log_user()
56 response = self.app.get(url('my_account_edit'))
56 response = self.app.get(url('my_account_edit'))
57
57
58 response.mustcontain('value="test_admin')
58 response.mustcontain('value="test_admin')
59
59
60 def test_my_account_my_repos(self):
61 self.log_user()
62 response = self.app.get(url('my_account_repos'))
63 repos = Repository.query().filter(
64 Repository.user == User.get_by_username(
65 TEST_USER_ADMIN_LOGIN)).all()
66 for repo in repos:
67 response.mustcontain('"name_raw": "%s"' % repo.repo_name)
68
69 def test_my_account_my_watched(self):
70 self.log_user()
71 response = self.app.get(url('my_account_watched'))
72
73 repos = UserFollowing.query().filter(
74 UserFollowing.user == User.get_by_username(
75 TEST_USER_ADMIN_LOGIN)).all()
76 for repo in repos:
77 response.mustcontain(
78 '"name_raw": "%s"' % repo.follows_repository.repo_name)
79
80 @pytest.mark.backends("git", "hg")
60 @pytest.mark.backends("git", "hg")
81 def test_my_account_my_pullrequests(self, pr_util):
61 def test_my_account_my_pullrequests(self, pr_util):
82 self.log_user()
62 self.log_user()
83 response = self.app.get(url('my_account_pullrequests'))
63 response = self.app.get(url('my_account_pullrequests'))
84 response.mustcontain('There are currently no open pull '
64 response.mustcontain('There are currently no open pull '
85 'requests requiring your participation.')
65 'requests requiring your participation.')
86
66
87 pr = pr_util.create_pull_request(title='TestMyAccountPR')
67 pr = pr_util.create_pull_request(title='TestMyAccountPR')
88 response = self.app.get(url('my_account_pullrequests'))
68 response = self.app.get(url('my_account_pullrequests'))
89 response.mustcontain('"name_raw": %s' % pr.pull_request_id)
69 response.mustcontain('"name_raw": %s' % pr.pull_request_id)
90 response.mustcontain('TestMyAccountPR')
70 response.mustcontain('TestMyAccountPR')
91
71
92
93
94 @pytest.mark.parametrize(
72 @pytest.mark.parametrize(
95 "name, attrs", [
73 "name, attrs", [
96 ('firstname', {'firstname': 'new_username'}),
74 ('firstname', {'firstname': 'new_username'}),
97 ('lastname', {'lastname': 'new_username'}),
75 ('lastname', {'lastname': 'new_username'}),
98 ('admin', {'admin': True}),
76 ('admin', {'admin': True}),
99 ('admin', {'admin': False}),
77 ('admin', {'admin': False}),
100 ('extern_type', {'extern_type': 'ldap'}),
78 ('extern_type', {'extern_type': 'ldap'}),
101 ('extern_type', {'extern_type': None}),
79 ('extern_type', {'extern_type': None}),
102 # ('extern_name', {'extern_name': 'test'}),
80 # ('extern_name', {'extern_name': 'test'}),
103 # ('extern_name', {'extern_name': None}),
81 # ('extern_name', {'extern_name': None}),
104 ('active', {'active': False}),
82 ('active', {'active': False}),
105 ('active', {'active': True}),
83 ('active', {'active': True}),
106 ('email', {'email': 'some@email.com'}),
84 ('email', {'email': 'some@email.com'}),
107 ])
85 ])
108 def test_my_account_update(self, name, attrs):
86 def test_my_account_update(self, name, attrs):
109 usr = fixture.create_user(self.test_user_1,
87 usr = fixture.create_user(self.test_user_1,
110 password=self.test_user_1_password,
88 password=self.test_user_1_password,
111 email='testme@rhodecode.org',
89 email='testme@rhodecode.org',
112 extern_type='rhodecode',
90 extern_type='rhodecode',
113 extern_name=self.test_user_1,
91 extern_name=self.test_user_1,
114 skip_if_exists=True)
92 skip_if_exists=True)
115 self.destroy_users.add(self.test_user_1)
93 self.destroy_users.add(self.test_user_1)
116
94
117 params = usr.get_api_data() # current user data
95 params = usr.get_api_data() # current user data
118 user_id = usr.user_id
96 user_id = usr.user_id
119 self.log_user(
97 self.log_user(
120 username=self.test_user_1, password=self.test_user_1_password)
98 username=self.test_user_1, password=self.test_user_1_password)
121
99
122 params.update({'password_confirmation': ''})
100 params.update({'password_confirmation': ''})
123 params.update({'new_password': ''})
101 params.update({'new_password': ''})
124 params.update({'extern_type': 'rhodecode'})
102 params.update({'extern_type': 'rhodecode'})
125 params.update({'extern_name': self.test_user_1})
103 params.update({'extern_name': self.test_user_1})
126 params.update({'csrf_token': self.csrf_token})
104 params.update({'csrf_token': self.csrf_token})
127
105
128 params.update(attrs)
106 params.update(attrs)
129 # my account page cannot set language param yet, only for admins
107 # my account page cannot set language param yet, only for admins
130 del params['language']
108 del params['language']
131 response = self.app.post(url('my_account'), params)
109 response = self.app.post(url('my_account'), params)
132
110
133 assert_session_flash(
111 assert_session_flash(
134 response, 'Your account was updated successfully')
112 response, 'Your account was updated successfully')
135
113
136 del params['csrf_token']
114 del params['csrf_token']
137
115
138 updated_user = User.get_by_username(self.test_user_1)
116 updated_user = User.get_by_username(self.test_user_1)
139 updated_params = updated_user.get_api_data()
117 updated_params = updated_user.get_api_data()
140 updated_params.update({'password_confirmation': ''})
118 updated_params.update({'password_confirmation': ''})
141 updated_params.update({'new_password': ''})
119 updated_params.update({'new_password': ''})
142
120
143 params['last_login'] = updated_params['last_login']
121 params['last_login'] = updated_params['last_login']
144 params['last_activity'] = updated_params['last_activity']
122 params['last_activity'] = updated_params['last_activity']
145 # my account page cannot set language param yet, only for admins
123 # my account page cannot set language param yet, only for admins
146 # but we get this info from API anyway
124 # but we get this info from API anyway
147 params['language'] = updated_params['language']
125 params['language'] = updated_params['language']
148
126
149 if name == 'email':
127 if name == 'email':
150 params['emails'] = [attrs['email']]
128 params['emails'] = [attrs['email']]
151 if name == 'extern_type':
129 if name == 'extern_type':
152 # cannot update this via form, expected value is original one
130 # cannot update this via form, expected value is original one
153 params['extern_type'] = "rhodecode"
131 params['extern_type'] = "rhodecode"
154 if name == 'extern_name':
132 if name == 'extern_name':
155 # cannot update this via form, expected value is original one
133 # cannot update this via form, expected value is original one
156 params['extern_name'] = str(user_id)
134 params['extern_name'] = str(user_id)
157 if name == 'active':
135 if name == 'active':
158 # my account cannot deactivate account
136 # my account cannot deactivate account
159 params['active'] = True
137 params['active'] = True
160 if name == 'admin':
138 if name == 'admin':
161 # my account cannot make you an admin !
139 # my account cannot make you an admin !
162 params['admin'] = False
140 params['admin'] = False
163
141
164 assert params == updated_params
142 assert params == updated_params
165
143
166 def test_my_account_update_err_email_exists(self):
144 def test_my_account_update_err_email_exists(self):
167 self.log_user()
145 self.log_user()
168
146
169 new_email = 'test_regular@mail.com' # already exisitn email
147 new_email = 'test_regular@mail.com' # already exisitn email
170 response = self.app.post(url('my_account'),
148 response = self.app.post(url('my_account'),
171 params={
149 params={
172 'username': 'test_admin',
150 'username': 'test_admin',
173 'new_password': 'test12',
151 'new_password': 'test12',
174 'password_confirmation': 'test122',
152 'password_confirmation': 'test122',
175 'firstname': 'NewName',
153 'firstname': 'NewName',
176 'lastname': 'NewLastname',
154 'lastname': 'NewLastname',
177 'email': new_email,
155 'email': new_email,
178 'csrf_token': self.csrf_token,
156 'csrf_token': self.csrf_token,
179 })
157 })
180
158
181 response.mustcontain('This e-mail address is already taken')
159 response.mustcontain('This e-mail address is already taken')
182
160
183 def test_my_account_update_err(self):
161 def test_my_account_update_err(self):
184 self.log_user('test_regular2', 'test12')
162 self.log_user('test_regular2', 'test12')
185
163
186 new_email = 'newmail.pl'
164 new_email = 'newmail.pl'
187 response = self.app.post(url('my_account'),
165 response = self.app.post(url('my_account'),
188 params={
166 params={
189 'username': 'test_admin',
167 'username': 'test_admin',
190 'new_password': 'test12',
168 'new_password': 'test12',
191 'password_confirmation': 'test122',
169 'password_confirmation': 'test122',
192 'firstname': 'NewName',
170 'firstname': 'NewName',
193 'lastname': 'NewLastname',
171 'lastname': 'NewLastname',
194 'email': new_email,
172 'email': new_email,
195 'csrf_token': self.csrf_token,
173 'csrf_token': self.csrf_token,
196 })
174 })
197
175
198 response.mustcontain('An email address must contain a single @')
176 response.mustcontain('An email address must contain a single @')
199 from rhodecode.model import validators
177 from rhodecode.model import validators
200 msg = validators.ValidUsername(
178 msg = validators.ValidUsername(
201 edit=False, old_data={})._messages['username_exists']
179 edit=False, old_data={})._messages['username_exists']
202 msg = h.html_escape(msg % {'username': 'test_admin'})
180 msg = h.html_escape(msg % {'username': 'test_admin'})
203 response.mustcontain(u"%s" % msg)
181 response.mustcontain(u"%s" % msg)
General Comments 0
You need to be logged in to leave comments. Login now