##// END OF EJS Templates
my-account: moved profile page into pyramid.
marcink -
r1540:e835784f default
parent child Browse files
Show More
@@ -0,0 +1,55 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.tests import (
25 TestController, TEST_USER_ADMIN_LOGIN,
26 TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
27 from rhodecode.tests.fixture import Fixture
28
29 fixture = Fixture()
30
31
32 def route_path(name, **kwargs):
33 return {
34 'my_account':
35 ADMIN_PREFIX + '/my_account/profile',
36 }[name].format(**kwargs)
37
38
39 class TestMyAccountProfile(TestController):
40
41 def test_my_account(self):
42 self.log_user()
43 response = self.app.get(route_path('my_account'))
44
45 response.mustcontain(TEST_USER_ADMIN_LOGIN)
46 response.mustcontain('href="/_admin/my_account/edit"')
47 response.mustcontain('Photo')
48
49 def test_my_account_regular_user(self):
50 self.log_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
51 response = self.app.get(route_path('my_account'))
52
53 response.mustcontain(TEST_USER_REGULAR_LOGIN)
54 response.mustcontain('href="/_admin/my_account/edit"')
55 response.mustcontain('Photo')
@@ -1,48 +1,52 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',
29 pattern=ADMIN_PREFIX + '/my_account/profile')
30
31 config.add_route(
28 name='my_account_password',
32 name='my_account_password',
29 pattern=ADMIN_PREFIX + '/my_account/password')
33 pattern=ADMIN_PREFIX + '/my_account/password')
30
34
31 config.add_route(
35 config.add_route(
32 name='my_account_password_update',
36 name='my_account_password_update',
33 pattern=ADMIN_PREFIX + '/my_account/password')
37 pattern=ADMIN_PREFIX + '/my_account/password')
34
38
35 config.add_route(
39 config.add_route(
36 name='my_account_auth_tokens',
40 name='my_account_auth_tokens',
37 pattern=ADMIN_PREFIX + '/my_account/auth_tokens')
41 pattern=ADMIN_PREFIX + '/my_account/auth_tokens')
38 config.add_route(
42 config.add_route(
39 name='my_account_auth_tokens_add',
43 name='my_account_auth_tokens_add',
40 pattern=ADMIN_PREFIX + '/my_account/auth_tokens/new',
44 pattern=ADMIN_PREFIX + '/my_account/auth_tokens/new',
41 )
45 )
42 config.add_route(
46 config.add_route(
43 name='my_account_auth_tokens_delete',
47 name='my_account_auth_tokens_delete',
44 pattern=ADMIN_PREFIX + '/my_account/auth_tokens/delete',
48 pattern=ADMIN_PREFIX + '/my_account/auth_tokens/delete',
45 )
49 )
46
50
47 # Scan module for configuration decorators.
51 # Scan module for configuration decorators.
48 config.scan()
52 config.scan()
@@ -1,184 +1,194 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
22
23 from pyramid.httpexceptions import HTTPFound
23 from pyramid.httpexceptions import HTTPFound
24 from pyramid.view import view_config
24 from pyramid.view import view_config
25
25
26 from rhodecode.apps._base import BaseAppView
26 from rhodecode.apps._base import BaseAppView
27 from rhodecode import forms
27 from rhodecode import forms
28 from rhodecode.lib.auth import LoginRequired, NotAnonymous, CSRFRequired
28 from rhodecode.lib.auth import LoginRequired, NotAnonymous, CSRFRequired
29 from rhodecode.lib import helpers as h
29 from rhodecode.lib import helpers as h
30 from rhodecode.lib.utils2 import safe_int, md5
30 from rhodecode.lib.utils2 import safe_int, md5
31 from rhodecode.model.auth_token import AuthTokenModel
31 from rhodecode.model.auth_token import AuthTokenModel
32 from rhodecode.model.meta import Session
32 from rhodecode.model.meta import Session
33 from rhodecode.model.user import UserModel
33 from rhodecode.model.user import UserModel
34 from rhodecode.model.validation_schema.schemas import user_schema
34 from rhodecode.model.validation_schema.schemas import user_schema
35
35
36 log = logging.getLogger(__name__)
36 log = logging.getLogger(__name__)
37
37
38
38
39 class MyAccountView(BaseAppView):
39 class MyAccountView(BaseAppView):
40 ALLOW_SCOPED_TOKENS = False
40 ALLOW_SCOPED_TOKENS = False
41 """
41 """
42 This view has alternative version inside EE, if modified please take a look
42 This view has alternative version inside EE, if modified please take a look
43 in there as well.
43 in there as well.
44 """
44 """
45
45
46 def load_default_context(self):
46 def load_default_context(self):
47 c = self._get_local_tmpl_context()
47 c = self._get_local_tmpl_context()
48 c.user = c.auth_user.get_instance()
48 c.user = c.auth_user.get_instance()
49 c.allow_scoped_tokens = self.ALLOW_SCOPED_TOKENS
49 c.allow_scoped_tokens = self.ALLOW_SCOPED_TOKENS
50 self._register_global_c(c)
50 self._register_global_c(c)
51 return c
51 return c
52
52
53 @LoginRequired()
53 @LoginRequired()
54 @NotAnonymous()
54 @NotAnonymous()
55 @view_config(
55 @view_config(
56 route_name='my_account_profile', request_method='GET',
57 renderer='rhodecode:templates/admin/my_account/my_account.mako')
58 def my_account_profile(self):
59 c = self.load_default_context()
60 c.active = 'profile'
61 return self._get_template_context(c)
62
63 @LoginRequired()
64 @NotAnonymous()
65 @view_config(
56 route_name='my_account_password', request_method='GET',
66 route_name='my_account_password', request_method='GET',
57 renderer='rhodecode:templates/admin/my_account/my_account.mako')
67 renderer='rhodecode:templates/admin/my_account/my_account.mako')
58 def my_account_password(self):
68 def my_account_password(self):
59 c = self.load_default_context()
69 c = self.load_default_context()
60 c.active = 'password'
70 c.active = 'password'
61 c.extern_type = c.user.extern_type
71 c.extern_type = c.user.extern_type
62
72
63 schema = user_schema.ChangePasswordSchema().bind(
73 schema = user_schema.ChangePasswordSchema().bind(
64 username=c.user.username)
74 username=c.user.username)
65
75
66 form = forms.Form(
76 form = forms.Form(
67 schema, buttons=(forms.buttons.save, forms.buttons.reset))
77 schema, buttons=(forms.buttons.save, forms.buttons.reset))
68
78
69 c.form = form
79 c.form = form
70 return self._get_template_context(c)
80 return self._get_template_context(c)
71
81
72 @LoginRequired()
82 @LoginRequired()
73 @NotAnonymous()
83 @NotAnonymous()
74 @CSRFRequired()
84 @CSRFRequired()
75 @view_config(
85 @view_config(
76 route_name='my_account_password', request_method='POST',
86 route_name='my_account_password', request_method='POST',
77 renderer='rhodecode:templates/admin/my_account/my_account.mako')
87 renderer='rhodecode:templates/admin/my_account/my_account.mako')
78 def my_account_password_update(self):
88 def my_account_password_update(self):
79 _ = self.request.translate
89 _ = self.request.translate
80 c = self.load_default_context()
90 c = self.load_default_context()
81 c.active = 'password'
91 c.active = 'password'
82 c.extern_type = c.user.extern_type
92 c.extern_type = c.user.extern_type
83
93
84 schema = user_schema.ChangePasswordSchema().bind(
94 schema = user_schema.ChangePasswordSchema().bind(
85 username=c.user.username)
95 username=c.user.username)
86
96
87 form = forms.Form(
97 form = forms.Form(
88 schema, buttons=(forms.buttons.save, forms.buttons.reset))
98 schema, buttons=(forms.buttons.save, forms.buttons.reset))
89
99
90 if c.extern_type != 'rhodecode':
100 if c.extern_type != 'rhodecode':
91 raise HTTPFound(self.request.route_path('my_account_password'))
101 raise HTTPFound(self.request.route_path('my_account_password'))
92
102
93 controls = self.request.POST.items()
103 controls = self.request.POST.items()
94 try:
104 try:
95 valid_data = form.validate(controls)
105 valid_data = form.validate(controls)
96 UserModel().update_user(c.user.user_id, **valid_data)
106 UserModel().update_user(c.user.user_id, **valid_data)
97 c.user.update_userdata(force_password_change=False)
107 c.user.update_userdata(force_password_change=False)
98 Session().commit()
108 Session().commit()
99 except forms.ValidationFailure as e:
109 except forms.ValidationFailure as e:
100 c.form = e
110 c.form = e
101 return self._get_template_context(c)
111 return self._get_template_context(c)
102
112
103 except Exception:
113 except Exception:
104 log.exception("Exception updating password")
114 log.exception("Exception updating password")
105 h.flash(_('Error occurred during update of user password'),
115 h.flash(_('Error occurred during update of user password'),
106 category='error')
116 category='error')
107 else:
117 else:
108 instance = c.auth_user.get_instance()
118 instance = c.auth_user.get_instance()
109 self.session.setdefault('rhodecode_user', {}).update(
119 self.session.setdefault('rhodecode_user', {}).update(
110 {'password': md5(instance.password)})
120 {'password': md5(instance.password)})
111 self.session.save()
121 self.session.save()
112 h.flash(_("Successfully updated password"), category='success')
122 h.flash(_("Successfully updated password"), category='success')
113
123
114 raise HTTPFound(self.request.route_path('my_account_password'))
124 raise HTTPFound(self.request.route_path('my_account_password'))
115
125
116 @LoginRequired()
126 @LoginRequired()
117 @NotAnonymous()
127 @NotAnonymous()
118 @view_config(
128 @view_config(
119 route_name='my_account_auth_tokens', request_method='GET',
129 route_name='my_account_auth_tokens', request_method='GET',
120 renderer='rhodecode:templates/admin/my_account/my_account.mako')
130 renderer='rhodecode:templates/admin/my_account/my_account.mako')
121 def my_account_auth_tokens(self):
131 def my_account_auth_tokens(self):
122 _ = self.request.translate
132 _ = self.request.translate
123
133
124 c = self.load_default_context()
134 c = self.load_default_context()
125 c.active = 'auth_tokens'
135 c.active = 'auth_tokens'
126
136
127 c.lifetime_values = [
137 c.lifetime_values = [
128 (str(-1), _('forever')),
138 (str(-1), _('forever')),
129 (str(5), _('5 minutes')),
139 (str(5), _('5 minutes')),
130 (str(60), _('1 hour')),
140 (str(60), _('1 hour')),
131 (str(60 * 24), _('1 day')),
141 (str(60 * 24), _('1 day')),
132 (str(60 * 24 * 30), _('1 month')),
142 (str(60 * 24 * 30), _('1 month')),
133 ]
143 ]
134 c.lifetime_options = [(c.lifetime_values, _("Lifetime"))]
144 c.lifetime_options = [(c.lifetime_values, _("Lifetime"))]
135 c.role_values = [
145 c.role_values = [
136 (x, AuthTokenModel.cls._get_role_name(x))
146 (x, AuthTokenModel.cls._get_role_name(x))
137 for x in AuthTokenModel.cls.ROLES]
147 for x in AuthTokenModel.cls.ROLES]
138 c.role_options = [(c.role_values, _("Role"))]
148 c.role_options = [(c.role_values, _("Role"))]
139 c.user_auth_tokens = AuthTokenModel().get_auth_tokens(
149 c.user_auth_tokens = AuthTokenModel().get_auth_tokens(
140 c.user.user_id, show_expired=True)
150 c.user.user_id, show_expired=True)
141 return self._get_template_context(c)
151 return self._get_template_context(c)
142
152
143 def maybe_attach_token_scope(self, token):
153 def maybe_attach_token_scope(self, token):
144 # implemented in EE edition
154 # implemented in EE edition
145 pass
155 pass
146
156
147 @LoginRequired()
157 @LoginRequired()
148 @NotAnonymous()
158 @NotAnonymous()
149 @CSRFRequired()
159 @CSRFRequired()
150 @view_config(
160 @view_config(
151 route_name='my_account_auth_tokens_add', request_method='POST')
161 route_name='my_account_auth_tokens_add', request_method='POST')
152 def my_account_auth_tokens_add(self):
162 def my_account_auth_tokens_add(self):
153 _ = self.request.translate
163 _ = self.request.translate
154 c = self.load_default_context()
164 c = self.load_default_context()
155
165
156 lifetime = safe_int(self.request.POST.get('lifetime'), -1)
166 lifetime = safe_int(self.request.POST.get('lifetime'), -1)
157 description = self.request.POST.get('description')
167 description = self.request.POST.get('description')
158 role = self.request.POST.get('role')
168 role = self.request.POST.get('role')
159
169
160 token = AuthTokenModel().create(
170 token = AuthTokenModel().create(
161 c.user.user_id, description, lifetime, role)
171 c.user.user_id, description, lifetime, role)
162 self.maybe_attach_token_scope(token)
172 self.maybe_attach_token_scope(token)
163 Session().commit()
173 Session().commit()
164
174
165 h.flash(_("Auth token successfully created"), category='success')
175 h.flash(_("Auth token successfully created"), category='success')
166 return HTTPFound(h.route_path('my_account_auth_tokens'))
176 return HTTPFound(h.route_path('my_account_auth_tokens'))
167
177
168 @LoginRequired()
178 @LoginRequired()
169 @NotAnonymous()
179 @NotAnonymous()
170 @CSRFRequired()
180 @CSRFRequired()
171 @view_config(
181 @view_config(
172 route_name='my_account_auth_tokens_delete', request_method='POST')
182 route_name='my_account_auth_tokens_delete', request_method='POST')
173 def my_account_auth_tokens_delete(self):
183 def my_account_auth_tokens_delete(self):
174 _ = self.request.translate
184 _ = self.request.translate
175 c = self.load_default_context()
185 c = self.load_default_context()
176
186
177 del_auth_token = self.request.POST.get('del_auth_token')
187 del_auth_token = self.request.POST.get('del_auth_token')
178
188
179 if del_auth_token:
189 if del_auth_token:
180 AuthTokenModel().delete(del_auth_token, c.user.user_id)
190 AuthTokenModel().delete(del_auth_token, c.user.user_id)
181 Session().commit()
191 Session().commit()
182 h.flash(_("Auth token successfully deleted"), category='success')
192 h.flash(_("Auth token successfully deleted"), category='success')
183
193
184 return HTTPFound(h.route_path('my_account_auth_tokens'))
194 return HTTPFound(h.route_path('my_account_auth_tokens'))
@@ -1,1151 +1,1149 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 from rhodecode.config import routing_links
35 from rhodecode.config import routing_links
36
36
37 # prefix for non repository related links needs to be prefixed with `/`
37 # prefix for non repository related links needs to be prefixed with `/`
38 ADMIN_PREFIX = '/_admin'
38 ADMIN_PREFIX = '/_admin'
39 STATIC_FILE_PREFIX = '/_static'
39 STATIC_FILE_PREFIX = '/_static'
40
40
41 # Default requirements for URL parts
41 # Default requirements for URL parts
42 URL_NAME_REQUIREMENTS = {
42 URL_NAME_REQUIREMENTS = {
43 # group name can have a slash in them, but they must not end with a slash
43 # group name can have a slash in them, but they must not end with a slash
44 'group_name': r'.*?[^/]',
44 'group_name': r'.*?[^/]',
45 'repo_group_name': r'.*?[^/]',
45 'repo_group_name': r'.*?[^/]',
46 # repo names can have a slash in them, but they must not end with a slash
46 # repo names can have a slash in them, but they must not end with a slash
47 'repo_name': r'.*?[^/]',
47 'repo_name': r'.*?[^/]',
48 # file path eats up everything at the end
48 # file path eats up everything at the end
49 'f_path': r'.*',
49 'f_path': r'.*',
50 # reference types
50 # reference types
51 'source_ref_type': '(branch|book|tag|rev|\%\(source_ref_type\)s)',
51 'source_ref_type': '(branch|book|tag|rev|\%\(source_ref_type\)s)',
52 'target_ref_type': '(branch|book|tag|rev|\%\(target_ref_type\)s)',
52 'target_ref_type': '(branch|book|tag|rev|\%\(target_ref_type\)s)',
53 }
53 }
54
54
55
55
56 def add_route_requirements(route_path, requirements):
56 def add_route_requirements(route_path, requirements):
57 """
57 """
58 Adds regex requirements to pyramid routes using a mapping dict
58 Adds regex requirements to pyramid routes using a mapping dict
59
59
60 >>> add_route_requirements('/{action}/{id}', {'id': r'\d+'})
60 >>> add_route_requirements('/{action}/{id}', {'id': r'\d+'})
61 '/{action}/{id:\d+}'
61 '/{action}/{id:\d+}'
62
62
63 """
63 """
64 for key, regex in requirements.items():
64 for key, regex in requirements.items():
65 route_path = route_path.replace('{%s}' % key, '{%s:%s}' % (key, regex))
65 route_path = route_path.replace('{%s}' % key, '{%s:%s}' % (key, regex))
66 return route_path
66 return route_path
67
67
68
68
69 class JSRoutesMapper(Mapper):
69 class JSRoutesMapper(Mapper):
70 """
70 """
71 Wrapper for routes.Mapper to make pyroutes compatible url definitions
71 Wrapper for routes.Mapper to make pyroutes compatible url definitions
72 """
72 """
73 _named_route_regex = re.compile(r'^[a-z-_0-9A-Z]+$')
73 _named_route_regex = re.compile(r'^[a-z-_0-9A-Z]+$')
74 _argument_prog = re.compile('\{(.*?)\}|:\((.*)\)')
74 _argument_prog = re.compile('\{(.*?)\}|:\((.*)\)')
75 def __init__(self, *args, **kw):
75 def __init__(self, *args, **kw):
76 super(JSRoutesMapper, self).__init__(*args, **kw)
76 super(JSRoutesMapper, self).__init__(*args, **kw)
77 self._jsroutes = []
77 self._jsroutes = []
78
78
79 def connect(self, *args, **kw):
79 def connect(self, *args, **kw):
80 """
80 """
81 Wrapper for connect to take an extra argument jsroute=True
81 Wrapper for connect to take an extra argument jsroute=True
82
82
83 :param jsroute: boolean, if True will add the route to the pyroutes list
83 :param jsroute: boolean, if True will add the route to the pyroutes list
84 """
84 """
85 if kw.pop('jsroute', False):
85 if kw.pop('jsroute', False):
86 if not self._named_route_regex.match(args[0]):
86 if not self._named_route_regex.match(args[0]):
87 raise Exception('only named routes can be added to pyroutes')
87 raise Exception('only named routes can be added to pyroutes')
88 self._jsroutes.append(args[0])
88 self._jsroutes.append(args[0])
89
89
90 super(JSRoutesMapper, self).connect(*args, **kw)
90 super(JSRoutesMapper, self).connect(*args, **kw)
91
91
92 def _extract_route_information(self, route):
92 def _extract_route_information(self, route):
93 """
93 """
94 Convert a route into tuple(name, path, args), eg:
94 Convert a route into tuple(name, path, args), eg:
95 ('show_user', '/profile/%(username)s', ['username'])
95 ('show_user', '/profile/%(username)s', ['username'])
96 """
96 """
97 routepath = route.routepath
97 routepath = route.routepath
98 def replace(matchobj):
98 def replace(matchobj):
99 if matchobj.group(1):
99 if matchobj.group(1):
100 return "%%(%s)s" % matchobj.group(1).split(':')[0]
100 return "%%(%s)s" % matchobj.group(1).split(':')[0]
101 else:
101 else:
102 return "%%(%s)s" % matchobj.group(2)
102 return "%%(%s)s" % matchobj.group(2)
103
103
104 routepath = self._argument_prog.sub(replace, routepath)
104 routepath = self._argument_prog.sub(replace, routepath)
105 return (
105 return (
106 route.name,
106 route.name,
107 routepath,
107 routepath,
108 [(arg[0].split(':')[0] if arg[0] != '' else arg[1])
108 [(arg[0].split(':')[0] if arg[0] != '' else arg[1])
109 for arg in self._argument_prog.findall(route.routepath)]
109 for arg in self._argument_prog.findall(route.routepath)]
110 )
110 )
111
111
112 def jsroutes(self):
112 def jsroutes(self):
113 """
113 """
114 Return a list of pyroutes.js compatible routes
114 Return a list of pyroutes.js compatible routes
115 """
115 """
116 for route_name in self._jsroutes:
116 for route_name in self._jsroutes:
117 yield self._extract_route_information(self._routenames[route_name])
117 yield self._extract_route_information(self._routenames[route_name])
118
118
119
119
120 def make_map(config):
120 def make_map(config):
121 """Create, configure and return the routes Mapper"""
121 """Create, configure and return the routes Mapper"""
122 rmap = JSRoutesMapper(directory=config['pylons.paths']['controllers'],
122 rmap = JSRoutesMapper(directory=config['pylons.paths']['controllers'],
123 always_scan=config['debug'])
123 always_scan=config['debug'])
124 rmap.minimization = False
124 rmap.minimization = False
125 rmap.explicit = False
125 rmap.explicit = False
126
126
127 from rhodecode.lib.utils2 import str2bool
127 from rhodecode.lib.utils2 import str2bool
128 from rhodecode.model import repo, repo_group
128 from rhodecode.model import repo, repo_group
129
129
130 def check_repo(environ, match_dict):
130 def check_repo(environ, match_dict):
131 """
131 """
132 check for valid repository for proper 404 handling
132 check for valid repository for proper 404 handling
133
133
134 :param environ:
134 :param environ:
135 :param match_dict:
135 :param match_dict:
136 """
136 """
137 repo_name = match_dict.get('repo_name')
137 repo_name = match_dict.get('repo_name')
138
138
139 if match_dict.get('f_path'):
139 if match_dict.get('f_path'):
140 # fix for multiple initial slashes that causes errors
140 # fix for multiple initial slashes that causes errors
141 match_dict['f_path'] = match_dict['f_path'].lstrip('/')
141 match_dict['f_path'] = match_dict['f_path'].lstrip('/')
142 repo_model = repo.RepoModel()
142 repo_model = repo.RepoModel()
143 by_name_match = repo_model.get_by_repo_name(repo_name)
143 by_name_match = repo_model.get_by_repo_name(repo_name)
144 # if we match quickly from database, short circuit the operation,
144 # if we match quickly from database, short circuit the operation,
145 # and validate repo based on the type.
145 # and validate repo based on the type.
146 if by_name_match:
146 if by_name_match:
147 return True
147 return True
148
148
149 by_id_match = repo_model.get_repo_by_id(repo_name)
149 by_id_match = repo_model.get_repo_by_id(repo_name)
150 if by_id_match:
150 if by_id_match:
151 repo_name = by_id_match.repo_name
151 repo_name = by_id_match.repo_name
152 match_dict['repo_name'] = repo_name
152 match_dict['repo_name'] = repo_name
153 return True
153 return True
154
154
155 return False
155 return False
156
156
157 def check_group(environ, match_dict):
157 def check_group(environ, match_dict):
158 """
158 """
159 check for valid repository group path for proper 404 handling
159 check for valid repository group path for proper 404 handling
160
160
161 :param environ:
161 :param environ:
162 :param match_dict:
162 :param match_dict:
163 """
163 """
164 repo_group_name = match_dict.get('group_name')
164 repo_group_name = match_dict.get('group_name')
165 repo_group_model = repo_group.RepoGroupModel()
165 repo_group_model = repo_group.RepoGroupModel()
166 by_name_match = repo_group_model.get_by_group_name(repo_group_name)
166 by_name_match = repo_group_model.get_by_group_name(repo_group_name)
167 if by_name_match:
167 if by_name_match:
168 return True
168 return True
169
169
170 return False
170 return False
171
171
172 def check_user_group(environ, match_dict):
172 def check_user_group(environ, match_dict):
173 """
173 """
174 check for valid user group for proper 404 handling
174 check for valid user group for proper 404 handling
175
175
176 :param environ:
176 :param environ:
177 :param match_dict:
177 :param match_dict:
178 """
178 """
179 return True
179 return True
180
180
181 def check_int(environ, match_dict):
181 def check_int(environ, match_dict):
182 return match_dict.get('id').isdigit()
182 return match_dict.get('id').isdigit()
183
183
184
184
185 #==========================================================================
185 #==========================================================================
186 # CUSTOM ROUTES HERE
186 # CUSTOM ROUTES HERE
187 #==========================================================================
187 #==========================================================================
188
188
189 # MAIN PAGE
189 # MAIN PAGE
190 rmap.connect('home', '/', controller='home', action='index', jsroute=True)
190 rmap.connect('home', '/', controller='home', action='index', jsroute=True)
191 rmap.connect('goto_switcher_data', '/_goto_data', controller='home',
191 rmap.connect('goto_switcher_data', '/_goto_data', controller='home',
192 action='goto_switcher_data')
192 action='goto_switcher_data')
193 rmap.connect('repo_list_data', '/_repos', controller='home',
193 rmap.connect('repo_list_data', '/_repos', controller='home',
194 action='repo_list_data')
194 action='repo_list_data')
195
195
196 rmap.connect('user_autocomplete_data', '/_users', controller='home',
196 rmap.connect('user_autocomplete_data', '/_users', controller='home',
197 action='user_autocomplete_data', jsroute=True)
197 action='user_autocomplete_data', jsroute=True)
198 rmap.connect('user_group_autocomplete_data', '/_user_groups', controller='home',
198 rmap.connect('user_group_autocomplete_data', '/_user_groups', controller='home',
199 action='user_group_autocomplete_data', jsroute=True)
199 action='user_group_autocomplete_data', jsroute=True)
200
200
201 # TODO: johbo: Static links, to be replaced by our redirection mechanism
201 # TODO: johbo: Static links, to be replaced by our redirection mechanism
202 rmap.connect('rst_help',
202 rmap.connect('rst_help',
203 'http://docutils.sourceforge.net/docs/user/rst/quickref.html',
203 'http://docutils.sourceforge.net/docs/user/rst/quickref.html',
204 _static=True)
204 _static=True)
205 rmap.connect('markdown_help',
205 rmap.connect('markdown_help',
206 'http://daringfireball.net/projects/markdown/syntax',
206 'http://daringfireball.net/projects/markdown/syntax',
207 _static=True)
207 _static=True)
208 rmap.connect('rhodecode_official', 'https://rhodecode.com', _static=True)
208 rmap.connect('rhodecode_official', 'https://rhodecode.com', _static=True)
209 rmap.connect('rhodecode_support', 'https://rhodecode.com/help/', _static=True)
209 rmap.connect('rhodecode_support', 'https://rhodecode.com/help/', _static=True)
210 rmap.connect('rhodecode_translations', 'https://rhodecode.com/translate/enterprise', _static=True)
210 rmap.connect('rhodecode_translations', 'https://rhodecode.com/translate/enterprise', _static=True)
211 # TODO: anderson - making this a static link since redirect won't play
211 # TODO: anderson - making this a static link since redirect won't play
212 # nice with POST requests
212 # nice with POST requests
213 rmap.connect('enterprise_license_convert_from_old',
213 rmap.connect('enterprise_license_convert_from_old',
214 'https://rhodecode.com/u/license-upgrade',
214 'https://rhodecode.com/u/license-upgrade',
215 _static=True)
215 _static=True)
216
216
217 routing_links.connect_redirection_links(rmap)
217 routing_links.connect_redirection_links(rmap)
218
218
219 rmap.connect('ping', '%s/ping' % (ADMIN_PREFIX,), controller='home', action='ping')
219 rmap.connect('ping', '%s/ping' % (ADMIN_PREFIX,), controller='home', action='ping')
220 rmap.connect('error_test', '%s/error_test' % (ADMIN_PREFIX,), controller='home', action='error_test')
220 rmap.connect('error_test', '%s/error_test' % (ADMIN_PREFIX,), controller='home', action='error_test')
221
221
222 # ADMIN REPOSITORY ROUTES
222 # ADMIN REPOSITORY ROUTES
223 with rmap.submapper(path_prefix=ADMIN_PREFIX,
223 with rmap.submapper(path_prefix=ADMIN_PREFIX,
224 controller='admin/repos') as m:
224 controller='admin/repos') as m:
225 m.connect('repos', '/repos',
225 m.connect('repos', '/repos',
226 action='create', conditions={'method': ['POST']})
226 action='create', conditions={'method': ['POST']})
227 m.connect('repos', '/repos',
227 m.connect('repos', '/repos',
228 action='index', conditions={'method': ['GET']})
228 action='index', conditions={'method': ['GET']})
229 m.connect('new_repo', '/create_repository', jsroute=True,
229 m.connect('new_repo', '/create_repository', jsroute=True,
230 action='create_repository', conditions={'method': ['GET']})
230 action='create_repository', conditions={'method': ['GET']})
231 m.connect('/repos/{repo_name}',
231 m.connect('/repos/{repo_name}',
232 action='update', conditions={'method': ['PUT'],
232 action='update', conditions={'method': ['PUT'],
233 'function': check_repo},
233 'function': check_repo},
234 requirements=URL_NAME_REQUIREMENTS)
234 requirements=URL_NAME_REQUIREMENTS)
235 m.connect('delete_repo', '/repos/{repo_name}',
235 m.connect('delete_repo', '/repos/{repo_name}',
236 action='delete', conditions={'method': ['DELETE']},
236 action='delete', conditions={'method': ['DELETE']},
237 requirements=URL_NAME_REQUIREMENTS)
237 requirements=URL_NAME_REQUIREMENTS)
238 m.connect('repo', '/repos/{repo_name}',
238 m.connect('repo', '/repos/{repo_name}',
239 action='show', conditions={'method': ['GET'],
239 action='show', conditions={'method': ['GET'],
240 'function': check_repo},
240 'function': check_repo},
241 requirements=URL_NAME_REQUIREMENTS)
241 requirements=URL_NAME_REQUIREMENTS)
242
242
243 # ADMIN REPOSITORY GROUPS ROUTES
243 # ADMIN REPOSITORY GROUPS ROUTES
244 with rmap.submapper(path_prefix=ADMIN_PREFIX,
244 with rmap.submapper(path_prefix=ADMIN_PREFIX,
245 controller='admin/repo_groups') as m:
245 controller='admin/repo_groups') as m:
246 m.connect('repo_groups', '/repo_groups',
246 m.connect('repo_groups', '/repo_groups',
247 action='create', conditions={'method': ['POST']})
247 action='create', conditions={'method': ['POST']})
248 m.connect('repo_groups', '/repo_groups',
248 m.connect('repo_groups', '/repo_groups',
249 action='index', conditions={'method': ['GET']})
249 action='index', conditions={'method': ['GET']})
250 m.connect('new_repo_group', '/repo_groups/new',
250 m.connect('new_repo_group', '/repo_groups/new',
251 action='new', conditions={'method': ['GET']})
251 action='new', conditions={'method': ['GET']})
252 m.connect('update_repo_group', '/repo_groups/{group_name}',
252 m.connect('update_repo_group', '/repo_groups/{group_name}',
253 action='update', conditions={'method': ['PUT'],
253 action='update', conditions={'method': ['PUT'],
254 'function': check_group},
254 'function': check_group},
255 requirements=URL_NAME_REQUIREMENTS)
255 requirements=URL_NAME_REQUIREMENTS)
256
256
257 # EXTRAS REPO GROUP ROUTES
257 # EXTRAS REPO GROUP ROUTES
258 m.connect('edit_repo_group', '/repo_groups/{group_name}/edit',
258 m.connect('edit_repo_group', '/repo_groups/{group_name}/edit',
259 action='edit',
259 action='edit',
260 conditions={'method': ['GET'], 'function': check_group},
260 conditions={'method': ['GET'], 'function': check_group},
261 requirements=URL_NAME_REQUIREMENTS)
261 requirements=URL_NAME_REQUIREMENTS)
262 m.connect('edit_repo_group', '/repo_groups/{group_name}/edit',
262 m.connect('edit_repo_group', '/repo_groups/{group_name}/edit',
263 action='edit',
263 action='edit',
264 conditions={'method': ['PUT'], 'function': check_group},
264 conditions={'method': ['PUT'], 'function': check_group},
265 requirements=URL_NAME_REQUIREMENTS)
265 requirements=URL_NAME_REQUIREMENTS)
266
266
267 m.connect('edit_repo_group_advanced', '/repo_groups/{group_name}/edit/advanced',
267 m.connect('edit_repo_group_advanced', '/repo_groups/{group_name}/edit/advanced',
268 action='edit_repo_group_advanced',
268 action='edit_repo_group_advanced',
269 conditions={'method': ['GET'], 'function': check_group},
269 conditions={'method': ['GET'], 'function': check_group},
270 requirements=URL_NAME_REQUIREMENTS)
270 requirements=URL_NAME_REQUIREMENTS)
271 m.connect('edit_repo_group_advanced', '/repo_groups/{group_name}/edit/advanced',
271 m.connect('edit_repo_group_advanced', '/repo_groups/{group_name}/edit/advanced',
272 action='edit_repo_group_advanced',
272 action='edit_repo_group_advanced',
273 conditions={'method': ['PUT'], 'function': check_group},
273 conditions={'method': ['PUT'], 'function': check_group},
274 requirements=URL_NAME_REQUIREMENTS)
274 requirements=URL_NAME_REQUIREMENTS)
275
275
276 m.connect('edit_repo_group_perms', '/repo_groups/{group_name}/edit/permissions',
276 m.connect('edit_repo_group_perms', '/repo_groups/{group_name}/edit/permissions',
277 action='edit_repo_group_perms',
277 action='edit_repo_group_perms',
278 conditions={'method': ['GET'], 'function': check_group},
278 conditions={'method': ['GET'], 'function': check_group},
279 requirements=URL_NAME_REQUIREMENTS)
279 requirements=URL_NAME_REQUIREMENTS)
280 m.connect('edit_repo_group_perms', '/repo_groups/{group_name}/edit/permissions',
280 m.connect('edit_repo_group_perms', '/repo_groups/{group_name}/edit/permissions',
281 action='update_perms',
281 action='update_perms',
282 conditions={'method': ['PUT'], 'function': check_group},
282 conditions={'method': ['PUT'], 'function': check_group},
283 requirements=URL_NAME_REQUIREMENTS)
283 requirements=URL_NAME_REQUIREMENTS)
284
284
285 m.connect('delete_repo_group', '/repo_groups/{group_name}',
285 m.connect('delete_repo_group', '/repo_groups/{group_name}',
286 action='delete', conditions={'method': ['DELETE'],
286 action='delete', conditions={'method': ['DELETE'],
287 'function': check_group},
287 'function': check_group},
288 requirements=URL_NAME_REQUIREMENTS)
288 requirements=URL_NAME_REQUIREMENTS)
289
289
290 # ADMIN USER ROUTES
290 # ADMIN USER ROUTES
291 with rmap.submapper(path_prefix=ADMIN_PREFIX,
291 with rmap.submapper(path_prefix=ADMIN_PREFIX,
292 controller='admin/users') as m:
292 controller='admin/users') as m:
293 m.connect('users', '/users',
293 m.connect('users', '/users',
294 action='create', conditions={'method': ['POST']})
294 action='create', conditions={'method': ['POST']})
295 m.connect('new_user', '/users/new',
295 m.connect('new_user', '/users/new',
296 action='new', conditions={'method': ['GET']})
296 action='new', conditions={'method': ['GET']})
297 m.connect('update_user', '/users/{user_id}',
297 m.connect('update_user', '/users/{user_id}',
298 action='update', conditions={'method': ['PUT']})
298 action='update', conditions={'method': ['PUT']})
299 m.connect('delete_user', '/users/{user_id}',
299 m.connect('delete_user', '/users/{user_id}',
300 action='delete', conditions={'method': ['DELETE']})
300 action='delete', conditions={'method': ['DELETE']})
301 m.connect('edit_user', '/users/{user_id}/edit',
301 m.connect('edit_user', '/users/{user_id}/edit',
302 action='edit', conditions={'method': ['GET']}, jsroute=True)
302 action='edit', conditions={'method': ['GET']}, jsroute=True)
303 m.connect('user', '/users/{user_id}',
303 m.connect('user', '/users/{user_id}',
304 action='show', conditions={'method': ['GET']})
304 action='show', conditions={'method': ['GET']})
305 m.connect('force_password_reset_user', '/users/{user_id}/password_reset',
305 m.connect('force_password_reset_user', '/users/{user_id}/password_reset',
306 action='reset_password', conditions={'method': ['POST']})
306 action='reset_password', conditions={'method': ['POST']})
307 m.connect('create_personal_repo_group', '/users/{user_id}/create_repo_group',
307 m.connect('create_personal_repo_group', '/users/{user_id}/create_repo_group',
308 action='create_personal_repo_group', conditions={'method': ['POST']})
308 action='create_personal_repo_group', conditions={'method': ['POST']})
309
309
310 # EXTRAS USER ROUTES
310 # EXTRAS USER ROUTES
311 m.connect('edit_user_advanced', '/users/{user_id}/edit/advanced',
311 m.connect('edit_user_advanced', '/users/{user_id}/edit/advanced',
312 action='edit_advanced', conditions={'method': ['GET']})
312 action='edit_advanced', conditions={'method': ['GET']})
313 m.connect('edit_user_advanced', '/users/{user_id}/edit/advanced',
313 m.connect('edit_user_advanced', '/users/{user_id}/edit/advanced',
314 action='update_advanced', conditions={'method': ['PUT']})
314 action='update_advanced', conditions={'method': ['PUT']})
315
315
316 m.connect('edit_user_global_perms', '/users/{user_id}/edit/global_permissions',
316 m.connect('edit_user_global_perms', '/users/{user_id}/edit/global_permissions',
317 action='edit_global_perms', conditions={'method': ['GET']})
317 action='edit_global_perms', conditions={'method': ['GET']})
318 m.connect('edit_user_global_perms', '/users/{user_id}/edit/global_permissions',
318 m.connect('edit_user_global_perms', '/users/{user_id}/edit/global_permissions',
319 action='update_global_perms', conditions={'method': ['PUT']})
319 action='update_global_perms', conditions={'method': ['PUT']})
320
320
321 m.connect('edit_user_perms_summary', '/users/{user_id}/edit/permissions_summary',
321 m.connect('edit_user_perms_summary', '/users/{user_id}/edit/permissions_summary',
322 action='edit_perms_summary', conditions={'method': ['GET']})
322 action='edit_perms_summary', conditions={'method': ['GET']})
323
323
324 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
324 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
325 action='edit_emails', conditions={'method': ['GET']})
325 action='edit_emails', conditions={'method': ['GET']})
326 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
326 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
327 action='add_email', conditions={'method': ['PUT']})
327 action='add_email', conditions={'method': ['PUT']})
328 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
328 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
329 action='delete_email', conditions={'method': ['DELETE']})
329 action='delete_email', conditions={'method': ['DELETE']})
330
330
331 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
331 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
332 action='edit_ips', conditions={'method': ['GET']})
332 action='edit_ips', conditions={'method': ['GET']})
333 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
333 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
334 action='add_ip', conditions={'method': ['PUT']})
334 action='add_ip', conditions={'method': ['PUT']})
335 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
335 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
336 action='delete_ip', conditions={'method': ['DELETE']})
336 action='delete_ip', conditions={'method': ['DELETE']})
337
337
338 # ADMIN USER GROUPS REST ROUTES
338 # ADMIN USER GROUPS REST ROUTES
339 with rmap.submapper(path_prefix=ADMIN_PREFIX,
339 with rmap.submapper(path_prefix=ADMIN_PREFIX,
340 controller='admin/user_groups') as m:
340 controller='admin/user_groups') as m:
341 m.connect('users_groups', '/user_groups',
341 m.connect('users_groups', '/user_groups',
342 action='create', conditions={'method': ['POST']})
342 action='create', conditions={'method': ['POST']})
343 m.connect('users_groups', '/user_groups',
343 m.connect('users_groups', '/user_groups',
344 action='index', conditions={'method': ['GET']})
344 action='index', conditions={'method': ['GET']})
345 m.connect('new_users_group', '/user_groups/new',
345 m.connect('new_users_group', '/user_groups/new',
346 action='new', conditions={'method': ['GET']})
346 action='new', conditions={'method': ['GET']})
347 m.connect('update_users_group', '/user_groups/{user_group_id}',
347 m.connect('update_users_group', '/user_groups/{user_group_id}',
348 action='update', conditions={'method': ['PUT']})
348 action='update', conditions={'method': ['PUT']})
349 m.connect('delete_users_group', '/user_groups/{user_group_id}',
349 m.connect('delete_users_group', '/user_groups/{user_group_id}',
350 action='delete', conditions={'method': ['DELETE']})
350 action='delete', conditions={'method': ['DELETE']})
351 m.connect('edit_users_group', '/user_groups/{user_group_id}/edit',
351 m.connect('edit_users_group', '/user_groups/{user_group_id}/edit',
352 action='edit', conditions={'method': ['GET']},
352 action='edit', conditions={'method': ['GET']},
353 function=check_user_group)
353 function=check_user_group)
354
354
355 # EXTRAS USER GROUP ROUTES
355 # EXTRAS USER GROUP ROUTES
356 m.connect('edit_user_group_global_perms',
356 m.connect('edit_user_group_global_perms',
357 '/user_groups/{user_group_id}/edit/global_permissions',
357 '/user_groups/{user_group_id}/edit/global_permissions',
358 action='edit_global_perms', conditions={'method': ['GET']})
358 action='edit_global_perms', conditions={'method': ['GET']})
359 m.connect('edit_user_group_global_perms',
359 m.connect('edit_user_group_global_perms',
360 '/user_groups/{user_group_id}/edit/global_permissions',
360 '/user_groups/{user_group_id}/edit/global_permissions',
361 action='update_global_perms', conditions={'method': ['PUT']})
361 action='update_global_perms', conditions={'method': ['PUT']})
362 m.connect('edit_user_group_perms_summary',
362 m.connect('edit_user_group_perms_summary',
363 '/user_groups/{user_group_id}/edit/permissions_summary',
363 '/user_groups/{user_group_id}/edit/permissions_summary',
364 action='edit_perms_summary', conditions={'method': ['GET']})
364 action='edit_perms_summary', conditions={'method': ['GET']})
365
365
366 m.connect('edit_user_group_perms',
366 m.connect('edit_user_group_perms',
367 '/user_groups/{user_group_id}/edit/permissions',
367 '/user_groups/{user_group_id}/edit/permissions',
368 action='edit_perms', conditions={'method': ['GET']})
368 action='edit_perms', conditions={'method': ['GET']})
369 m.connect('edit_user_group_perms',
369 m.connect('edit_user_group_perms',
370 '/user_groups/{user_group_id}/edit/permissions',
370 '/user_groups/{user_group_id}/edit/permissions',
371 action='update_perms', conditions={'method': ['PUT']})
371 action='update_perms', conditions={'method': ['PUT']})
372
372
373 m.connect('edit_user_group_advanced',
373 m.connect('edit_user_group_advanced',
374 '/user_groups/{user_group_id}/edit/advanced',
374 '/user_groups/{user_group_id}/edit/advanced',
375 action='edit_advanced', conditions={'method': ['GET']})
375 action='edit_advanced', conditions={'method': ['GET']})
376
376
377 m.connect('edit_user_group_members',
377 m.connect('edit_user_group_members',
378 '/user_groups/{user_group_id}/edit/members', jsroute=True,
378 '/user_groups/{user_group_id}/edit/members', jsroute=True,
379 action='user_group_members', conditions={'method': ['GET']})
379 action='user_group_members', conditions={'method': ['GET']})
380
380
381 # ADMIN PERMISSIONS ROUTES
381 # ADMIN PERMISSIONS ROUTES
382 with rmap.submapper(path_prefix=ADMIN_PREFIX,
382 with rmap.submapper(path_prefix=ADMIN_PREFIX,
383 controller='admin/permissions') as m:
383 controller='admin/permissions') as m:
384 m.connect('admin_permissions_application', '/permissions/application',
384 m.connect('admin_permissions_application', '/permissions/application',
385 action='permission_application_update', conditions={'method': ['POST']})
385 action='permission_application_update', conditions={'method': ['POST']})
386 m.connect('admin_permissions_application', '/permissions/application',
386 m.connect('admin_permissions_application', '/permissions/application',
387 action='permission_application', conditions={'method': ['GET']})
387 action='permission_application', conditions={'method': ['GET']})
388
388
389 m.connect('admin_permissions_global', '/permissions/global',
389 m.connect('admin_permissions_global', '/permissions/global',
390 action='permission_global_update', conditions={'method': ['POST']})
390 action='permission_global_update', conditions={'method': ['POST']})
391 m.connect('admin_permissions_global', '/permissions/global',
391 m.connect('admin_permissions_global', '/permissions/global',
392 action='permission_global', conditions={'method': ['GET']})
392 action='permission_global', conditions={'method': ['GET']})
393
393
394 m.connect('admin_permissions_object', '/permissions/object',
394 m.connect('admin_permissions_object', '/permissions/object',
395 action='permission_objects_update', conditions={'method': ['POST']})
395 action='permission_objects_update', conditions={'method': ['POST']})
396 m.connect('admin_permissions_object', '/permissions/object',
396 m.connect('admin_permissions_object', '/permissions/object',
397 action='permission_objects', conditions={'method': ['GET']})
397 action='permission_objects', conditions={'method': ['GET']})
398
398
399 m.connect('admin_permissions_ips', '/permissions/ips',
399 m.connect('admin_permissions_ips', '/permissions/ips',
400 action='permission_ips', conditions={'method': ['POST']})
400 action='permission_ips', conditions={'method': ['POST']})
401 m.connect('admin_permissions_ips', '/permissions/ips',
401 m.connect('admin_permissions_ips', '/permissions/ips',
402 action='permission_ips', conditions={'method': ['GET']})
402 action='permission_ips', conditions={'method': ['GET']})
403
403
404 m.connect('admin_permissions_overview', '/permissions/overview',
404 m.connect('admin_permissions_overview', '/permissions/overview',
405 action='permission_perms', conditions={'method': ['GET']})
405 action='permission_perms', conditions={'method': ['GET']})
406
406
407 # ADMIN DEFAULTS REST ROUTES
407 # ADMIN DEFAULTS REST ROUTES
408 with rmap.submapper(path_prefix=ADMIN_PREFIX,
408 with rmap.submapper(path_prefix=ADMIN_PREFIX,
409 controller='admin/defaults') as m:
409 controller='admin/defaults') as m:
410 m.connect('admin_defaults_repositories', '/defaults/repositories',
410 m.connect('admin_defaults_repositories', '/defaults/repositories',
411 action='update_repository_defaults', conditions={'method': ['POST']})
411 action='update_repository_defaults', conditions={'method': ['POST']})
412 m.connect('admin_defaults_repositories', '/defaults/repositories',
412 m.connect('admin_defaults_repositories', '/defaults/repositories',
413 action='index', conditions={'method': ['GET']})
413 action='index', conditions={'method': ['GET']})
414
414
415 # ADMIN DEBUG STYLE ROUTES
415 # ADMIN DEBUG STYLE ROUTES
416 if str2bool(config.get('debug_style')):
416 if str2bool(config.get('debug_style')):
417 with rmap.submapper(path_prefix=ADMIN_PREFIX + '/debug_style',
417 with rmap.submapper(path_prefix=ADMIN_PREFIX + '/debug_style',
418 controller='debug_style') as m:
418 controller='debug_style') as m:
419 m.connect('debug_style_home', '',
419 m.connect('debug_style_home', '',
420 action='index', conditions={'method': ['GET']})
420 action='index', conditions={'method': ['GET']})
421 m.connect('debug_style_template', '/t/{t_path}',
421 m.connect('debug_style_template', '/t/{t_path}',
422 action='template', conditions={'method': ['GET']})
422 action='template', conditions={'method': ['GET']})
423
423
424 # ADMIN SETTINGS ROUTES
424 # ADMIN SETTINGS ROUTES
425 with rmap.submapper(path_prefix=ADMIN_PREFIX,
425 with rmap.submapper(path_prefix=ADMIN_PREFIX,
426 controller='admin/settings') as m:
426 controller='admin/settings') as m:
427
427
428 # default
428 # default
429 m.connect('admin_settings', '/settings',
429 m.connect('admin_settings', '/settings',
430 action='settings_global_update',
430 action='settings_global_update',
431 conditions={'method': ['POST']})
431 conditions={'method': ['POST']})
432 m.connect('admin_settings', '/settings',
432 m.connect('admin_settings', '/settings',
433 action='settings_global', conditions={'method': ['GET']})
433 action='settings_global', conditions={'method': ['GET']})
434
434
435 m.connect('admin_settings_vcs', '/settings/vcs',
435 m.connect('admin_settings_vcs', '/settings/vcs',
436 action='settings_vcs_update',
436 action='settings_vcs_update',
437 conditions={'method': ['POST']})
437 conditions={'method': ['POST']})
438 m.connect('admin_settings_vcs', '/settings/vcs',
438 m.connect('admin_settings_vcs', '/settings/vcs',
439 action='settings_vcs',
439 action='settings_vcs',
440 conditions={'method': ['GET']})
440 conditions={'method': ['GET']})
441 m.connect('admin_settings_vcs', '/settings/vcs',
441 m.connect('admin_settings_vcs', '/settings/vcs',
442 action='delete_svn_pattern',
442 action='delete_svn_pattern',
443 conditions={'method': ['DELETE']})
443 conditions={'method': ['DELETE']})
444
444
445 m.connect('admin_settings_mapping', '/settings/mapping',
445 m.connect('admin_settings_mapping', '/settings/mapping',
446 action='settings_mapping_update',
446 action='settings_mapping_update',
447 conditions={'method': ['POST']})
447 conditions={'method': ['POST']})
448 m.connect('admin_settings_mapping', '/settings/mapping',
448 m.connect('admin_settings_mapping', '/settings/mapping',
449 action='settings_mapping', conditions={'method': ['GET']})
449 action='settings_mapping', conditions={'method': ['GET']})
450
450
451 m.connect('admin_settings_global', '/settings/global',
451 m.connect('admin_settings_global', '/settings/global',
452 action='settings_global_update',
452 action='settings_global_update',
453 conditions={'method': ['POST']})
453 conditions={'method': ['POST']})
454 m.connect('admin_settings_global', '/settings/global',
454 m.connect('admin_settings_global', '/settings/global',
455 action='settings_global', conditions={'method': ['GET']})
455 action='settings_global', conditions={'method': ['GET']})
456
456
457 m.connect('admin_settings_visual', '/settings/visual',
457 m.connect('admin_settings_visual', '/settings/visual',
458 action='settings_visual_update',
458 action='settings_visual_update',
459 conditions={'method': ['POST']})
459 conditions={'method': ['POST']})
460 m.connect('admin_settings_visual', '/settings/visual',
460 m.connect('admin_settings_visual', '/settings/visual',
461 action='settings_visual', conditions={'method': ['GET']})
461 action='settings_visual', conditions={'method': ['GET']})
462
462
463 m.connect('admin_settings_issuetracker',
463 m.connect('admin_settings_issuetracker',
464 '/settings/issue-tracker', action='settings_issuetracker',
464 '/settings/issue-tracker', action='settings_issuetracker',
465 conditions={'method': ['GET']})
465 conditions={'method': ['GET']})
466 m.connect('admin_settings_issuetracker_save',
466 m.connect('admin_settings_issuetracker_save',
467 '/settings/issue-tracker/save',
467 '/settings/issue-tracker/save',
468 action='settings_issuetracker_save',
468 action='settings_issuetracker_save',
469 conditions={'method': ['POST']})
469 conditions={'method': ['POST']})
470 m.connect('admin_issuetracker_test', '/settings/issue-tracker/test',
470 m.connect('admin_issuetracker_test', '/settings/issue-tracker/test',
471 action='settings_issuetracker_test',
471 action='settings_issuetracker_test',
472 conditions={'method': ['POST']})
472 conditions={'method': ['POST']})
473 m.connect('admin_issuetracker_delete',
473 m.connect('admin_issuetracker_delete',
474 '/settings/issue-tracker/delete',
474 '/settings/issue-tracker/delete',
475 action='settings_issuetracker_delete',
475 action='settings_issuetracker_delete',
476 conditions={'method': ['DELETE']})
476 conditions={'method': ['DELETE']})
477
477
478 m.connect('admin_settings_email', '/settings/email',
478 m.connect('admin_settings_email', '/settings/email',
479 action='settings_email_update',
479 action='settings_email_update',
480 conditions={'method': ['POST']})
480 conditions={'method': ['POST']})
481 m.connect('admin_settings_email', '/settings/email',
481 m.connect('admin_settings_email', '/settings/email',
482 action='settings_email', conditions={'method': ['GET']})
482 action='settings_email', conditions={'method': ['GET']})
483
483
484 m.connect('admin_settings_hooks', '/settings/hooks',
484 m.connect('admin_settings_hooks', '/settings/hooks',
485 action='settings_hooks_update',
485 action='settings_hooks_update',
486 conditions={'method': ['POST', 'DELETE']})
486 conditions={'method': ['POST', 'DELETE']})
487 m.connect('admin_settings_hooks', '/settings/hooks',
487 m.connect('admin_settings_hooks', '/settings/hooks',
488 action='settings_hooks', conditions={'method': ['GET']})
488 action='settings_hooks', conditions={'method': ['GET']})
489
489
490 m.connect('admin_settings_search', '/settings/search',
490 m.connect('admin_settings_search', '/settings/search',
491 action='settings_search', conditions={'method': ['GET']})
491 action='settings_search', conditions={'method': ['GET']})
492
492
493 m.connect('admin_settings_supervisor', '/settings/supervisor',
493 m.connect('admin_settings_supervisor', '/settings/supervisor',
494 action='settings_supervisor', conditions={'method': ['GET']})
494 action='settings_supervisor', conditions={'method': ['GET']})
495 m.connect('admin_settings_supervisor_log', '/settings/supervisor/{procid}/log',
495 m.connect('admin_settings_supervisor_log', '/settings/supervisor/{procid}/log',
496 action='settings_supervisor_log', conditions={'method': ['GET']})
496 action='settings_supervisor_log', conditions={'method': ['GET']})
497
497
498 m.connect('admin_settings_labs', '/settings/labs',
498 m.connect('admin_settings_labs', '/settings/labs',
499 action='settings_labs_update',
499 action='settings_labs_update',
500 conditions={'method': ['POST']})
500 conditions={'method': ['POST']})
501 m.connect('admin_settings_labs', '/settings/labs',
501 m.connect('admin_settings_labs', '/settings/labs',
502 action='settings_labs', conditions={'method': ['GET']})
502 action='settings_labs', conditions={'method': ['GET']})
503
503
504 # ADMIN MY ACCOUNT
504 # ADMIN MY ACCOUNT
505 with rmap.submapper(path_prefix=ADMIN_PREFIX,
505 with rmap.submapper(path_prefix=ADMIN_PREFIX,
506 controller='admin/my_account') as m:
506 controller='admin/my_account') as m:
507
507
508 m.connect('my_account', '/my_account',
509 action='my_account', conditions={'method': ['GET']})
510 m.connect('my_account_edit', '/my_account/edit',
508 m.connect('my_account_edit', '/my_account/edit',
511 action='my_account_edit', conditions={'method': ['GET']})
509 action='my_account_edit', conditions={'method': ['GET']})
512 m.connect('my_account', '/my_account',
510 m.connect('my_account', '/my_account/update',
513 action='my_account_update', conditions={'method': ['POST']})
511 action='my_account_update', conditions={'method': ['POST']})
514
512
515 # NOTE(marcink): this needs to be kept for password force flag to be
513 # NOTE(marcink): this needs to be kept for password force flag to be
516 # handler, remove after migration to pyramid
514 # handler, remove after migration to pyramid
517 m.connect('my_account_password', '/my_account/password',
515 m.connect('my_account_password', '/my_account/password',
518 action='my_account_password', conditions={'method': ['GET']})
516 action='my_account_password', conditions={'method': ['GET']})
519
517
520 m.connect('my_account_repos', '/my_account/repos',
518 m.connect('my_account_repos', '/my_account/repos',
521 action='my_account_repos', conditions={'method': ['GET']})
519 action='my_account_repos', conditions={'method': ['GET']})
522
520
523 m.connect('my_account_watched', '/my_account/watched',
521 m.connect('my_account_watched', '/my_account/watched',
524 action='my_account_watched', conditions={'method': ['GET']})
522 action='my_account_watched', conditions={'method': ['GET']})
525
523
526 m.connect('my_account_pullrequests', '/my_account/pull_requests',
524 m.connect('my_account_pullrequests', '/my_account/pull_requests',
527 action='my_account_pullrequests', conditions={'method': ['GET']})
525 action='my_account_pullrequests', conditions={'method': ['GET']})
528
526
529 m.connect('my_account_perms', '/my_account/perms',
527 m.connect('my_account_perms', '/my_account/perms',
530 action='my_account_perms', conditions={'method': ['GET']})
528 action='my_account_perms', conditions={'method': ['GET']})
531
529
532 m.connect('my_account_emails', '/my_account/emails',
530 m.connect('my_account_emails', '/my_account/emails',
533 action='my_account_emails', conditions={'method': ['GET']})
531 action='my_account_emails', conditions={'method': ['GET']})
534 m.connect('my_account_emails', '/my_account/emails',
532 m.connect('my_account_emails', '/my_account/emails',
535 action='my_account_emails_add', conditions={'method': ['POST']})
533 action='my_account_emails_add', conditions={'method': ['POST']})
536 m.connect('my_account_emails', '/my_account/emails',
534 m.connect('my_account_emails', '/my_account/emails',
537 action='my_account_emails_delete', conditions={'method': ['DELETE']})
535 action='my_account_emails_delete', conditions={'method': ['DELETE']})
538
536
539 m.connect('my_account_notifications', '/my_account/notifications',
537 m.connect('my_account_notifications', '/my_account/notifications',
540 action='my_notifications',
538 action='my_notifications',
541 conditions={'method': ['GET']})
539 conditions={'method': ['GET']})
542 m.connect('my_account_notifications_toggle_visibility',
540 m.connect('my_account_notifications_toggle_visibility',
543 '/my_account/toggle_visibility',
541 '/my_account/toggle_visibility',
544 action='my_notifications_toggle_visibility',
542 action='my_notifications_toggle_visibility',
545 conditions={'method': ['POST']})
543 conditions={'method': ['POST']})
546 m.connect('my_account_notifications_test_channelstream',
544 m.connect('my_account_notifications_test_channelstream',
547 '/my_account/test_channelstream',
545 '/my_account/test_channelstream',
548 action='my_account_notifications_test_channelstream',
546 action='my_account_notifications_test_channelstream',
549 conditions={'method': ['POST']})
547 conditions={'method': ['POST']})
550
548
551 # NOTIFICATION REST ROUTES
549 # NOTIFICATION REST ROUTES
552 with rmap.submapper(path_prefix=ADMIN_PREFIX,
550 with rmap.submapper(path_prefix=ADMIN_PREFIX,
553 controller='admin/notifications') as m:
551 controller='admin/notifications') as m:
554 m.connect('notifications', '/notifications',
552 m.connect('notifications', '/notifications',
555 action='index', conditions={'method': ['GET']})
553 action='index', conditions={'method': ['GET']})
556 m.connect('notifications_mark_all_read', '/notifications/mark_all_read',
554 m.connect('notifications_mark_all_read', '/notifications/mark_all_read',
557 action='mark_all_read', conditions={'method': ['POST']})
555 action='mark_all_read', conditions={'method': ['POST']})
558 m.connect('/notifications/{notification_id}',
556 m.connect('/notifications/{notification_id}',
559 action='update', conditions={'method': ['PUT']})
557 action='update', conditions={'method': ['PUT']})
560 m.connect('/notifications/{notification_id}',
558 m.connect('/notifications/{notification_id}',
561 action='delete', conditions={'method': ['DELETE']})
559 action='delete', conditions={'method': ['DELETE']})
562 m.connect('notification', '/notifications/{notification_id}',
560 m.connect('notification', '/notifications/{notification_id}',
563 action='show', conditions={'method': ['GET']})
561 action='show', conditions={'method': ['GET']})
564
562
565 # ADMIN GIST
563 # ADMIN GIST
566 with rmap.submapper(path_prefix=ADMIN_PREFIX,
564 with rmap.submapper(path_prefix=ADMIN_PREFIX,
567 controller='admin/gists') as m:
565 controller='admin/gists') as m:
568 m.connect('gists', '/gists',
566 m.connect('gists', '/gists',
569 action='create', conditions={'method': ['POST']})
567 action='create', conditions={'method': ['POST']})
570 m.connect('gists', '/gists', jsroute=True,
568 m.connect('gists', '/gists', jsroute=True,
571 action='index', conditions={'method': ['GET']})
569 action='index', conditions={'method': ['GET']})
572 m.connect('new_gist', '/gists/new', jsroute=True,
570 m.connect('new_gist', '/gists/new', jsroute=True,
573 action='new', conditions={'method': ['GET']})
571 action='new', conditions={'method': ['GET']})
574
572
575 m.connect('/gists/{gist_id}',
573 m.connect('/gists/{gist_id}',
576 action='delete', conditions={'method': ['DELETE']})
574 action='delete', conditions={'method': ['DELETE']})
577 m.connect('edit_gist', '/gists/{gist_id}/edit',
575 m.connect('edit_gist', '/gists/{gist_id}/edit',
578 action='edit_form', conditions={'method': ['GET']})
576 action='edit_form', conditions={'method': ['GET']})
579 m.connect('edit_gist', '/gists/{gist_id}/edit',
577 m.connect('edit_gist', '/gists/{gist_id}/edit',
580 action='edit', conditions={'method': ['POST']})
578 action='edit', conditions={'method': ['POST']})
581 m.connect(
579 m.connect(
582 'edit_gist_check_revision', '/gists/{gist_id}/edit/check_revision',
580 'edit_gist_check_revision', '/gists/{gist_id}/edit/check_revision',
583 action='check_revision', conditions={'method': ['GET']})
581 action='check_revision', conditions={'method': ['GET']})
584
582
585 m.connect('gist', '/gists/{gist_id}',
583 m.connect('gist', '/gists/{gist_id}',
586 action='show', conditions={'method': ['GET']})
584 action='show', conditions={'method': ['GET']})
587 m.connect('gist_rev', '/gists/{gist_id}/{revision}',
585 m.connect('gist_rev', '/gists/{gist_id}/{revision}',
588 revision='tip',
586 revision='tip',
589 action='show', conditions={'method': ['GET']})
587 action='show', conditions={'method': ['GET']})
590 m.connect('formatted_gist', '/gists/{gist_id}/{revision}/{format}',
588 m.connect('formatted_gist', '/gists/{gist_id}/{revision}/{format}',
591 revision='tip',
589 revision='tip',
592 action='show', conditions={'method': ['GET']})
590 action='show', conditions={'method': ['GET']})
593 m.connect('formatted_gist_file', '/gists/{gist_id}/{revision}/{format}/{f_path}',
591 m.connect('formatted_gist_file', '/gists/{gist_id}/{revision}/{format}/{f_path}',
594 revision='tip',
592 revision='tip',
595 action='show', conditions={'method': ['GET']},
593 action='show', conditions={'method': ['GET']},
596 requirements=URL_NAME_REQUIREMENTS)
594 requirements=URL_NAME_REQUIREMENTS)
597
595
598 # ADMIN MAIN PAGES
596 # ADMIN MAIN PAGES
599 with rmap.submapper(path_prefix=ADMIN_PREFIX,
597 with rmap.submapper(path_prefix=ADMIN_PREFIX,
600 controller='admin/admin') as m:
598 controller='admin/admin') as m:
601 m.connect('admin_home', '', action='index')
599 m.connect('admin_home', '', action='index')
602 m.connect('admin_add_repo', '/add_repo/{new_repo:[a-z0-9\. _-]*}',
600 m.connect('admin_add_repo', '/add_repo/{new_repo:[a-z0-9\. _-]*}',
603 action='add_repo')
601 action='add_repo')
604 m.connect(
602 m.connect(
605 'pull_requests_global_0', '/pull_requests/{pull_request_id:[0-9]+}',
603 'pull_requests_global_0', '/pull_requests/{pull_request_id:[0-9]+}',
606 action='pull_requests')
604 action='pull_requests')
607 m.connect(
605 m.connect(
608 'pull_requests_global_1', '/pull-requests/{pull_request_id:[0-9]+}',
606 'pull_requests_global_1', '/pull-requests/{pull_request_id:[0-9]+}',
609 action='pull_requests')
607 action='pull_requests')
610 m.connect(
608 m.connect(
611 'pull_requests_global', '/pull-request/{pull_request_id:[0-9]+}',
609 'pull_requests_global', '/pull-request/{pull_request_id:[0-9]+}',
612 action='pull_requests')
610 action='pull_requests')
613
611
614 # USER JOURNAL
612 # USER JOURNAL
615 rmap.connect('journal', '%s/journal' % (ADMIN_PREFIX,),
613 rmap.connect('journal', '%s/journal' % (ADMIN_PREFIX,),
616 controller='journal', action='index')
614 controller='journal', action='index')
617 rmap.connect('journal_rss', '%s/journal/rss' % (ADMIN_PREFIX,),
615 rmap.connect('journal_rss', '%s/journal/rss' % (ADMIN_PREFIX,),
618 controller='journal', action='journal_rss')
616 controller='journal', action='journal_rss')
619 rmap.connect('journal_atom', '%s/journal/atom' % (ADMIN_PREFIX,),
617 rmap.connect('journal_atom', '%s/journal/atom' % (ADMIN_PREFIX,),
620 controller='journal', action='journal_atom')
618 controller='journal', action='journal_atom')
621
619
622 rmap.connect('public_journal', '%s/public_journal' % (ADMIN_PREFIX,),
620 rmap.connect('public_journal', '%s/public_journal' % (ADMIN_PREFIX,),
623 controller='journal', action='public_journal')
621 controller='journal', action='public_journal')
624
622
625 rmap.connect('public_journal_rss', '%s/public_journal/rss' % (ADMIN_PREFIX,),
623 rmap.connect('public_journal_rss', '%s/public_journal/rss' % (ADMIN_PREFIX,),
626 controller='journal', action='public_journal_rss')
624 controller='journal', action='public_journal_rss')
627
625
628 rmap.connect('public_journal_rss_old', '%s/public_journal_rss' % (ADMIN_PREFIX,),
626 rmap.connect('public_journal_rss_old', '%s/public_journal_rss' % (ADMIN_PREFIX,),
629 controller='journal', action='public_journal_rss')
627 controller='journal', action='public_journal_rss')
630
628
631 rmap.connect('public_journal_atom',
629 rmap.connect('public_journal_atom',
632 '%s/public_journal/atom' % (ADMIN_PREFIX,), controller='journal',
630 '%s/public_journal/atom' % (ADMIN_PREFIX,), controller='journal',
633 action='public_journal_atom')
631 action='public_journal_atom')
634
632
635 rmap.connect('public_journal_atom_old',
633 rmap.connect('public_journal_atom_old',
636 '%s/public_journal_atom' % (ADMIN_PREFIX,), controller='journal',
634 '%s/public_journal_atom' % (ADMIN_PREFIX,), controller='journal',
637 action='public_journal_atom')
635 action='public_journal_atom')
638
636
639 rmap.connect('toggle_following', '%s/toggle_following' % (ADMIN_PREFIX,),
637 rmap.connect('toggle_following', '%s/toggle_following' % (ADMIN_PREFIX,),
640 controller='journal', action='toggle_following', jsroute=True,
638 controller='journal', action='toggle_following', jsroute=True,
641 conditions={'method': ['POST']})
639 conditions={'method': ['POST']})
642
640
643 # FULL TEXT SEARCH
641 # FULL TEXT SEARCH
644 rmap.connect('search', '%s/search' % (ADMIN_PREFIX,),
642 rmap.connect('search', '%s/search' % (ADMIN_PREFIX,),
645 controller='search')
643 controller='search')
646 rmap.connect('search_repo_home', '/{repo_name}/search',
644 rmap.connect('search_repo_home', '/{repo_name}/search',
647 controller='search',
645 controller='search',
648 action='index',
646 action='index',
649 conditions={'function': check_repo},
647 conditions={'function': check_repo},
650 requirements=URL_NAME_REQUIREMENTS)
648 requirements=URL_NAME_REQUIREMENTS)
651
649
652 # FEEDS
650 # FEEDS
653 rmap.connect('rss_feed_home', '/{repo_name}/feed/rss',
651 rmap.connect('rss_feed_home', '/{repo_name}/feed/rss',
654 controller='feed', action='rss',
652 controller='feed', action='rss',
655 conditions={'function': check_repo},
653 conditions={'function': check_repo},
656 requirements=URL_NAME_REQUIREMENTS)
654 requirements=URL_NAME_REQUIREMENTS)
657
655
658 rmap.connect('atom_feed_home', '/{repo_name}/feed/atom',
656 rmap.connect('atom_feed_home', '/{repo_name}/feed/atom',
659 controller='feed', action='atom',
657 controller='feed', action='atom',
660 conditions={'function': check_repo},
658 conditions={'function': check_repo},
661 requirements=URL_NAME_REQUIREMENTS)
659 requirements=URL_NAME_REQUIREMENTS)
662
660
663 #==========================================================================
661 #==========================================================================
664 # REPOSITORY ROUTES
662 # REPOSITORY ROUTES
665 #==========================================================================
663 #==========================================================================
666
664
667 rmap.connect('repo_creating_home', '/{repo_name}/repo_creating',
665 rmap.connect('repo_creating_home', '/{repo_name}/repo_creating',
668 controller='admin/repos', action='repo_creating',
666 controller='admin/repos', action='repo_creating',
669 requirements=URL_NAME_REQUIREMENTS)
667 requirements=URL_NAME_REQUIREMENTS)
670 rmap.connect('repo_check_home', '/{repo_name}/crepo_check',
668 rmap.connect('repo_check_home', '/{repo_name}/crepo_check',
671 controller='admin/repos', action='repo_check',
669 controller='admin/repos', action='repo_check',
672 requirements=URL_NAME_REQUIREMENTS)
670 requirements=URL_NAME_REQUIREMENTS)
673
671
674 rmap.connect('repo_stats', '/{repo_name}/repo_stats/{commit_id}',
672 rmap.connect('repo_stats', '/{repo_name}/repo_stats/{commit_id}',
675 controller='summary', action='repo_stats',
673 controller='summary', action='repo_stats',
676 conditions={'function': check_repo},
674 conditions={'function': check_repo},
677 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
675 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
678
676
679 rmap.connect('repo_refs_data', '/{repo_name}/refs-data',
677 rmap.connect('repo_refs_data', '/{repo_name}/refs-data',
680 controller='summary', action='repo_refs_data',
678 controller='summary', action='repo_refs_data',
681 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
679 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
682 rmap.connect('repo_refs_changelog_data', '/{repo_name}/refs-data-changelog',
680 rmap.connect('repo_refs_changelog_data', '/{repo_name}/refs-data-changelog',
683 controller='summary', action='repo_refs_changelog_data',
681 controller='summary', action='repo_refs_changelog_data',
684 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
682 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
685 rmap.connect('repo_default_reviewers_data', '/{repo_name}/default-reviewers',
683 rmap.connect('repo_default_reviewers_data', '/{repo_name}/default-reviewers',
686 controller='summary', action='repo_default_reviewers_data',
684 controller='summary', action='repo_default_reviewers_data',
687 jsroute=True, requirements=URL_NAME_REQUIREMENTS)
685 jsroute=True, requirements=URL_NAME_REQUIREMENTS)
688
686
689 rmap.connect('changeset_home', '/{repo_name}/changeset/{revision}',
687 rmap.connect('changeset_home', '/{repo_name}/changeset/{revision}',
690 controller='changeset', revision='tip',
688 controller='changeset', revision='tip',
691 conditions={'function': check_repo},
689 conditions={'function': check_repo},
692 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
690 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
693 rmap.connect('changeset_children', '/{repo_name}/changeset_children/{revision}',
691 rmap.connect('changeset_children', '/{repo_name}/changeset_children/{revision}',
694 controller='changeset', revision='tip', action='changeset_children',
692 controller='changeset', revision='tip', action='changeset_children',
695 conditions={'function': check_repo},
693 conditions={'function': check_repo},
696 requirements=URL_NAME_REQUIREMENTS)
694 requirements=URL_NAME_REQUIREMENTS)
697 rmap.connect('changeset_parents', '/{repo_name}/changeset_parents/{revision}',
695 rmap.connect('changeset_parents', '/{repo_name}/changeset_parents/{revision}',
698 controller='changeset', revision='tip', action='changeset_parents',
696 controller='changeset', revision='tip', action='changeset_parents',
699 conditions={'function': check_repo},
697 conditions={'function': check_repo},
700 requirements=URL_NAME_REQUIREMENTS)
698 requirements=URL_NAME_REQUIREMENTS)
701
699
702 # repo edit options
700 # repo edit options
703 rmap.connect('edit_repo', '/{repo_name}/settings', jsroute=True,
701 rmap.connect('edit_repo', '/{repo_name}/settings', jsroute=True,
704 controller='admin/repos', action='edit',
702 controller='admin/repos', action='edit',
705 conditions={'method': ['GET'], 'function': check_repo},
703 conditions={'method': ['GET'], 'function': check_repo},
706 requirements=URL_NAME_REQUIREMENTS)
704 requirements=URL_NAME_REQUIREMENTS)
707
705
708 rmap.connect('edit_repo_perms', '/{repo_name}/settings/permissions',
706 rmap.connect('edit_repo_perms', '/{repo_name}/settings/permissions',
709 jsroute=True,
707 jsroute=True,
710 controller='admin/repos', action='edit_permissions',
708 controller='admin/repos', action='edit_permissions',
711 conditions={'method': ['GET'], 'function': check_repo},
709 conditions={'method': ['GET'], 'function': check_repo},
712 requirements=URL_NAME_REQUIREMENTS)
710 requirements=URL_NAME_REQUIREMENTS)
713 rmap.connect('edit_repo_perms_update', '/{repo_name}/settings/permissions',
711 rmap.connect('edit_repo_perms_update', '/{repo_name}/settings/permissions',
714 controller='admin/repos', action='edit_permissions_update',
712 controller='admin/repos', action='edit_permissions_update',
715 conditions={'method': ['PUT'], 'function': check_repo},
713 conditions={'method': ['PUT'], 'function': check_repo},
716 requirements=URL_NAME_REQUIREMENTS)
714 requirements=URL_NAME_REQUIREMENTS)
717
715
718 rmap.connect('edit_repo_fields', '/{repo_name}/settings/fields',
716 rmap.connect('edit_repo_fields', '/{repo_name}/settings/fields',
719 controller='admin/repos', action='edit_fields',
717 controller='admin/repos', action='edit_fields',
720 conditions={'method': ['GET'], 'function': check_repo},
718 conditions={'method': ['GET'], 'function': check_repo},
721 requirements=URL_NAME_REQUIREMENTS)
719 requirements=URL_NAME_REQUIREMENTS)
722 rmap.connect('create_repo_fields', '/{repo_name}/settings/fields/new',
720 rmap.connect('create_repo_fields', '/{repo_name}/settings/fields/new',
723 controller='admin/repos', action='create_repo_field',
721 controller='admin/repos', action='create_repo_field',
724 conditions={'method': ['PUT'], 'function': check_repo},
722 conditions={'method': ['PUT'], 'function': check_repo},
725 requirements=URL_NAME_REQUIREMENTS)
723 requirements=URL_NAME_REQUIREMENTS)
726 rmap.connect('delete_repo_fields', '/{repo_name}/settings/fields/{field_id}',
724 rmap.connect('delete_repo_fields', '/{repo_name}/settings/fields/{field_id}',
727 controller='admin/repos', action='delete_repo_field',
725 controller='admin/repos', action='delete_repo_field',
728 conditions={'method': ['DELETE'], 'function': check_repo},
726 conditions={'method': ['DELETE'], 'function': check_repo},
729 requirements=URL_NAME_REQUIREMENTS)
727 requirements=URL_NAME_REQUIREMENTS)
730
728
731 rmap.connect('edit_repo_advanced', '/{repo_name}/settings/advanced',
729 rmap.connect('edit_repo_advanced', '/{repo_name}/settings/advanced',
732 controller='admin/repos', action='edit_advanced',
730 controller='admin/repos', action='edit_advanced',
733 conditions={'method': ['GET'], 'function': check_repo},
731 conditions={'method': ['GET'], 'function': check_repo},
734 requirements=URL_NAME_REQUIREMENTS)
732 requirements=URL_NAME_REQUIREMENTS)
735
733
736 rmap.connect('edit_repo_advanced_locking', '/{repo_name}/settings/advanced/locking',
734 rmap.connect('edit_repo_advanced_locking', '/{repo_name}/settings/advanced/locking',
737 controller='admin/repos', action='edit_advanced_locking',
735 controller='admin/repos', action='edit_advanced_locking',
738 conditions={'method': ['PUT'], 'function': check_repo},
736 conditions={'method': ['PUT'], 'function': check_repo},
739 requirements=URL_NAME_REQUIREMENTS)
737 requirements=URL_NAME_REQUIREMENTS)
740 rmap.connect('toggle_locking', '/{repo_name}/settings/advanced/locking_toggle',
738 rmap.connect('toggle_locking', '/{repo_name}/settings/advanced/locking_toggle',
741 controller='admin/repos', action='toggle_locking',
739 controller='admin/repos', action='toggle_locking',
742 conditions={'method': ['GET'], 'function': check_repo},
740 conditions={'method': ['GET'], 'function': check_repo},
743 requirements=URL_NAME_REQUIREMENTS)
741 requirements=URL_NAME_REQUIREMENTS)
744
742
745 rmap.connect('edit_repo_advanced_journal', '/{repo_name}/settings/advanced/journal',
743 rmap.connect('edit_repo_advanced_journal', '/{repo_name}/settings/advanced/journal',
746 controller='admin/repos', action='edit_advanced_journal',
744 controller='admin/repos', action='edit_advanced_journal',
747 conditions={'method': ['PUT'], 'function': check_repo},
745 conditions={'method': ['PUT'], 'function': check_repo},
748 requirements=URL_NAME_REQUIREMENTS)
746 requirements=URL_NAME_REQUIREMENTS)
749
747
750 rmap.connect('edit_repo_advanced_fork', '/{repo_name}/settings/advanced/fork',
748 rmap.connect('edit_repo_advanced_fork', '/{repo_name}/settings/advanced/fork',
751 controller='admin/repos', action='edit_advanced_fork',
749 controller='admin/repos', action='edit_advanced_fork',
752 conditions={'method': ['PUT'], 'function': check_repo},
750 conditions={'method': ['PUT'], 'function': check_repo},
753 requirements=URL_NAME_REQUIREMENTS)
751 requirements=URL_NAME_REQUIREMENTS)
754
752
755 rmap.connect('edit_repo_caches', '/{repo_name}/settings/caches',
753 rmap.connect('edit_repo_caches', '/{repo_name}/settings/caches',
756 controller='admin/repos', action='edit_caches_form',
754 controller='admin/repos', action='edit_caches_form',
757 conditions={'method': ['GET'], 'function': check_repo},
755 conditions={'method': ['GET'], 'function': check_repo},
758 requirements=URL_NAME_REQUIREMENTS)
756 requirements=URL_NAME_REQUIREMENTS)
759 rmap.connect('edit_repo_caches', '/{repo_name}/settings/caches',
757 rmap.connect('edit_repo_caches', '/{repo_name}/settings/caches',
760 controller='admin/repos', action='edit_caches',
758 controller='admin/repos', action='edit_caches',
761 conditions={'method': ['PUT'], 'function': check_repo},
759 conditions={'method': ['PUT'], 'function': check_repo},
762 requirements=URL_NAME_REQUIREMENTS)
760 requirements=URL_NAME_REQUIREMENTS)
763
761
764 rmap.connect('edit_repo_remote', '/{repo_name}/settings/remote',
762 rmap.connect('edit_repo_remote', '/{repo_name}/settings/remote',
765 controller='admin/repos', action='edit_remote_form',
763 controller='admin/repos', action='edit_remote_form',
766 conditions={'method': ['GET'], 'function': check_repo},
764 conditions={'method': ['GET'], 'function': check_repo},
767 requirements=URL_NAME_REQUIREMENTS)
765 requirements=URL_NAME_REQUIREMENTS)
768 rmap.connect('edit_repo_remote', '/{repo_name}/settings/remote',
766 rmap.connect('edit_repo_remote', '/{repo_name}/settings/remote',
769 controller='admin/repos', action='edit_remote',
767 controller='admin/repos', action='edit_remote',
770 conditions={'method': ['PUT'], 'function': check_repo},
768 conditions={'method': ['PUT'], 'function': check_repo},
771 requirements=URL_NAME_REQUIREMENTS)
769 requirements=URL_NAME_REQUIREMENTS)
772
770
773 rmap.connect('edit_repo_statistics', '/{repo_name}/settings/statistics',
771 rmap.connect('edit_repo_statistics', '/{repo_name}/settings/statistics',
774 controller='admin/repos', action='edit_statistics_form',
772 controller='admin/repos', action='edit_statistics_form',
775 conditions={'method': ['GET'], 'function': check_repo},
773 conditions={'method': ['GET'], 'function': check_repo},
776 requirements=URL_NAME_REQUIREMENTS)
774 requirements=URL_NAME_REQUIREMENTS)
777 rmap.connect('edit_repo_statistics', '/{repo_name}/settings/statistics',
775 rmap.connect('edit_repo_statistics', '/{repo_name}/settings/statistics',
778 controller='admin/repos', action='edit_statistics',
776 controller='admin/repos', action='edit_statistics',
779 conditions={'method': ['PUT'], 'function': check_repo},
777 conditions={'method': ['PUT'], 'function': check_repo},
780 requirements=URL_NAME_REQUIREMENTS)
778 requirements=URL_NAME_REQUIREMENTS)
781 rmap.connect('repo_settings_issuetracker',
779 rmap.connect('repo_settings_issuetracker',
782 '/{repo_name}/settings/issue-tracker',
780 '/{repo_name}/settings/issue-tracker',
783 controller='admin/repos', action='repo_issuetracker',
781 controller='admin/repos', action='repo_issuetracker',
784 conditions={'method': ['GET'], 'function': check_repo},
782 conditions={'method': ['GET'], 'function': check_repo},
785 requirements=URL_NAME_REQUIREMENTS)
783 requirements=URL_NAME_REQUIREMENTS)
786 rmap.connect('repo_issuetracker_test',
784 rmap.connect('repo_issuetracker_test',
787 '/{repo_name}/settings/issue-tracker/test',
785 '/{repo_name}/settings/issue-tracker/test',
788 controller='admin/repos', action='repo_issuetracker_test',
786 controller='admin/repos', action='repo_issuetracker_test',
789 conditions={'method': ['POST'], 'function': check_repo},
787 conditions={'method': ['POST'], 'function': check_repo},
790 requirements=URL_NAME_REQUIREMENTS)
788 requirements=URL_NAME_REQUIREMENTS)
791 rmap.connect('repo_issuetracker_delete',
789 rmap.connect('repo_issuetracker_delete',
792 '/{repo_name}/settings/issue-tracker/delete',
790 '/{repo_name}/settings/issue-tracker/delete',
793 controller='admin/repos', action='repo_issuetracker_delete',
791 controller='admin/repos', action='repo_issuetracker_delete',
794 conditions={'method': ['DELETE'], 'function': check_repo},
792 conditions={'method': ['DELETE'], 'function': check_repo},
795 requirements=URL_NAME_REQUIREMENTS)
793 requirements=URL_NAME_REQUIREMENTS)
796 rmap.connect('repo_issuetracker_save',
794 rmap.connect('repo_issuetracker_save',
797 '/{repo_name}/settings/issue-tracker/save',
795 '/{repo_name}/settings/issue-tracker/save',
798 controller='admin/repos', action='repo_issuetracker_save',
796 controller='admin/repos', action='repo_issuetracker_save',
799 conditions={'method': ['POST'], 'function': check_repo},
797 conditions={'method': ['POST'], 'function': check_repo},
800 requirements=URL_NAME_REQUIREMENTS)
798 requirements=URL_NAME_REQUIREMENTS)
801 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
799 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
802 controller='admin/repos', action='repo_settings_vcs_update',
800 controller='admin/repos', action='repo_settings_vcs_update',
803 conditions={'method': ['POST'], 'function': check_repo},
801 conditions={'method': ['POST'], 'function': check_repo},
804 requirements=URL_NAME_REQUIREMENTS)
802 requirements=URL_NAME_REQUIREMENTS)
805 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
803 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
806 controller='admin/repos', action='repo_settings_vcs',
804 controller='admin/repos', action='repo_settings_vcs',
807 conditions={'method': ['GET'], 'function': check_repo},
805 conditions={'method': ['GET'], 'function': check_repo},
808 requirements=URL_NAME_REQUIREMENTS)
806 requirements=URL_NAME_REQUIREMENTS)
809 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
807 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
810 controller='admin/repos', action='repo_delete_svn_pattern',
808 controller='admin/repos', action='repo_delete_svn_pattern',
811 conditions={'method': ['DELETE'], 'function': check_repo},
809 conditions={'method': ['DELETE'], 'function': check_repo},
812 requirements=URL_NAME_REQUIREMENTS)
810 requirements=URL_NAME_REQUIREMENTS)
813 rmap.connect('repo_pullrequest_settings', '/{repo_name}/settings/pullrequest',
811 rmap.connect('repo_pullrequest_settings', '/{repo_name}/settings/pullrequest',
814 controller='admin/repos', action='repo_settings_pullrequest',
812 controller='admin/repos', action='repo_settings_pullrequest',
815 conditions={'method': ['GET', 'POST'], 'function': check_repo},
813 conditions={'method': ['GET', 'POST'], 'function': check_repo},
816 requirements=URL_NAME_REQUIREMENTS)
814 requirements=URL_NAME_REQUIREMENTS)
817
815
818 # still working url for backward compat.
816 # still working url for backward compat.
819 rmap.connect('raw_changeset_home_depraced',
817 rmap.connect('raw_changeset_home_depraced',
820 '/{repo_name}/raw-changeset/{revision}',
818 '/{repo_name}/raw-changeset/{revision}',
821 controller='changeset', action='changeset_raw',
819 controller='changeset', action='changeset_raw',
822 revision='tip', conditions={'function': check_repo},
820 revision='tip', conditions={'function': check_repo},
823 requirements=URL_NAME_REQUIREMENTS)
821 requirements=URL_NAME_REQUIREMENTS)
824
822
825 # new URLs
823 # new URLs
826 rmap.connect('changeset_raw_home',
824 rmap.connect('changeset_raw_home',
827 '/{repo_name}/changeset-diff/{revision}',
825 '/{repo_name}/changeset-diff/{revision}',
828 controller='changeset', action='changeset_raw',
826 controller='changeset', action='changeset_raw',
829 revision='tip', conditions={'function': check_repo},
827 revision='tip', conditions={'function': check_repo},
830 requirements=URL_NAME_REQUIREMENTS)
828 requirements=URL_NAME_REQUIREMENTS)
831
829
832 rmap.connect('changeset_patch_home',
830 rmap.connect('changeset_patch_home',
833 '/{repo_name}/changeset-patch/{revision}',
831 '/{repo_name}/changeset-patch/{revision}',
834 controller='changeset', action='changeset_patch',
832 controller='changeset', action='changeset_patch',
835 revision='tip', conditions={'function': check_repo},
833 revision='tip', conditions={'function': check_repo},
836 requirements=URL_NAME_REQUIREMENTS)
834 requirements=URL_NAME_REQUIREMENTS)
837
835
838 rmap.connect('changeset_download_home',
836 rmap.connect('changeset_download_home',
839 '/{repo_name}/changeset-download/{revision}',
837 '/{repo_name}/changeset-download/{revision}',
840 controller='changeset', action='changeset_download',
838 controller='changeset', action='changeset_download',
841 revision='tip', conditions={'function': check_repo},
839 revision='tip', conditions={'function': check_repo},
842 requirements=URL_NAME_REQUIREMENTS)
840 requirements=URL_NAME_REQUIREMENTS)
843
841
844 rmap.connect('changeset_comment',
842 rmap.connect('changeset_comment',
845 '/{repo_name}/changeset/{revision}/comment', jsroute=True,
843 '/{repo_name}/changeset/{revision}/comment', jsroute=True,
846 controller='changeset', revision='tip', action='comment',
844 controller='changeset', revision='tip', action='comment',
847 conditions={'function': check_repo},
845 conditions={'function': check_repo},
848 requirements=URL_NAME_REQUIREMENTS)
846 requirements=URL_NAME_REQUIREMENTS)
849
847
850 rmap.connect('changeset_comment_preview',
848 rmap.connect('changeset_comment_preview',
851 '/{repo_name}/changeset/comment/preview', jsroute=True,
849 '/{repo_name}/changeset/comment/preview', jsroute=True,
852 controller='changeset', action='preview_comment',
850 controller='changeset', action='preview_comment',
853 conditions={'function': check_repo, 'method': ['POST']},
851 conditions={'function': check_repo, 'method': ['POST']},
854 requirements=URL_NAME_REQUIREMENTS)
852 requirements=URL_NAME_REQUIREMENTS)
855
853
856 rmap.connect('changeset_comment_delete',
854 rmap.connect('changeset_comment_delete',
857 '/{repo_name}/changeset/comment/{comment_id}/delete',
855 '/{repo_name}/changeset/comment/{comment_id}/delete',
858 controller='changeset', action='delete_comment',
856 controller='changeset', action='delete_comment',
859 conditions={'function': check_repo, 'method': ['DELETE']},
857 conditions={'function': check_repo, 'method': ['DELETE']},
860 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
858 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
861
859
862 rmap.connect('changeset_info', '/{repo_name}/changeset_info/{revision}',
860 rmap.connect('changeset_info', '/{repo_name}/changeset_info/{revision}',
863 controller='changeset', action='changeset_info',
861 controller='changeset', action='changeset_info',
864 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
862 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
865
863
866 rmap.connect('compare_home',
864 rmap.connect('compare_home',
867 '/{repo_name}/compare',
865 '/{repo_name}/compare',
868 controller='compare', action='index',
866 controller='compare', action='index',
869 conditions={'function': check_repo},
867 conditions={'function': check_repo},
870 requirements=URL_NAME_REQUIREMENTS)
868 requirements=URL_NAME_REQUIREMENTS)
871
869
872 rmap.connect('compare_url',
870 rmap.connect('compare_url',
873 '/{repo_name}/compare/{source_ref_type}@{source_ref:.*?}...{target_ref_type}@{target_ref:.*?}',
871 '/{repo_name}/compare/{source_ref_type}@{source_ref:.*?}...{target_ref_type}@{target_ref:.*?}',
874 controller='compare', action='compare',
872 controller='compare', action='compare',
875 conditions={'function': check_repo},
873 conditions={'function': check_repo},
876 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
874 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
877
875
878 rmap.connect('pullrequest_home',
876 rmap.connect('pullrequest_home',
879 '/{repo_name}/pull-request/new', controller='pullrequests',
877 '/{repo_name}/pull-request/new', controller='pullrequests',
880 action='index', conditions={'function': check_repo,
878 action='index', conditions={'function': check_repo,
881 'method': ['GET']},
879 'method': ['GET']},
882 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
880 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
883
881
884 rmap.connect('pullrequest',
882 rmap.connect('pullrequest',
885 '/{repo_name}/pull-request/new', controller='pullrequests',
883 '/{repo_name}/pull-request/new', controller='pullrequests',
886 action='create', conditions={'function': check_repo,
884 action='create', conditions={'function': check_repo,
887 'method': ['POST']},
885 'method': ['POST']},
888 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
886 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
889
887
890 rmap.connect('pullrequest_repo_refs',
888 rmap.connect('pullrequest_repo_refs',
891 '/{repo_name}/pull-request/refs/{target_repo_name:.*?[^/]}',
889 '/{repo_name}/pull-request/refs/{target_repo_name:.*?[^/]}',
892 controller='pullrequests',
890 controller='pullrequests',
893 action='get_repo_refs',
891 action='get_repo_refs',
894 conditions={'function': check_repo, 'method': ['GET']},
892 conditions={'function': check_repo, 'method': ['GET']},
895 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
893 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
896
894
897 rmap.connect('pullrequest_repo_destinations',
895 rmap.connect('pullrequest_repo_destinations',
898 '/{repo_name}/pull-request/repo-destinations',
896 '/{repo_name}/pull-request/repo-destinations',
899 controller='pullrequests',
897 controller='pullrequests',
900 action='get_repo_destinations',
898 action='get_repo_destinations',
901 conditions={'function': check_repo, 'method': ['GET']},
899 conditions={'function': check_repo, 'method': ['GET']},
902 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
900 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
903
901
904 rmap.connect('pullrequest_show',
902 rmap.connect('pullrequest_show',
905 '/{repo_name}/pull-request/{pull_request_id}',
903 '/{repo_name}/pull-request/{pull_request_id}',
906 controller='pullrequests',
904 controller='pullrequests',
907 action='show', conditions={'function': check_repo,
905 action='show', conditions={'function': check_repo,
908 'method': ['GET']},
906 'method': ['GET']},
909 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
907 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
910
908
911 rmap.connect('pullrequest_update',
909 rmap.connect('pullrequest_update',
912 '/{repo_name}/pull-request/{pull_request_id}',
910 '/{repo_name}/pull-request/{pull_request_id}',
913 controller='pullrequests',
911 controller='pullrequests',
914 action='update', conditions={'function': check_repo,
912 action='update', conditions={'function': check_repo,
915 'method': ['PUT']},
913 'method': ['PUT']},
916 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
914 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
917
915
918 rmap.connect('pullrequest_merge',
916 rmap.connect('pullrequest_merge',
919 '/{repo_name}/pull-request/{pull_request_id}',
917 '/{repo_name}/pull-request/{pull_request_id}',
920 controller='pullrequests',
918 controller='pullrequests',
921 action='merge', conditions={'function': check_repo,
919 action='merge', conditions={'function': check_repo,
922 'method': ['POST']},
920 'method': ['POST']},
923 requirements=URL_NAME_REQUIREMENTS)
921 requirements=URL_NAME_REQUIREMENTS)
924
922
925 rmap.connect('pullrequest_delete',
923 rmap.connect('pullrequest_delete',
926 '/{repo_name}/pull-request/{pull_request_id}',
924 '/{repo_name}/pull-request/{pull_request_id}',
927 controller='pullrequests',
925 controller='pullrequests',
928 action='delete', conditions={'function': check_repo,
926 action='delete', conditions={'function': check_repo,
929 'method': ['DELETE']},
927 'method': ['DELETE']},
930 requirements=URL_NAME_REQUIREMENTS)
928 requirements=URL_NAME_REQUIREMENTS)
931
929
932 rmap.connect('pullrequest_show_all',
930 rmap.connect('pullrequest_show_all',
933 '/{repo_name}/pull-request',
931 '/{repo_name}/pull-request',
934 controller='pullrequests',
932 controller='pullrequests',
935 action='show_all', conditions={'function': check_repo,
933 action='show_all', conditions={'function': check_repo,
936 'method': ['GET']},
934 'method': ['GET']},
937 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
935 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
938
936
939 rmap.connect('pullrequest_comment',
937 rmap.connect('pullrequest_comment',
940 '/{repo_name}/pull-request-comment/{pull_request_id}',
938 '/{repo_name}/pull-request-comment/{pull_request_id}',
941 controller='pullrequests',
939 controller='pullrequests',
942 action='comment', conditions={'function': check_repo,
940 action='comment', conditions={'function': check_repo,
943 'method': ['POST']},
941 'method': ['POST']},
944 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
942 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
945
943
946 rmap.connect('pullrequest_comment_delete',
944 rmap.connect('pullrequest_comment_delete',
947 '/{repo_name}/pull-request-comment/{comment_id}/delete',
945 '/{repo_name}/pull-request-comment/{comment_id}/delete',
948 controller='pullrequests', action='delete_comment',
946 controller='pullrequests', action='delete_comment',
949 conditions={'function': check_repo, 'method': ['DELETE']},
947 conditions={'function': check_repo, 'method': ['DELETE']},
950 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
948 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
951
949
952 rmap.connect('summary_home_explicit', '/{repo_name}/summary',
950 rmap.connect('summary_home_explicit', '/{repo_name}/summary',
953 controller='summary', conditions={'function': check_repo},
951 controller='summary', conditions={'function': check_repo},
954 requirements=URL_NAME_REQUIREMENTS)
952 requirements=URL_NAME_REQUIREMENTS)
955
953
956 rmap.connect('branches_home', '/{repo_name}/branches',
954 rmap.connect('branches_home', '/{repo_name}/branches',
957 controller='branches', conditions={'function': check_repo},
955 controller='branches', conditions={'function': check_repo},
958 requirements=URL_NAME_REQUIREMENTS)
956 requirements=URL_NAME_REQUIREMENTS)
959
957
960 rmap.connect('tags_home', '/{repo_name}/tags',
958 rmap.connect('tags_home', '/{repo_name}/tags',
961 controller='tags', conditions={'function': check_repo},
959 controller='tags', conditions={'function': check_repo},
962 requirements=URL_NAME_REQUIREMENTS)
960 requirements=URL_NAME_REQUIREMENTS)
963
961
964 rmap.connect('bookmarks_home', '/{repo_name}/bookmarks',
962 rmap.connect('bookmarks_home', '/{repo_name}/bookmarks',
965 controller='bookmarks', conditions={'function': check_repo},
963 controller='bookmarks', conditions={'function': check_repo},
966 requirements=URL_NAME_REQUIREMENTS)
964 requirements=URL_NAME_REQUIREMENTS)
967
965
968 rmap.connect('changelog_home', '/{repo_name}/changelog', jsroute=True,
966 rmap.connect('changelog_home', '/{repo_name}/changelog', jsroute=True,
969 controller='changelog', conditions={'function': check_repo},
967 controller='changelog', conditions={'function': check_repo},
970 requirements=URL_NAME_REQUIREMENTS)
968 requirements=URL_NAME_REQUIREMENTS)
971
969
972 rmap.connect('changelog_summary_home', '/{repo_name}/changelog_summary',
970 rmap.connect('changelog_summary_home', '/{repo_name}/changelog_summary',
973 controller='changelog', action='changelog_summary',
971 controller='changelog', action='changelog_summary',
974 conditions={'function': check_repo},
972 conditions={'function': check_repo},
975 requirements=URL_NAME_REQUIREMENTS)
973 requirements=URL_NAME_REQUIREMENTS)
976
974
977 rmap.connect('changelog_file_home',
975 rmap.connect('changelog_file_home',
978 '/{repo_name}/changelog/{revision}/{f_path}',
976 '/{repo_name}/changelog/{revision}/{f_path}',
979 controller='changelog', f_path=None,
977 controller='changelog', f_path=None,
980 conditions={'function': check_repo},
978 conditions={'function': check_repo},
981 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
979 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
982
980
983 rmap.connect('changelog_elements', '/{repo_name}/changelog_details',
981 rmap.connect('changelog_elements', '/{repo_name}/changelog_details',
984 controller='changelog', action='changelog_elements',
982 controller='changelog', action='changelog_elements',
985 conditions={'function': check_repo},
983 conditions={'function': check_repo},
986 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
984 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
987
985
988 rmap.connect('files_home', '/{repo_name}/files/{revision}/{f_path}',
986 rmap.connect('files_home', '/{repo_name}/files/{revision}/{f_path}',
989 controller='files', revision='tip', f_path='',
987 controller='files', revision='tip', f_path='',
990 conditions={'function': check_repo},
988 conditions={'function': check_repo},
991 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
989 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
992
990
993 rmap.connect('files_home_simple_catchrev',
991 rmap.connect('files_home_simple_catchrev',
994 '/{repo_name}/files/{revision}',
992 '/{repo_name}/files/{revision}',
995 controller='files', revision='tip', f_path='',
993 controller='files', revision='tip', f_path='',
996 conditions={'function': check_repo},
994 conditions={'function': check_repo},
997 requirements=URL_NAME_REQUIREMENTS)
995 requirements=URL_NAME_REQUIREMENTS)
998
996
999 rmap.connect('files_home_simple_catchall',
997 rmap.connect('files_home_simple_catchall',
1000 '/{repo_name}/files',
998 '/{repo_name}/files',
1001 controller='files', revision='tip', f_path='',
999 controller='files', revision='tip', f_path='',
1002 conditions={'function': check_repo},
1000 conditions={'function': check_repo},
1003 requirements=URL_NAME_REQUIREMENTS)
1001 requirements=URL_NAME_REQUIREMENTS)
1004
1002
1005 rmap.connect('files_history_home',
1003 rmap.connect('files_history_home',
1006 '/{repo_name}/history/{revision}/{f_path}',
1004 '/{repo_name}/history/{revision}/{f_path}',
1007 controller='files', action='history', revision='tip', f_path='',
1005 controller='files', action='history', revision='tip', f_path='',
1008 conditions={'function': check_repo},
1006 conditions={'function': check_repo},
1009 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1007 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1010
1008
1011 rmap.connect('files_authors_home',
1009 rmap.connect('files_authors_home',
1012 '/{repo_name}/authors/{revision}/{f_path}',
1010 '/{repo_name}/authors/{revision}/{f_path}',
1013 controller='files', action='authors', revision='tip', f_path='',
1011 controller='files', action='authors', revision='tip', f_path='',
1014 conditions={'function': check_repo},
1012 conditions={'function': check_repo},
1015 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1013 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1016
1014
1017 rmap.connect('files_diff_home', '/{repo_name}/diff/{f_path}',
1015 rmap.connect('files_diff_home', '/{repo_name}/diff/{f_path}',
1018 controller='files', action='diff', f_path='',
1016 controller='files', action='diff', f_path='',
1019 conditions={'function': check_repo},
1017 conditions={'function': check_repo},
1020 requirements=URL_NAME_REQUIREMENTS)
1018 requirements=URL_NAME_REQUIREMENTS)
1021
1019
1022 rmap.connect('files_diff_2way_home',
1020 rmap.connect('files_diff_2way_home',
1023 '/{repo_name}/diff-2way/{f_path}',
1021 '/{repo_name}/diff-2way/{f_path}',
1024 controller='files', action='diff_2way', f_path='',
1022 controller='files', action='diff_2way', f_path='',
1025 conditions={'function': check_repo},
1023 conditions={'function': check_repo},
1026 requirements=URL_NAME_REQUIREMENTS)
1024 requirements=URL_NAME_REQUIREMENTS)
1027
1025
1028 rmap.connect('files_rawfile_home',
1026 rmap.connect('files_rawfile_home',
1029 '/{repo_name}/rawfile/{revision}/{f_path}',
1027 '/{repo_name}/rawfile/{revision}/{f_path}',
1030 controller='files', action='rawfile', revision='tip',
1028 controller='files', action='rawfile', revision='tip',
1031 f_path='', conditions={'function': check_repo},
1029 f_path='', conditions={'function': check_repo},
1032 requirements=URL_NAME_REQUIREMENTS)
1030 requirements=URL_NAME_REQUIREMENTS)
1033
1031
1034 rmap.connect('files_raw_home',
1032 rmap.connect('files_raw_home',
1035 '/{repo_name}/raw/{revision}/{f_path}',
1033 '/{repo_name}/raw/{revision}/{f_path}',
1036 controller='files', action='raw', revision='tip', f_path='',
1034 controller='files', action='raw', revision='tip', f_path='',
1037 conditions={'function': check_repo},
1035 conditions={'function': check_repo},
1038 requirements=URL_NAME_REQUIREMENTS)
1036 requirements=URL_NAME_REQUIREMENTS)
1039
1037
1040 rmap.connect('files_render_home',
1038 rmap.connect('files_render_home',
1041 '/{repo_name}/render/{revision}/{f_path}',
1039 '/{repo_name}/render/{revision}/{f_path}',
1042 controller='files', action='index', revision='tip', f_path='',
1040 controller='files', action='index', revision='tip', f_path='',
1043 rendered=True, conditions={'function': check_repo},
1041 rendered=True, conditions={'function': check_repo},
1044 requirements=URL_NAME_REQUIREMENTS)
1042 requirements=URL_NAME_REQUIREMENTS)
1045
1043
1046 rmap.connect('files_annotate_home',
1044 rmap.connect('files_annotate_home',
1047 '/{repo_name}/annotate/{revision}/{f_path}',
1045 '/{repo_name}/annotate/{revision}/{f_path}',
1048 controller='files', action='index', revision='tip',
1046 controller='files', action='index', revision='tip',
1049 f_path='', annotate=True, conditions={'function': check_repo},
1047 f_path='', annotate=True, conditions={'function': check_repo},
1050 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1048 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1051
1049
1052 rmap.connect('files_annotate_previous',
1050 rmap.connect('files_annotate_previous',
1053 '/{repo_name}/annotate-previous/{revision}/{f_path}',
1051 '/{repo_name}/annotate-previous/{revision}/{f_path}',
1054 controller='files', action='annotate_previous', revision='tip',
1052 controller='files', action='annotate_previous', revision='tip',
1055 f_path='', annotate=True, conditions={'function': check_repo},
1053 f_path='', annotate=True, conditions={'function': check_repo},
1056 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1054 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1057
1055
1058 rmap.connect('files_edit',
1056 rmap.connect('files_edit',
1059 '/{repo_name}/edit/{revision}/{f_path}',
1057 '/{repo_name}/edit/{revision}/{f_path}',
1060 controller='files', action='edit', revision='tip',
1058 controller='files', action='edit', revision='tip',
1061 f_path='',
1059 f_path='',
1062 conditions={'function': check_repo, 'method': ['POST']},
1060 conditions={'function': check_repo, 'method': ['POST']},
1063 requirements=URL_NAME_REQUIREMENTS)
1061 requirements=URL_NAME_REQUIREMENTS)
1064
1062
1065 rmap.connect('files_edit_home',
1063 rmap.connect('files_edit_home',
1066 '/{repo_name}/edit/{revision}/{f_path}',
1064 '/{repo_name}/edit/{revision}/{f_path}',
1067 controller='files', action='edit_home', revision='tip',
1065 controller='files', action='edit_home', revision='tip',
1068 f_path='', conditions={'function': check_repo},
1066 f_path='', conditions={'function': check_repo},
1069 requirements=URL_NAME_REQUIREMENTS)
1067 requirements=URL_NAME_REQUIREMENTS)
1070
1068
1071 rmap.connect('files_add',
1069 rmap.connect('files_add',
1072 '/{repo_name}/add/{revision}/{f_path}',
1070 '/{repo_name}/add/{revision}/{f_path}',
1073 controller='files', action='add', revision='tip',
1071 controller='files', action='add', revision='tip',
1074 f_path='',
1072 f_path='',
1075 conditions={'function': check_repo, 'method': ['POST']},
1073 conditions={'function': check_repo, 'method': ['POST']},
1076 requirements=URL_NAME_REQUIREMENTS)
1074 requirements=URL_NAME_REQUIREMENTS)
1077
1075
1078 rmap.connect('files_add_home',
1076 rmap.connect('files_add_home',
1079 '/{repo_name}/add/{revision}/{f_path}',
1077 '/{repo_name}/add/{revision}/{f_path}',
1080 controller='files', action='add_home', revision='tip',
1078 controller='files', action='add_home', revision='tip',
1081 f_path='', conditions={'function': check_repo},
1079 f_path='', conditions={'function': check_repo},
1082 requirements=URL_NAME_REQUIREMENTS)
1080 requirements=URL_NAME_REQUIREMENTS)
1083
1081
1084 rmap.connect('files_delete',
1082 rmap.connect('files_delete',
1085 '/{repo_name}/delete/{revision}/{f_path}',
1083 '/{repo_name}/delete/{revision}/{f_path}',
1086 controller='files', action='delete', revision='tip',
1084 controller='files', action='delete', revision='tip',
1087 f_path='',
1085 f_path='',
1088 conditions={'function': check_repo, 'method': ['POST']},
1086 conditions={'function': check_repo, 'method': ['POST']},
1089 requirements=URL_NAME_REQUIREMENTS)
1087 requirements=URL_NAME_REQUIREMENTS)
1090
1088
1091 rmap.connect('files_delete_home',
1089 rmap.connect('files_delete_home',
1092 '/{repo_name}/delete/{revision}/{f_path}',
1090 '/{repo_name}/delete/{revision}/{f_path}',
1093 controller='files', action='delete_home', revision='tip',
1091 controller='files', action='delete_home', revision='tip',
1094 f_path='', conditions={'function': check_repo},
1092 f_path='', conditions={'function': check_repo},
1095 requirements=URL_NAME_REQUIREMENTS)
1093 requirements=URL_NAME_REQUIREMENTS)
1096
1094
1097 rmap.connect('files_archive_home', '/{repo_name}/archive/{fname}',
1095 rmap.connect('files_archive_home', '/{repo_name}/archive/{fname}',
1098 controller='files', action='archivefile',
1096 controller='files', action='archivefile',
1099 conditions={'function': check_repo},
1097 conditions={'function': check_repo},
1100 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1098 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1101
1099
1102 rmap.connect('files_nodelist_home',
1100 rmap.connect('files_nodelist_home',
1103 '/{repo_name}/nodelist/{revision}/{f_path}',
1101 '/{repo_name}/nodelist/{revision}/{f_path}',
1104 controller='files', action='nodelist',
1102 controller='files', action='nodelist',
1105 conditions={'function': check_repo},
1103 conditions={'function': check_repo},
1106 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1104 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1107
1105
1108 rmap.connect('files_nodetree_full',
1106 rmap.connect('files_nodetree_full',
1109 '/{repo_name}/nodetree_full/{commit_id}/{f_path}',
1107 '/{repo_name}/nodetree_full/{commit_id}/{f_path}',
1110 controller='files', action='nodetree_full',
1108 controller='files', action='nodetree_full',
1111 conditions={'function': check_repo},
1109 conditions={'function': check_repo},
1112 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1110 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1113
1111
1114 rmap.connect('repo_fork_create_home', '/{repo_name}/fork',
1112 rmap.connect('repo_fork_create_home', '/{repo_name}/fork',
1115 controller='forks', action='fork_create',
1113 controller='forks', action='fork_create',
1116 conditions={'function': check_repo, 'method': ['POST']},
1114 conditions={'function': check_repo, 'method': ['POST']},
1117 requirements=URL_NAME_REQUIREMENTS)
1115 requirements=URL_NAME_REQUIREMENTS)
1118
1116
1119 rmap.connect('repo_fork_home', '/{repo_name}/fork',
1117 rmap.connect('repo_fork_home', '/{repo_name}/fork',
1120 controller='forks', action='fork',
1118 controller='forks', action='fork',
1121 conditions={'function': check_repo},
1119 conditions={'function': check_repo},
1122 requirements=URL_NAME_REQUIREMENTS)
1120 requirements=URL_NAME_REQUIREMENTS)
1123
1121
1124 rmap.connect('repo_forks_home', '/{repo_name}/forks',
1122 rmap.connect('repo_forks_home', '/{repo_name}/forks',
1125 controller='forks', action='forks',
1123 controller='forks', action='forks',
1126 conditions={'function': check_repo},
1124 conditions={'function': check_repo},
1127 requirements=URL_NAME_REQUIREMENTS)
1125 requirements=URL_NAME_REQUIREMENTS)
1128
1126
1129 # must be here for proper group/repo catching pattern
1127 # must be here for proper group/repo catching pattern
1130 _connect_with_slash(
1128 _connect_with_slash(
1131 rmap, 'repo_group_home', '/{group_name}',
1129 rmap, 'repo_group_home', '/{group_name}',
1132 controller='home', action='index_repo_group',
1130 controller='home', action='index_repo_group',
1133 conditions={'function': check_group},
1131 conditions={'function': check_group},
1134 requirements=URL_NAME_REQUIREMENTS)
1132 requirements=URL_NAME_REQUIREMENTS)
1135
1133
1136 # catch all, at the end
1134 # catch all, at the end
1137 _connect_with_slash(
1135 _connect_with_slash(
1138 rmap, 'summary_home', '/{repo_name}', jsroute=True,
1136 rmap, 'summary_home', '/{repo_name}', jsroute=True,
1139 controller='summary', action='index',
1137 controller='summary', action='index',
1140 conditions={'function': check_repo},
1138 conditions={'function': check_repo},
1141 requirements=URL_NAME_REQUIREMENTS)
1139 requirements=URL_NAME_REQUIREMENTS)
1142
1140
1143 return rmap
1141 return rmap
1144
1142
1145
1143
1146 def _connect_with_slash(mapper, name, path, *args, **kwargs):
1144 def _connect_with_slash(mapper, name, path, *args, **kwargs):
1147 """
1145 """
1148 Connect a route with an optional trailing slash in `path`.
1146 Connect a route with an optional trailing slash in `path`.
1149 """
1147 """
1150 mapper.connect(name + '_slash', path + '/', *args, **kwargs)
1148 mapper.connect(name + '_slash', path + '/', *args, **kwargs)
1151 mapper.connect(name, path, *args, **kwargs)
1149 mapper.connect(name, path, *args, **kwargs)
@@ -1,375 +1,364 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 import datetime
27 import datetime
28
28
29 import formencode
29 import formencode
30 from formencode import htmlfill
30 from formencode import htmlfill
31 from pyramid.threadlocal import get_current_registry
31 from pyramid.threadlocal import get_current_registry
32 from pylons import request, tmpl_context as c, url, session
32 from pyramid.httpexceptions import HTTPFound
33
34 from pylons import request, tmpl_context as c, url
33 from pylons.controllers.util import redirect
35 from pylons.controllers.util import redirect
34 from pylons.i18n.translation import _
36 from pylons.i18n.translation import _
35 from sqlalchemy.orm import joinedload
37 from sqlalchemy.orm import joinedload
36
38
37 from rhodecode.lib import helpers as h
39 from rhodecode.lib import helpers as h
38 from rhodecode.lib import auth
40 from rhodecode.lib import auth
39 from rhodecode.lib.auth import (
41 from rhodecode.lib.auth import (
40 LoginRequired, NotAnonymous, AuthUser)
42 LoginRequired, NotAnonymous, AuthUser)
41 from rhodecode.lib.base import BaseController, render
43 from rhodecode.lib.base import BaseController, render
42 from rhodecode.lib.utils import jsonify
44 from rhodecode.lib.utils import jsonify
43 from rhodecode.lib.utils2 import safe_int, str2bool
45 from rhodecode.lib.utils2 import safe_int, str2bool
44 from rhodecode.lib.ext_json import json
46 from rhodecode.lib.ext_json import json
45 from rhodecode.lib.channelstream import channelstream_request, \
47 from rhodecode.lib.channelstream import channelstream_request, \
46 ChannelstreamException
48 ChannelstreamException
47
49
48 from rhodecode.model.db import (
50 from rhodecode.model.db import (
49 Repository, PullRequest, UserEmailMap, User, UserFollowing)
51 Repository, PullRequest, UserEmailMap, User, UserFollowing)
50 from rhodecode.model.forms import UserForm
52 from rhodecode.model.forms import UserForm
51 from rhodecode.model.scm import RepoList
53 from rhodecode.model.scm import RepoList
52 from rhodecode.model.user import UserModel
54 from rhodecode.model.user import UserModel
53 from rhodecode.model.repo import RepoModel
55 from rhodecode.model.repo import RepoModel
54 from rhodecode.model.meta import Session
56 from rhodecode.model.meta import Session
55 from rhodecode.model.pull_request import PullRequestModel
57 from rhodecode.model.pull_request import PullRequestModel
56 from rhodecode.model.comment import CommentsModel
58 from rhodecode.model.comment import CommentsModel
57
59
58 log = logging.getLogger(__name__)
60 log = logging.getLogger(__name__)
59
61
60
62
61 class MyAccountController(BaseController):
63 class MyAccountController(BaseController):
62 """REST Controller styled on the Atom Publishing Protocol"""
64 """REST Controller styled on the Atom Publishing Protocol"""
63 # To properly map this controller, ensure your config/routing.py
65 # To properly map this controller, ensure your config/routing.py
64 # file has a resource setup:
66 # file has a resource setup:
65 # map.resource('setting', 'settings', controller='admin/settings',
67 # map.resource('setting', 'settings', controller='admin/settings',
66 # path_prefix='/admin', name_prefix='admin_')
68 # path_prefix='/admin', name_prefix='admin_')
67
69
68 @LoginRequired()
70 @LoginRequired()
69 @NotAnonymous()
71 @NotAnonymous()
70 def __before__(self):
72 def __before__(self):
71 super(MyAccountController, self).__before__()
73 super(MyAccountController, self).__before__()
72
74
73 def __load_data(self):
75 def __load_data(self):
74 c.user = User.get(c.rhodecode_user.user_id)
76 c.user = User.get(c.rhodecode_user.user_id)
75 if c.user.username == User.DEFAULT_USER:
77 if c.user.username == User.DEFAULT_USER:
76 h.flash(_("You can't edit this user since it's"
78 h.flash(_("You can't edit this user since it's"
77 " crucial for entire application"), category='warning')
79 " crucial for entire application"), category='warning')
78 return redirect(h.route_path('users'))
80 return redirect(h.route_path('users'))
79
81
80 c.auth_user = AuthUser(
82 c.auth_user = AuthUser(
81 user_id=c.rhodecode_user.user_id, ip_addr=self.ip_addr)
83 user_id=c.rhodecode_user.user_id, ip_addr=self.ip_addr)
82
84
83 def _load_my_repos_data(self, watched=False):
85 def _load_my_repos_data(self, watched=False):
84 if watched:
86 if watched:
85 admin = False
87 admin = False
86 follows_repos = Session().query(UserFollowing)\
88 follows_repos = Session().query(UserFollowing)\
87 .filter(UserFollowing.user_id == c.rhodecode_user.user_id)\
89 .filter(UserFollowing.user_id == c.rhodecode_user.user_id)\
88 .options(joinedload(UserFollowing.follows_repository))\
90 .options(joinedload(UserFollowing.follows_repository))\
89 .all()
91 .all()
90 repo_list = [x.follows_repository for x in follows_repos]
92 repo_list = [x.follows_repository for x in follows_repos]
91 else:
93 else:
92 admin = True
94 admin = True
93 repo_list = Repository.get_all_repos(
95 repo_list = Repository.get_all_repos(
94 user_id=c.rhodecode_user.user_id)
96 user_id=c.rhodecode_user.user_id)
95 repo_list = RepoList(repo_list, perm_set=[
97 repo_list = RepoList(repo_list, perm_set=[
96 'repository.read', 'repository.write', 'repository.admin'])
98 'repository.read', 'repository.write', 'repository.admin'])
97
99
98 repos_data = RepoModel().get_repos_as_dict(
100 repos_data = RepoModel().get_repos_as_dict(
99 repo_list=repo_list, admin=admin)
101 repo_list=repo_list, admin=admin)
100 # json used to render the grid
102 # json used to render the grid
101 return json.dumps(repos_data)
103 return json.dumps(repos_data)
102
104
103 @auth.CSRFRequired()
105 @auth.CSRFRequired()
104 def my_account_update(self):
106 def my_account_update(self):
105 """
107 """
106 POST /_admin/my_account Updates info of my account
108 POST /_admin/my_account Updates info of my account
107 """
109 """
108 # url('my_account')
110 # url('my_account')
109 c.active = 'profile_edit'
111 c.active = 'profile_edit'
110 self.__load_data()
112 self.__load_data()
111 c.perm_user = c.auth_user
113 c.perm_user = c.auth_user
112 c.extern_type = c.user.extern_type
114 c.extern_type = c.user.extern_type
113 c.extern_name = c.user.extern_name
115 c.extern_name = c.user.extern_name
114
116
115 defaults = c.user.get_dict()
117 defaults = c.user.get_dict()
116 update = False
118 update = False
117 _form = UserForm(edit=True,
119 _form = UserForm(edit=True,
118 old_data={'user_id': c.rhodecode_user.user_id,
120 old_data={'user_id': c.rhodecode_user.user_id,
119 'email': c.rhodecode_user.email})()
121 'email': c.rhodecode_user.email})()
120 form_result = {}
122 form_result = {}
121 try:
123 try:
122 post_data = dict(request.POST)
124 post_data = dict(request.POST)
123 post_data['new_password'] = ''
125 post_data['new_password'] = ''
124 post_data['password_confirmation'] = ''
126 post_data['password_confirmation'] = ''
125 form_result = _form.to_python(post_data)
127 form_result = _form.to_python(post_data)
126 # skip updating those attrs for my account
128 # skip updating those attrs for my account
127 skip_attrs = ['admin', 'active', 'extern_type', 'extern_name',
129 skip_attrs = ['admin', 'active', 'extern_type', 'extern_name',
128 'new_password', 'password_confirmation']
130 'new_password', 'password_confirmation']
129 # TODO: plugin should define if username can be updated
131 # TODO: plugin should define if username can be updated
130 if c.extern_type != "rhodecode":
132 if c.extern_type != "rhodecode":
131 # forbid updating username for external accounts
133 # forbid updating username for external accounts
132 skip_attrs.append('username')
134 skip_attrs.append('username')
133
135
134 UserModel().update_user(
136 UserModel().update_user(
135 c.rhodecode_user.user_id, skip_attrs=skip_attrs, **form_result)
137 c.rhodecode_user.user_id, skip_attrs=skip_attrs, **form_result)
136 h.flash(_('Your account was updated successfully'),
138 h.flash(_('Your account was updated successfully'),
137 category='success')
139 category='success')
138 Session().commit()
140 Session().commit()
139 update = True
141 update = True
140
142
141 except formencode.Invalid as errors:
143 except formencode.Invalid as errors:
142 return htmlfill.render(
144 return htmlfill.render(
143 render('admin/my_account/my_account.mako'),
145 render('admin/my_account/my_account.mako'),
144 defaults=errors.value,
146 defaults=errors.value,
145 errors=errors.error_dict or {},
147 errors=errors.error_dict or {},
146 prefix_error=False,
148 prefix_error=False,
147 encoding="UTF-8",
149 encoding="UTF-8",
148 force_defaults=False)
150 force_defaults=False)
149 except Exception:
151 except Exception:
150 log.exception("Exception updating user")
152 log.exception("Exception updating user")
151 h.flash(_('Error occurred during update of user %s')
153 h.flash(_('Error occurred during update of user %s')
152 % form_result.get('username'), category='error')
154 % form_result.get('username'), category='error')
153
155
154 if update:
156 if update:
155 return redirect('my_account')
157 raise HTTPFound(h.route_path('my_account_profile'))
156
158
157 return htmlfill.render(
159 return htmlfill.render(
158 render('admin/my_account/my_account.mako'),
160 render('admin/my_account/my_account.mako'),
159 defaults=defaults,
161 defaults=defaults,
160 encoding="UTF-8",
162 encoding="UTF-8",
161 force_defaults=False
163 force_defaults=False
162 )
164 )
163
165
164 def my_account(self):
165 """
166 GET /_admin/my_account Displays info about my account
167 """
168 # url('my_account')
169 c.active = 'profile'
170 self.__load_data()
171
172 defaults = c.user.get_dict()
173 return htmlfill.render(
174 render('admin/my_account/my_account.mako'),
175 defaults=defaults, encoding="UTF-8", force_defaults=False)
176
177 def my_account_edit(self):
166 def my_account_edit(self):
178 """
167 """
179 GET /_admin/my_account/edit Displays edit form of my account
168 GET /_admin/my_account/edit Displays edit form of my account
180 """
169 """
181 c.active = 'profile_edit'
170 c.active = 'profile_edit'
182 self.__load_data()
171 self.__load_data()
183 c.perm_user = c.auth_user
172 c.perm_user = c.auth_user
184 c.extern_type = c.user.extern_type
173 c.extern_type = c.user.extern_type
185 c.extern_name = c.user.extern_name
174 c.extern_name = c.user.extern_name
186
175
187 defaults = c.user.get_dict()
176 defaults = c.user.get_dict()
188 return htmlfill.render(
177 return htmlfill.render(
189 render('admin/my_account/my_account.mako'),
178 render('admin/my_account/my_account.mako'),
190 defaults=defaults,
179 defaults=defaults,
191 encoding="UTF-8",
180 encoding="UTF-8",
192 force_defaults=False
181 force_defaults=False
193 )
182 )
194
183
195 def my_account_repos(self):
184 def my_account_repos(self):
196 c.active = 'repos'
185 c.active = 'repos'
197 self.__load_data()
186 self.__load_data()
198
187
199 # json used to render the grid
188 # json used to render the grid
200 c.data = self._load_my_repos_data()
189 c.data = self._load_my_repos_data()
201 return render('admin/my_account/my_account.mako')
190 return render('admin/my_account/my_account.mako')
202
191
203 def my_account_watched(self):
192 def my_account_watched(self):
204 c.active = 'watched'
193 c.active = 'watched'
205 self.__load_data()
194 self.__load_data()
206
195
207 # json used to render the grid
196 # json used to render the grid
208 c.data = self._load_my_repos_data(watched=True)
197 c.data = self._load_my_repos_data(watched=True)
209 return render('admin/my_account/my_account.mako')
198 return render('admin/my_account/my_account.mako')
210
199
211 def my_account_perms(self):
200 def my_account_perms(self):
212 c.active = 'perms'
201 c.active = 'perms'
213 self.__load_data()
202 self.__load_data()
214 c.perm_user = c.auth_user
203 c.perm_user = c.auth_user
215
204
216 return render('admin/my_account/my_account.mako')
205 return render('admin/my_account/my_account.mako')
217
206
218 def my_account_emails(self):
207 def my_account_emails(self):
219 c.active = 'emails'
208 c.active = 'emails'
220 self.__load_data()
209 self.__load_data()
221
210
222 c.user_email_map = UserEmailMap.query()\
211 c.user_email_map = UserEmailMap.query()\
223 .filter(UserEmailMap.user == c.user).all()
212 .filter(UserEmailMap.user == c.user).all()
224 return render('admin/my_account/my_account.mako')
213 return render('admin/my_account/my_account.mako')
225
214
226 @auth.CSRFRequired()
215 @auth.CSRFRequired()
227 def my_account_emails_add(self):
216 def my_account_emails_add(self):
228 email = request.POST.get('new_email')
217 email = request.POST.get('new_email')
229
218
230 try:
219 try:
231 UserModel().add_extra_email(c.rhodecode_user.user_id, email)
220 UserModel().add_extra_email(c.rhodecode_user.user_id, email)
232 Session().commit()
221 Session().commit()
233 h.flash(_("Added new email address `%s` for user account") % email,
222 h.flash(_("Added new email address `%s` for user account") % email,
234 category='success')
223 category='success')
235 except formencode.Invalid as error:
224 except formencode.Invalid as error:
236 msg = error.error_dict['email']
225 msg = error.error_dict['email']
237 h.flash(msg, category='error')
226 h.flash(msg, category='error')
238 except Exception:
227 except Exception:
239 log.exception("Exception in my_account_emails")
228 log.exception("Exception in my_account_emails")
240 h.flash(_('An error occurred during email saving'),
229 h.flash(_('An error occurred during email saving'),
241 category='error')
230 category='error')
242 return redirect(url('my_account_emails'))
231 return redirect(url('my_account_emails'))
243
232
244 @auth.CSRFRequired()
233 @auth.CSRFRequired()
245 def my_account_emails_delete(self):
234 def my_account_emails_delete(self):
246 email_id = request.POST.get('del_email_id')
235 email_id = request.POST.get('del_email_id')
247 user_model = UserModel()
236 user_model = UserModel()
248 user_model.delete_extra_email(c.rhodecode_user.user_id, email_id)
237 user_model.delete_extra_email(c.rhodecode_user.user_id, email_id)
249 Session().commit()
238 Session().commit()
250 h.flash(_("Removed email address from user account"),
239 h.flash(_("Removed email address from user account"),
251 category='success')
240 category='success')
252 return redirect(url('my_account_emails'))
241 return redirect(url('my_account_emails'))
253
242
254 def _extract_ordering(self, request):
243 def _extract_ordering(self, request):
255 column_index = safe_int(request.GET.get('order[0][column]'))
244 column_index = safe_int(request.GET.get('order[0][column]'))
256 order_dir = request.GET.get('order[0][dir]', 'desc')
245 order_dir = request.GET.get('order[0][dir]', 'desc')
257 order_by = request.GET.get(
246 order_by = request.GET.get(
258 'columns[%s][data][sort]' % column_index, 'name_raw')
247 'columns[%s][data][sort]' % column_index, 'name_raw')
259 return order_by, order_dir
248 return order_by, order_dir
260
249
261 def _get_pull_requests_list(self, statuses):
250 def _get_pull_requests_list(self, statuses):
262 start = safe_int(request.GET.get('start'), 0)
251 start = safe_int(request.GET.get('start'), 0)
263 length = safe_int(request.GET.get('length'), c.visual.dashboard_items)
252 length = safe_int(request.GET.get('length'), c.visual.dashboard_items)
264 order_by, order_dir = self._extract_ordering(request)
253 order_by, order_dir = self._extract_ordering(request)
265
254
266 pull_requests = PullRequestModel().get_im_participating_in(
255 pull_requests = PullRequestModel().get_im_participating_in(
267 user_id=c.rhodecode_user.user_id,
256 user_id=c.rhodecode_user.user_id,
268 statuses=statuses,
257 statuses=statuses,
269 offset=start, length=length, order_by=order_by,
258 offset=start, length=length, order_by=order_by,
270 order_dir=order_dir)
259 order_dir=order_dir)
271
260
272 pull_requests_total_count = PullRequestModel().count_im_participating_in(
261 pull_requests_total_count = PullRequestModel().count_im_participating_in(
273 user_id=c.rhodecode_user.user_id, statuses=statuses)
262 user_id=c.rhodecode_user.user_id, statuses=statuses)
274
263
275 from rhodecode.lib.utils import PartialRenderer
264 from rhodecode.lib.utils import PartialRenderer
276 _render = PartialRenderer('data_table/_dt_elements.mako')
265 _render = PartialRenderer('data_table/_dt_elements.mako')
277 data = []
266 data = []
278 for pr in pull_requests:
267 for pr in pull_requests:
279 repo_id = pr.target_repo_id
268 repo_id = pr.target_repo_id
280 comments = CommentsModel().get_all_comments(
269 comments = CommentsModel().get_all_comments(
281 repo_id, pull_request=pr)
270 repo_id, pull_request=pr)
282 owned = pr.user_id == c.rhodecode_user.user_id
271 owned = pr.user_id == c.rhodecode_user.user_id
283 status = pr.calculated_review_status()
272 status = pr.calculated_review_status()
284
273
285 data.append({
274 data.append({
286 'target_repo': _render('pullrequest_target_repo',
275 'target_repo': _render('pullrequest_target_repo',
287 pr.target_repo.repo_name),
276 pr.target_repo.repo_name),
288 'name': _render('pullrequest_name',
277 'name': _render('pullrequest_name',
289 pr.pull_request_id, pr.target_repo.repo_name,
278 pr.pull_request_id, pr.target_repo.repo_name,
290 short=True),
279 short=True),
291 'name_raw': pr.pull_request_id,
280 'name_raw': pr.pull_request_id,
292 'status': _render('pullrequest_status', status),
281 'status': _render('pullrequest_status', status),
293 'title': _render(
282 'title': _render(
294 'pullrequest_title', pr.title, pr.description),
283 'pullrequest_title', pr.title, pr.description),
295 'description': h.escape(pr.description),
284 'description': h.escape(pr.description),
296 'updated_on': _render('pullrequest_updated_on',
285 'updated_on': _render('pullrequest_updated_on',
297 h.datetime_to_time(pr.updated_on)),
286 h.datetime_to_time(pr.updated_on)),
298 'updated_on_raw': h.datetime_to_time(pr.updated_on),
287 'updated_on_raw': h.datetime_to_time(pr.updated_on),
299 'created_on': _render('pullrequest_updated_on',
288 'created_on': _render('pullrequest_updated_on',
300 h.datetime_to_time(pr.created_on)),
289 h.datetime_to_time(pr.created_on)),
301 'created_on_raw': h.datetime_to_time(pr.created_on),
290 'created_on_raw': h.datetime_to_time(pr.created_on),
302 'author': _render('pullrequest_author',
291 'author': _render('pullrequest_author',
303 pr.author.full_contact, ),
292 pr.author.full_contact, ),
304 'author_raw': pr.author.full_name,
293 'author_raw': pr.author.full_name,
305 'comments': _render('pullrequest_comments', len(comments)),
294 'comments': _render('pullrequest_comments', len(comments)),
306 'comments_raw': len(comments),
295 'comments_raw': len(comments),
307 'closed': pr.is_closed(),
296 'closed': pr.is_closed(),
308 'owned': owned
297 'owned': owned
309 })
298 })
310 # json used to render the grid
299 # json used to render the grid
311 data = ({
300 data = ({
312 'data': data,
301 'data': data,
313 'recordsTotal': pull_requests_total_count,
302 'recordsTotal': pull_requests_total_count,
314 'recordsFiltered': pull_requests_total_count,
303 'recordsFiltered': pull_requests_total_count,
315 })
304 })
316 return data
305 return data
317
306
318 def my_account_pullrequests(self):
307 def my_account_pullrequests(self):
319 c.active = 'pullrequests'
308 c.active = 'pullrequests'
320 self.__load_data()
309 self.__load_data()
321 c.show_closed = str2bool(request.GET.get('pr_show_closed'))
310 c.show_closed = str2bool(request.GET.get('pr_show_closed'))
322
311
323 statuses = [PullRequest.STATUS_NEW, PullRequest.STATUS_OPEN]
312 statuses = [PullRequest.STATUS_NEW, PullRequest.STATUS_OPEN]
324 if c.show_closed:
313 if c.show_closed:
325 statuses += [PullRequest.STATUS_CLOSED]
314 statuses += [PullRequest.STATUS_CLOSED]
326 data = self._get_pull_requests_list(statuses)
315 data = self._get_pull_requests_list(statuses)
327 if not request.is_xhr:
316 if not request.is_xhr:
328 c.data_participate = json.dumps(data['data'])
317 c.data_participate = json.dumps(data['data'])
329 c.records_total_participate = data['recordsTotal']
318 c.records_total_participate = data['recordsTotal']
330 return render('admin/my_account/my_account.mako')
319 return render('admin/my_account/my_account.mako')
331 else:
320 else:
332 return json.dumps(data)
321 return json.dumps(data)
333
322
334 def my_notifications(self):
323 def my_notifications(self):
335 c.active = 'notifications'
324 c.active = 'notifications'
336 return render('admin/my_account/my_account.mako')
325 return render('admin/my_account/my_account.mako')
337
326
338 @auth.CSRFRequired()
327 @auth.CSRFRequired()
339 @jsonify
328 @jsonify
340 def my_notifications_toggle_visibility(self):
329 def my_notifications_toggle_visibility(self):
341 user = c.rhodecode_user.get_instance()
330 user = c.rhodecode_user.get_instance()
342 new_status = not user.user_data.get('notification_status', True)
331 new_status = not user.user_data.get('notification_status', True)
343 user.update_userdata(notification_status=new_status)
332 user.update_userdata(notification_status=new_status)
344 Session().commit()
333 Session().commit()
345 return user.user_data['notification_status']
334 return user.user_data['notification_status']
346
335
347 @auth.CSRFRequired()
336 @auth.CSRFRequired()
348 @jsonify
337 @jsonify
349 def my_account_notifications_test_channelstream(self):
338 def my_account_notifications_test_channelstream(self):
350 message = 'Test message sent via Channelstream by user: {}, on {}'.format(
339 message = 'Test message sent via Channelstream by user: {}, on {}'.format(
351 c.rhodecode_user.username, datetime.datetime.now())
340 c.rhodecode_user.username, datetime.datetime.now())
352 payload = {
341 payload = {
353 'type': 'message',
342 'type': 'message',
354 'timestamp': datetime.datetime.utcnow(),
343 'timestamp': datetime.datetime.utcnow(),
355 'user': 'system',
344 'user': 'system',
356 #'channel': 'broadcast',
345 #'channel': 'broadcast',
357 'pm_users': [c.rhodecode_user.username],
346 'pm_users': [c.rhodecode_user.username],
358 'message': {
347 'message': {
359 'message': message,
348 'message': message,
360 'level': 'info',
349 'level': 'info',
361 'topic': '/notifications'
350 'topic': '/notifications'
362 }
351 }
363 }
352 }
364
353
365 registry = get_current_registry()
354 registry = get_current_registry()
366 rhodecode_plugins = getattr(registry, 'rhodecode_plugins', {})
355 rhodecode_plugins = getattr(registry, 'rhodecode_plugins', {})
367 channelstream_config = rhodecode_plugins.get('channelstream', {})
356 channelstream_config = rhodecode_plugins.get('channelstream', {})
368
357
369 try:
358 try:
370 channelstream_request(channelstream_config, [payload], '/message')
359 channelstream_request(channelstream_config, [payload], '/message')
371 except ChannelstreamException as e:
360 except ChannelstreamException as e:
372 log.exception('Failed to send channelstream data')
361 log.exception('Failed to send channelstream data')
373 return {"response": 'ERROR: {}'.format(e.__class__.__name__)}
362 return {"response": 'ERROR: {}'.format(e.__class__.__name__)}
374 return {"response": 'Channelstream data sent. '
363 return {"response": 'Channelstream data sent. '
375 'You should see a new live message now.'}
364 'You should see a new live message now.'}
@@ -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.url('my_account')}">${_('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.url('my_account_emails')}">${_('Emails')}</a></li>
37 <li class="${'active' if c.active=='emails' else ''}"><a href="${h.url('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.url('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.url('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.url('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.url('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,113 +1,113 b''
1 <%namespace name="base" file="/base/base.mako"/>
1 <%namespace name="base" file="/base/base.mako"/>
2 <div class="panel panel-default user-profile">
2 <div class="panel panel-default user-profile">
3 <div class="panel-heading">
3 <div class="panel-heading">
4 <h3 class="panel-title">${_('My Profile')}</h3>
4 <h3 class="panel-title">${_('My Profile')}</h3>
5 <a href="${url('my_account')}" class="panel-edit">Close</a>
5 <a href="${h.route_path('my_account_profile')}" class="panel-edit">Close</a>
6 </div>
6 </div>
7
7
8 <div class="panel-body">
8 <div class="panel-body">
9 ${h.secure_form(url('my_account'), method='post', class_='form')}
9 ${h.secure_form(url('my_account'), method='post', class_='form')}
10 <% readonly = None %>
10 <% readonly = None %>
11 <% disabled = "" %>
11 <% disabled = "" %>
12
12
13 % if c.extern_type != 'rhodecode':
13 % if c.extern_type != 'rhodecode':
14 <% readonly = "readonly" %>
14 <% readonly = "readonly" %>
15 <% disabled = "disabled" %>
15 <% disabled = "disabled" %>
16 <div class="infoform">
16 <div class="infoform">
17 <div class="fields">
17 <div class="fields">
18 <p>${_('Your user account details are managed by an external source. Details cannot be managed here.')}
18 <p>${_('Your user account details are managed by an external source. Details cannot be managed here.')}
19 <br/>${_('Source type')}: <strong>${c.extern_type}</strong>
19 <br/>${_('Source type')}: <strong>${c.extern_type}</strong>
20 </p>
20 </p>
21
21
22 <div class="field">
22 <div class="field">
23 <div class="label">
23 <div class="label">
24 <label for="username">${_('Username')}:</label>
24 <label for="username">${_('Username')}:</label>
25 </div>
25 </div>
26 <div class="input">
26 <div class="input">
27 ${h.text('username', class_='input-valuedisplay', readonly=readonly)}
27 ${h.text('username', class_='input-valuedisplay', readonly=readonly)}
28 </div>
28 </div>
29 </div>
29 </div>
30
30
31 <div class="field">
31 <div class="field">
32 <div class="label">
32 <div class="label">
33 <label for="name">${_('First Name')}:</label>
33 <label for="name">${_('First Name')}:</label>
34 </div>
34 </div>
35 <div class="input">
35 <div class="input">
36 ${h.text('firstname', class_='input-valuedisplay', readonly=readonly)}
36 ${h.text('firstname', class_='input-valuedisplay', readonly=readonly)}
37 </div>
37 </div>
38 </div>
38 </div>
39
39
40 <div class="field">
40 <div class="field">
41 <div class="label">
41 <div class="label">
42 <label for="lastname">${_('Last Name')}:</label>
42 <label for="lastname">${_('Last Name')}:</label>
43 </div>
43 </div>
44 <div class="input-valuedisplay">
44 <div class="input-valuedisplay">
45 ${h.text('lastname', class_='input-valuedisplay', readonly=readonly)}
45 ${h.text('lastname', class_='input-valuedisplay', readonly=readonly)}
46 </div>
46 </div>
47 </div>
47 </div>
48 </div>
48 </div>
49 </div>
49 </div>
50 % else:
50 % else:
51 <div class="form">
51 <div class="form">
52 <div class="fields">
52 <div class="fields">
53 <div class="field">
53 <div class="field">
54 <div class="label photo">
54 <div class="label photo">
55 ${_('Photo')}:
55 ${_('Photo')}:
56 </div>
56 </div>
57 <div class="input profile">
57 <div class="input profile">
58 %if c.visual.use_gravatar:
58 %if c.visual.use_gravatar:
59 ${base.gravatar(c.user.email, 100)}
59 ${base.gravatar(c.user.email, 100)}
60 <p class="help-block">${_('Change your avatar at')} <a href="http://gravatar.com">gravatar.com</a>.</p>
60 <p class="help-block">${_('Change your avatar at')} <a href="http://gravatar.com">gravatar.com</a>.</p>
61 %else:
61 %else:
62 ${base.gravatar(c.user.email, 20)}
62 ${base.gravatar(c.user.email, 20)}
63 ${_('Avatars are disabled')}
63 ${_('Avatars are disabled')}
64 %endif
64 %endif
65 </div>
65 </div>
66 </div>
66 </div>
67 <div class="field">
67 <div class="field">
68 <div class="label">
68 <div class="label">
69 <label for="username">${_('Username')}:</label>
69 <label for="username">${_('Username')}:</label>
70 </div>
70 </div>
71 <div class="input">
71 <div class="input">
72 ${h.text('username', class_='medium%s' % disabled, readonly=readonly)}
72 ${h.text('username', class_='medium%s' % disabled, readonly=readonly)}
73 ${h.hidden('extern_name', c.extern_name)}
73 ${h.hidden('extern_name', c.extern_name)}
74 ${h.hidden('extern_type', c.extern_type)}
74 ${h.hidden('extern_type', c.extern_type)}
75 </div>
75 </div>
76 </div>
76 </div>
77 <div class="field">
77 <div class="field">
78 <div class="label">
78 <div class="label">
79 <label for="name">${_('First Name')}:</label>
79 <label for="name">${_('First Name')}:</label>
80 </div>
80 </div>
81 <div class="input">
81 <div class="input">
82 ${h.text('firstname', class_="medium")}
82 ${h.text('firstname', class_="medium")}
83 </div>
83 </div>
84 </div>
84 </div>
85
85
86 <div class="field">
86 <div class="field">
87 <div class="label">
87 <div class="label">
88 <label for="lastname">${_('Last Name')}:</label>
88 <label for="lastname">${_('Last Name')}:</label>
89 </div>
89 </div>
90 <div class="input">
90 <div class="input">
91 ${h.text('lastname', class_="medium")}
91 ${h.text('lastname', class_="medium")}
92 </div>
92 </div>
93 </div>
93 </div>
94
94
95 <div class="field">
95 <div class="field">
96 <div class="label">
96 <div class="label">
97 <label for="email">${_('Email')}:</label>
97 <label for="email">${_('Email')}:</label>
98 </div>
98 </div>
99 <div class="input">
99 <div class="input">
100 ## we should be able to edit email !
100 ## we should be able to edit email !
101 ${h.text('email', class_="medium")}
101 ${h.text('email', class_="medium")}
102 </div>
102 </div>
103 </div>
103 </div>
104
104
105 <div class="buttons">
105 <div class="buttons">
106 ${h.submit('save', _('Save'), class_="btn")}
106 ${h.submit('save', _('Save'), class_="btn")}
107 ${h.reset('reset', _('Reset'), class_="btn")}
107 ${h.reset('reset', _('Reset'), class_="btn")}
108 </div>
108 </div>
109 </div>
109 </div>
110 </div>
110 </div>
111 % endif
111 % endif
112 </div>
112 </div>
113 </div> No newline at end of file
113 </div>
@@ -1,601 +1,601 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="root.mako"/>
2 <%inherit file="root.mako"/>
3
3
4 <div class="outerwrapper">
4 <div class="outerwrapper">
5 <!-- HEADER -->
5 <!-- HEADER -->
6 <div class="header">
6 <div class="header">
7 <div id="header-inner" class="wrapper">
7 <div id="header-inner" class="wrapper">
8 <div id="logo">
8 <div id="logo">
9 <div class="logo-wrapper">
9 <div class="logo-wrapper">
10 <a href="${h.url('home')}"><img src="${h.asset('images/rhodecode-logo-white-216x60.png')}" alt="RhodeCode"/></a>
10 <a href="${h.url('home')}"><img src="${h.asset('images/rhodecode-logo-white-216x60.png')}" alt="RhodeCode"/></a>
11 </div>
11 </div>
12 %if c.rhodecode_name:
12 %if c.rhodecode_name:
13 <div class="branding">- ${h.branding(c.rhodecode_name)}</div>
13 <div class="branding">- ${h.branding(c.rhodecode_name)}</div>
14 %endif
14 %endif
15 </div>
15 </div>
16 <!-- MENU BAR NAV -->
16 <!-- MENU BAR NAV -->
17 ${self.menu_bar_nav()}
17 ${self.menu_bar_nav()}
18 <!-- END MENU BAR NAV -->
18 <!-- END MENU BAR NAV -->
19 </div>
19 </div>
20 </div>
20 </div>
21 ${self.menu_bar_subnav()}
21 ${self.menu_bar_subnav()}
22 <!-- END HEADER -->
22 <!-- END HEADER -->
23
23
24 <!-- CONTENT -->
24 <!-- CONTENT -->
25 <div id="content" class="wrapper">
25 <div id="content" class="wrapper">
26
26
27 <rhodecode-toast id="notifications"></rhodecode-toast>
27 <rhodecode-toast id="notifications"></rhodecode-toast>
28
28
29 <div class="main">
29 <div class="main">
30 ${next.main()}
30 ${next.main()}
31 </div>
31 </div>
32 </div>
32 </div>
33 <!-- END CONTENT -->
33 <!-- END CONTENT -->
34
34
35 </div>
35 </div>
36 <!-- FOOTER -->
36 <!-- FOOTER -->
37 <div id="footer">
37 <div id="footer">
38 <div id="footer-inner" class="title wrapper">
38 <div id="footer-inner" class="title wrapper">
39 <div>
39 <div>
40 <p class="footer-link-right">
40 <p class="footer-link-right">
41 % if c.visual.show_version:
41 % if c.visual.show_version:
42 RhodeCode Enterprise ${c.rhodecode_version} ${c.rhodecode_edition}
42 RhodeCode Enterprise ${c.rhodecode_version} ${c.rhodecode_edition}
43 % endif
43 % endif
44 &copy; 2010-${h.datetime.today().year}, <a href="${h.url('rhodecode_official')}" target="_blank">RhodeCode GmbH</a>. All rights reserved.
44 &copy; 2010-${h.datetime.today().year}, <a href="${h.url('rhodecode_official')}" target="_blank">RhodeCode GmbH</a>. All rights reserved.
45 % if c.visual.rhodecode_support_url:
45 % if c.visual.rhodecode_support_url:
46 <a href="${c.visual.rhodecode_support_url}" target="_blank">${_('Support')}</a>
46 <a href="${c.visual.rhodecode_support_url}" target="_blank">${_('Support')}</a>
47 % endif
47 % endif
48 </p>
48 </p>
49 <% sid = 'block' if request.GET.get('showrcid') else 'none' %>
49 <% sid = 'block' if request.GET.get('showrcid') else 'none' %>
50 <p class="server-instance" style="display:${sid}">
50 <p class="server-instance" style="display:${sid}">
51 ## display hidden instance ID if specially defined
51 ## display hidden instance ID if specially defined
52 % if c.rhodecode_instanceid:
52 % if c.rhodecode_instanceid:
53 ${_('RhodeCode instance id: %s') % c.rhodecode_instanceid}
53 ${_('RhodeCode instance id: %s') % c.rhodecode_instanceid}
54 % endif
54 % endif
55 </p>
55 </p>
56 </div>
56 </div>
57 </div>
57 </div>
58 </div>
58 </div>
59
59
60 <!-- END FOOTER -->
60 <!-- END FOOTER -->
61
61
62 ### MAKO DEFS ###
62 ### MAKO DEFS ###
63
63
64 <%def name="menu_bar_subnav()">
64 <%def name="menu_bar_subnav()">
65 </%def>
65 </%def>
66
66
67 <%def name="breadcrumbs(class_='breadcrumbs')">
67 <%def name="breadcrumbs(class_='breadcrumbs')">
68 <div class="${class_}">
68 <div class="${class_}">
69 ${self.breadcrumbs_links()}
69 ${self.breadcrumbs_links()}
70 </div>
70 </div>
71 </%def>
71 </%def>
72
72
73 <%def name="admin_menu()">
73 <%def name="admin_menu()">
74 <ul class="admin_menu submenu">
74 <ul class="admin_menu submenu">
75 <li><a href="${h.url('admin_home')}">${_('Admin journal')}</a></li>
75 <li><a href="${h.url('admin_home')}">${_('Admin journal')}</a></li>
76 <li><a href="${h.url('repos')}">${_('Repositories')}</a></li>
76 <li><a href="${h.url('repos')}">${_('Repositories')}</a></li>
77 <li><a href="${h.url('repo_groups')}">${_('Repository groups')}</a></li>
77 <li><a href="${h.url('repo_groups')}">${_('Repository groups')}</a></li>
78 <li><a href="${h.route_path('users')}">${_('Users')}</a></li>
78 <li><a href="${h.route_path('users')}">${_('Users')}</a></li>
79 <li><a href="${h.url('users_groups')}">${_('User groups')}</a></li>
79 <li><a href="${h.url('users_groups')}">${_('User groups')}</a></li>
80 <li><a href="${h.url('admin_permissions_application')}">${_('Permissions')}</a></li>
80 <li><a href="${h.url('admin_permissions_application')}">${_('Permissions')}</a></li>
81 <li><a href="${h.route_path('auth_home', traverse='')}">${_('Authentication')}</a></li>
81 <li><a href="${h.route_path('auth_home', traverse='')}">${_('Authentication')}</a></li>
82 <li><a href="${h.route_path('global_integrations_home')}">${_('Integrations')}</a></li>
82 <li><a href="${h.route_path('global_integrations_home')}">${_('Integrations')}</a></li>
83 <li><a href="${h.url('admin_defaults_repositories')}">${_('Defaults')}</a></li>
83 <li><a href="${h.url('admin_defaults_repositories')}">${_('Defaults')}</a></li>
84 <li class="last"><a href="${h.url('admin_settings')}">${_('Settings')}</a></li>
84 <li class="last"><a href="${h.url('admin_settings')}">${_('Settings')}</a></li>
85 </ul>
85 </ul>
86 </%def>
86 </%def>
87
87
88
88
89 <%def name="dt_info_panel(elements)">
89 <%def name="dt_info_panel(elements)">
90 <dl class="dl-horizontal">
90 <dl class="dl-horizontal">
91 %for dt, dd, title, show_items in elements:
91 %for dt, dd, title, show_items in elements:
92 <dt>${dt}:</dt>
92 <dt>${dt}:</dt>
93 <dd title="${title}">
93 <dd title="${title}">
94 %if callable(dd):
94 %if callable(dd):
95 ## allow lazy evaluation of elements
95 ## allow lazy evaluation of elements
96 ${dd()}
96 ${dd()}
97 %else:
97 %else:
98 ${dd}
98 ${dd}
99 %endif
99 %endif
100 %if show_items:
100 %if show_items:
101 <span class="btn-collapse" data-toggle="item-${h.md5_safe(dt)[:6]}-details">${_('Show More')} </span>
101 <span class="btn-collapse" data-toggle="item-${h.md5_safe(dt)[:6]}-details">${_('Show More')} </span>
102 %endif
102 %endif
103 </dd>
103 </dd>
104
104
105 %if show_items:
105 %if show_items:
106 <div class="collapsable-content" data-toggle="item-${h.md5_safe(dt)[:6]}-details" style="display: none">
106 <div class="collapsable-content" data-toggle="item-${h.md5_safe(dt)[:6]}-details" style="display: none">
107 %for item in show_items:
107 %for item in show_items:
108 <dt></dt>
108 <dt></dt>
109 <dd>${item}</dd>
109 <dd>${item}</dd>
110 %endfor
110 %endfor
111 </div>
111 </div>
112 %endif
112 %endif
113
113
114 %endfor
114 %endfor
115 </dl>
115 </dl>
116 </%def>
116 </%def>
117
117
118
118
119 <%def name="gravatar(email, size=16)">
119 <%def name="gravatar(email, size=16)">
120 <%
120 <%
121 if (size > 16):
121 if (size > 16):
122 gravatar_class = 'gravatar gravatar-large'
122 gravatar_class = 'gravatar gravatar-large'
123 else:
123 else:
124 gravatar_class = 'gravatar'
124 gravatar_class = 'gravatar'
125 %>
125 %>
126 <%doc>
126 <%doc>
127 TODO: johbo: For now we serve double size images to make it smooth
127 TODO: johbo: For now we serve double size images to make it smooth
128 for retina. This is how it worked until now. Should be replaced
128 for retina. This is how it worked until now. Should be replaced
129 with a better solution at some point.
129 with a better solution at some point.
130 </%doc>
130 </%doc>
131 <img class="${gravatar_class}" src="${h.gravatar_url(email, size * 2)}" height="${size}" width="${size}">
131 <img class="${gravatar_class}" src="${h.gravatar_url(email, size * 2)}" height="${size}" width="${size}">
132 </%def>
132 </%def>
133
133
134
134
135 <%def name="gravatar_with_user(contact, size=16, show_disabled=False)">
135 <%def name="gravatar_with_user(contact, size=16, show_disabled=False)">
136 <% email = h.email_or_none(contact) %>
136 <% email = h.email_or_none(contact) %>
137 <div class="rc-user tooltip" title="${h.author_string(email)}">
137 <div class="rc-user tooltip" title="${h.author_string(email)}">
138 ${self.gravatar(email, size)}
138 ${self.gravatar(email, size)}
139 <span class="${'user user-disabled' if show_disabled else 'user'}"> ${h.link_to_user(contact)}</span>
139 <span class="${'user user-disabled' if show_disabled else 'user'}"> ${h.link_to_user(contact)}</span>
140 </div>
140 </div>
141 </%def>
141 </%def>
142
142
143
143
144 ## admin menu used for people that have some admin resources
144 ## admin menu used for people that have some admin resources
145 <%def name="admin_menu_simple(repositories=None, repository_groups=None, user_groups=None)">
145 <%def name="admin_menu_simple(repositories=None, repository_groups=None, user_groups=None)">
146 <ul class="submenu">
146 <ul class="submenu">
147 %if repositories:
147 %if repositories:
148 <li class="local-admin-repos"><a href="${h.url('repos')}">${_('Repositories')}</a></li>
148 <li class="local-admin-repos"><a href="${h.url('repos')}">${_('Repositories')}</a></li>
149 %endif
149 %endif
150 %if repository_groups:
150 %if repository_groups:
151 <li class="local-admin-repo-groups"><a href="${h.url('repo_groups')}">${_('Repository groups')}</a></li>
151 <li class="local-admin-repo-groups"><a href="${h.url('repo_groups')}">${_('Repository groups')}</a></li>
152 %endif
152 %endif
153 %if user_groups:
153 %if user_groups:
154 <li class="local-admin-user-groups"><a href="${h.url('users_groups')}">${_('User groups')}</a></li>
154 <li class="local-admin-user-groups"><a href="${h.url('users_groups')}">${_('User groups')}</a></li>
155 %endif
155 %endif
156 </ul>
156 </ul>
157 </%def>
157 </%def>
158
158
159 <%def name="repo_page_title(repo_instance)">
159 <%def name="repo_page_title(repo_instance)">
160 <div class="title-content">
160 <div class="title-content">
161 <div class="title-main">
161 <div class="title-main">
162 ## SVN/HG/GIT icons
162 ## SVN/HG/GIT icons
163 %if h.is_hg(repo_instance):
163 %if h.is_hg(repo_instance):
164 <i class="icon-hg"></i>
164 <i class="icon-hg"></i>
165 %endif
165 %endif
166 %if h.is_git(repo_instance):
166 %if h.is_git(repo_instance):
167 <i class="icon-git"></i>
167 <i class="icon-git"></i>
168 %endif
168 %endif
169 %if h.is_svn(repo_instance):
169 %if h.is_svn(repo_instance):
170 <i class="icon-svn"></i>
170 <i class="icon-svn"></i>
171 %endif
171 %endif
172
172
173 ## public/private
173 ## public/private
174 %if repo_instance.private:
174 %if repo_instance.private:
175 <i class="icon-repo-private"></i>
175 <i class="icon-repo-private"></i>
176 %else:
176 %else:
177 <i class="icon-repo-public"></i>
177 <i class="icon-repo-public"></i>
178 %endif
178 %endif
179
179
180 ## repo name with group name
180 ## repo name with group name
181 ${h.breadcrumb_repo_link(c.rhodecode_db_repo)}
181 ${h.breadcrumb_repo_link(c.rhodecode_db_repo)}
182
182
183 </div>
183 </div>
184
184
185 ## FORKED
185 ## FORKED
186 %if repo_instance.fork:
186 %if repo_instance.fork:
187 <p>
187 <p>
188 <i class="icon-code-fork"></i> ${_('Fork of')}
188 <i class="icon-code-fork"></i> ${_('Fork of')}
189 <a href="${h.url('summary_home',repo_name=repo_instance.fork.repo_name)}">${repo_instance.fork.repo_name}</a>
189 <a href="${h.url('summary_home',repo_name=repo_instance.fork.repo_name)}">${repo_instance.fork.repo_name}</a>
190 </p>
190 </p>
191 %endif
191 %endif
192
192
193 ## IMPORTED FROM REMOTE
193 ## IMPORTED FROM REMOTE
194 %if repo_instance.clone_uri:
194 %if repo_instance.clone_uri:
195 <p>
195 <p>
196 <i class="icon-code-fork"></i> ${_('Clone from')}
196 <i class="icon-code-fork"></i> ${_('Clone from')}
197 <a href="${h.url(h.safe_str(h.hide_credentials(repo_instance.clone_uri)))}">${h.hide_credentials(repo_instance.clone_uri)}</a>
197 <a href="${h.url(h.safe_str(h.hide_credentials(repo_instance.clone_uri)))}">${h.hide_credentials(repo_instance.clone_uri)}</a>
198 </p>
198 </p>
199 %endif
199 %endif
200
200
201 ## LOCKING STATUS
201 ## LOCKING STATUS
202 %if repo_instance.locked[0]:
202 %if repo_instance.locked[0]:
203 <p class="locking_locked">
203 <p class="locking_locked">
204 <i class="icon-repo-lock"></i>
204 <i class="icon-repo-lock"></i>
205 ${_('Repository locked by %(user)s') % {'user': h.person_by_id(repo_instance.locked[0])}}
205 ${_('Repository locked by %(user)s') % {'user': h.person_by_id(repo_instance.locked[0])}}
206 </p>
206 </p>
207 %elif repo_instance.enable_locking:
207 %elif repo_instance.enable_locking:
208 <p class="locking_unlocked">
208 <p class="locking_unlocked">
209 <i class="icon-repo-unlock"></i>
209 <i class="icon-repo-unlock"></i>
210 ${_('Repository not locked. Pull repository to lock it.')}
210 ${_('Repository not locked. Pull repository to lock it.')}
211 </p>
211 </p>
212 %endif
212 %endif
213
213
214 </div>
214 </div>
215 </%def>
215 </%def>
216
216
217 <%def name="repo_menu(active=None)">
217 <%def name="repo_menu(active=None)">
218 <%
218 <%
219 def is_active(selected):
219 def is_active(selected):
220 if selected == active:
220 if selected == active:
221 return "active"
221 return "active"
222 %>
222 %>
223
223
224 <!--- CONTEXT BAR -->
224 <!--- CONTEXT BAR -->
225 <div id="context-bar">
225 <div id="context-bar">
226 <div class="wrapper">
226 <div class="wrapper">
227 <ul id="context-pages" class="horizontal-list navigation">
227 <ul id="context-pages" class="horizontal-list navigation">
228 <li class="${is_active('summary')}"><a class="menulink" href="${h.url('summary_home', repo_name=c.repo_name)}"><div class="menulabel">${_('Summary')}</div></a></li>
228 <li class="${is_active('summary')}"><a class="menulink" href="${h.url('summary_home', repo_name=c.repo_name)}"><div class="menulabel">${_('Summary')}</div></a></li>
229 <li class="${is_active('changelog')}"><a class="menulink" href="${h.url('changelog_home', repo_name=c.repo_name)}"><div class="menulabel">${_('Changelog')}</div></a></li>
229 <li class="${is_active('changelog')}"><a class="menulink" href="${h.url('changelog_home', repo_name=c.repo_name)}"><div class="menulabel">${_('Changelog')}</div></a></li>
230 <li class="${is_active('files')}"><a class="menulink" href="${h.url('files_home', repo_name=c.repo_name, revision=c.rhodecode_db_repo.landing_rev[1])}"><div class="menulabel">${_('Files')}</div></a></li>
230 <li class="${is_active('files')}"><a class="menulink" href="${h.url('files_home', repo_name=c.repo_name, revision=c.rhodecode_db_repo.landing_rev[1])}"><div class="menulabel">${_('Files')}</div></a></li>
231 <li class="${is_active('compare')}">
231 <li class="${is_active('compare')}">
232 <a class="menulink" href="${h.url('compare_home',repo_name=c.repo_name)}"><div class="menulabel">${_('Compare')}</div></a>
232 <a class="menulink" href="${h.url('compare_home',repo_name=c.repo_name)}"><div class="menulabel">${_('Compare')}</div></a>
233 </li>
233 </li>
234 ## TODO: anderson: ideally it would have a function on the scm_instance "enable_pullrequest() and enable_fork()"
234 ## TODO: anderson: ideally it would have a function on the scm_instance "enable_pullrequest() and enable_fork()"
235 %if c.rhodecode_db_repo.repo_type in ['git','hg']:
235 %if c.rhodecode_db_repo.repo_type in ['git','hg']:
236 <li class="${is_active('showpullrequest')}">
236 <li class="${is_active('showpullrequest')}">
237 <a class="menulink" href="${h.url('pullrequest_show_all',repo_name=c.repo_name)}" title="${_('Show Pull Requests for %s') % c.repo_name}">
237 <a class="menulink" href="${h.url('pullrequest_show_all',repo_name=c.repo_name)}" title="${_('Show Pull Requests for %s') % c.repo_name}">
238 %if c.repository_pull_requests:
238 %if c.repository_pull_requests:
239 <span class="pr_notifications">${c.repository_pull_requests}</span>
239 <span class="pr_notifications">${c.repository_pull_requests}</span>
240 %endif
240 %endif
241 <div class="menulabel">${_('Pull Requests')}</div>
241 <div class="menulabel">${_('Pull Requests')}</div>
242 </a>
242 </a>
243 </li>
243 </li>
244 %endif
244 %endif
245 <li class="${is_active('options')}">
245 <li class="${is_active('options')}">
246 <a class="menulink" href="#" class="dropdown"><div class="menulabel">${_('Options')} <div class="show_more"></div></div></a>
246 <a class="menulink" href="#" class="dropdown"><div class="menulabel">${_('Options')} <div class="show_more"></div></div></a>
247 <ul class="submenu">
247 <ul class="submenu">
248 %if h.HasRepoPermissionAll('repository.admin')(c.repo_name):
248 %if h.HasRepoPermissionAll('repository.admin')(c.repo_name):
249 <li><a href="${h.url('edit_repo',repo_name=c.repo_name)}">${_('Settings')}</a></li>
249 <li><a href="${h.url('edit_repo',repo_name=c.repo_name)}">${_('Settings')}</a></li>
250 %endif
250 %endif
251 %if c.rhodecode_db_repo.fork:
251 %if c.rhodecode_db_repo.fork:
252 <li><a href="${h.url('compare_url',repo_name=c.rhodecode_db_repo.fork.repo_name,source_ref_type=c.rhodecode_db_repo.landing_rev[0],source_ref=c.rhodecode_db_repo.landing_rev[1], target_repo=c.repo_name,target_ref_type='branch' if request.GET.get('branch') else c.rhodecode_db_repo.landing_rev[0],target_ref=request.GET.get('branch') or c.rhodecode_db_repo.landing_rev[1], merge=1)}">
252 <li><a href="${h.url('compare_url',repo_name=c.rhodecode_db_repo.fork.repo_name,source_ref_type=c.rhodecode_db_repo.landing_rev[0],source_ref=c.rhodecode_db_repo.landing_rev[1], target_repo=c.repo_name,target_ref_type='branch' if request.GET.get('branch') else c.rhodecode_db_repo.landing_rev[0],target_ref=request.GET.get('branch') or c.rhodecode_db_repo.landing_rev[1], merge=1)}">
253 ${_('Compare fork')}</a></li>
253 ${_('Compare fork')}</a></li>
254 %endif
254 %endif
255
255
256 <li><a href="${h.url('search_repo_home',repo_name=c.repo_name)}">${_('Search')}</a></li>
256 <li><a href="${h.url('search_repo_home',repo_name=c.repo_name)}">${_('Search')}</a></li>
257
257
258 %if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name) and c.rhodecode_db_repo.enable_locking:
258 %if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name) and c.rhodecode_db_repo.enable_locking:
259 %if c.rhodecode_db_repo.locked[0]:
259 %if c.rhodecode_db_repo.locked[0]:
260 <li><a class="locking_del" href="${h.url('toggle_locking',repo_name=c.repo_name)}">${_('Unlock')}</a></li>
260 <li><a class="locking_del" href="${h.url('toggle_locking',repo_name=c.repo_name)}">${_('Unlock')}</a></li>
261 %else:
261 %else:
262 <li><a class="locking_add" href="${h.url('toggle_locking',repo_name=c.repo_name)}">${_('Lock')}</a></li>
262 <li><a class="locking_add" href="${h.url('toggle_locking',repo_name=c.repo_name)}">${_('Lock')}</a></li>
263 %endif
263 %endif
264 %endif
264 %endif
265 %if c.rhodecode_user.username != h.DEFAULT_USER:
265 %if c.rhodecode_user.username != h.DEFAULT_USER:
266 %if c.rhodecode_db_repo.repo_type in ['git','hg']:
266 %if c.rhodecode_db_repo.repo_type in ['git','hg']:
267 <li><a href="${h.url('repo_fork_home',repo_name=c.repo_name)}">${_('Fork')}</a></li>
267 <li><a href="${h.url('repo_fork_home',repo_name=c.repo_name)}">${_('Fork')}</a></li>
268 <li><a href="${h.url('pullrequest_home',repo_name=c.repo_name)}">${_('Create Pull Request')}</a></li>
268 <li><a href="${h.url('pullrequest_home',repo_name=c.repo_name)}">${_('Create Pull Request')}</a></li>
269 %endif
269 %endif
270 %endif
270 %endif
271 </ul>
271 </ul>
272 </li>
272 </li>
273 </ul>
273 </ul>
274 </div>
274 </div>
275 <div class="clear"></div>
275 <div class="clear"></div>
276 </div>
276 </div>
277 <!--- END CONTEXT BAR -->
277 <!--- END CONTEXT BAR -->
278
278
279 </%def>
279 </%def>
280
280
281 <%def name="usermenu(active=False)">
281 <%def name="usermenu(active=False)">
282 ## USER MENU
282 ## USER MENU
283 <li id="quick_login_li" class="${'active' if active else ''}">
283 <li id="quick_login_li" class="${'active' if active else ''}">
284 <a id="quick_login_link" class="menulink childs">
284 <a id="quick_login_link" class="menulink childs">
285 ${gravatar(c.rhodecode_user.email, 20)}
285 ${gravatar(c.rhodecode_user.email, 20)}
286 <span class="user">
286 <span class="user">
287 %if c.rhodecode_user.username != h.DEFAULT_USER:
287 %if c.rhodecode_user.username != h.DEFAULT_USER:
288 <span class="menu_link_user">${c.rhodecode_user.username}</span><div class="show_more"></div>
288 <span class="menu_link_user">${c.rhodecode_user.username}</span><div class="show_more"></div>
289 %else:
289 %else:
290 <span>${_('Sign in')}</span>
290 <span>${_('Sign in')}</span>
291 %endif
291 %endif
292 </span>
292 </span>
293 </a>
293 </a>
294
294
295 <div class="user-menu submenu">
295 <div class="user-menu submenu">
296 <div id="quick_login">
296 <div id="quick_login">
297 %if c.rhodecode_user.username == h.DEFAULT_USER:
297 %if c.rhodecode_user.username == h.DEFAULT_USER:
298 <h4>${_('Sign in to your account')}</h4>
298 <h4>${_('Sign in to your account')}</h4>
299 ${h.form(h.route_path('login', _query={'came_from': h.url.current()}), needs_csrf_token=False)}
299 ${h.form(h.route_path('login', _query={'came_from': h.url.current()}), needs_csrf_token=False)}
300 <div class="form form-vertical">
300 <div class="form form-vertical">
301 <div class="fields">
301 <div class="fields">
302 <div class="field">
302 <div class="field">
303 <div class="label">
303 <div class="label">
304 <label for="username">${_('Username')}:</label>
304 <label for="username">${_('Username')}:</label>
305 </div>
305 </div>
306 <div class="input">
306 <div class="input">
307 ${h.text('username',class_='focus',tabindex=1)}
307 ${h.text('username',class_='focus',tabindex=1)}
308 </div>
308 </div>
309
309
310 </div>
310 </div>
311 <div class="field">
311 <div class="field">
312 <div class="label">
312 <div class="label">
313 <label for="password">${_('Password')}:</label>
313 <label for="password">${_('Password')}:</label>
314 %if h.HasPermissionAny('hg.password_reset.enabled')():
314 %if h.HasPermissionAny('hg.password_reset.enabled')():
315 <span class="forgot_password">${h.link_to(_('(Forgot password?)'),h.route_path('reset_password'), class_='pwd_reset')}</span>
315 <span class="forgot_password">${h.link_to(_('(Forgot password?)'),h.route_path('reset_password'), class_='pwd_reset')}</span>
316 %endif
316 %endif
317 </div>
317 </div>
318 <div class="input">
318 <div class="input">
319 ${h.password('password',class_='focus',tabindex=2)}
319 ${h.password('password',class_='focus',tabindex=2)}
320 </div>
320 </div>
321 </div>
321 </div>
322 <div class="buttons">
322 <div class="buttons">
323 <div class="register">
323 <div class="register">
324 %if h.HasPermissionAny('hg.admin', 'hg.register.auto_activate', 'hg.register.manual_activate')():
324 %if h.HasPermissionAny('hg.admin', 'hg.register.auto_activate', 'hg.register.manual_activate')():
325 ${h.link_to(_("Don't have an account ?"),h.route_path('register'))}
325 ${h.link_to(_("Don't have an account ?"),h.route_path('register'))}
326 %endif
326 %endif
327 </div>
327 </div>
328 <div class="submit">
328 <div class="submit">
329 ${h.submit('sign_in',_('Sign In'),class_="btn btn-small",tabindex=3)}
329 ${h.submit('sign_in',_('Sign In'),class_="btn btn-small",tabindex=3)}
330 </div>
330 </div>
331 </div>
331 </div>
332 </div>
332 </div>
333 </div>
333 </div>
334 ${h.end_form()}
334 ${h.end_form()}
335 %else:
335 %else:
336 <div class="">
336 <div class="">
337 <div class="big_gravatar">${gravatar(c.rhodecode_user.email, 48)}</div>
337 <div class="big_gravatar">${gravatar(c.rhodecode_user.email, 48)}</div>
338 <div class="full_name">${c.rhodecode_user.full_name_or_username}</div>
338 <div class="full_name">${c.rhodecode_user.full_name_or_username}</div>
339 <div class="email">${c.rhodecode_user.email}</div>
339 <div class="email">${c.rhodecode_user.email}</div>
340 </div>
340 </div>
341 <div class="">
341 <div class="">
342 <ol class="links">
342 <ol class="links">
343 <li>${h.link_to(_(u'My account'),h.url('my_account'))}</li>
343 <li>${h.link_to(_(u'My account'),h.route_path('my_account_profile'))}</li>
344 % if c.rhodecode_user.personal_repo_group:
344 % if c.rhodecode_user.personal_repo_group:
345 <li>${h.link_to(_(u'My personal group'), h.url('repo_group_home', group_name=c.rhodecode_user.personal_repo_group.group_name))}</li>
345 <li>${h.link_to(_(u'My personal group'), h.url('repo_group_home', group_name=c.rhodecode_user.personal_repo_group.group_name))}</li>
346 % endif
346 % endif
347 <li class="logout">
347 <li class="logout">
348 ${h.secure_form(h.route_path('logout'))}
348 ${h.secure_form(h.route_path('logout'))}
349 ${h.submit('log_out', _(u'Sign Out'),class_="btn btn-primary")}
349 ${h.submit('log_out', _(u'Sign Out'),class_="btn btn-primary")}
350 ${h.end_form()}
350 ${h.end_form()}
351 </li>
351 </li>
352 </ol>
352 </ol>
353 </div>
353 </div>
354 %endif
354 %endif
355 </div>
355 </div>
356 </div>
356 </div>
357 %if c.rhodecode_user.username != h.DEFAULT_USER:
357 %if c.rhodecode_user.username != h.DEFAULT_USER:
358 <div class="pill_container">
358 <div class="pill_container">
359 % if c.unread_notifications == 0:
359 % if c.unread_notifications == 0:
360 <a class="menu_link_notifications empty" href="${h.url('notifications')}">${c.unread_notifications}</a>
360 <a class="menu_link_notifications empty" href="${h.url('notifications')}">${c.unread_notifications}</a>
361 % else:
361 % else:
362 <a class="menu_link_notifications" href="${h.url('notifications')}">${c.unread_notifications}</a>
362 <a class="menu_link_notifications" href="${h.url('notifications')}">${c.unread_notifications}</a>
363 % endif
363 % endif
364 </div>
364 </div>
365 % endif
365 % endif
366 </li>
366 </li>
367 </%def>
367 </%def>
368
368
369 <%def name="menu_items(active=None)">
369 <%def name="menu_items(active=None)">
370 <%
370 <%
371 def is_active(selected):
371 def is_active(selected):
372 if selected == active:
372 if selected == active:
373 return "active"
373 return "active"
374 return ""
374 return ""
375 %>
375 %>
376 <ul id="quick" class="main_nav navigation horizontal-list">
376 <ul id="quick" class="main_nav navigation horizontal-list">
377 <!-- repo switcher -->
377 <!-- repo switcher -->
378 <li class="${is_active('repositories')} repo_switcher_li has_select2">
378 <li class="${is_active('repositories')} repo_switcher_li has_select2">
379 <input id="repo_switcher" name="repo_switcher" type="hidden">
379 <input id="repo_switcher" name="repo_switcher" type="hidden">
380 </li>
380 </li>
381
381
382 ## ROOT MENU
382 ## ROOT MENU
383 %if c.rhodecode_user.username != h.DEFAULT_USER:
383 %if c.rhodecode_user.username != h.DEFAULT_USER:
384 <li class="${is_active('journal')}">
384 <li class="${is_active('journal')}">
385 <a class="menulink" title="${_('Show activity journal')}" href="${h.url('journal')}">
385 <a class="menulink" title="${_('Show activity journal')}" href="${h.url('journal')}">
386 <div class="menulabel">${_('Journal')}</div>
386 <div class="menulabel">${_('Journal')}</div>
387 </a>
387 </a>
388 </li>
388 </li>
389 %else:
389 %else:
390 <li class="${is_active('journal')}">
390 <li class="${is_active('journal')}">
391 <a class="menulink" title="${_('Show Public activity journal')}" href="${h.url('public_journal')}">
391 <a class="menulink" title="${_('Show Public activity journal')}" href="${h.url('public_journal')}">
392 <div class="menulabel">${_('Public journal')}</div>
392 <div class="menulabel">${_('Public journal')}</div>
393 </a>
393 </a>
394 </li>
394 </li>
395 %endif
395 %endif
396 <li class="${is_active('gists')}">
396 <li class="${is_active('gists')}">
397 <a class="menulink childs" title="${_('Show Gists')}" href="${h.url('gists')}">
397 <a class="menulink childs" title="${_('Show Gists')}" href="${h.url('gists')}">
398 <div class="menulabel">${_('Gists')}</div>
398 <div class="menulabel">${_('Gists')}</div>
399 </a>
399 </a>
400 </li>
400 </li>
401 <li class="${is_active('search')}">
401 <li class="${is_active('search')}">
402 <a class="menulink" title="${_('Search in repositories you have access to')}" href="${h.url('search')}">
402 <a class="menulink" title="${_('Search in repositories you have access to')}" href="${h.url('search')}">
403 <div class="menulabel">${_('Search')}</div>
403 <div class="menulabel">${_('Search')}</div>
404 </a>
404 </a>
405 </li>
405 </li>
406 % if h.HasPermissionAll('hg.admin')('access admin main page'):
406 % if h.HasPermissionAll('hg.admin')('access admin main page'):
407 <li class="${is_active('admin')}">
407 <li class="${is_active('admin')}">
408 <a class="menulink childs" title="${_('Admin settings')}" href="#" onclick="return false;">
408 <a class="menulink childs" title="${_('Admin settings')}" href="#" onclick="return false;">
409 <div class="menulabel">${_('Admin')} <div class="show_more"></div></div>
409 <div class="menulabel">${_('Admin')} <div class="show_more"></div></div>
410 </a>
410 </a>
411 ${admin_menu()}
411 ${admin_menu()}
412 </li>
412 </li>
413 % elif c.rhodecode_user.repositories_admin or c.rhodecode_user.repository_groups_admin or c.rhodecode_user.user_groups_admin:
413 % elif c.rhodecode_user.repositories_admin or c.rhodecode_user.repository_groups_admin or c.rhodecode_user.user_groups_admin:
414 <li class="${is_active('admin')}">
414 <li class="${is_active('admin')}">
415 <a class="menulink childs" title="${_('Delegated Admin settings')}">
415 <a class="menulink childs" title="${_('Delegated Admin settings')}">
416 <div class="menulabel">${_('Admin')} <div class="show_more"></div></div>
416 <div class="menulabel">${_('Admin')} <div class="show_more"></div></div>
417 </a>
417 </a>
418 ${admin_menu_simple(c.rhodecode_user.repositories_admin,
418 ${admin_menu_simple(c.rhodecode_user.repositories_admin,
419 c.rhodecode_user.repository_groups_admin,
419 c.rhodecode_user.repository_groups_admin,
420 c.rhodecode_user.user_groups_admin or h.HasPermissionAny('hg.usergroup.create.true')())}
420 c.rhodecode_user.user_groups_admin or h.HasPermissionAny('hg.usergroup.create.true')())}
421 </li>
421 </li>
422 % endif
422 % endif
423 % if c.debug_style:
423 % if c.debug_style:
424 <li class="${is_active('debug_style')}">
424 <li class="${is_active('debug_style')}">
425 <a class="menulink" title="${_('Style')}" href="${h.url('debug_style_home')}">
425 <a class="menulink" title="${_('Style')}" href="${h.url('debug_style_home')}">
426 <div class="menulabel">${_('Style')}</div>
426 <div class="menulabel">${_('Style')}</div>
427 </a>
427 </a>
428 </li>
428 </li>
429 % endif
429 % endif
430 ## render extra user menu
430 ## render extra user menu
431 ${usermenu(active=(active=='my_account'))}
431 ${usermenu(active=(active=='my_account'))}
432 </ul>
432 </ul>
433
433
434 <script type="text/javascript">
434 <script type="text/javascript">
435 var visual_show_public_icon = "${c.visual.show_public_icon}" == "True";
435 var visual_show_public_icon = "${c.visual.show_public_icon}" == "True";
436
436
437 /*format the look of items in the list*/
437 /*format the look of items in the list*/
438 var format = function(state, escapeMarkup){
438 var format = function(state, escapeMarkup){
439 if (!state.id){
439 if (!state.id){
440 return state.text; // optgroup
440 return state.text; // optgroup
441 }
441 }
442 var obj_dict = state.obj;
442 var obj_dict = state.obj;
443 var tmpl = '';
443 var tmpl = '';
444
444
445 if(obj_dict && state.type == 'repo'){
445 if(obj_dict && state.type == 'repo'){
446 if(obj_dict['repo_type'] === 'hg'){
446 if(obj_dict['repo_type'] === 'hg'){
447 tmpl += '<i class="icon-hg"></i> ';
447 tmpl += '<i class="icon-hg"></i> ';
448 }
448 }
449 else if(obj_dict['repo_type'] === 'git'){
449 else if(obj_dict['repo_type'] === 'git'){
450 tmpl += '<i class="icon-git"></i> ';
450 tmpl += '<i class="icon-git"></i> ';
451 }
451 }
452 else if(obj_dict['repo_type'] === 'svn'){
452 else if(obj_dict['repo_type'] === 'svn'){
453 tmpl += '<i class="icon-svn"></i> ';
453 tmpl += '<i class="icon-svn"></i> ';
454 }
454 }
455 if(obj_dict['private']){
455 if(obj_dict['private']){
456 tmpl += '<i class="icon-lock" ></i> ';
456 tmpl += '<i class="icon-lock" ></i> ';
457 }
457 }
458 else if(visual_show_public_icon){
458 else if(visual_show_public_icon){
459 tmpl += '<i class="icon-unlock-alt"></i> ';
459 tmpl += '<i class="icon-unlock-alt"></i> ';
460 }
460 }
461 }
461 }
462 if(obj_dict && state.type == 'commit') {
462 if(obj_dict && state.type == 'commit') {
463 tmpl += '<i class="icon-tag"></i>';
463 tmpl += '<i class="icon-tag"></i>';
464 }
464 }
465 if(obj_dict && state.type == 'group'){
465 if(obj_dict && state.type == 'group'){
466 tmpl += '<i class="icon-folder-close"></i> ';
466 tmpl += '<i class="icon-folder-close"></i> ';
467 }
467 }
468 tmpl += escapeMarkup(state.text);
468 tmpl += escapeMarkup(state.text);
469 return tmpl;
469 return tmpl;
470 };
470 };
471
471
472 var formatResult = function(result, container, query, escapeMarkup) {
472 var formatResult = function(result, container, query, escapeMarkup) {
473 return format(result, escapeMarkup);
473 return format(result, escapeMarkup);
474 };
474 };
475
475
476 var formatSelection = function(data, container, escapeMarkup) {
476 var formatSelection = function(data, container, escapeMarkup) {
477 return format(data, escapeMarkup);
477 return format(data, escapeMarkup);
478 };
478 };
479
479
480 $("#repo_switcher").select2({
480 $("#repo_switcher").select2({
481 cachedDataSource: {},
481 cachedDataSource: {},
482 minimumInputLength: 2,
482 minimumInputLength: 2,
483 placeholder: '<div class="menulabel">${_('Go to')} <div class="show_more"></div></div>',
483 placeholder: '<div class="menulabel">${_('Go to')} <div class="show_more"></div></div>',
484 dropdownAutoWidth: true,
484 dropdownAutoWidth: true,
485 formatResult: formatResult,
485 formatResult: formatResult,
486 formatSelection: formatSelection,
486 formatSelection: formatSelection,
487 containerCssClass: "repo-switcher",
487 containerCssClass: "repo-switcher",
488 dropdownCssClass: "repo-switcher-dropdown",
488 dropdownCssClass: "repo-switcher-dropdown",
489 escapeMarkup: function(m){
489 escapeMarkup: function(m){
490 // don't escape our custom placeholder
490 // don't escape our custom placeholder
491 if(m.substr(0,23) == '<div class="menulabel">'){
491 if(m.substr(0,23) == '<div class="menulabel">'){
492 return m;
492 return m;
493 }
493 }
494
494
495 return Select2.util.escapeMarkup(m);
495 return Select2.util.escapeMarkup(m);
496 },
496 },
497 query: $.debounce(250, function(query){
497 query: $.debounce(250, function(query){
498 self = this;
498 self = this;
499 var cacheKey = query.term;
499 var cacheKey = query.term;
500 var cachedData = self.cachedDataSource[cacheKey];
500 var cachedData = self.cachedDataSource[cacheKey];
501
501
502 if (cachedData) {
502 if (cachedData) {
503 query.callback({results: cachedData.results});
503 query.callback({results: cachedData.results});
504 } else {
504 } else {
505 $.ajax({
505 $.ajax({
506 url: "${h.url('goto_switcher_data')}",
506 url: "${h.url('goto_switcher_data')}",
507 data: {'query': query.term},
507 data: {'query': query.term},
508 dataType: 'json',
508 dataType: 'json',
509 type: 'GET',
509 type: 'GET',
510 success: function(data) {
510 success: function(data) {
511 self.cachedDataSource[cacheKey] = data;
511 self.cachedDataSource[cacheKey] = data;
512 query.callback({results: data.results});
512 query.callback({results: data.results});
513 },
513 },
514 error: function(data, textStatus, errorThrown) {
514 error: function(data, textStatus, errorThrown) {
515 alert("Error while fetching entries.\nError code {0} ({1}).".format(data.status, data.statusText));
515 alert("Error while fetching entries.\nError code {0} ({1}).".format(data.status, data.statusText));
516 }
516 }
517 })
517 })
518 }
518 }
519 })
519 })
520 });
520 });
521
521
522 $("#repo_switcher").on('select2-selecting', function(e){
522 $("#repo_switcher").on('select2-selecting', function(e){
523 e.preventDefault();
523 e.preventDefault();
524 window.location = e.choice.url;
524 window.location = e.choice.url;
525 });
525 });
526
526
527 </script>
527 </script>
528 <script src="${h.asset('js/rhodecode/base/keyboard-bindings.js', ver=c.rhodecode_version_hash)}"></script>
528 <script src="${h.asset('js/rhodecode/base/keyboard-bindings.js', ver=c.rhodecode_version_hash)}"></script>
529 </%def>
529 </%def>
530
530
531 <div class="modal" id="help_kb" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
531 <div class="modal" id="help_kb" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
532 <div class="modal-dialog">
532 <div class="modal-dialog">
533 <div class="modal-content">
533 <div class="modal-content">
534 <div class="modal-header">
534 <div class="modal-header">
535 <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
535 <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
536 <h4 class="modal-title" id="myModalLabel">${_('Keyboard shortcuts')}</h4>
536 <h4 class="modal-title" id="myModalLabel">${_('Keyboard shortcuts')}</h4>
537 </div>
537 </div>
538 <div class="modal-body">
538 <div class="modal-body">
539 <div class="block-left">
539 <div class="block-left">
540 <table class="keyboard-mappings">
540 <table class="keyboard-mappings">
541 <tbody>
541 <tbody>
542 <tr>
542 <tr>
543 <th></th>
543 <th></th>
544 <th>${_('Site-wide shortcuts')}</th>
544 <th>${_('Site-wide shortcuts')}</th>
545 </tr>
545 </tr>
546 <%
546 <%
547 elems = [
547 elems = [
548 ('/', 'Open quick search box'),
548 ('/', 'Open quick search box'),
549 ('g h', 'Goto home page'),
549 ('g h', 'Goto home page'),
550 ('g g', 'Goto my private gists page'),
550 ('g g', 'Goto my private gists page'),
551 ('g G', 'Goto my public gists page'),
551 ('g G', 'Goto my public gists page'),
552 ('n r', 'New repository page'),
552 ('n r', 'New repository page'),
553 ('n g', 'New gist page'),
553 ('n g', 'New gist page'),
554 ]
554 ]
555 %>
555 %>
556 %for key, desc in elems:
556 %for key, desc in elems:
557 <tr>
557 <tr>
558 <td class="keys">
558 <td class="keys">
559 <span class="key tag">${key}</span>
559 <span class="key tag">${key}</span>
560 </td>
560 </td>
561 <td>${desc}</td>
561 <td>${desc}</td>
562 </tr>
562 </tr>
563 %endfor
563 %endfor
564 </tbody>
564 </tbody>
565 </table>
565 </table>
566 </div>
566 </div>
567 <div class="block-left">
567 <div class="block-left">
568 <table class="keyboard-mappings">
568 <table class="keyboard-mappings">
569 <tbody>
569 <tbody>
570 <tr>
570 <tr>
571 <th></th>
571 <th></th>
572 <th>${_('Repositories')}</th>
572 <th>${_('Repositories')}</th>
573 </tr>
573 </tr>
574 <%
574 <%
575 elems = [
575 elems = [
576 ('g s', 'Goto summary page'),
576 ('g s', 'Goto summary page'),
577 ('g c', 'Goto changelog page'),
577 ('g c', 'Goto changelog page'),
578 ('g f', 'Goto files page'),
578 ('g f', 'Goto files page'),
579 ('g F', 'Goto files page with file search activated'),
579 ('g F', 'Goto files page with file search activated'),
580 ('g p', 'Goto pull requests page'),
580 ('g p', 'Goto pull requests page'),
581 ('g o', 'Goto repository settings'),
581 ('g o', 'Goto repository settings'),
582 ('g O', 'Goto repository permissions settings'),
582 ('g O', 'Goto repository permissions settings'),
583 ]
583 ]
584 %>
584 %>
585 %for key, desc in elems:
585 %for key, desc in elems:
586 <tr>
586 <tr>
587 <td class="keys">
587 <td class="keys">
588 <span class="key tag">${key}</span>
588 <span class="key tag">${key}</span>
589 </td>
589 </td>
590 <td>${desc}</td>
590 <td>${desc}</td>
591 </tr>
591 </tr>
592 %endfor
592 %endfor
593 </tbody>
593 </tbody>
594 </table>
594 </table>
595 </div>
595 </div>
596 </div>
596 </div>
597 <div class="modal-footer">
597 <div class="modal-footer">
598 </div>
598 </div>
599 </div><!-- /.modal-content -->
599 </div><!-- /.modal-content -->
600 </div><!-- /.modal-dialog -->
600 </div><!-- /.modal-dialog -->
601 </div><!-- /.modal -->
601 </div><!-- /.modal -->
@@ -1,253 +1,246 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, TEST_USER_REGULAR_EMAIL,
26 TestController, url, TEST_USER_ADMIN_LOGIN, TEST_USER_REGULAR_EMAIL,
27 assert_session_flash)
27 assert_session_flash)
28 from rhodecode.tests.fixture import Fixture
28 from rhodecode.tests.fixture import Fixture
29 from rhodecode.tests.utils import AssertResponse
29 from rhodecode.tests.utils import AssertResponse
30
30
31 fixture = Fixture()
31 fixture = Fixture()
32
32
33
33
34 class TestMyAccountController(TestController):
34 class TestMyAccountController(TestController):
35 test_user_1 = 'testme'
35 test_user_1 = 'testme'
36 test_user_1_password = '0jd83nHNS/d23n'
36 test_user_1_password = '0jd83nHNS/d23n'
37 destroy_users = set()
37 destroy_users = set()
38
38
39 @classmethod
39 @classmethod
40 def teardown_class(cls):
40 def teardown_class(cls):
41 fixture.destroy_users(cls.destroy_users)
41 fixture.destroy_users(cls.destroy_users)
42
42
43 def test_my_account(self):
44 self.log_user()
45 response = self.app.get(url('my_account'))
46
47 response.mustcontain('test_admin')
48 response.mustcontain('href="/_admin/my_account/edit"')
49
50 def test_logout_form_contains_csrf(self, autologin_user, csrf_token):
43 def test_logout_form_contains_csrf(self, autologin_user, csrf_token):
51 response = self.app.get(url('my_account'))
44 response = self.app.get(url('home'))
52 assert_response = AssertResponse(response)
45 assert_response = AssertResponse(response)
53 element = assert_response.get_element('.logout #csrf_token')
46 element = assert_response.get_element('.logout #csrf_token')
54 assert element.value == csrf_token
47 assert element.value == csrf_token
55
48
56 def test_my_account_edit(self):
49 def test_my_account_edit(self):
57 self.log_user()
50 self.log_user()
58 response = self.app.get(url('my_account_edit'))
51 response = self.app.get(url('my_account_edit'))
59
52
60 response.mustcontain('value="test_admin')
53 response.mustcontain('value="test_admin')
61
54
62 def test_my_account_my_repos(self):
55 def test_my_account_my_repos(self):
63 self.log_user()
56 self.log_user()
64 response = self.app.get(url('my_account_repos'))
57 response = self.app.get(url('my_account_repos'))
65 repos = Repository.query().filter(
58 repos = Repository.query().filter(
66 Repository.user == User.get_by_username(
59 Repository.user == User.get_by_username(
67 TEST_USER_ADMIN_LOGIN)).all()
60 TEST_USER_ADMIN_LOGIN)).all()
68 for repo in repos:
61 for repo in repos:
69 response.mustcontain('"name_raw": "%s"' % repo.repo_name)
62 response.mustcontain('"name_raw": "%s"' % repo.repo_name)
70
63
71 def test_my_account_my_watched(self):
64 def test_my_account_my_watched(self):
72 self.log_user()
65 self.log_user()
73 response = self.app.get(url('my_account_watched'))
66 response = self.app.get(url('my_account_watched'))
74
67
75 repos = UserFollowing.query().filter(
68 repos = UserFollowing.query().filter(
76 UserFollowing.user == User.get_by_username(
69 UserFollowing.user == User.get_by_username(
77 TEST_USER_ADMIN_LOGIN)).all()
70 TEST_USER_ADMIN_LOGIN)).all()
78 for repo in repos:
71 for repo in repos:
79 response.mustcontain(
72 response.mustcontain(
80 '"name_raw": "%s"' % repo.follows_repository.repo_name)
73 '"name_raw": "%s"' % repo.follows_repository.repo_name)
81
74
82 @pytest.mark.backends("git", "hg")
75 @pytest.mark.backends("git", "hg")
83 def test_my_account_my_pullrequests(self, pr_util):
76 def test_my_account_my_pullrequests(self, pr_util):
84 self.log_user()
77 self.log_user()
85 response = self.app.get(url('my_account_pullrequests'))
78 response = self.app.get(url('my_account_pullrequests'))
86 response.mustcontain('There are currently no open pull '
79 response.mustcontain('There are currently no open pull '
87 'requests requiring your participation.')
80 'requests requiring your participation.')
88
81
89 pr = pr_util.create_pull_request(title='TestMyAccountPR')
82 pr = pr_util.create_pull_request(title='TestMyAccountPR')
90 response = self.app.get(url('my_account_pullrequests'))
83 response = self.app.get(url('my_account_pullrequests'))
91 response.mustcontain('"name_raw": %s' % pr.pull_request_id)
84 response.mustcontain('"name_raw": %s' % pr.pull_request_id)
92 response.mustcontain('TestMyAccountPR')
85 response.mustcontain('TestMyAccountPR')
93
86
94 def test_my_account_my_emails(self):
87 def test_my_account_my_emails(self):
95 self.log_user()
88 self.log_user()
96 response = self.app.get(url('my_account_emails'))
89 response = self.app.get(url('my_account_emails'))
97 response.mustcontain('No additional emails specified')
90 response.mustcontain('No additional emails specified')
98
91
99 def test_my_account_my_emails_add_existing_email(self):
92 def test_my_account_my_emails_add_existing_email(self):
100 self.log_user()
93 self.log_user()
101 response = self.app.get(url('my_account_emails'))
94 response = self.app.get(url('my_account_emails'))
102 response.mustcontain('No additional emails specified')
95 response.mustcontain('No additional emails specified')
103 response = self.app.post(url('my_account_emails'),
96 response = self.app.post(url('my_account_emails'),
104 {'new_email': TEST_USER_REGULAR_EMAIL,
97 {'new_email': TEST_USER_REGULAR_EMAIL,
105 'csrf_token': self.csrf_token})
98 'csrf_token': self.csrf_token})
106 assert_session_flash(response, 'This e-mail address is already taken')
99 assert_session_flash(response, 'This e-mail address is already taken')
107
100
108 def test_my_account_my_emails_add_mising_email_in_form(self):
101 def test_my_account_my_emails_add_mising_email_in_form(self):
109 self.log_user()
102 self.log_user()
110 response = self.app.get(url('my_account_emails'))
103 response = self.app.get(url('my_account_emails'))
111 response.mustcontain('No additional emails specified')
104 response.mustcontain('No additional emails specified')
112 response = self.app.post(url('my_account_emails'),
105 response = self.app.post(url('my_account_emails'),
113 {'csrf_token': self.csrf_token})
106 {'csrf_token': self.csrf_token})
114 assert_session_flash(response, 'Please enter an email address')
107 assert_session_flash(response, 'Please enter an email address')
115
108
116 def test_my_account_my_emails_add_remove(self):
109 def test_my_account_my_emails_add_remove(self):
117 self.log_user()
110 self.log_user()
118 response = self.app.get(url('my_account_emails'))
111 response = self.app.get(url('my_account_emails'))
119 response.mustcontain('No additional emails specified')
112 response.mustcontain('No additional emails specified')
120
113
121 response = self.app.post(url('my_account_emails'),
114 response = self.app.post(url('my_account_emails'),
122 {'new_email': 'foo@barz.com',
115 {'new_email': 'foo@barz.com',
123 'csrf_token': self.csrf_token})
116 'csrf_token': self.csrf_token})
124
117
125 response = self.app.get(url('my_account_emails'))
118 response = self.app.get(url('my_account_emails'))
126
119
127 from rhodecode.model.db import UserEmailMap
120 from rhodecode.model.db import UserEmailMap
128 email_id = UserEmailMap.query().filter(
121 email_id = UserEmailMap.query().filter(
129 UserEmailMap.user == User.get_by_username(
122 UserEmailMap.user == User.get_by_username(
130 TEST_USER_ADMIN_LOGIN)).filter(
123 TEST_USER_ADMIN_LOGIN)).filter(
131 UserEmailMap.email == 'foo@barz.com').one().email_id
124 UserEmailMap.email == 'foo@barz.com').one().email_id
132
125
133 response.mustcontain('foo@barz.com')
126 response.mustcontain('foo@barz.com')
134 response.mustcontain('<input id="del_email_id" name="del_email_id" '
127 response.mustcontain('<input id="del_email_id" name="del_email_id" '
135 'type="hidden" value="%s" />' % email_id)
128 'type="hidden" value="%s" />' % email_id)
136
129
137 response = self.app.post(
130 response = self.app.post(
138 url('my_account_emails'), {
131 url('my_account_emails'), {
139 'del_email_id': email_id, '_method': 'delete',
132 'del_email_id': email_id, '_method': 'delete',
140 'csrf_token': self.csrf_token})
133 'csrf_token': self.csrf_token})
141 assert_session_flash(response, 'Removed email address from user account')
134 assert_session_flash(response, 'Removed email address from user account')
142 response = self.app.get(url('my_account_emails'))
135 response = self.app.get(url('my_account_emails'))
143 response.mustcontain('No additional emails specified')
136 response.mustcontain('No additional emails specified')
144
137
145 @pytest.mark.parametrize(
138 @pytest.mark.parametrize(
146 "name, attrs", [
139 "name, attrs", [
147 ('firstname', {'firstname': 'new_username'}),
140 ('firstname', {'firstname': 'new_username'}),
148 ('lastname', {'lastname': 'new_username'}),
141 ('lastname', {'lastname': 'new_username'}),
149 ('admin', {'admin': True}),
142 ('admin', {'admin': True}),
150 ('admin', {'admin': False}),
143 ('admin', {'admin': False}),
151 ('extern_type', {'extern_type': 'ldap'}),
144 ('extern_type', {'extern_type': 'ldap'}),
152 ('extern_type', {'extern_type': None}),
145 ('extern_type', {'extern_type': None}),
153 # ('extern_name', {'extern_name': 'test'}),
146 # ('extern_name', {'extern_name': 'test'}),
154 # ('extern_name', {'extern_name': None}),
147 # ('extern_name', {'extern_name': None}),
155 ('active', {'active': False}),
148 ('active', {'active': False}),
156 ('active', {'active': True}),
149 ('active', {'active': True}),
157 ('email', {'email': 'some@email.com'}),
150 ('email', {'email': 'some@email.com'}),
158 ])
151 ])
159 def test_my_account_update(self, name, attrs):
152 def test_my_account_update(self, name, attrs):
160 usr = fixture.create_user(self.test_user_1,
153 usr = fixture.create_user(self.test_user_1,
161 password=self.test_user_1_password,
154 password=self.test_user_1_password,
162 email='testme@rhodecode.org',
155 email='testme@rhodecode.org',
163 extern_type='rhodecode',
156 extern_type='rhodecode',
164 extern_name=self.test_user_1,
157 extern_name=self.test_user_1,
165 skip_if_exists=True)
158 skip_if_exists=True)
166 self.destroy_users.add(self.test_user_1)
159 self.destroy_users.add(self.test_user_1)
167
160
168 params = usr.get_api_data() # current user data
161 params = usr.get_api_data() # current user data
169 user_id = usr.user_id
162 user_id = usr.user_id
170 self.log_user(
163 self.log_user(
171 username=self.test_user_1, password=self.test_user_1_password)
164 username=self.test_user_1, password=self.test_user_1_password)
172
165
173 params.update({'password_confirmation': ''})
166 params.update({'password_confirmation': ''})
174 params.update({'new_password': ''})
167 params.update({'new_password': ''})
175 params.update({'extern_type': 'rhodecode'})
168 params.update({'extern_type': 'rhodecode'})
176 params.update({'extern_name': self.test_user_1})
169 params.update({'extern_name': self.test_user_1})
177 params.update({'csrf_token': self.csrf_token})
170 params.update({'csrf_token': self.csrf_token})
178
171
179 params.update(attrs)
172 params.update(attrs)
180 # my account page cannot set language param yet, only for admins
173 # my account page cannot set language param yet, only for admins
181 del params['language']
174 del params['language']
182 response = self.app.post(url('my_account'), params)
175 response = self.app.post(url('my_account'), params)
183
176
184 assert_session_flash(
177 assert_session_flash(
185 response, 'Your account was updated successfully')
178 response, 'Your account was updated successfully')
186
179
187 del params['csrf_token']
180 del params['csrf_token']
188
181
189 updated_user = User.get_by_username(self.test_user_1)
182 updated_user = User.get_by_username(self.test_user_1)
190 updated_params = updated_user.get_api_data()
183 updated_params = updated_user.get_api_data()
191 updated_params.update({'password_confirmation': ''})
184 updated_params.update({'password_confirmation': ''})
192 updated_params.update({'new_password': ''})
185 updated_params.update({'new_password': ''})
193
186
194 params['last_login'] = updated_params['last_login']
187 params['last_login'] = updated_params['last_login']
195 # my account page cannot set language param yet, only for admins
188 # my account page cannot set language param yet, only for admins
196 # but we get this info from API anyway
189 # but we get this info from API anyway
197 params['language'] = updated_params['language']
190 params['language'] = updated_params['language']
198
191
199 if name == 'email':
192 if name == 'email':
200 params['emails'] = [attrs['email']]
193 params['emails'] = [attrs['email']]
201 if name == 'extern_type':
194 if name == 'extern_type':
202 # cannot update this via form, expected value is original one
195 # cannot update this via form, expected value is original one
203 params['extern_type'] = "rhodecode"
196 params['extern_type'] = "rhodecode"
204 if name == 'extern_name':
197 if name == 'extern_name':
205 # cannot update this via form, expected value is original one
198 # cannot update this via form, expected value is original one
206 params['extern_name'] = str(user_id)
199 params['extern_name'] = str(user_id)
207 if name == 'active':
200 if name == 'active':
208 # my account cannot deactivate account
201 # my account cannot deactivate account
209 params['active'] = True
202 params['active'] = True
210 if name == 'admin':
203 if name == 'admin':
211 # my account cannot make you an admin !
204 # my account cannot make you an admin !
212 params['admin'] = False
205 params['admin'] = False
213
206
214 assert params == updated_params
207 assert params == updated_params
215
208
216 def test_my_account_update_err_email_exists(self):
209 def test_my_account_update_err_email_exists(self):
217 self.log_user()
210 self.log_user()
218
211
219 new_email = 'test_regular@mail.com' # already exisitn email
212 new_email = 'test_regular@mail.com' # already exisitn email
220 response = self.app.post(url('my_account'),
213 response = self.app.post(url('my_account'),
221 params={
214 params={
222 'username': 'test_admin',
215 'username': 'test_admin',
223 'new_password': 'test12',
216 'new_password': 'test12',
224 'password_confirmation': 'test122',
217 'password_confirmation': 'test122',
225 'firstname': 'NewName',
218 'firstname': 'NewName',
226 'lastname': 'NewLastname',
219 'lastname': 'NewLastname',
227 'email': new_email,
220 'email': new_email,
228 'csrf_token': self.csrf_token,
221 'csrf_token': self.csrf_token,
229 })
222 })
230
223
231 response.mustcontain('This e-mail address is already taken')
224 response.mustcontain('This e-mail address is already taken')
232
225
233 def test_my_account_update_err(self):
226 def test_my_account_update_err(self):
234 self.log_user('test_regular2', 'test12')
227 self.log_user('test_regular2', 'test12')
235
228
236 new_email = 'newmail.pl'
229 new_email = 'newmail.pl'
237 response = self.app.post(url('my_account'),
230 response = self.app.post(url('my_account'),
238 params={
231 params={
239 'username': 'test_admin',
232 'username': 'test_admin',
240 'new_password': 'test12',
233 'new_password': 'test12',
241 'password_confirmation': 'test122',
234 'password_confirmation': 'test122',
242 'firstname': 'NewName',
235 'firstname': 'NewName',
243 'lastname': 'NewLastname',
236 'lastname': 'NewLastname',
244 'email': new_email,
237 'email': new_email,
245 'csrf_token': self.csrf_token,
238 'csrf_token': self.csrf_token,
246 })
239 })
247
240
248 response.mustcontain('An email address must contain a single @')
241 response.mustcontain('An email address must contain a single @')
249 from rhodecode.model import validators
242 from rhodecode.model import validators
250 msg = validators.ValidUsername(
243 msg = validators.ValidUsername(
251 edit=False, old_data={})._messages['username_exists']
244 edit=False, old_data={})._messages['username_exists']
252 msg = h.html_escape(msg % {'username': 'test_admin'})
245 msg = h.html_escape(msg % {'username': 'test_admin'})
253 response.mustcontain(u"%s" % msg)
246 response.mustcontain(u"%s" % msg)
General Comments 0
You need to be logged in to leave comments. Login now