##// END OF EJS Templates
admin: moved auth tokens into pyramid view....
marcink -
r1518:6474fb97 default
parent child Browse files
Show More
@@ -0,0 +1,141 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 #
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
21 import logging
22
23 from pyramid.httpexceptions import HTTPFound
24 from pyramid.view import view_config
25
26 from rhodecode.apps._base import BaseAppView
27 from rhodecode.lib.auth import (
28 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
29 from rhodecode.lib import helpers as h
30 from rhodecode.lib.utils import PartialRenderer
31 from rhodecode.lib.utils2 import safe_int
32 from rhodecode.model.auth_token import AuthTokenModel
33 from rhodecode.model.db import User
34 from rhodecode.model.meta import Session
35
36 log = logging.getLogger(__name__)
37
38
39 class AdminUsersView(BaseAppView):
40 ALLOW_SCOPED_TOKENS = False
41 """
42 This view has alternative version inside EE, if modified please take a look
43 in there as well.
44 """
45
46 def load_default_context(self):
47 c = self._get_local_tmpl_context()
48 c.auth_user = self.request.user
49 c.allow_scoped_tokens = self.ALLOW_SCOPED_TOKENS
50 self._register_global_c(c)
51 return c
52
53 def _redirect_for_default_user(self, username):
54 _ = self.request.translate
55 if username == User.DEFAULT_USER:
56 h.flash(_("You can't edit this user"), category='warning')
57 # TODO(marcink): redirect to 'users' admin panel once this
58 # is a pyramid view
59 raise HTTPFound('/')
60
61 @LoginRequired()
62 @HasPermissionAllDecorator('hg.admin')
63 @view_config(
64 route_name='edit_user_auth_tokens', request_method='GET',
65 renderer='rhodecode:templates/admin/users/user_edit.mako')
66 def auth_tokens(self):
67 _ = self.request.translate
68 c = self.load_default_context()
69
70 user_id = self.request.matchdict.get('user_id')
71 c.user = User.get_or_404(user_id, pyramid_exc=True)
72 self._redirect_for_default_user(c.user.username)
73
74 c.active = 'auth_tokens'
75
76 c.lifetime_values = [
77 (str(-1), _('forever')),
78 (str(5), _('5 minutes')),
79 (str(60), _('1 hour')),
80 (str(60 * 24), _('1 day')),
81 (str(60 * 24 * 30), _('1 month')),
82 ]
83 c.lifetime_options = [(c.lifetime_values, _("Lifetime"))]
84 c.role_values = [
85 (x, AuthTokenModel.cls._get_role_name(x))
86 for x in AuthTokenModel.cls.ROLES]
87 c.role_options = [(c.role_values, _("Role"))]
88 c.user_auth_tokens = AuthTokenModel().get_auth_tokens(
89 c.user.user_id, show_expired=True)
90 return self._get_template_context(c)
91
92 def maybe_attach_token_scope(self, token):
93 # implemented in EE edition
94 pass
95
96 @LoginRequired()
97 @HasPermissionAllDecorator('hg.admin')
98 @CSRFRequired()
99 @view_config(
100 route_name='edit_user_auth_tokens_add', request_method='POST')
101 def auth_tokens_add(self):
102 _ = self.request.translate
103 c = self.load_default_context()
104
105 user_id = self.request.matchdict.get('user_id')
106 c.user = User.get_or_404(user_id, pyramid_exc=True)
107 self._redirect_for_default_user(c.user.username)
108
109 lifetime = safe_int(self.request.POST.get('lifetime'), -1)
110 description = self.request.POST.get('description')
111 role = self.request.POST.get('role')
112
113 token = AuthTokenModel().create(
114 c.user.user_id, description, lifetime, role)
115 self.maybe_attach_token_scope(token)
116 Session().commit()
117
118 h.flash(_("Auth token successfully created"), category='success')
119 return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id))
120
121 @LoginRequired()
122 @HasPermissionAllDecorator('hg.admin')
123 @CSRFRequired()
124 @view_config(
125 route_name='edit_user_auth_tokens_delete', request_method='POST')
126 def auth_tokens_delete(self):
127 _ = self.request.translate
128 c = self.load_default_context()
129
130 user_id = self.request.matchdict.get('user_id')
131 c.user = User.get_or_404(user_id, pyramid_exc=True)
132 self._redirect_for_default_user(c.user.username)
133
134 del_auth_token = self.request.POST.get('del_auth_token')
135
136 if del_auth_token:
137 AuthTokenModel().delete(del_auth_token, c.user.user_id)
138 Session().commit()
139 h.flash(_("Auth token successfully deleted"), category='success')
140
141 return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id))
@@ -1,57 +1,68 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.admin.navigation import NavigationRegistry
22 from rhodecode.apps.admin.navigation import NavigationRegistry
23 from rhodecode.config.routing import ADMIN_PREFIX
23 from rhodecode.config.routing import ADMIN_PREFIX
24 from rhodecode.lib.utils2 import str2bool
24 from rhodecode.lib.utils2 import str2bool
25
25
26
26
27 def includeme(config):
27 def includeme(config):
28 settings = config.get_settings()
28 settings = config.get_settings()
29
29
30 # Create admin navigation registry and add it to the pyramid registry.
30 # Create admin navigation registry and add it to the pyramid registry.
31 labs_active = str2bool(settings.get('labs_settings_active', False))
31 labs_active = str2bool(settings.get('labs_settings_active', False))
32 navigation_registry = NavigationRegistry(labs_active=labs_active)
32 navigation_registry = NavigationRegistry(labs_active=labs_active)
33 config.registry.registerUtility(navigation_registry)
33 config.registry.registerUtility(navigation_registry)
34
34
35 config.add_route(
35 config.add_route(
36 name='admin_settings_open_source',
36 name='admin_settings_open_source',
37 pattern=ADMIN_PREFIX + '/settings/open_source')
37 pattern=ADMIN_PREFIX + '/settings/open_source')
38 config.add_route(
38 config.add_route(
39 name='admin_settings_vcs_svn_generate_cfg',
39 name='admin_settings_vcs_svn_generate_cfg',
40 pattern=ADMIN_PREFIX + '/settings/vcs/svn_generate_cfg')
40 pattern=ADMIN_PREFIX + '/settings/vcs/svn_generate_cfg')
41
41
42 config.add_route(
42 config.add_route(
43 name='admin_settings_system',
43 name='admin_settings_system',
44 pattern=ADMIN_PREFIX + '/settings/system')
44 pattern=ADMIN_PREFIX + '/settings/system')
45 config.add_route(
45 config.add_route(
46 name='admin_settings_system_update',
46 name='admin_settings_system_update',
47 pattern=ADMIN_PREFIX + '/settings/system/updates')
47 pattern=ADMIN_PREFIX + '/settings/system/updates')
48
48
49 config.add_route(
49 config.add_route(
50 name='admin_settings_sessions',
50 name='admin_settings_sessions',
51 pattern=ADMIN_PREFIX + '/settings/sessions')
51 pattern=ADMIN_PREFIX + '/settings/sessions')
52 config.add_route(
52 config.add_route(
53 name='admin_settings_sessions_cleanup',
53 name='admin_settings_sessions_cleanup',
54 pattern=ADMIN_PREFIX + '/settings/sessions/cleanup')
54 pattern=ADMIN_PREFIX + '/settings/sessions/cleanup')
55
55
56 # user auth tokens
57 config.add_route(
58 name='edit_user_auth_tokens',
59 pattern=ADMIN_PREFIX + '/users/{user_id:\d+}/edit/auth_tokens')
60 config.add_route(
61 name='edit_user_auth_tokens_add',
62 pattern=ADMIN_PREFIX + '/users/{user_id:\d+}/edit/auth_tokens/new')
63 config.add_route(
64 name='edit_user_auth_tokens_delete',
65 pattern=ADMIN_PREFIX + '/users/{user_id:\d+}/edit/auth_tokens/delete')
66
56 # Scan module for configuration decorators.
67 # Scan module for configuration decorators.
57 config.scan()
68 config.scan()
@@ -1,1163 +1,1156 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('users', '/users',
295 m.connect('users', '/users',
296 action='index', conditions={'method': ['GET']})
296 action='index', conditions={'method': ['GET']})
297 m.connect('new_user', '/users/new',
297 m.connect('new_user', '/users/new',
298 action='new', conditions={'method': ['GET']})
298 action='new', conditions={'method': ['GET']})
299 m.connect('update_user', '/users/{user_id}',
299 m.connect('update_user', '/users/{user_id}',
300 action='update', conditions={'method': ['PUT']})
300 action='update', conditions={'method': ['PUT']})
301 m.connect('delete_user', '/users/{user_id}',
301 m.connect('delete_user', '/users/{user_id}',
302 action='delete', conditions={'method': ['DELETE']})
302 action='delete', conditions={'method': ['DELETE']})
303 m.connect('edit_user', '/users/{user_id}/edit',
303 m.connect('edit_user', '/users/{user_id}/edit',
304 action='edit', conditions={'method': ['GET']}, jsroute=True)
304 action='edit', conditions={'method': ['GET']}, jsroute=True)
305 m.connect('user', '/users/{user_id}',
305 m.connect('user', '/users/{user_id}',
306 action='show', conditions={'method': ['GET']})
306 action='show', conditions={'method': ['GET']})
307 m.connect('force_password_reset_user', '/users/{user_id}/password_reset',
307 m.connect('force_password_reset_user', '/users/{user_id}/password_reset',
308 action='reset_password', conditions={'method': ['POST']})
308 action='reset_password', conditions={'method': ['POST']})
309 m.connect('create_personal_repo_group', '/users/{user_id}/create_repo_group',
309 m.connect('create_personal_repo_group', '/users/{user_id}/create_repo_group',
310 action='create_personal_repo_group', conditions={'method': ['POST']})
310 action='create_personal_repo_group', conditions={'method': ['POST']})
311
311
312 # EXTRAS USER ROUTES
312 # EXTRAS USER ROUTES
313 m.connect('edit_user_advanced', '/users/{user_id}/edit/advanced',
313 m.connect('edit_user_advanced', '/users/{user_id}/edit/advanced',
314 action='edit_advanced', conditions={'method': ['GET']})
314 action='edit_advanced', conditions={'method': ['GET']})
315 m.connect('edit_user_advanced', '/users/{user_id}/edit/advanced',
315 m.connect('edit_user_advanced', '/users/{user_id}/edit/advanced',
316 action='update_advanced', conditions={'method': ['PUT']})
316 action='update_advanced', conditions={'method': ['PUT']})
317
317
318 m.connect('edit_user_auth_tokens', '/users/{user_id}/edit/auth_tokens',
319 action='edit_auth_tokens', conditions={'method': ['GET']})
320 m.connect('edit_user_auth_tokens', '/users/{user_id}/edit/auth_tokens',
321 action='add_auth_token', conditions={'method': ['PUT']})
322 m.connect('edit_user_auth_tokens', '/users/{user_id}/edit/auth_tokens',
323 action='delete_auth_token', conditions={'method': ['DELETE']})
324
325 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',
326 action='edit_global_perms', conditions={'method': ['GET']})
319 action='edit_global_perms', conditions={'method': ['GET']})
327 m.connect('edit_user_global_perms', '/users/{user_id}/edit/global_permissions',
320 m.connect('edit_user_global_perms', '/users/{user_id}/edit/global_permissions',
328 action='update_global_perms', conditions={'method': ['PUT']})
321 action='update_global_perms', conditions={'method': ['PUT']})
329
322
330 m.connect('edit_user_perms_summary', '/users/{user_id}/edit/permissions_summary',
323 m.connect('edit_user_perms_summary', '/users/{user_id}/edit/permissions_summary',
331 action='edit_perms_summary', conditions={'method': ['GET']})
324 action='edit_perms_summary', conditions={'method': ['GET']})
332
325
333 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
326 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
334 action='edit_emails', conditions={'method': ['GET']})
327 action='edit_emails', conditions={'method': ['GET']})
335 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
328 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
336 action='add_email', conditions={'method': ['PUT']})
329 action='add_email', conditions={'method': ['PUT']})
337 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
330 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
338 action='delete_email', conditions={'method': ['DELETE']})
331 action='delete_email', conditions={'method': ['DELETE']})
339
332
340 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
333 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
341 action='edit_ips', conditions={'method': ['GET']})
334 action='edit_ips', conditions={'method': ['GET']})
342 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
335 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
343 action='add_ip', conditions={'method': ['PUT']})
336 action='add_ip', conditions={'method': ['PUT']})
344 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
337 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
345 action='delete_ip', conditions={'method': ['DELETE']})
338 action='delete_ip', conditions={'method': ['DELETE']})
346
339
347 # ADMIN USER GROUPS REST ROUTES
340 # ADMIN USER GROUPS REST ROUTES
348 with rmap.submapper(path_prefix=ADMIN_PREFIX,
341 with rmap.submapper(path_prefix=ADMIN_PREFIX,
349 controller='admin/user_groups') as m:
342 controller='admin/user_groups') as m:
350 m.connect('users_groups', '/user_groups',
343 m.connect('users_groups', '/user_groups',
351 action='create', conditions={'method': ['POST']})
344 action='create', conditions={'method': ['POST']})
352 m.connect('users_groups', '/user_groups',
345 m.connect('users_groups', '/user_groups',
353 action='index', conditions={'method': ['GET']})
346 action='index', conditions={'method': ['GET']})
354 m.connect('new_users_group', '/user_groups/new',
347 m.connect('new_users_group', '/user_groups/new',
355 action='new', conditions={'method': ['GET']})
348 action='new', conditions={'method': ['GET']})
356 m.connect('update_users_group', '/user_groups/{user_group_id}',
349 m.connect('update_users_group', '/user_groups/{user_group_id}',
357 action='update', conditions={'method': ['PUT']})
350 action='update', conditions={'method': ['PUT']})
358 m.connect('delete_users_group', '/user_groups/{user_group_id}',
351 m.connect('delete_users_group', '/user_groups/{user_group_id}',
359 action='delete', conditions={'method': ['DELETE']})
352 action='delete', conditions={'method': ['DELETE']})
360 m.connect('edit_users_group', '/user_groups/{user_group_id}/edit',
353 m.connect('edit_users_group', '/user_groups/{user_group_id}/edit',
361 action='edit', conditions={'method': ['GET']},
354 action='edit', conditions={'method': ['GET']},
362 function=check_user_group)
355 function=check_user_group)
363
356
364 # EXTRAS USER GROUP ROUTES
357 # EXTRAS USER GROUP ROUTES
365 m.connect('edit_user_group_global_perms',
358 m.connect('edit_user_group_global_perms',
366 '/user_groups/{user_group_id}/edit/global_permissions',
359 '/user_groups/{user_group_id}/edit/global_permissions',
367 action='edit_global_perms', conditions={'method': ['GET']})
360 action='edit_global_perms', conditions={'method': ['GET']})
368 m.connect('edit_user_group_global_perms',
361 m.connect('edit_user_group_global_perms',
369 '/user_groups/{user_group_id}/edit/global_permissions',
362 '/user_groups/{user_group_id}/edit/global_permissions',
370 action='update_global_perms', conditions={'method': ['PUT']})
363 action='update_global_perms', conditions={'method': ['PUT']})
371 m.connect('edit_user_group_perms_summary',
364 m.connect('edit_user_group_perms_summary',
372 '/user_groups/{user_group_id}/edit/permissions_summary',
365 '/user_groups/{user_group_id}/edit/permissions_summary',
373 action='edit_perms_summary', conditions={'method': ['GET']})
366 action='edit_perms_summary', conditions={'method': ['GET']})
374
367
375 m.connect('edit_user_group_perms',
368 m.connect('edit_user_group_perms',
376 '/user_groups/{user_group_id}/edit/permissions',
369 '/user_groups/{user_group_id}/edit/permissions',
377 action='edit_perms', conditions={'method': ['GET']})
370 action='edit_perms', conditions={'method': ['GET']})
378 m.connect('edit_user_group_perms',
371 m.connect('edit_user_group_perms',
379 '/user_groups/{user_group_id}/edit/permissions',
372 '/user_groups/{user_group_id}/edit/permissions',
380 action='update_perms', conditions={'method': ['PUT']})
373 action='update_perms', conditions={'method': ['PUT']})
381
374
382 m.connect('edit_user_group_advanced',
375 m.connect('edit_user_group_advanced',
383 '/user_groups/{user_group_id}/edit/advanced',
376 '/user_groups/{user_group_id}/edit/advanced',
384 action='edit_advanced', conditions={'method': ['GET']})
377 action='edit_advanced', conditions={'method': ['GET']})
385
378
386 m.connect('edit_user_group_members',
379 m.connect('edit_user_group_members',
387 '/user_groups/{user_group_id}/edit/members', jsroute=True,
380 '/user_groups/{user_group_id}/edit/members', jsroute=True,
388 action='user_group_members', conditions={'method': ['GET']})
381 action='user_group_members', conditions={'method': ['GET']})
389
382
390 # ADMIN PERMISSIONS ROUTES
383 # ADMIN PERMISSIONS ROUTES
391 with rmap.submapper(path_prefix=ADMIN_PREFIX,
384 with rmap.submapper(path_prefix=ADMIN_PREFIX,
392 controller='admin/permissions') as m:
385 controller='admin/permissions') as m:
393 m.connect('admin_permissions_application', '/permissions/application',
386 m.connect('admin_permissions_application', '/permissions/application',
394 action='permission_application_update', conditions={'method': ['POST']})
387 action='permission_application_update', conditions={'method': ['POST']})
395 m.connect('admin_permissions_application', '/permissions/application',
388 m.connect('admin_permissions_application', '/permissions/application',
396 action='permission_application', conditions={'method': ['GET']})
389 action='permission_application', conditions={'method': ['GET']})
397
390
398 m.connect('admin_permissions_global', '/permissions/global',
391 m.connect('admin_permissions_global', '/permissions/global',
399 action='permission_global_update', conditions={'method': ['POST']})
392 action='permission_global_update', conditions={'method': ['POST']})
400 m.connect('admin_permissions_global', '/permissions/global',
393 m.connect('admin_permissions_global', '/permissions/global',
401 action='permission_global', conditions={'method': ['GET']})
394 action='permission_global', conditions={'method': ['GET']})
402
395
403 m.connect('admin_permissions_object', '/permissions/object',
396 m.connect('admin_permissions_object', '/permissions/object',
404 action='permission_objects_update', conditions={'method': ['POST']})
397 action='permission_objects_update', conditions={'method': ['POST']})
405 m.connect('admin_permissions_object', '/permissions/object',
398 m.connect('admin_permissions_object', '/permissions/object',
406 action='permission_objects', conditions={'method': ['GET']})
399 action='permission_objects', conditions={'method': ['GET']})
407
400
408 m.connect('admin_permissions_ips', '/permissions/ips',
401 m.connect('admin_permissions_ips', '/permissions/ips',
409 action='permission_ips', conditions={'method': ['POST']})
402 action='permission_ips', conditions={'method': ['POST']})
410 m.connect('admin_permissions_ips', '/permissions/ips',
403 m.connect('admin_permissions_ips', '/permissions/ips',
411 action='permission_ips', conditions={'method': ['GET']})
404 action='permission_ips', conditions={'method': ['GET']})
412
405
413 m.connect('admin_permissions_overview', '/permissions/overview',
406 m.connect('admin_permissions_overview', '/permissions/overview',
414 action='permission_perms', conditions={'method': ['GET']})
407 action='permission_perms', conditions={'method': ['GET']})
415
408
416 # ADMIN DEFAULTS REST ROUTES
409 # ADMIN DEFAULTS REST ROUTES
417 with rmap.submapper(path_prefix=ADMIN_PREFIX,
410 with rmap.submapper(path_prefix=ADMIN_PREFIX,
418 controller='admin/defaults') as m:
411 controller='admin/defaults') as m:
419 m.connect('admin_defaults_repositories', '/defaults/repositories',
412 m.connect('admin_defaults_repositories', '/defaults/repositories',
420 action='update_repository_defaults', conditions={'method': ['POST']})
413 action='update_repository_defaults', conditions={'method': ['POST']})
421 m.connect('admin_defaults_repositories', '/defaults/repositories',
414 m.connect('admin_defaults_repositories', '/defaults/repositories',
422 action='index', conditions={'method': ['GET']})
415 action='index', conditions={'method': ['GET']})
423
416
424 # ADMIN DEBUG STYLE ROUTES
417 # ADMIN DEBUG STYLE ROUTES
425 if str2bool(config.get('debug_style')):
418 if str2bool(config.get('debug_style')):
426 with rmap.submapper(path_prefix=ADMIN_PREFIX + '/debug_style',
419 with rmap.submapper(path_prefix=ADMIN_PREFIX + '/debug_style',
427 controller='debug_style') as m:
420 controller='debug_style') as m:
428 m.connect('debug_style_home', '',
421 m.connect('debug_style_home', '',
429 action='index', conditions={'method': ['GET']})
422 action='index', conditions={'method': ['GET']})
430 m.connect('debug_style_template', '/t/{t_path}',
423 m.connect('debug_style_template', '/t/{t_path}',
431 action='template', conditions={'method': ['GET']})
424 action='template', conditions={'method': ['GET']})
432
425
433 # ADMIN SETTINGS ROUTES
426 # ADMIN SETTINGS ROUTES
434 with rmap.submapper(path_prefix=ADMIN_PREFIX,
427 with rmap.submapper(path_prefix=ADMIN_PREFIX,
435 controller='admin/settings') as m:
428 controller='admin/settings') as m:
436
429
437 # default
430 # default
438 m.connect('admin_settings', '/settings',
431 m.connect('admin_settings', '/settings',
439 action='settings_global_update',
432 action='settings_global_update',
440 conditions={'method': ['POST']})
433 conditions={'method': ['POST']})
441 m.connect('admin_settings', '/settings',
434 m.connect('admin_settings', '/settings',
442 action='settings_global', conditions={'method': ['GET']})
435 action='settings_global', conditions={'method': ['GET']})
443
436
444 m.connect('admin_settings_vcs', '/settings/vcs',
437 m.connect('admin_settings_vcs', '/settings/vcs',
445 action='settings_vcs_update',
438 action='settings_vcs_update',
446 conditions={'method': ['POST']})
439 conditions={'method': ['POST']})
447 m.connect('admin_settings_vcs', '/settings/vcs',
440 m.connect('admin_settings_vcs', '/settings/vcs',
448 action='settings_vcs',
441 action='settings_vcs',
449 conditions={'method': ['GET']})
442 conditions={'method': ['GET']})
450 m.connect('admin_settings_vcs', '/settings/vcs',
443 m.connect('admin_settings_vcs', '/settings/vcs',
451 action='delete_svn_pattern',
444 action='delete_svn_pattern',
452 conditions={'method': ['DELETE']})
445 conditions={'method': ['DELETE']})
453
446
454 m.connect('admin_settings_mapping', '/settings/mapping',
447 m.connect('admin_settings_mapping', '/settings/mapping',
455 action='settings_mapping_update',
448 action='settings_mapping_update',
456 conditions={'method': ['POST']})
449 conditions={'method': ['POST']})
457 m.connect('admin_settings_mapping', '/settings/mapping',
450 m.connect('admin_settings_mapping', '/settings/mapping',
458 action='settings_mapping', conditions={'method': ['GET']})
451 action='settings_mapping', conditions={'method': ['GET']})
459
452
460 m.connect('admin_settings_global', '/settings/global',
453 m.connect('admin_settings_global', '/settings/global',
461 action='settings_global_update',
454 action='settings_global_update',
462 conditions={'method': ['POST']})
455 conditions={'method': ['POST']})
463 m.connect('admin_settings_global', '/settings/global',
456 m.connect('admin_settings_global', '/settings/global',
464 action='settings_global', conditions={'method': ['GET']})
457 action='settings_global', conditions={'method': ['GET']})
465
458
466 m.connect('admin_settings_visual', '/settings/visual',
459 m.connect('admin_settings_visual', '/settings/visual',
467 action='settings_visual_update',
460 action='settings_visual_update',
468 conditions={'method': ['POST']})
461 conditions={'method': ['POST']})
469 m.connect('admin_settings_visual', '/settings/visual',
462 m.connect('admin_settings_visual', '/settings/visual',
470 action='settings_visual', conditions={'method': ['GET']})
463 action='settings_visual', conditions={'method': ['GET']})
471
464
472 m.connect('admin_settings_issuetracker',
465 m.connect('admin_settings_issuetracker',
473 '/settings/issue-tracker', action='settings_issuetracker',
466 '/settings/issue-tracker', action='settings_issuetracker',
474 conditions={'method': ['GET']})
467 conditions={'method': ['GET']})
475 m.connect('admin_settings_issuetracker_save',
468 m.connect('admin_settings_issuetracker_save',
476 '/settings/issue-tracker/save',
469 '/settings/issue-tracker/save',
477 action='settings_issuetracker_save',
470 action='settings_issuetracker_save',
478 conditions={'method': ['POST']})
471 conditions={'method': ['POST']})
479 m.connect('admin_issuetracker_test', '/settings/issue-tracker/test',
472 m.connect('admin_issuetracker_test', '/settings/issue-tracker/test',
480 action='settings_issuetracker_test',
473 action='settings_issuetracker_test',
481 conditions={'method': ['POST']})
474 conditions={'method': ['POST']})
482 m.connect('admin_issuetracker_delete',
475 m.connect('admin_issuetracker_delete',
483 '/settings/issue-tracker/delete',
476 '/settings/issue-tracker/delete',
484 action='settings_issuetracker_delete',
477 action='settings_issuetracker_delete',
485 conditions={'method': ['DELETE']})
478 conditions={'method': ['DELETE']})
486
479
487 m.connect('admin_settings_email', '/settings/email',
480 m.connect('admin_settings_email', '/settings/email',
488 action='settings_email_update',
481 action='settings_email_update',
489 conditions={'method': ['POST']})
482 conditions={'method': ['POST']})
490 m.connect('admin_settings_email', '/settings/email',
483 m.connect('admin_settings_email', '/settings/email',
491 action='settings_email', conditions={'method': ['GET']})
484 action='settings_email', conditions={'method': ['GET']})
492
485
493 m.connect('admin_settings_hooks', '/settings/hooks',
486 m.connect('admin_settings_hooks', '/settings/hooks',
494 action='settings_hooks_update',
487 action='settings_hooks_update',
495 conditions={'method': ['POST', 'DELETE']})
488 conditions={'method': ['POST', 'DELETE']})
496 m.connect('admin_settings_hooks', '/settings/hooks',
489 m.connect('admin_settings_hooks', '/settings/hooks',
497 action='settings_hooks', conditions={'method': ['GET']})
490 action='settings_hooks', conditions={'method': ['GET']})
498
491
499 m.connect('admin_settings_search', '/settings/search',
492 m.connect('admin_settings_search', '/settings/search',
500 action='settings_search', conditions={'method': ['GET']})
493 action='settings_search', conditions={'method': ['GET']})
501
494
502 m.connect('admin_settings_supervisor', '/settings/supervisor',
495 m.connect('admin_settings_supervisor', '/settings/supervisor',
503 action='settings_supervisor', conditions={'method': ['GET']})
496 action='settings_supervisor', conditions={'method': ['GET']})
504 m.connect('admin_settings_supervisor_log', '/settings/supervisor/{procid}/log',
497 m.connect('admin_settings_supervisor_log', '/settings/supervisor/{procid}/log',
505 action='settings_supervisor_log', conditions={'method': ['GET']})
498 action='settings_supervisor_log', conditions={'method': ['GET']})
506
499
507 m.connect('admin_settings_labs', '/settings/labs',
500 m.connect('admin_settings_labs', '/settings/labs',
508 action='settings_labs_update',
501 action='settings_labs_update',
509 conditions={'method': ['POST']})
502 conditions={'method': ['POST']})
510 m.connect('admin_settings_labs', '/settings/labs',
503 m.connect('admin_settings_labs', '/settings/labs',
511 action='settings_labs', conditions={'method': ['GET']})
504 action='settings_labs', conditions={'method': ['GET']})
512
505
513 # ADMIN MY ACCOUNT
506 # ADMIN MY ACCOUNT
514 with rmap.submapper(path_prefix=ADMIN_PREFIX,
507 with rmap.submapper(path_prefix=ADMIN_PREFIX,
515 controller='admin/my_account') as m:
508 controller='admin/my_account') as m:
516
509
517 m.connect('my_account', '/my_account',
510 m.connect('my_account', '/my_account',
518 action='my_account', conditions={'method': ['GET']})
511 action='my_account', conditions={'method': ['GET']})
519 m.connect('my_account_edit', '/my_account/edit',
512 m.connect('my_account_edit', '/my_account/edit',
520 action='my_account_edit', conditions={'method': ['GET']})
513 action='my_account_edit', conditions={'method': ['GET']})
521 m.connect('my_account', '/my_account',
514 m.connect('my_account', '/my_account',
522 action='my_account_update', conditions={'method': ['POST']})
515 action='my_account_update', conditions={'method': ['POST']})
523
516
524 m.connect('my_account_password', '/my_account/password',
517 m.connect('my_account_password', '/my_account/password',
525 action='my_account_password', conditions={'method': ['GET', 'POST']})
518 action='my_account_password', conditions={'method': ['GET', 'POST']})
526
519
527 m.connect('my_account_repos', '/my_account/repos',
520 m.connect('my_account_repos', '/my_account/repos',
528 action='my_account_repos', conditions={'method': ['GET']})
521 action='my_account_repos', conditions={'method': ['GET']})
529
522
530 m.connect('my_account_watched', '/my_account/watched',
523 m.connect('my_account_watched', '/my_account/watched',
531 action='my_account_watched', conditions={'method': ['GET']})
524 action='my_account_watched', conditions={'method': ['GET']})
532
525
533 m.connect('my_account_pullrequests', '/my_account/pull_requests',
526 m.connect('my_account_pullrequests', '/my_account/pull_requests',
534 action='my_account_pullrequests', conditions={'method': ['GET']})
527 action='my_account_pullrequests', conditions={'method': ['GET']})
535
528
536 m.connect('my_account_perms', '/my_account/perms',
529 m.connect('my_account_perms', '/my_account/perms',
537 action='my_account_perms', conditions={'method': ['GET']})
530 action='my_account_perms', conditions={'method': ['GET']})
538
531
539 m.connect('my_account_emails', '/my_account/emails',
532 m.connect('my_account_emails', '/my_account/emails',
540 action='my_account_emails', conditions={'method': ['GET']})
533 action='my_account_emails', conditions={'method': ['GET']})
541 m.connect('my_account_emails', '/my_account/emails',
534 m.connect('my_account_emails', '/my_account/emails',
542 action='my_account_emails_add', conditions={'method': ['POST']})
535 action='my_account_emails_add', conditions={'method': ['POST']})
543 m.connect('my_account_emails', '/my_account/emails',
536 m.connect('my_account_emails', '/my_account/emails',
544 action='my_account_emails_delete', conditions={'method': ['DELETE']})
537 action='my_account_emails_delete', conditions={'method': ['DELETE']})
545
538
546 m.connect('my_account_notifications', '/my_account/notifications',
539 m.connect('my_account_notifications', '/my_account/notifications',
547 action='my_notifications',
540 action='my_notifications',
548 conditions={'method': ['GET']})
541 conditions={'method': ['GET']})
549 m.connect('my_account_notifications_toggle_visibility',
542 m.connect('my_account_notifications_toggle_visibility',
550 '/my_account/toggle_visibility',
543 '/my_account/toggle_visibility',
551 action='my_notifications_toggle_visibility',
544 action='my_notifications_toggle_visibility',
552 conditions={'method': ['POST']})
545 conditions={'method': ['POST']})
553 m.connect('my_account_notifications_test_channelstream',
546 m.connect('my_account_notifications_test_channelstream',
554 '/my_account/test_channelstream',
547 '/my_account/test_channelstream',
555 action='my_account_notifications_test_channelstream',
548 action='my_account_notifications_test_channelstream',
556 conditions={'method': ['POST']})
549 conditions={'method': ['POST']})
557
550
558 # NOTIFICATION REST ROUTES
551 # NOTIFICATION REST ROUTES
559 with rmap.submapper(path_prefix=ADMIN_PREFIX,
552 with rmap.submapper(path_prefix=ADMIN_PREFIX,
560 controller='admin/notifications') as m:
553 controller='admin/notifications') as m:
561 m.connect('notifications', '/notifications',
554 m.connect('notifications', '/notifications',
562 action='index', conditions={'method': ['GET']})
555 action='index', conditions={'method': ['GET']})
563 m.connect('notifications_mark_all_read', '/notifications/mark_all_read',
556 m.connect('notifications_mark_all_read', '/notifications/mark_all_read',
564 action='mark_all_read', conditions={'method': ['POST']})
557 action='mark_all_read', conditions={'method': ['POST']})
565 m.connect('/notifications/{notification_id}',
558 m.connect('/notifications/{notification_id}',
566 action='update', conditions={'method': ['PUT']})
559 action='update', conditions={'method': ['PUT']})
567 m.connect('/notifications/{notification_id}',
560 m.connect('/notifications/{notification_id}',
568 action='delete', conditions={'method': ['DELETE']})
561 action='delete', conditions={'method': ['DELETE']})
569 m.connect('notification', '/notifications/{notification_id}',
562 m.connect('notification', '/notifications/{notification_id}',
570 action='show', conditions={'method': ['GET']})
563 action='show', conditions={'method': ['GET']})
571
564
572 # ADMIN GIST
565 # ADMIN GIST
573 with rmap.submapper(path_prefix=ADMIN_PREFIX,
566 with rmap.submapper(path_prefix=ADMIN_PREFIX,
574 controller='admin/gists') as m:
567 controller='admin/gists') as m:
575 m.connect('gists', '/gists',
568 m.connect('gists', '/gists',
576 action='create', conditions={'method': ['POST']})
569 action='create', conditions={'method': ['POST']})
577 m.connect('gists', '/gists', jsroute=True,
570 m.connect('gists', '/gists', jsroute=True,
578 action='index', conditions={'method': ['GET']})
571 action='index', conditions={'method': ['GET']})
579 m.connect('new_gist', '/gists/new', jsroute=True,
572 m.connect('new_gist', '/gists/new', jsroute=True,
580 action='new', conditions={'method': ['GET']})
573 action='new', conditions={'method': ['GET']})
581
574
582 m.connect('/gists/{gist_id}',
575 m.connect('/gists/{gist_id}',
583 action='delete', conditions={'method': ['DELETE']})
576 action='delete', conditions={'method': ['DELETE']})
584 m.connect('edit_gist', '/gists/{gist_id}/edit',
577 m.connect('edit_gist', '/gists/{gist_id}/edit',
585 action='edit_form', conditions={'method': ['GET']})
578 action='edit_form', conditions={'method': ['GET']})
586 m.connect('edit_gist', '/gists/{gist_id}/edit',
579 m.connect('edit_gist', '/gists/{gist_id}/edit',
587 action='edit', conditions={'method': ['POST']})
580 action='edit', conditions={'method': ['POST']})
588 m.connect(
581 m.connect(
589 'edit_gist_check_revision', '/gists/{gist_id}/edit/check_revision',
582 'edit_gist_check_revision', '/gists/{gist_id}/edit/check_revision',
590 action='check_revision', conditions={'method': ['GET']})
583 action='check_revision', conditions={'method': ['GET']})
591
584
592 m.connect('gist', '/gists/{gist_id}',
585 m.connect('gist', '/gists/{gist_id}',
593 action='show', conditions={'method': ['GET']})
586 action='show', conditions={'method': ['GET']})
594 m.connect('gist_rev', '/gists/{gist_id}/{revision}',
587 m.connect('gist_rev', '/gists/{gist_id}/{revision}',
595 revision='tip',
588 revision='tip',
596 action='show', conditions={'method': ['GET']})
589 action='show', conditions={'method': ['GET']})
597 m.connect('formatted_gist', '/gists/{gist_id}/{revision}/{format}',
590 m.connect('formatted_gist', '/gists/{gist_id}/{revision}/{format}',
598 revision='tip',
591 revision='tip',
599 action='show', conditions={'method': ['GET']})
592 action='show', conditions={'method': ['GET']})
600 m.connect('formatted_gist_file', '/gists/{gist_id}/{revision}/{format}/{f_path}',
593 m.connect('formatted_gist_file', '/gists/{gist_id}/{revision}/{format}/{f_path}',
601 revision='tip',
594 revision='tip',
602 action='show', conditions={'method': ['GET']},
595 action='show', conditions={'method': ['GET']},
603 requirements=URL_NAME_REQUIREMENTS)
596 requirements=URL_NAME_REQUIREMENTS)
604
597
605 # ADMIN MAIN PAGES
598 # ADMIN MAIN PAGES
606 with rmap.submapper(path_prefix=ADMIN_PREFIX,
599 with rmap.submapper(path_prefix=ADMIN_PREFIX,
607 controller='admin/admin') as m:
600 controller='admin/admin') as m:
608 m.connect('admin_home', '', action='index')
601 m.connect('admin_home', '', action='index')
609 m.connect('admin_add_repo', '/add_repo/{new_repo:[a-z0-9\. _-]*}',
602 m.connect('admin_add_repo', '/add_repo/{new_repo:[a-z0-9\. _-]*}',
610 action='add_repo')
603 action='add_repo')
611 m.connect(
604 m.connect(
612 'pull_requests_global_0', '/pull_requests/{pull_request_id:[0-9]+}',
605 'pull_requests_global_0', '/pull_requests/{pull_request_id:[0-9]+}',
613 action='pull_requests')
606 action='pull_requests')
614 m.connect(
607 m.connect(
615 'pull_requests_global_1', '/pull-requests/{pull_request_id:[0-9]+}',
608 'pull_requests_global_1', '/pull-requests/{pull_request_id:[0-9]+}',
616 action='pull_requests')
609 action='pull_requests')
617 m.connect(
610 m.connect(
618 'pull_requests_global', '/pull-request/{pull_request_id:[0-9]+}',
611 'pull_requests_global', '/pull-request/{pull_request_id:[0-9]+}',
619 action='pull_requests')
612 action='pull_requests')
620
613
621 # USER JOURNAL
614 # USER JOURNAL
622 rmap.connect('journal', '%s/journal' % (ADMIN_PREFIX,),
615 rmap.connect('journal', '%s/journal' % (ADMIN_PREFIX,),
623 controller='journal', action='index')
616 controller='journal', action='index')
624 rmap.connect('journal_rss', '%s/journal/rss' % (ADMIN_PREFIX,),
617 rmap.connect('journal_rss', '%s/journal/rss' % (ADMIN_PREFIX,),
625 controller='journal', action='journal_rss')
618 controller='journal', action='journal_rss')
626 rmap.connect('journal_atom', '%s/journal/atom' % (ADMIN_PREFIX,),
619 rmap.connect('journal_atom', '%s/journal/atom' % (ADMIN_PREFIX,),
627 controller='journal', action='journal_atom')
620 controller='journal', action='journal_atom')
628
621
629 rmap.connect('public_journal', '%s/public_journal' % (ADMIN_PREFIX,),
622 rmap.connect('public_journal', '%s/public_journal' % (ADMIN_PREFIX,),
630 controller='journal', action='public_journal')
623 controller='journal', action='public_journal')
631
624
632 rmap.connect('public_journal_rss', '%s/public_journal/rss' % (ADMIN_PREFIX,),
625 rmap.connect('public_journal_rss', '%s/public_journal/rss' % (ADMIN_PREFIX,),
633 controller='journal', action='public_journal_rss')
626 controller='journal', action='public_journal_rss')
634
627
635 rmap.connect('public_journal_rss_old', '%s/public_journal_rss' % (ADMIN_PREFIX,),
628 rmap.connect('public_journal_rss_old', '%s/public_journal_rss' % (ADMIN_PREFIX,),
636 controller='journal', action='public_journal_rss')
629 controller='journal', action='public_journal_rss')
637
630
638 rmap.connect('public_journal_atom',
631 rmap.connect('public_journal_atom',
639 '%s/public_journal/atom' % (ADMIN_PREFIX,), controller='journal',
632 '%s/public_journal/atom' % (ADMIN_PREFIX,), controller='journal',
640 action='public_journal_atom')
633 action='public_journal_atom')
641
634
642 rmap.connect('public_journal_atom_old',
635 rmap.connect('public_journal_atom_old',
643 '%s/public_journal_atom' % (ADMIN_PREFIX,), controller='journal',
636 '%s/public_journal_atom' % (ADMIN_PREFIX,), controller='journal',
644 action='public_journal_atom')
637 action='public_journal_atom')
645
638
646 rmap.connect('toggle_following', '%s/toggle_following' % (ADMIN_PREFIX,),
639 rmap.connect('toggle_following', '%s/toggle_following' % (ADMIN_PREFIX,),
647 controller='journal', action='toggle_following', jsroute=True,
640 controller='journal', action='toggle_following', jsroute=True,
648 conditions={'method': ['POST']})
641 conditions={'method': ['POST']})
649
642
650 # FULL TEXT SEARCH
643 # FULL TEXT SEARCH
651 rmap.connect('search', '%s/search' % (ADMIN_PREFIX,),
644 rmap.connect('search', '%s/search' % (ADMIN_PREFIX,),
652 controller='search')
645 controller='search')
653 rmap.connect('search_repo_home', '/{repo_name}/search',
646 rmap.connect('search_repo_home', '/{repo_name}/search',
654 controller='search',
647 controller='search',
655 action='index',
648 action='index',
656 conditions={'function': check_repo},
649 conditions={'function': check_repo},
657 requirements=URL_NAME_REQUIREMENTS)
650 requirements=URL_NAME_REQUIREMENTS)
658
651
659 # FEEDS
652 # FEEDS
660 rmap.connect('rss_feed_home', '/{repo_name}/feed/rss',
653 rmap.connect('rss_feed_home', '/{repo_name}/feed/rss',
661 controller='feed', action='rss',
654 controller='feed', action='rss',
662 conditions={'function': check_repo},
655 conditions={'function': check_repo},
663 requirements=URL_NAME_REQUIREMENTS)
656 requirements=URL_NAME_REQUIREMENTS)
664
657
665 rmap.connect('atom_feed_home', '/{repo_name}/feed/atom',
658 rmap.connect('atom_feed_home', '/{repo_name}/feed/atom',
666 controller='feed', action='atom',
659 controller='feed', action='atom',
667 conditions={'function': check_repo},
660 conditions={'function': check_repo},
668 requirements=URL_NAME_REQUIREMENTS)
661 requirements=URL_NAME_REQUIREMENTS)
669
662
670 #==========================================================================
663 #==========================================================================
671 # REPOSITORY ROUTES
664 # REPOSITORY ROUTES
672 #==========================================================================
665 #==========================================================================
673
666
674 rmap.connect('repo_creating_home', '/{repo_name}/repo_creating',
667 rmap.connect('repo_creating_home', '/{repo_name}/repo_creating',
675 controller='admin/repos', action='repo_creating',
668 controller='admin/repos', action='repo_creating',
676 requirements=URL_NAME_REQUIREMENTS)
669 requirements=URL_NAME_REQUIREMENTS)
677 rmap.connect('repo_check_home', '/{repo_name}/crepo_check',
670 rmap.connect('repo_check_home', '/{repo_name}/crepo_check',
678 controller='admin/repos', action='repo_check',
671 controller='admin/repos', action='repo_check',
679 requirements=URL_NAME_REQUIREMENTS)
672 requirements=URL_NAME_REQUIREMENTS)
680
673
681 rmap.connect('repo_stats', '/{repo_name}/repo_stats/{commit_id}',
674 rmap.connect('repo_stats', '/{repo_name}/repo_stats/{commit_id}',
682 controller='summary', action='repo_stats',
675 controller='summary', action='repo_stats',
683 conditions={'function': check_repo},
676 conditions={'function': check_repo},
684 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
677 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
685
678
686 rmap.connect('repo_refs_data', '/{repo_name}/refs-data',
679 rmap.connect('repo_refs_data', '/{repo_name}/refs-data',
687 controller='summary', action='repo_refs_data',
680 controller='summary', action='repo_refs_data',
688 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
681 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
689 rmap.connect('repo_refs_changelog_data', '/{repo_name}/refs-data-changelog',
682 rmap.connect('repo_refs_changelog_data', '/{repo_name}/refs-data-changelog',
690 controller='summary', action='repo_refs_changelog_data',
683 controller='summary', action='repo_refs_changelog_data',
691 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
684 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
692 rmap.connect('repo_default_reviewers_data', '/{repo_name}/default-reviewers',
685 rmap.connect('repo_default_reviewers_data', '/{repo_name}/default-reviewers',
693 controller='summary', action='repo_default_reviewers_data',
686 controller='summary', action='repo_default_reviewers_data',
694 jsroute=True, requirements=URL_NAME_REQUIREMENTS)
687 jsroute=True, requirements=URL_NAME_REQUIREMENTS)
695
688
696 rmap.connect('changeset_home', '/{repo_name}/changeset/{revision}',
689 rmap.connect('changeset_home', '/{repo_name}/changeset/{revision}',
697 controller='changeset', revision='tip',
690 controller='changeset', revision='tip',
698 conditions={'function': check_repo},
691 conditions={'function': check_repo},
699 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
692 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
700 rmap.connect('changeset_children', '/{repo_name}/changeset_children/{revision}',
693 rmap.connect('changeset_children', '/{repo_name}/changeset_children/{revision}',
701 controller='changeset', revision='tip', action='changeset_children',
694 controller='changeset', revision='tip', action='changeset_children',
702 conditions={'function': check_repo},
695 conditions={'function': check_repo},
703 requirements=URL_NAME_REQUIREMENTS)
696 requirements=URL_NAME_REQUIREMENTS)
704 rmap.connect('changeset_parents', '/{repo_name}/changeset_parents/{revision}',
697 rmap.connect('changeset_parents', '/{repo_name}/changeset_parents/{revision}',
705 controller='changeset', revision='tip', action='changeset_parents',
698 controller='changeset', revision='tip', action='changeset_parents',
706 conditions={'function': check_repo},
699 conditions={'function': check_repo},
707 requirements=URL_NAME_REQUIREMENTS)
700 requirements=URL_NAME_REQUIREMENTS)
708
701
709 # repo edit options
702 # repo edit options
710 rmap.connect('edit_repo', '/{repo_name}/settings', jsroute=True,
703 rmap.connect('edit_repo', '/{repo_name}/settings', jsroute=True,
711 controller='admin/repos', action='edit',
704 controller='admin/repos', action='edit',
712 conditions={'method': ['GET'], 'function': check_repo},
705 conditions={'method': ['GET'], 'function': check_repo},
713 requirements=URL_NAME_REQUIREMENTS)
706 requirements=URL_NAME_REQUIREMENTS)
714
707
715 rmap.connect('edit_repo_perms', '/{repo_name}/settings/permissions',
708 rmap.connect('edit_repo_perms', '/{repo_name}/settings/permissions',
716 jsroute=True,
709 jsroute=True,
717 controller='admin/repos', action='edit_permissions',
710 controller='admin/repos', action='edit_permissions',
718 conditions={'method': ['GET'], 'function': check_repo},
711 conditions={'method': ['GET'], 'function': check_repo},
719 requirements=URL_NAME_REQUIREMENTS)
712 requirements=URL_NAME_REQUIREMENTS)
720 rmap.connect('edit_repo_perms_update', '/{repo_name}/settings/permissions',
713 rmap.connect('edit_repo_perms_update', '/{repo_name}/settings/permissions',
721 controller='admin/repos', action='edit_permissions_update',
714 controller='admin/repos', action='edit_permissions_update',
722 conditions={'method': ['PUT'], 'function': check_repo},
715 conditions={'method': ['PUT'], 'function': check_repo},
723 requirements=URL_NAME_REQUIREMENTS)
716 requirements=URL_NAME_REQUIREMENTS)
724
717
725 rmap.connect('edit_repo_fields', '/{repo_name}/settings/fields',
718 rmap.connect('edit_repo_fields', '/{repo_name}/settings/fields',
726 controller='admin/repos', action='edit_fields',
719 controller='admin/repos', action='edit_fields',
727 conditions={'method': ['GET'], 'function': check_repo},
720 conditions={'method': ['GET'], 'function': check_repo},
728 requirements=URL_NAME_REQUIREMENTS)
721 requirements=URL_NAME_REQUIREMENTS)
729 rmap.connect('create_repo_fields', '/{repo_name}/settings/fields/new',
722 rmap.connect('create_repo_fields', '/{repo_name}/settings/fields/new',
730 controller='admin/repos', action='create_repo_field',
723 controller='admin/repos', action='create_repo_field',
731 conditions={'method': ['PUT'], 'function': check_repo},
724 conditions={'method': ['PUT'], 'function': check_repo},
732 requirements=URL_NAME_REQUIREMENTS)
725 requirements=URL_NAME_REQUIREMENTS)
733 rmap.connect('delete_repo_fields', '/{repo_name}/settings/fields/{field_id}',
726 rmap.connect('delete_repo_fields', '/{repo_name}/settings/fields/{field_id}',
734 controller='admin/repos', action='delete_repo_field',
727 controller='admin/repos', action='delete_repo_field',
735 conditions={'method': ['DELETE'], 'function': check_repo},
728 conditions={'method': ['DELETE'], 'function': check_repo},
736 requirements=URL_NAME_REQUIREMENTS)
729 requirements=URL_NAME_REQUIREMENTS)
737
730
738 rmap.connect('edit_repo_advanced', '/{repo_name}/settings/advanced',
731 rmap.connect('edit_repo_advanced', '/{repo_name}/settings/advanced',
739 controller='admin/repos', action='edit_advanced',
732 controller='admin/repos', action='edit_advanced',
740 conditions={'method': ['GET'], 'function': check_repo},
733 conditions={'method': ['GET'], 'function': check_repo},
741 requirements=URL_NAME_REQUIREMENTS)
734 requirements=URL_NAME_REQUIREMENTS)
742
735
743 rmap.connect('edit_repo_advanced_locking', '/{repo_name}/settings/advanced/locking',
736 rmap.connect('edit_repo_advanced_locking', '/{repo_name}/settings/advanced/locking',
744 controller='admin/repos', action='edit_advanced_locking',
737 controller='admin/repos', action='edit_advanced_locking',
745 conditions={'method': ['PUT'], 'function': check_repo},
738 conditions={'method': ['PUT'], 'function': check_repo},
746 requirements=URL_NAME_REQUIREMENTS)
739 requirements=URL_NAME_REQUIREMENTS)
747 rmap.connect('toggle_locking', '/{repo_name}/settings/advanced/locking_toggle',
740 rmap.connect('toggle_locking', '/{repo_name}/settings/advanced/locking_toggle',
748 controller='admin/repos', action='toggle_locking',
741 controller='admin/repos', action='toggle_locking',
749 conditions={'method': ['GET'], 'function': check_repo},
742 conditions={'method': ['GET'], 'function': check_repo},
750 requirements=URL_NAME_REQUIREMENTS)
743 requirements=URL_NAME_REQUIREMENTS)
751
744
752 rmap.connect('edit_repo_advanced_journal', '/{repo_name}/settings/advanced/journal',
745 rmap.connect('edit_repo_advanced_journal', '/{repo_name}/settings/advanced/journal',
753 controller='admin/repos', action='edit_advanced_journal',
746 controller='admin/repos', action='edit_advanced_journal',
754 conditions={'method': ['PUT'], 'function': check_repo},
747 conditions={'method': ['PUT'], 'function': check_repo},
755 requirements=URL_NAME_REQUIREMENTS)
748 requirements=URL_NAME_REQUIREMENTS)
756
749
757 rmap.connect('edit_repo_advanced_fork', '/{repo_name}/settings/advanced/fork',
750 rmap.connect('edit_repo_advanced_fork', '/{repo_name}/settings/advanced/fork',
758 controller='admin/repos', action='edit_advanced_fork',
751 controller='admin/repos', action='edit_advanced_fork',
759 conditions={'method': ['PUT'], 'function': check_repo},
752 conditions={'method': ['PUT'], 'function': check_repo},
760 requirements=URL_NAME_REQUIREMENTS)
753 requirements=URL_NAME_REQUIREMENTS)
761
754
762 rmap.connect('edit_repo_caches', '/{repo_name}/settings/caches',
755 rmap.connect('edit_repo_caches', '/{repo_name}/settings/caches',
763 controller='admin/repos', action='edit_caches_form',
756 controller='admin/repos', action='edit_caches_form',
764 conditions={'method': ['GET'], 'function': check_repo},
757 conditions={'method': ['GET'], 'function': check_repo},
765 requirements=URL_NAME_REQUIREMENTS)
758 requirements=URL_NAME_REQUIREMENTS)
766 rmap.connect('edit_repo_caches', '/{repo_name}/settings/caches',
759 rmap.connect('edit_repo_caches', '/{repo_name}/settings/caches',
767 controller='admin/repos', action='edit_caches',
760 controller='admin/repos', action='edit_caches',
768 conditions={'method': ['PUT'], 'function': check_repo},
761 conditions={'method': ['PUT'], 'function': check_repo},
769 requirements=URL_NAME_REQUIREMENTS)
762 requirements=URL_NAME_REQUIREMENTS)
770
763
771 rmap.connect('edit_repo_remote', '/{repo_name}/settings/remote',
764 rmap.connect('edit_repo_remote', '/{repo_name}/settings/remote',
772 controller='admin/repos', action='edit_remote_form',
765 controller='admin/repos', action='edit_remote_form',
773 conditions={'method': ['GET'], 'function': check_repo},
766 conditions={'method': ['GET'], 'function': check_repo},
774 requirements=URL_NAME_REQUIREMENTS)
767 requirements=URL_NAME_REQUIREMENTS)
775 rmap.connect('edit_repo_remote', '/{repo_name}/settings/remote',
768 rmap.connect('edit_repo_remote', '/{repo_name}/settings/remote',
776 controller='admin/repos', action='edit_remote',
769 controller='admin/repos', action='edit_remote',
777 conditions={'method': ['PUT'], 'function': check_repo},
770 conditions={'method': ['PUT'], 'function': check_repo},
778 requirements=URL_NAME_REQUIREMENTS)
771 requirements=URL_NAME_REQUIREMENTS)
779
772
780 rmap.connect('edit_repo_statistics', '/{repo_name}/settings/statistics',
773 rmap.connect('edit_repo_statistics', '/{repo_name}/settings/statistics',
781 controller='admin/repos', action='edit_statistics_form',
774 controller='admin/repos', action='edit_statistics_form',
782 conditions={'method': ['GET'], 'function': check_repo},
775 conditions={'method': ['GET'], 'function': check_repo},
783 requirements=URL_NAME_REQUIREMENTS)
776 requirements=URL_NAME_REQUIREMENTS)
784 rmap.connect('edit_repo_statistics', '/{repo_name}/settings/statistics',
777 rmap.connect('edit_repo_statistics', '/{repo_name}/settings/statistics',
785 controller='admin/repos', action='edit_statistics',
778 controller='admin/repos', action='edit_statistics',
786 conditions={'method': ['PUT'], 'function': check_repo},
779 conditions={'method': ['PUT'], 'function': check_repo},
787 requirements=URL_NAME_REQUIREMENTS)
780 requirements=URL_NAME_REQUIREMENTS)
788 rmap.connect('repo_settings_issuetracker',
781 rmap.connect('repo_settings_issuetracker',
789 '/{repo_name}/settings/issue-tracker',
782 '/{repo_name}/settings/issue-tracker',
790 controller='admin/repos', action='repo_issuetracker',
783 controller='admin/repos', action='repo_issuetracker',
791 conditions={'method': ['GET'], 'function': check_repo},
784 conditions={'method': ['GET'], 'function': check_repo},
792 requirements=URL_NAME_REQUIREMENTS)
785 requirements=URL_NAME_REQUIREMENTS)
793 rmap.connect('repo_issuetracker_test',
786 rmap.connect('repo_issuetracker_test',
794 '/{repo_name}/settings/issue-tracker/test',
787 '/{repo_name}/settings/issue-tracker/test',
795 controller='admin/repos', action='repo_issuetracker_test',
788 controller='admin/repos', action='repo_issuetracker_test',
796 conditions={'method': ['POST'], 'function': check_repo},
789 conditions={'method': ['POST'], 'function': check_repo},
797 requirements=URL_NAME_REQUIREMENTS)
790 requirements=URL_NAME_REQUIREMENTS)
798 rmap.connect('repo_issuetracker_delete',
791 rmap.connect('repo_issuetracker_delete',
799 '/{repo_name}/settings/issue-tracker/delete',
792 '/{repo_name}/settings/issue-tracker/delete',
800 controller='admin/repos', action='repo_issuetracker_delete',
793 controller='admin/repos', action='repo_issuetracker_delete',
801 conditions={'method': ['DELETE'], 'function': check_repo},
794 conditions={'method': ['DELETE'], 'function': check_repo},
802 requirements=URL_NAME_REQUIREMENTS)
795 requirements=URL_NAME_REQUIREMENTS)
803 rmap.connect('repo_issuetracker_save',
796 rmap.connect('repo_issuetracker_save',
804 '/{repo_name}/settings/issue-tracker/save',
797 '/{repo_name}/settings/issue-tracker/save',
805 controller='admin/repos', action='repo_issuetracker_save',
798 controller='admin/repos', action='repo_issuetracker_save',
806 conditions={'method': ['POST'], 'function': check_repo},
799 conditions={'method': ['POST'], 'function': check_repo},
807 requirements=URL_NAME_REQUIREMENTS)
800 requirements=URL_NAME_REQUIREMENTS)
808 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
801 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
809 controller='admin/repos', action='repo_settings_vcs_update',
802 controller='admin/repos', action='repo_settings_vcs_update',
810 conditions={'method': ['POST'], 'function': check_repo},
803 conditions={'method': ['POST'], 'function': check_repo},
811 requirements=URL_NAME_REQUIREMENTS)
804 requirements=URL_NAME_REQUIREMENTS)
812 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
805 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
813 controller='admin/repos', action='repo_settings_vcs',
806 controller='admin/repos', action='repo_settings_vcs',
814 conditions={'method': ['GET'], 'function': check_repo},
807 conditions={'method': ['GET'], 'function': check_repo},
815 requirements=URL_NAME_REQUIREMENTS)
808 requirements=URL_NAME_REQUIREMENTS)
816 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
809 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
817 controller='admin/repos', action='repo_delete_svn_pattern',
810 controller='admin/repos', action='repo_delete_svn_pattern',
818 conditions={'method': ['DELETE'], 'function': check_repo},
811 conditions={'method': ['DELETE'], 'function': check_repo},
819 requirements=URL_NAME_REQUIREMENTS)
812 requirements=URL_NAME_REQUIREMENTS)
820 rmap.connect('repo_pullrequest_settings', '/{repo_name}/settings/pullrequest',
813 rmap.connect('repo_pullrequest_settings', '/{repo_name}/settings/pullrequest',
821 controller='admin/repos', action='repo_settings_pullrequest',
814 controller='admin/repos', action='repo_settings_pullrequest',
822 conditions={'method': ['GET', 'POST'], 'function': check_repo},
815 conditions={'method': ['GET', 'POST'], 'function': check_repo},
823 requirements=URL_NAME_REQUIREMENTS)
816 requirements=URL_NAME_REQUIREMENTS)
824
817
825 # still working url for backward compat.
818 # still working url for backward compat.
826 rmap.connect('raw_changeset_home_depraced',
819 rmap.connect('raw_changeset_home_depraced',
827 '/{repo_name}/raw-changeset/{revision}',
820 '/{repo_name}/raw-changeset/{revision}',
828 controller='changeset', action='changeset_raw',
821 controller='changeset', action='changeset_raw',
829 revision='tip', conditions={'function': check_repo},
822 revision='tip', conditions={'function': check_repo},
830 requirements=URL_NAME_REQUIREMENTS)
823 requirements=URL_NAME_REQUIREMENTS)
831
824
832 # new URLs
825 # new URLs
833 rmap.connect('changeset_raw_home',
826 rmap.connect('changeset_raw_home',
834 '/{repo_name}/changeset-diff/{revision}',
827 '/{repo_name}/changeset-diff/{revision}',
835 controller='changeset', action='changeset_raw',
828 controller='changeset', action='changeset_raw',
836 revision='tip', conditions={'function': check_repo},
829 revision='tip', conditions={'function': check_repo},
837 requirements=URL_NAME_REQUIREMENTS)
830 requirements=URL_NAME_REQUIREMENTS)
838
831
839 rmap.connect('changeset_patch_home',
832 rmap.connect('changeset_patch_home',
840 '/{repo_name}/changeset-patch/{revision}',
833 '/{repo_name}/changeset-patch/{revision}',
841 controller='changeset', action='changeset_patch',
834 controller='changeset', action='changeset_patch',
842 revision='tip', conditions={'function': check_repo},
835 revision='tip', conditions={'function': check_repo},
843 requirements=URL_NAME_REQUIREMENTS)
836 requirements=URL_NAME_REQUIREMENTS)
844
837
845 rmap.connect('changeset_download_home',
838 rmap.connect('changeset_download_home',
846 '/{repo_name}/changeset-download/{revision}',
839 '/{repo_name}/changeset-download/{revision}',
847 controller='changeset', action='changeset_download',
840 controller='changeset', action='changeset_download',
848 revision='tip', conditions={'function': check_repo},
841 revision='tip', conditions={'function': check_repo},
849 requirements=URL_NAME_REQUIREMENTS)
842 requirements=URL_NAME_REQUIREMENTS)
850
843
851 rmap.connect('changeset_comment',
844 rmap.connect('changeset_comment',
852 '/{repo_name}/changeset/{revision}/comment', jsroute=True,
845 '/{repo_name}/changeset/{revision}/comment', jsroute=True,
853 controller='changeset', revision='tip', action='comment',
846 controller='changeset', revision='tip', action='comment',
854 conditions={'function': check_repo},
847 conditions={'function': check_repo},
855 requirements=URL_NAME_REQUIREMENTS)
848 requirements=URL_NAME_REQUIREMENTS)
856
849
857 rmap.connect('changeset_comment_preview',
850 rmap.connect('changeset_comment_preview',
858 '/{repo_name}/changeset/comment/preview', jsroute=True,
851 '/{repo_name}/changeset/comment/preview', jsroute=True,
859 controller='changeset', action='preview_comment',
852 controller='changeset', action='preview_comment',
860 conditions={'function': check_repo, 'method': ['POST']},
853 conditions={'function': check_repo, 'method': ['POST']},
861 requirements=URL_NAME_REQUIREMENTS)
854 requirements=URL_NAME_REQUIREMENTS)
862
855
863 rmap.connect('changeset_comment_delete',
856 rmap.connect('changeset_comment_delete',
864 '/{repo_name}/changeset/comment/{comment_id}/delete',
857 '/{repo_name}/changeset/comment/{comment_id}/delete',
865 controller='changeset', action='delete_comment',
858 controller='changeset', action='delete_comment',
866 conditions={'function': check_repo, 'method': ['DELETE']},
859 conditions={'function': check_repo, 'method': ['DELETE']},
867 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
860 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
868
861
869 rmap.connect('changeset_info', '/{repo_name}/changeset_info/{revision}',
862 rmap.connect('changeset_info', '/{repo_name}/changeset_info/{revision}',
870 controller='changeset', action='changeset_info',
863 controller='changeset', action='changeset_info',
871 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
864 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
872
865
873 rmap.connect('compare_home',
866 rmap.connect('compare_home',
874 '/{repo_name}/compare',
867 '/{repo_name}/compare',
875 controller='compare', action='index',
868 controller='compare', action='index',
876 conditions={'function': check_repo},
869 conditions={'function': check_repo},
877 requirements=URL_NAME_REQUIREMENTS)
870 requirements=URL_NAME_REQUIREMENTS)
878
871
879 rmap.connect('compare_url',
872 rmap.connect('compare_url',
880 '/{repo_name}/compare/{source_ref_type}@{source_ref:.*?}...{target_ref_type}@{target_ref:.*?}',
873 '/{repo_name}/compare/{source_ref_type}@{source_ref:.*?}...{target_ref_type}@{target_ref:.*?}',
881 controller='compare', action='compare',
874 controller='compare', action='compare',
882 conditions={'function': check_repo},
875 conditions={'function': check_repo},
883 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
876 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
884
877
885 rmap.connect('pullrequest_home',
878 rmap.connect('pullrequest_home',
886 '/{repo_name}/pull-request/new', controller='pullrequests',
879 '/{repo_name}/pull-request/new', controller='pullrequests',
887 action='index', conditions={'function': check_repo,
880 action='index', conditions={'function': check_repo,
888 'method': ['GET']},
881 'method': ['GET']},
889 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
882 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
890
883
891 rmap.connect('pullrequest',
884 rmap.connect('pullrequest',
892 '/{repo_name}/pull-request/new', controller='pullrequests',
885 '/{repo_name}/pull-request/new', controller='pullrequests',
893 action='create', conditions={'function': check_repo,
886 action='create', conditions={'function': check_repo,
894 'method': ['POST']},
887 'method': ['POST']},
895 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
888 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
896
889
897 rmap.connect('pullrequest_repo_refs',
890 rmap.connect('pullrequest_repo_refs',
898 '/{repo_name}/pull-request/refs/{target_repo_name:.*?[^/]}',
891 '/{repo_name}/pull-request/refs/{target_repo_name:.*?[^/]}',
899 controller='pullrequests',
892 controller='pullrequests',
900 action='get_repo_refs',
893 action='get_repo_refs',
901 conditions={'function': check_repo, 'method': ['GET']},
894 conditions={'function': check_repo, 'method': ['GET']},
902 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
895 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
903
896
904 rmap.connect('pullrequest_repo_destinations',
897 rmap.connect('pullrequest_repo_destinations',
905 '/{repo_name}/pull-request/repo-destinations',
898 '/{repo_name}/pull-request/repo-destinations',
906 controller='pullrequests',
899 controller='pullrequests',
907 action='get_repo_destinations',
900 action='get_repo_destinations',
908 conditions={'function': check_repo, 'method': ['GET']},
901 conditions={'function': check_repo, 'method': ['GET']},
909 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
902 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
910
903
911 rmap.connect('pullrequest_show',
904 rmap.connect('pullrequest_show',
912 '/{repo_name}/pull-request/{pull_request_id}',
905 '/{repo_name}/pull-request/{pull_request_id}',
913 controller='pullrequests',
906 controller='pullrequests',
914 action='show', conditions={'function': check_repo,
907 action='show', conditions={'function': check_repo,
915 'method': ['GET']},
908 'method': ['GET']},
916 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
909 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
917
910
918 rmap.connect('pullrequest_update',
911 rmap.connect('pullrequest_update',
919 '/{repo_name}/pull-request/{pull_request_id}',
912 '/{repo_name}/pull-request/{pull_request_id}',
920 controller='pullrequests',
913 controller='pullrequests',
921 action='update', conditions={'function': check_repo,
914 action='update', conditions={'function': check_repo,
922 'method': ['PUT']},
915 'method': ['PUT']},
923 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
916 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
924
917
925 rmap.connect('pullrequest_merge',
918 rmap.connect('pullrequest_merge',
926 '/{repo_name}/pull-request/{pull_request_id}',
919 '/{repo_name}/pull-request/{pull_request_id}',
927 controller='pullrequests',
920 controller='pullrequests',
928 action='merge', conditions={'function': check_repo,
921 action='merge', conditions={'function': check_repo,
929 'method': ['POST']},
922 'method': ['POST']},
930 requirements=URL_NAME_REQUIREMENTS)
923 requirements=URL_NAME_REQUIREMENTS)
931
924
932 rmap.connect('pullrequest_delete',
925 rmap.connect('pullrequest_delete',
933 '/{repo_name}/pull-request/{pull_request_id}',
926 '/{repo_name}/pull-request/{pull_request_id}',
934 controller='pullrequests',
927 controller='pullrequests',
935 action='delete', conditions={'function': check_repo,
928 action='delete', conditions={'function': check_repo,
936 'method': ['DELETE']},
929 'method': ['DELETE']},
937 requirements=URL_NAME_REQUIREMENTS)
930 requirements=URL_NAME_REQUIREMENTS)
938
931
939 rmap.connect('pullrequest_show_all',
932 rmap.connect('pullrequest_show_all',
940 '/{repo_name}/pull-request',
933 '/{repo_name}/pull-request',
941 controller='pullrequests',
934 controller='pullrequests',
942 action='show_all', conditions={'function': check_repo,
935 action='show_all', conditions={'function': check_repo,
943 'method': ['GET']},
936 'method': ['GET']},
944 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
937 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
945
938
946 rmap.connect('pullrequest_comment',
939 rmap.connect('pullrequest_comment',
947 '/{repo_name}/pull-request-comment/{pull_request_id}',
940 '/{repo_name}/pull-request-comment/{pull_request_id}',
948 controller='pullrequests',
941 controller='pullrequests',
949 action='comment', conditions={'function': check_repo,
942 action='comment', conditions={'function': check_repo,
950 'method': ['POST']},
943 'method': ['POST']},
951 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
944 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
952
945
953 rmap.connect('pullrequest_comment_delete',
946 rmap.connect('pullrequest_comment_delete',
954 '/{repo_name}/pull-request-comment/{comment_id}/delete',
947 '/{repo_name}/pull-request-comment/{comment_id}/delete',
955 controller='pullrequests', action='delete_comment',
948 controller='pullrequests', action='delete_comment',
956 conditions={'function': check_repo, 'method': ['DELETE']},
949 conditions={'function': check_repo, 'method': ['DELETE']},
957 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
950 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
958
951
959 rmap.connect('summary_home_explicit', '/{repo_name}/summary',
952 rmap.connect('summary_home_explicit', '/{repo_name}/summary',
960 controller='summary', conditions={'function': check_repo},
953 controller='summary', conditions={'function': check_repo},
961 requirements=URL_NAME_REQUIREMENTS)
954 requirements=URL_NAME_REQUIREMENTS)
962
955
963 rmap.connect('branches_home', '/{repo_name}/branches',
956 rmap.connect('branches_home', '/{repo_name}/branches',
964 controller='branches', conditions={'function': check_repo},
957 controller='branches', conditions={'function': check_repo},
965 requirements=URL_NAME_REQUIREMENTS)
958 requirements=URL_NAME_REQUIREMENTS)
966
959
967 rmap.connect('tags_home', '/{repo_name}/tags',
960 rmap.connect('tags_home', '/{repo_name}/tags',
968 controller='tags', conditions={'function': check_repo},
961 controller='tags', conditions={'function': check_repo},
969 requirements=URL_NAME_REQUIREMENTS)
962 requirements=URL_NAME_REQUIREMENTS)
970
963
971 rmap.connect('bookmarks_home', '/{repo_name}/bookmarks',
964 rmap.connect('bookmarks_home', '/{repo_name}/bookmarks',
972 controller='bookmarks', conditions={'function': check_repo},
965 controller='bookmarks', conditions={'function': check_repo},
973 requirements=URL_NAME_REQUIREMENTS)
966 requirements=URL_NAME_REQUIREMENTS)
974
967
975 rmap.connect('changelog_home', '/{repo_name}/changelog', jsroute=True,
968 rmap.connect('changelog_home', '/{repo_name}/changelog', jsroute=True,
976 controller='changelog', conditions={'function': check_repo},
969 controller='changelog', conditions={'function': check_repo},
977 requirements=URL_NAME_REQUIREMENTS)
970 requirements=URL_NAME_REQUIREMENTS)
978
971
979 rmap.connect('changelog_summary_home', '/{repo_name}/changelog_summary',
972 rmap.connect('changelog_summary_home', '/{repo_name}/changelog_summary',
980 controller='changelog', action='changelog_summary',
973 controller='changelog', action='changelog_summary',
981 conditions={'function': check_repo},
974 conditions={'function': check_repo},
982 requirements=URL_NAME_REQUIREMENTS)
975 requirements=URL_NAME_REQUIREMENTS)
983
976
984 rmap.connect('changelog_file_home',
977 rmap.connect('changelog_file_home',
985 '/{repo_name}/changelog/{revision}/{f_path}',
978 '/{repo_name}/changelog/{revision}/{f_path}',
986 controller='changelog', f_path=None,
979 controller='changelog', f_path=None,
987 conditions={'function': check_repo},
980 conditions={'function': check_repo},
988 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
981 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
989
982
990 rmap.connect('changelog_elements', '/{repo_name}/changelog_details',
983 rmap.connect('changelog_elements', '/{repo_name}/changelog_details',
991 controller='changelog', action='changelog_elements',
984 controller='changelog', action='changelog_elements',
992 conditions={'function': check_repo},
985 conditions={'function': check_repo},
993 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
986 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
994
987
995 rmap.connect('files_home', '/{repo_name}/files/{revision}/{f_path}',
988 rmap.connect('files_home', '/{repo_name}/files/{revision}/{f_path}',
996 controller='files', revision='tip', f_path='',
989 controller='files', revision='tip', f_path='',
997 conditions={'function': check_repo},
990 conditions={'function': check_repo},
998 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
991 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
999
992
1000 rmap.connect('files_home_simple_catchrev',
993 rmap.connect('files_home_simple_catchrev',
1001 '/{repo_name}/files/{revision}',
994 '/{repo_name}/files/{revision}',
1002 controller='files', revision='tip', f_path='',
995 controller='files', revision='tip', f_path='',
1003 conditions={'function': check_repo},
996 conditions={'function': check_repo},
1004 requirements=URL_NAME_REQUIREMENTS)
997 requirements=URL_NAME_REQUIREMENTS)
1005
998
1006 rmap.connect('files_home_simple_catchall',
999 rmap.connect('files_home_simple_catchall',
1007 '/{repo_name}/files',
1000 '/{repo_name}/files',
1008 controller='files', revision='tip', f_path='',
1001 controller='files', revision='tip', f_path='',
1009 conditions={'function': check_repo},
1002 conditions={'function': check_repo},
1010 requirements=URL_NAME_REQUIREMENTS)
1003 requirements=URL_NAME_REQUIREMENTS)
1011
1004
1012 rmap.connect('files_history_home',
1005 rmap.connect('files_history_home',
1013 '/{repo_name}/history/{revision}/{f_path}',
1006 '/{repo_name}/history/{revision}/{f_path}',
1014 controller='files', action='history', revision='tip', f_path='',
1007 controller='files', action='history', revision='tip', f_path='',
1015 conditions={'function': check_repo},
1008 conditions={'function': check_repo},
1016 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1009 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1017
1010
1018 rmap.connect('files_authors_home',
1011 rmap.connect('files_authors_home',
1019 '/{repo_name}/authors/{revision}/{f_path}',
1012 '/{repo_name}/authors/{revision}/{f_path}',
1020 controller='files', action='authors', revision='tip', f_path='',
1013 controller='files', action='authors', revision='tip', f_path='',
1021 conditions={'function': check_repo},
1014 conditions={'function': check_repo},
1022 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1015 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1023
1016
1024 rmap.connect('files_diff_home', '/{repo_name}/diff/{f_path}',
1017 rmap.connect('files_diff_home', '/{repo_name}/diff/{f_path}',
1025 controller='files', action='diff', f_path='',
1018 controller='files', action='diff', f_path='',
1026 conditions={'function': check_repo},
1019 conditions={'function': check_repo},
1027 requirements=URL_NAME_REQUIREMENTS)
1020 requirements=URL_NAME_REQUIREMENTS)
1028
1021
1029 rmap.connect('files_diff_2way_home',
1022 rmap.connect('files_diff_2way_home',
1030 '/{repo_name}/diff-2way/{f_path}',
1023 '/{repo_name}/diff-2way/{f_path}',
1031 controller='files', action='diff_2way', f_path='',
1024 controller='files', action='diff_2way', f_path='',
1032 conditions={'function': check_repo},
1025 conditions={'function': check_repo},
1033 requirements=URL_NAME_REQUIREMENTS)
1026 requirements=URL_NAME_REQUIREMENTS)
1034
1027
1035 rmap.connect('files_rawfile_home',
1028 rmap.connect('files_rawfile_home',
1036 '/{repo_name}/rawfile/{revision}/{f_path}',
1029 '/{repo_name}/rawfile/{revision}/{f_path}',
1037 controller='files', action='rawfile', revision='tip',
1030 controller='files', action='rawfile', revision='tip',
1038 f_path='', conditions={'function': check_repo},
1031 f_path='', conditions={'function': check_repo},
1039 requirements=URL_NAME_REQUIREMENTS)
1032 requirements=URL_NAME_REQUIREMENTS)
1040
1033
1041 rmap.connect('files_raw_home',
1034 rmap.connect('files_raw_home',
1042 '/{repo_name}/raw/{revision}/{f_path}',
1035 '/{repo_name}/raw/{revision}/{f_path}',
1043 controller='files', action='raw', revision='tip', f_path='',
1036 controller='files', action='raw', revision='tip', f_path='',
1044 conditions={'function': check_repo},
1037 conditions={'function': check_repo},
1045 requirements=URL_NAME_REQUIREMENTS)
1038 requirements=URL_NAME_REQUIREMENTS)
1046
1039
1047 rmap.connect('files_render_home',
1040 rmap.connect('files_render_home',
1048 '/{repo_name}/render/{revision}/{f_path}',
1041 '/{repo_name}/render/{revision}/{f_path}',
1049 controller='files', action='index', revision='tip', f_path='',
1042 controller='files', action='index', revision='tip', f_path='',
1050 rendered=True, conditions={'function': check_repo},
1043 rendered=True, conditions={'function': check_repo},
1051 requirements=URL_NAME_REQUIREMENTS)
1044 requirements=URL_NAME_REQUIREMENTS)
1052
1045
1053 rmap.connect('files_annotate_home',
1046 rmap.connect('files_annotate_home',
1054 '/{repo_name}/annotate/{revision}/{f_path}',
1047 '/{repo_name}/annotate/{revision}/{f_path}',
1055 controller='files', action='index', revision='tip',
1048 controller='files', action='index', revision='tip',
1056 f_path='', annotate=True, conditions={'function': check_repo},
1049 f_path='', annotate=True, conditions={'function': check_repo},
1057 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1050 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1058
1051
1059 rmap.connect('files_annotate_previous',
1052 rmap.connect('files_annotate_previous',
1060 '/{repo_name}/annotate-previous/{revision}/{f_path}',
1053 '/{repo_name}/annotate-previous/{revision}/{f_path}',
1061 controller='files', action='annotate_previous', revision='tip',
1054 controller='files', action='annotate_previous', revision='tip',
1062 f_path='', annotate=True, conditions={'function': check_repo},
1055 f_path='', annotate=True, conditions={'function': check_repo},
1063 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1056 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1064
1057
1065 rmap.connect('files_edit',
1058 rmap.connect('files_edit',
1066 '/{repo_name}/edit/{revision}/{f_path}',
1059 '/{repo_name}/edit/{revision}/{f_path}',
1067 controller='files', action='edit', revision='tip',
1060 controller='files', action='edit', revision='tip',
1068 f_path='',
1061 f_path='',
1069 conditions={'function': check_repo, 'method': ['POST']},
1062 conditions={'function': check_repo, 'method': ['POST']},
1070 requirements=URL_NAME_REQUIREMENTS)
1063 requirements=URL_NAME_REQUIREMENTS)
1071
1064
1072 rmap.connect('files_edit_home',
1065 rmap.connect('files_edit_home',
1073 '/{repo_name}/edit/{revision}/{f_path}',
1066 '/{repo_name}/edit/{revision}/{f_path}',
1074 controller='files', action='edit_home', revision='tip',
1067 controller='files', action='edit_home', revision='tip',
1075 f_path='', conditions={'function': check_repo},
1068 f_path='', conditions={'function': check_repo},
1076 requirements=URL_NAME_REQUIREMENTS)
1069 requirements=URL_NAME_REQUIREMENTS)
1077
1070
1078 rmap.connect('files_add',
1071 rmap.connect('files_add',
1079 '/{repo_name}/add/{revision}/{f_path}',
1072 '/{repo_name}/add/{revision}/{f_path}',
1080 controller='files', action='add', revision='tip',
1073 controller='files', action='add', revision='tip',
1081 f_path='',
1074 f_path='',
1082 conditions={'function': check_repo, 'method': ['POST']},
1075 conditions={'function': check_repo, 'method': ['POST']},
1083 requirements=URL_NAME_REQUIREMENTS)
1076 requirements=URL_NAME_REQUIREMENTS)
1084
1077
1085 rmap.connect('files_add_home',
1078 rmap.connect('files_add_home',
1086 '/{repo_name}/add/{revision}/{f_path}',
1079 '/{repo_name}/add/{revision}/{f_path}',
1087 controller='files', action='add_home', revision='tip',
1080 controller='files', action='add_home', revision='tip',
1088 f_path='', conditions={'function': check_repo},
1081 f_path='', conditions={'function': check_repo},
1089 requirements=URL_NAME_REQUIREMENTS)
1082 requirements=URL_NAME_REQUIREMENTS)
1090
1083
1091 rmap.connect('files_delete',
1084 rmap.connect('files_delete',
1092 '/{repo_name}/delete/{revision}/{f_path}',
1085 '/{repo_name}/delete/{revision}/{f_path}',
1093 controller='files', action='delete', revision='tip',
1086 controller='files', action='delete', revision='tip',
1094 f_path='',
1087 f_path='',
1095 conditions={'function': check_repo, 'method': ['POST']},
1088 conditions={'function': check_repo, 'method': ['POST']},
1096 requirements=URL_NAME_REQUIREMENTS)
1089 requirements=URL_NAME_REQUIREMENTS)
1097
1090
1098 rmap.connect('files_delete_home',
1091 rmap.connect('files_delete_home',
1099 '/{repo_name}/delete/{revision}/{f_path}',
1092 '/{repo_name}/delete/{revision}/{f_path}',
1100 controller='files', action='delete_home', revision='tip',
1093 controller='files', action='delete_home', revision='tip',
1101 f_path='', conditions={'function': check_repo},
1094 f_path='', conditions={'function': check_repo},
1102 requirements=URL_NAME_REQUIREMENTS)
1095 requirements=URL_NAME_REQUIREMENTS)
1103
1096
1104 rmap.connect('files_archive_home', '/{repo_name}/archive/{fname}',
1097 rmap.connect('files_archive_home', '/{repo_name}/archive/{fname}',
1105 controller='files', action='archivefile',
1098 controller='files', action='archivefile',
1106 conditions={'function': check_repo},
1099 conditions={'function': check_repo},
1107 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1100 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1108
1101
1109 rmap.connect('files_nodelist_home',
1102 rmap.connect('files_nodelist_home',
1110 '/{repo_name}/nodelist/{revision}/{f_path}',
1103 '/{repo_name}/nodelist/{revision}/{f_path}',
1111 controller='files', action='nodelist',
1104 controller='files', action='nodelist',
1112 conditions={'function': check_repo},
1105 conditions={'function': check_repo},
1113 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1106 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1114
1107
1115 rmap.connect('files_nodetree_full',
1108 rmap.connect('files_nodetree_full',
1116 '/{repo_name}/nodetree_full/{commit_id}/{f_path}',
1109 '/{repo_name}/nodetree_full/{commit_id}/{f_path}',
1117 controller='files', action='nodetree_full',
1110 controller='files', action='nodetree_full',
1118 conditions={'function': check_repo},
1111 conditions={'function': check_repo},
1119 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1112 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1120
1113
1121 rmap.connect('repo_fork_create_home', '/{repo_name}/fork',
1114 rmap.connect('repo_fork_create_home', '/{repo_name}/fork',
1122 controller='forks', action='fork_create',
1115 controller='forks', action='fork_create',
1123 conditions={'function': check_repo, 'method': ['POST']},
1116 conditions={'function': check_repo, 'method': ['POST']},
1124 requirements=URL_NAME_REQUIREMENTS)
1117 requirements=URL_NAME_REQUIREMENTS)
1125
1118
1126 rmap.connect('repo_fork_home', '/{repo_name}/fork',
1119 rmap.connect('repo_fork_home', '/{repo_name}/fork',
1127 controller='forks', action='fork',
1120 controller='forks', action='fork',
1128 conditions={'function': check_repo},
1121 conditions={'function': check_repo},
1129 requirements=URL_NAME_REQUIREMENTS)
1122 requirements=URL_NAME_REQUIREMENTS)
1130
1123
1131 rmap.connect('repo_forks_home', '/{repo_name}/forks',
1124 rmap.connect('repo_forks_home', '/{repo_name}/forks',
1132 controller='forks', action='forks',
1125 controller='forks', action='forks',
1133 conditions={'function': check_repo},
1126 conditions={'function': check_repo},
1134 requirements=URL_NAME_REQUIREMENTS)
1127 requirements=URL_NAME_REQUIREMENTS)
1135
1128
1136 rmap.connect('repo_followers_home', '/{repo_name}/followers',
1129 rmap.connect('repo_followers_home', '/{repo_name}/followers',
1137 controller='followers', action='followers',
1130 controller='followers', action='followers',
1138 conditions={'function': check_repo},
1131 conditions={'function': check_repo},
1139 requirements=URL_NAME_REQUIREMENTS)
1132 requirements=URL_NAME_REQUIREMENTS)
1140
1133
1141 # must be here for proper group/repo catching pattern
1134 # must be here for proper group/repo catching pattern
1142 _connect_with_slash(
1135 _connect_with_slash(
1143 rmap, 'repo_group_home', '/{group_name}',
1136 rmap, 'repo_group_home', '/{group_name}',
1144 controller='home', action='index_repo_group',
1137 controller='home', action='index_repo_group',
1145 conditions={'function': check_group},
1138 conditions={'function': check_group},
1146 requirements=URL_NAME_REQUIREMENTS)
1139 requirements=URL_NAME_REQUIREMENTS)
1147
1140
1148 # catch all, at the end
1141 # catch all, at the end
1149 _connect_with_slash(
1142 _connect_with_slash(
1150 rmap, 'summary_home', '/{repo_name}', jsroute=True,
1143 rmap, 'summary_home', '/{repo_name}', jsroute=True,
1151 controller='summary', action='index',
1144 controller='summary', action='index',
1152 conditions={'function': check_repo},
1145 conditions={'function': check_repo},
1153 requirements=URL_NAME_REQUIREMENTS)
1146 requirements=URL_NAME_REQUIREMENTS)
1154
1147
1155 return rmap
1148 return rmap
1156
1149
1157
1150
1158 def _connect_with_slash(mapper, name, path, *args, **kwargs):
1151 def _connect_with_slash(mapper, name, path, *args, **kwargs):
1159 """
1152 """
1160 Connect a route with an optional trailing slash in `path`.
1153 Connect a route with an optional trailing slash in `path`.
1161 """
1154 """
1162 mapper.connect(name + '_slash', path + '/', *args, **kwargs)
1155 mapper.connect(name + '_slash', path + '/', *args, **kwargs)
1163 mapper.connect(name, path, *args, **kwargs)
1156 mapper.connect(name, path, *args, **kwargs)
@@ -1,741 +1,677 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 Users crud controller for pylons
22 Users crud controller for pylons
23 """
23 """
24
24
25 import logging
25 import logging
26 import formencode
26 import formencode
27
27
28 from formencode import htmlfill
28 from formencode import htmlfill
29 from pylons import request, tmpl_context as c, url, config
29 from pylons import request, tmpl_context as c, url, config
30 from pylons.controllers.util import redirect
30 from pylons.controllers.util import redirect
31 from pylons.i18n.translation import _
31 from pylons.i18n.translation import _
32
32
33 from rhodecode.authentication.plugins import auth_rhodecode
33 from rhodecode.authentication.plugins import auth_rhodecode
34 from rhodecode.lib.exceptions import (
34 from rhodecode.lib.exceptions import (
35 DefaultUserException, UserOwnsReposException, UserOwnsRepoGroupsException,
35 DefaultUserException, UserOwnsReposException, UserOwnsRepoGroupsException,
36 UserOwnsUserGroupsException, UserCreationError)
36 UserOwnsUserGroupsException, UserCreationError)
37 from rhodecode.lib import helpers as h
37 from rhodecode.lib import helpers as h
38 from rhodecode.lib import auth
38 from rhodecode.lib import auth
39 from rhodecode.lib.auth import (
39 from rhodecode.lib.auth import (
40 LoginRequired, HasPermissionAllDecorator, AuthUser, generate_auth_token)
40 LoginRequired, HasPermissionAllDecorator, AuthUser, generate_auth_token)
41 from rhodecode.lib.base import BaseController, render
41 from rhodecode.lib.base import BaseController, render
42 from rhodecode.model.auth_token import AuthTokenModel
42 from rhodecode.model.auth_token import AuthTokenModel
43
43
44 from rhodecode.model.db import (
44 from rhodecode.model.db import (
45 PullRequestReviewers, User, UserEmailMap, UserIpMap, RepoGroup)
45 PullRequestReviewers, User, UserEmailMap, UserIpMap, RepoGroup)
46 from rhodecode.model.forms import (
46 from rhodecode.model.forms import (
47 UserForm, UserPermissionsForm, UserIndividualPermissionsForm)
47 UserForm, UserPermissionsForm, UserIndividualPermissionsForm)
48 from rhodecode.model.repo_group import RepoGroupModel
48 from rhodecode.model.repo_group import RepoGroupModel
49 from rhodecode.model.user import UserModel
49 from rhodecode.model.user import UserModel
50 from rhodecode.model.meta import Session
50 from rhodecode.model.meta import Session
51 from rhodecode.model.permission import PermissionModel
51 from rhodecode.model.permission import PermissionModel
52 from rhodecode.lib.utils import action_logger
52 from rhodecode.lib.utils import action_logger
53 from rhodecode.lib.ext_json import json
53 from rhodecode.lib.ext_json import json
54 from rhodecode.lib.utils2 import datetime_to_time, safe_int, AttributeDict
54 from rhodecode.lib.utils2 import datetime_to_time, safe_int, AttributeDict
55
55
56 log = logging.getLogger(__name__)
56 log = logging.getLogger(__name__)
57
57
58
58
59 class UsersController(BaseController):
59 class UsersController(BaseController):
60 """REST Controller styled on the Atom Publishing Protocol"""
60 """REST Controller styled on the Atom Publishing Protocol"""
61
61
62 @LoginRequired()
62 @LoginRequired()
63 def __before__(self):
63 def __before__(self):
64 super(UsersController, self).__before__()
64 super(UsersController, self).__before__()
65 c.available_permissions = config['available_permissions']
65 c.available_permissions = config['available_permissions']
66 c.allowed_languages = [
66 c.allowed_languages = [
67 ('en', 'English (en)'),
67 ('en', 'English (en)'),
68 ('de', 'German (de)'),
68 ('de', 'German (de)'),
69 ('fr', 'French (fr)'),
69 ('fr', 'French (fr)'),
70 ('it', 'Italian (it)'),
70 ('it', 'Italian (it)'),
71 ('ja', 'Japanese (ja)'),
71 ('ja', 'Japanese (ja)'),
72 ('pl', 'Polish (pl)'),
72 ('pl', 'Polish (pl)'),
73 ('pt', 'Portuguese (pt)'),
73 ('pt', 'Portuguese (pt)'),
74 ('ru', 'Russian (ru)'),
74 ('ru', 'Russian (ru)'),
75 ('zh', 'Chinese (zh)'),
75 ('zh', 'Chinese (zh)'),
76 ]
76 ]
77 PermissionModel().set_global_permission_choices(c, gettext_translator=_)
77 PermissionModel().set_global_permission_choices(c, gettext_translator=_)
78
78
79 @HasPermissionAllDecorator('hg.admin')
79 @HasPermissionAllDecorator('hg.admin')
80 def index(self):
80 def index(self):
81 """GET /users: All items in the collection"""
81 """GET /users: All items in the collection"""
82 # url('users')
82 # url('users')
83
83
84 from rhodecode.lib.utils import PartialRenderer
84 from rhodecode.lib.utils import PartialRenderer
85 _render = PartialRenderer('data_table/_dt_elements.mako')
85 _render = PartialRenderer('data_table/_dt_elements.mako')
86
86
87 def username(user_id, username):
87 def username(user_id, username):
88 return _render("user_name", user_id, username)
88 return _render("user_name", user_id, username)
89
89
90 def user_actions(user_id, username):
90 def user_actions(user_id, username):
91 return _render("user_actions", user_id, username)
91 return _render("user_actions", user_id, username)
92
92
93 # json generate
93 # json generate
94 c.users_list = User.query()\
94 c.users_list = User.query()\
95 .filter(User.username != User.DEFAULT_USER) \
95 .filter(User.username != User.DEFAULT_USER) \
96 .all()
96 .all()
97
97
98 users_data = []
98 users_data = []
99 for user in c.users_list:
99 for user in c.users_list:
100 users_data.append({
100 users_data.append({
101 "username": h.gravatar_with_user(user.username),
101 "username": h.gravatar_with_user(user.username),
102 "username_raw": user.username,
102 "username_raw": user.username,
103 "email": user.email,
103 "email": user.email,
104 "first_name": h.escape(user.name),
104 "first_name": h.escape(user.name),
105 "last_name": h.escape(user.lastname),
105 "last_name": h.escape(user.lastname),
106 "last_login": h.format_date(user.last_login),
106 "last_login": h.format_date(user.last_login),
107 "last_login_raw": datetime_to_time(user.last_login),
107 "last_login_raw": datetime_to_time(user.last_login),
108 "last_activity": h.format_date(
108 "last_activity": h.format_date(
109 h.time_to_datetime(user.user_data.get('last_activity', 0))),
109 h.time_to_datetime(user.user_data.get('last_activity', 0))),
110 "last_activity_raw": user.user_data.get('last_activity', 0),
110 "last_activity_raw": user.user_data.get('last_activity', 0),
111 "active": h.bool2icon(user.active),
111 "active": h.bool2icon(user.active),
112 "active_raw": user.active,
112 "active_raw": user.active,
113 "admin": h.bool2icon(user.admin),
113 "admin": h.bool2icon(user.admin),
114 "admin_raw": user.admin,
114 "admin_raw": user.admin,
115 "extern_type": user.extern_type,
115 "extern_type": user.extern_type,
116 "extern_name": user.extern_name,
116 "extern_name": user.extern_name,
117 "action": user_actions(user.user_id, user.username),
117 "action": user_actions(user.user_id, user.username),
118 })
118 })
119
119
120
120
121 c.data = json.dumps(users_data)
121 c.data = json.dumps(users_data)
122 return render('admin/users/users.mako')
122 return render('admin/users/users.mako')
123
123
124 def _get_personal_repo_group_template_vars(self):
124 def _get_personal_repo_group_template_vars(self):
125 DummyUser = AttributeDict({
125 DummyUser = AttributeDict({
126 'username': '${username}',
126 'username': '${username}',
127 'user_id': '${user_id}',
127 'user_id': '${user_id}',
128 })
128 })
129 c.default_create_repo_group = RepoGroupModel() \
129 c.default_create_repo_group = RepoGroupModel() \
130 .get_default_create_personal_repo_group()
130 .get_default_create_personal_repo_group()
131 c.personal_repo_group_name = RepoGroupModel() \
131 c.personal_repo_group_name = RepoGroupModel() \
132 .get_personal_group_name(DummyUser)
132 .get_personal_group_name(DummyUser)
133
133
134 @HasPermissionAllDecorator('hg.admin')
134 @HasPermissionAllDecorator('hg.admin')
135 @auth.CSRFRequired()
135 @auth.CSRFRequired()
136 def create(self):
136 def create(self):
137 """POST /users: Create a new item"""
137 """POST /users: Create a new item"""
138 # url('users')
138 # url('users')
139 c.default_extern_type = auth_rhodecode.RhodeCodeAuthPlugin.name
139 c.default_extern_type = auth_rhodecode.RhodeCodeAuthPlugin.name
140 user_model = UserModel()
140 user_model = UserModel()
141 user_form = UserForm()()
141 user_form = UserForm()()
142 try:
142 try:
143 form_result = user_form.to_python(dict(request.POST))
143 form_result = user_form.to_python(dict(request.POST))
144 user = user_model.create(form_result)
144 user = user_model.create(form_result)
145 Session().flush()
145 Session().flush()
146 username = form_result['username']
146 username = form_result['username']
147 action_logger(c.rhodecode_user, 'admin_created_user:%s' % username,
147 action_logger(c.rhodecode_user, 'admin_created_user:%s' % username,
148 None, self.ip_addr, self.sa)
148 None, self.ip_addr, self.sa)
149
149
150 user_link = h.link_to(h.escape(username),
150 user_link = h.link_to(h.escape(username),
151 url('edit_user',
151 url('edit_user',
152 user_id=user.user_id))
152 user_id=user.user_id))
153 h.flash(h.literal(_('Created user %(user_link)s')
153 h.flash(h.literal(_('Created user %(user_link)s')
154 % {'user_link': user_link}), category='success')
154 % {'user_link': user_link}), category='success')
155 Session().commit()
155 Session().commit()
156 except formencode.Invalid as errors:
156 except formencode.Invalid as errors:
157 self._get_personal_repo_group_template_vars()
157 self._get_personal_repo_group_template_vars()
158 return htmlfill.render(
158 return htmlfill.render(
159 render('admin/users/user_add.mako'),
159 render('admin/users/user_add.mako'),
160 defaults=errors.value,
160 defaults=errors.value,
161 errors=errors.error_dict or {},
161 errors=errors.error_dict or {},
162 prefix_error=False,
162 prefix_error=False,
163 encoding="UTF-8",
163 encoding="UTF-8",
164 force_defaults=False)
164 force_defaults=False)
165 except UserCreationError as e:
165 except UserCreationError as e:
166 h.flash(e, 'error')
166 h.flash(e, 'error')
167 except Exception:
167 except Exception:
168 log.exception("Exception creation of user")
168 log.exception("Exception creation of user")
169 h.flash(_('Error occurred during creation of user %s')
169 h.flash(_('Error occurred during creation of user %s')
170 % request.POST.get('username'), category='error')
170 % request.POST.get('username'), category='error')
171 return redirect(url('users'))
171 return redirect(url('users'))
172
172
173 @HasPermissionAllDecorator('hg.admin')
173 @HasPermissionAllDecorator('hg.admin')
174 def new(self):
174 def new(self):
175 """GET /users/new: Form to create a new item"""
175 """GET /users/new: Form to create a new item"""
176 # url('new_user')
176 # url('new_user')
177 c.default_extern_type = auth_rhodecode.RhodeCodeAuthPlugin.name
177 c.default_extern_type = auth_rhodecode.RhodeCodeAuthPlugin.name
178 self._get_personal_repo_group_template_vars()
178 self._get_personal_repo_group_template_vars()
179 return render('admin/users/user_add.mako')
179 return render('admin/users/user_add.mako')
180
180
181 @HasPermissionAllDecorator('hg.admin')
181 @HasPermissionAllDecorator('hg.admin')
182 @auth.CSRFRequired()
182 @auth.CSRFRequired()
183 def update(self, user_id):
183 def update(self, user_id):
184 """PUT /users/user_id: Update an existing item"""
184 """PUT /users/user_id: Update an existing item"""
185 # Forms posted to this method should contain a hidden field:
185 # Forms posted to this method should contain a hidden field:
186 # <input type="hidden" name="_method" value="PUT" />
186 # <input type="hidden" name="_method" value="PUT" />
187 # Or using helpers:
187 # Or using helpers:
188 # h.form(url('update_user', user_id=ID),
188 # h.form(url('update_user', user_id=ID),
189 # method='put')
189 # method='put')
190 # url('user', user_id=ID)
190 # url('user', user_id=ID)
191 user_id = safe_int(user_id)
191 user_id = safe_int(user_id)
192 c.user = User.get_or_404(user_id)
192 c.user = User.get_or_404(user_id)
193 c.active = 'profile'
193 c.active = 'profile'
194 c.extern_type = c.user.extern_type
194 c.extern_type = c.user.extern_type
195 c.extern_name = c.user.extern_name
195 c.extern_name = c.user.extern_name
196 c.perm_user = AuthUser(user_id=user_id, ip_addr=self.ip_addr)
196 c.perm_user = AuthUser(user_id=user_id, ip_addr=self.ip_addr)
197 available_languages = [x[0] for x in c.allowed_languages]
197 available_languages = [x[0] for x in c.allowed_languages]
198 _form = UserForm(edit=True, available_languages=available_languages,
198 _form = UserForm(edit=True, available_languages=available_languages,
199 old_data={'user_id': user_id,
199 old_data={'user_id': user_id,
200 'email': c.user.email})()
200 'email': c.user.email})()
201 form_result = {}
201 form_result = {}
202 try:
202 try:
203 form_result = _form.to_python(dict(request.POST))
203 form_result = _form.to_python(dict(request.POST))
204 skip_attrs = ['extern_type', 'extern_name']
204 skip_attrs = ['extern_type', 'extern_name']
205 # TODO: plugin should define if username can be updated
205 # TODO: plugin should define if username can be updated
206 if c.extern_type != "rhodecode":
206 if c.extern_type != "rhodecode":
207 # forbid updating username for external accounts
207 # forbid updating username for external accounts
208 skip_attrs.append('username')
208 skip_attrs.append('username')
209
209
210 UserModel().update_user(user_id, skip_attrs=skip_attrs, **form_result)
210 UserModel().update_user(user_id, skip_attrs=skip_attrs, **form_result)
211 usr = form_result['username']
211 usr = form_result['username']
212 action_logger(c.rhodecode_user, 'admin_updated_user:%s' % usr,
212 action_logger(c.rhodecode_user, 'admin_updated_user:%s' % usr,
213 None, self.ip_addr, self.sa)
213 None, self.ip_addr, self.sa)
214 h.flash(_('User updated successfully'), category='success')
214 h.flash(_('User updated successfully'), category='success')
215 Session().commit()
215 Session().commit()
216 except formencode.Invalid as errors:
216 except formencode.Invalid as errors:
217 defaults = errors.value
217 defaults = errors.value
218 e = errors.error_dict or {}
218 e = errors.error_dict or {}
219
219
220 return htmlfill.render(
220 return htmlfill.render(
221 render('admin/users/user_edit.mako'),
221 render('admin/users/user_edit.mako'),
222 defaults=defaults,
222 defaults=defaults,
223 errors=e,
223 errors=e,
224 prefix_error=False,
224 prefix_error=False,
225 encoding="UTF-8",
225 encoding="UTF-8",
226 force_defaults=False)
226 force_defaults=False)
227 except UserCreationError as e:
227 except UserCreationError as e:
228 h.flash(e, 'error')
228 h.flash(e, 'error')
229 except Exception:
229 except Exception:
230 log.exception("Exception updating user")
230 log.exception("Exception updating user")
231 h.flash(_('Error occurred during update of user %s')
231 h.flash(_('Error occurred during update of user %s')
232 % form_result.get('username'), category='error')
232 % form_result.get('username'), category='error')
233 return redirect(url('edit_user', user_id=user_id))
233 return redirect(url('edit_user', user_id=user_id))
234
234
235 @HasPermissionAllDecorator('hg.admin')
235 @HasPermissionAllDecorator('hg.admin')
236 @auth.CSRFRequired()
236 @auth.CSRFRequired()
237 def delete(self, user_id):
237 def delete(self, user_id):
238 """DELETE /users/user_id: Delete an existing item"""
238 """DELETE /users/user_id: Delete an existing item"""
239 # Forms posted to this method should contain a hidden field:
239 # Forms posted to this method should contain a hidden field:
240 # <input type="hidden" name="_method" value="DELETE" />
240 # <input type="hidden" name="_method" value="DELETE" />
241 # Or using helpers:
241 # Or using helpers:
242 # h.form(url('delete_user', user_id=ID),
242 # h.form(url('delete_user', user_id=ID),
243 # method='delete')
243 # method='delete')
244 # url('user', user_id=ID)
244 # url('user', user_id=ID)
245 user_id = safe_int(user_id)
245 user_id = safe_int(user_id)
246 c.user = User.get_or_404(user_id)
246 c.user = User.get_or_404(user_id)
247
247
248 _repos = c.user.repositories
248 _repos = c.user.repositories
249 _repo_groups = c.user.repository_groups
249 _repo_groups = c.user.repository_groups
250 _user_groups = c.user.user_groups
250 _user_groups = c.user.user_groups
251
251
252 handle_repos = None
252 handle_repos = None
253 handle_repo_groups = None
253 handle_repo_groups = None
254 handle_user_groups = None
254 handle_user_groups = None
255 # dummy call for flash of handle
255 # dummy call for flash of handle
256 set_handle_flash_repos = lambda: None
256 set_handle_flash_repos = lambda: None
257 set_handle_flash_repo_groups = lambda: None
257 set_handle_flash_repo_groups = lambda: None
258 set_handle_flash_user_groups = lambda: None
258 set_handle_flash_user_groups = lambda: None
259
259
260 if _repos and request.POST.get('user_repos'):
260 if _repos and request.POST.get('user_repos'):
261 do = request.POST['user_repos']
261 do = request.POST['user_repos']
262 if do == 'detach':
262 if do == 'detach':
263 handle_repos = 'detach'
263 handle_repos = 'detach'
264 set_handle_flash_repos = lambda: h.flash(
264 set_handle_flash_repos = lambda: h.flash(
265 _('Detached %s repositories') % len(_repos),
265 _('Detached %s repositories') % len(_repos),
266 category='success')
266 category='success')
267 elif do == 'delete':
267 elif do == 'delete':
268 handle_repos = 'delete'
268 handle_repos = 'delete'
269 set_handle_flash_repos = lambda: h.flash(
269 set_handle_flash_repos = lambda: h.flash(
270 _('Deleted %s repositories') % len(_repos),
270 _('Deleted %s repositories') % len(_repos),
271 category='success')
271 category='success')
272
272
273 if _repo_groups and request.POST.get('user_repo_groups'):
273 if _repo_groups and request.POST.get('user_repo_groups'):
274 do = request.POST['user_repo_groups']
274 do = request.POST['user_repo_groups']
275 if do == 'detach':
275 if do == 'detach':
276 handle_repo_groups = 'detach'
276 handle_repo_groups = 'detach'
277 set_handle_flash_repo_groups = lambda: h.flash(
277 set_handle_flash_repo_groups = lambda: h.flash(
278 _('Detached %s repository groups') % len(_repo_groups),
278 _('Detached %s repository groups') % len(_repo_groups),
279 category='success')
279 category='success')
280 elif do == 'delete':
280 elif do == 'delete':
281 handle_repo_groups = 'delete'
281 handle_repo_groups = 'delete'
282 set_handle_flash_repo_groups = lambda: h.flash(
282 set_handle_flash_repo_groups = lambda: h.flash(
283 _('Deleted %s repository groups') % len(_repo_groups),
283 _('Deleted %s repository groups') % len(_repo_groups),
284 category='success')
284 category='success')
285
285
286 if _user_groups and request.POST.get('user_user_groups'):
286 if _user_groups and request.POST.get('user_user_groups'):
287 do = request.POST['user_user_groups']
287 do = request.POST['user_user_groups']
288 if do == 'detach':
288 if do == 'detach':
289 handle_user_groups = 'detach'
289 handle_user_groups = 'detach'
290 set_handle_flash_user_groups = lambda: h.flash(
290 set_handle_flash_user_groups = lambda: h.flash(
291 _('Detached %s user groups') % len(_user_groups),
291 _('Detached %s user groups') % len(_user_groups),
292 category='success')
292 category='success')
293 elif do == 'delete':
293 elif do == 'delete':
294 handle_user_groups = 'delete'
294 handle_user_groups = 'delete'
295 set_handle_flash_user_groups = lambda: h.flash(
295 set_handle_flash_user_groups = lambda: h.flash(
296 _('Deleted %s user groups') % len(_user_groups),
296 _('Deleted %s user groups') % len(_user_groups),
297 category='success')
297 category='success')
298
298
299 try:
299 try:
300 UserModel().delete(c.user, handle_repos=handle_repos,
300 UserModel().delete(c.user, handle_repos=handle_repos,
301 handle_repo_groups=handle_repo_groups,
301 handle_repo_groups=handle_repo_groups,
302 handle_user_groups=handle_user_groups)
302 handle_user_groups=handle_user_groups)
303 Session().commit()
303 Session().commit()
304 set_handle_flash_repos()
304 set_handle_flash_repos()
305 set_handle_flash_repo_groups()
305 set_handle_flash_repo_groups()
306 set_handle_flash_user_groups()
306 set_handle_flash_user_groups()
307 h.flash(_('Successfully deleted user'), category='success')
307 h.flash(_('Successfully deleted user'), category='success')
308 except (UserOwnsReposException, UserOwnsRepoGroupsException,
308 except (UserOwnsReposException, UserOwnsRepoGroupsException,
309 UserOwnsUserGroupsException, DefaultUserException) as e:
309 UserOwnsUserGroupsException, DefaultUserException) as e:
310 h.flash(e, category='warning')
310 h.flash(e, category='warning')
311 except Exception:
311 except Exception:
312 log.exception("Exception during deletion of user")
312 log.exception("Exception during deletion of user")
313 h.flash(_('An error occurred during deletion of user'),
313 h.flash(_('An error occurred during deletion of user'),
314 category='error')
314 category='error')
315 return redirect(url('users'))
315 return redirect(url('users'))
316
316
317 @HasPermissionAllDecorator('hg.admin')
317 @HasPermissionAllDecorator('hg.admin')
318 @auth.CSRFRequired()
318 @auth.CSRFRequired()
319 def reset_password(self, user_id):
319 def reset_password(self, user_id):
320 """
320 """
321 toggle reset password flag for this user
321 toggle reset password flag for this user
322
322
323 :param user_id:
323 :param user_id:
324 """
324 """
325 user_id = safe_int(user_id)
325 user_id = safe_int(user_id)
326 c.user = User.get_or_404(user_id)
326 c.user = User.get_or_404(user_id)
327 try:
327 try:
328 old_value = c.user.user_data.get('force_password_change')
328 old_value = c.user.user_data.get('force_password_change')
329 c.user.update_userdata(force_password_change=not old_value)
329 c.user.update_userdata(force_password_change=not old_value)
330 Session().commit()
330 Session().commit()
331 if old_value:
331 if old_value:
332 msg = _('Force password change disabled for user')
332 msg = _('Force password change disabled for user')
333 else:
333 else:
334 msg = _('Force password change enabled for user')
334 msg = _('Force password change enabled for user')
335 h.flash(msg, category='success')
335 h.flash(msg, category='success')
336 except Exception:
336 except Exception:
337 log.exception("Exception during password reset for user")
337 log.exception("Exception during password reset for user")
338 h.flash(_('An error occurred during password reset for user'),
338 h.flash(_('An error occurred during password reset for user'),
339 category='error')
339 category='error')
340
340
341 return redirect(url('edit_user_advanced', user_id=user_id))
341 return redirect(url('edit_user_advanced', user_id=user_id))
342
342
343 @HasPermissionAllDecorator('hg.admin')
343 @HasPermissionAllDecorator('hg.admin')
344 @auth.CSRFRequired()
344 @auth.CSRFRequired()
345 def create_personal_repo_group(self, user_id):
345 def create_personal_repo_group(self, user_id):
346 """
346 """
347 Create personal repository group for this user
347 Create personal repository group for this user
348
348
349 :param user_id:
349 :param user_id:
350 """
350 """
351 from rhodecode.model.repo_group import RepoGroupModel
351 from rhodecode.model.repo_group import RepoGroupModel
352
352
353 user_id = safe_int(user_id)
353 user_id = safe_int(user_id)
354 c.user = User.get_or_404(user_id)
354 c.user = User.get_or_404(user_id)
355 personal_repo_group = RepoGroup.get_user_personal_repo_group(
355 personal_repo_group = RepoGroup.get_user_personal_repo_group(
356 c.user.user_id)
356 c.user.user_id)
357 if personal_repo_group:
357 if personal_repo_group:
358 return redirect(url('edit_user_advanced', user_id=user_id))
358 return redirect(url('edit_user_advanced', user_id=user_id))
359
359
360 personal_repo_group_name = RepoGroupModel().get_personal_group_name(
360 personal_repo_group_name = RepoGroupModel().get_personal_group_name(
361 c.user)
361 c.user)
362 named_personal_group = RepoGroup.get_by_group_name(
362 named_personal_group = RepoGroup.get_by_group_name(
363 personal_repo_group_name)
363 personal_repo_group_name)
364 try:
364 try:
365
365
366 if named_personal_group and named_personal_group.user_id == c.user.user_id:
366 if named_personal_group and named_personal_group.user_id == c.user.user_id:
367 # migrate the same named group, and mark it as personal
367 # migrate the same named group, and mark it as personal
368 named_personal_group.personal = True
368 named_personal_group.personal = True
369 Session().add(named_personal_group)
369 Session().add(named_personal_group)
370 Session().commit()
370 Session().commit()
371 msg = _('Linked repository group `%s` as personal' % (
371 msg = _('Linked repository group `%s` as personal' % (
372 personal_repo_group_name,))
372 personal_repo_group_name,))
373 h.flash(msg, category='success')
373 h.flash(msg, category='success')
374 elif not named_personal_group:
374 elif not named_personal_group:
375 RepoGroupModel().create_personal_repo_group(c.user)
375 RepoGroupModel().create_personal_repo_group(c.user)
376
376
377 msg = _('Created repository group `%s`' % (
377 msg = _('Created repository group `%s`' % (
378 personal_repo_group_name,))
378 personal_repo_group_name,))
379 h.flash(msg, category='success')
379 h.flash(msg, category='success')
380 else:
380 else:
381 msg = _('Repository group `%s` is already taken' % (
381 msg = _('Repository group `%s` is already taken' % (
382 personal_repo_group_name,))
382 personal_repo_group_name,))
383 h.flash(msg, category='warning')
383 h.flash(msg, category='warning')
384 except Exception:
384 except Exception:
385 log.exception("Exception during repository group creation")
385 log.exception("Exception during repository group creation")
386 msg = _(
386 msg = _(
387 'An error occurred during repository group creation for user')
387 'An error occurred during repository group creation for user')
388 h.flash(msg, category='error')
388 h.flash(msg, category='error')
389 Session().rollback()
389 Session().rollback()
390
390
391 return redirect(url('edit_user_advanced', user_id=user_id))
391 return redirect(url('edit_user_advanced', user_id=user_id))
392
392
393 @HasPermissionAllDecorator('hg.admin')
393 @HasPermissionAllDecorator('hg.admin')
394 def show(self, user_id):
394 def show(self, user_id):
395 """GET /users/user_id: Show a specific item"""
395 """GET /users/user_id: Show a specific item"""
396 # url('user', user_id=ID)
396 # url('user', user_id=ID)
397 User.get_or_404(-1)
397 User.get_or_404(-1)
398
398
399 @HasPermissionAllDecorator('hg.admin')
399 @HasPermissionAllDecorator('hg.admin')
400 def edit(self, user_id):
400 def edit(self, user_id):
401 """GET /users/user_id/edit: Form to edit an existing item"""
401 """GET /users/user_id/edit: Form to edit an existing item"""
402 # url('edit_user', user_id=ID)
402 # url('edit_user', user_id=ID)
403 user_id = safe_int(user_id)
403 user_id = safe_int(user_id)
404 c.user = User.get_or_404(user_id)
404 c.user = User.get_or_404(user_id)
405 if c.user.username == User.DEFAULT_USER:
405 if c.user.username == User.DEFAULT_USER:
406 h.flash(_("You can't edit this user"), category='warning')
406 h.flash(_("You can't edit this user"), category='warning')
407 return redirect(url('users'))
407 return redirect(url('users'))
408
408
409 c.active = 'profile'
409 c.active = 'profile'
410 c.extern_type = c.user.extern_type
410 c.extern_type = c.user.extern_type
411 c.extern_name = c.user.extern_name
411 c.extern_name = c.user.extern_name
412 c.perm_user = AuthUser(user_id=user_id, ip_addr=self.ip_addr)
412 c.perm_user = AuthUser(user_id=user_id, ip_addr=self.ip_addr)
413
413
414 defaults = c.user.get_dict()
414 defaults = c.user.get_dict()
415 defaults.update({'language': c.user.user_data.get('language')})
415 defaults.update({'language': c.user.user_data.get('language')})
416 return htmlfill.render(
416 return htmlfill.render(
417 render('admin/users/user_edit.mako'),
417 render('admin/users/user_edit.mako'),
418 defaults=defaults,
418 defaults=defaults,
419 encoding="UTF-8",
419 encoding="UTF-8",
420 force_defaults=False)
420 force_defaults=False)
421
421
422 @HasPermissionAllDecorator('hg.admin')
422 @HasPermissionAllDecorator('hg.admin')
423 def edit_advanced(self, user_id):
423 def edit_advanced(self, user_id):
424 user_id = safe_int(user_id)
424 user_id = safe_int(user_id)
425 user = c.user = User.get_or_404(user_id)
425 user = c.user = User.get_or_404(user_id)
426 if user.username == User.DEFAULT_USER:
426 if user.username == User.DEFAULT_USER:
427 h.flash(_("You can't edit this user"), category='warning')
427 h.flash(_("You can't edit this user"), category='warning')
428 return redirect(url('users'))
428 return redirect(url('users'))
429
429
430 c.active = 'advanced'
430 c.active = 'advanced'
431 c.perm_user = AuthUser(user_id=user_id, ip_addr=self.ip_addr)
431 c.perm_user = AuthUser(user_id=user_id, ip_addr=self.ip_addr)
432 c.personal_repo_group = c.perm_user.personal_repo_group
432 c.personal_repo_group = c.perm_user.personal_repo_group
433 c.personal_repo_group_name = RepoGroupModel()\
433 c.personal_repo_group_name = RepoGroupModel()\
434 .get_personal_group_name(user)
434 .get_personal_group_name(user)
435 c.first_admin = User.get_first_super_admin()
435 c.first_admin = User.get_first_super_admin()
436 defaults = user.get_dict()
436 defaults = user.get_dict()
437
437
438 # Interim workaround if the user participated on any pull requests as a
438 # Interim workaround if the user participated on any pull requests as a
439 # reviewer.
439 # reviewer.
440 has_review = bool(PullRequestReviewers.query().filter(
440 has_review = bool(PullRequestReviewers.query().filter(
441 PullRequestReviewers.user_id == user_id).first())
441 PullRequestReviewers.user_id == user_id).first())
442 c.can_delete_user = not has_review
442 c.can_delete_user = not has_review
443 c.can_delete_user_message = _(
443 c.can_delete_user_message = _(
444 'The user participates as reviewer in pull requests and '
444 'The user participates as reviewer in pull requests and '
445 'cannot be deleted. You can set the user to '
445 'cannot be deleted. You can set the user to '
446 '"inactive" instead of deleting it.') if has_review else ''
446 '"inactive" instead of deleting it.') if has_review else ''
447
447
448 return htmlfill.render(
448 return htmlfill.render(
449 render('admin/users/user_edit.mako'),
449 render('admin/users/user_edit.mako'),
450 defaults=defaults,
450 defaults=defaults,
451 encoding="UTF-8",
451 encoding="UTF-8",
452 force_defaults=False)
452 force_defaults=False)
453
453
454 @HasPermissionAllDecorator('hg.admin')
454 @HasPermissionAllDecorator('hg.admin')
455 def edit_auth_tokens(self, user_id):
456 user_id = safe_int(user_id)
457 c.user = User.get_or_404(user_id)
458 if c.user.username == User.DEFAULT_USER:
459 h.flash(_("You can't edit this user"), category='warning')
460 return redirect(url('users'))
461
462 c.active = 'auth_tokens'
463 show_expired = True
464 c.lifetime_values = [
465 (str(-1), _('forever')),
466 (str(5), _('5 minutes')),
467 (str(60), _('1 hour')),
468 (str(60 * 24), _('1 day')),
469 (str(60 * 24 * 30), _('1 month')),
470 ]
471 c.lifetime_options = [(c.lifetime_values, _("Lifetime"))]
472 c.role_values = [(x, AuthTokenModel.cls._get_role_name(x))
473 for x in AuthTokenModel.cls.ROLES]
474 c.role_options = [(c.role_values, _("Role"))]
475 c.user_auth_tokens = AuthTokenModel().get_auth_tokens(
476 c.user.user_id, show_expired=show_expired)
477 defaults = c.user.get_dict()
478 return htmlfill.render(
479 render('admin/users/user_edit.mako'),
480 defaults=defaults,
481 encoding="UTF-8",
482 force_defaults=False)
483
484 @HasPermissionAllDecorator('hg.admin')
485 @auth.CSRFRequired()
486 def add_auth_token(self, user_id):
487 user_id = safe_int(user_id)
488 c.user = User.get_or_404(user_id)
489 if c.user.username == User.DEFAULT_USER:
490 h.flash(_("You can't edit this user"), category='warning')
491 return redirect(url('users'))
492
493 lifetime = safe_int(request.POST.get('lifetime'), -1)
494 description = request.POST.get('description')
495 role = request.POST.get('role')
496 AuthTokenModel().create(c.user.user_id, description, lifetime, role)
497 Session().commit()
498 h.flash(_("Auth token successfully created"), category='success')
499 return redirect(url('edit_user_auth_tokens', user_id=c.user.user_id))
500
501 @HasPermissionAllDecorator('hg.admin')
502 @auth.CSRFRequired()
503 def delete_auth_token(self, user_id):
504 user_id = safe_int(user_id)
505 c.user = User.get_or_404(user_id)
506 if c.user.username == User.DEFAULT_USER:
507 h.flash(_("You can't edit this user"), category='warning')
508 return redirect(url('users'))
509
510 del_auth_token = request.POST.get('del_auth_token')
511 if del_auth_token:
512 AuthTokenModel().delete(del_auth_token, c.user.user_id)
513 Session().commit()
514 h.flash(_("Auth token successfully deleted"), category='success')
515
516 return redirect(url('edit_user_auth_tokens', user_id=c.user.user_id))
517
518 @HasPermissionAllDecorator('hg.admin')
519 def edit_global_perms(self, user_id):
455 def edit_global_perms(self, user_id):
520 user_id = safe_int(user_id)
456 user_id = safe_int(user_id)
521 c.user = User.get_or_404(user_id)
457 c.user = User.get_or_404(user_id)
522 if c.user.username == User.DEFAULT_USER:
458 if c.user.username == User.DEFAULT_USER:
523 h.flash(_("You can't edit this user"), category='warning')
459 h.flash(_("You can't edit this user"), category='warning')
524 return redirect(url('users'))
460 return redirect(url('users'))
525
461
526 c.active = 'global_perms'
462 c.active = 'global_perms'
527
463
528 c.default_user = User.get_default_user()
464 c.default_user = User.get_default_user()
529 defaults = c.user.get_dict()
465 defaults = c.user.get_dict()
530 defaults.update(c.default_user.get_default_perms(suffix='_inherited'))
466 defaults.update(c.default_user.get_default_perms(suffix='_inherited'))
531 defaults.update(c.default_user.get_default_perms())
467 defaults.update(c.default_user.get_default_perms())
532 defaults.update(c.user.get_default_perms())
468 defaults.update(c.user.get_default_perms())
533
469
534 return htmlfill.render(
470 return htmlfill.render(
535 render('admin/users/user_edit.mako'),
471 render('admin/users/user_edit.mako'),
536 defaults=defaults,
472 defaults=defaults,
537 encoding="UTF-8",
473 encoding="UTF-8",
538 force_defaults=False)
474 force_defaults=False)
539
475
540 @HasPermissionAllDecorator('hg.admin')
476 @HasPermissionAllDecorator('hg.admin')
541 @auth.CSRFRequired()
477 @auth.CSRFRequired()
542 def update_global_perms(self, user_id):
478 def update_global_perms(self, user_id):
543 """PUT /users_perm/user_id: Update an existing item"""
479 """PUT /users_perm/user_id: Update an existing item"""
544 # url('user_perm', user_id=ID, method='put')
480 # url('user_perm', user_id=ID, method='put')
545 user_id = safe_int(user_id)
481 user_id = safe_int(user_id)
546 user = User.get_or_404(user_id)
482 user = User.get_or_404(user_id)
547 c.active = 'global_perms'
483 c.active = 'global_perms'
548 try:
484 try:
549 # first stage that verifies the checkbox
485 # first stage that verifies the checkbox
550 _form = UserIndividualPermissionsForm()
486 _form = UserIndividualPermissionsForm()
551 form_result = _form.to_python(dict(request.POST))
487 form_result = _form.to_python(dict(request.POST))
552 inherit_perms = form_result['inherit_default_permissions']
488 inherit_perms = form_result['inherit_default_permissions']
553 user.inherit_default_permissions = inherit_perms
489 user.inherit_default_permissions = inherit_perms
554 Session().add(user)
490 Session().add(user)
555
491
556 if not inherit_perms:
492 if not inherit_perms:
557 # only update the individual ones if we un check the flag
493 # only update the individual ones if we un check the flag
558 _form = UserPermissionsForm(
494 _form = UserPermissionsForm(
559 [x[0] for x in c.repo_create_choices],
495 [x[0] for x in c.repo_create_choices],
560 [x[0] for x in c.repo_create_on_write_choices],
496 [x[0] for x in c.repo_create_on_write_choices],
561 [x[0] for x in c.repo_group_create_choices],
497 [x[0] for x in c.repo_group_create_choices],
562 [x[0] for x in c.user_group_create_choices],
498 [x[0] for x in c.user_group_create_choices],
563 [x[0] for x in c.fork_choices],
499 [x[0] for x in c.fork_choices],
564 [x[0] for x in c.inherit_default_permission_choices])()
500 [x[0] for x in c.inherit_default_permission_choices])()
565
501
566 form_result = _form.to_python(dict(request.POST))
502 form_result = _form.to_python(dict(request.POST))
567 form_result.update({'perm_user_id': user.user_id})
503 form_result.update({'perm_user_id': user.user_id})
568
504
569 PermissionModel().update_user_permissions(form_result)
505 PermissionModel().update_user_permissions(form_result)
570
506
571 Session().commit()
507 Session().commit()
572 h.flash(_('User global permissions updated successfully'),
508 h.flash(_('User global permissions updated successfully'),
573 category='success')
509 category='success')
574
510
575 Session().commit()
511 Session().commit()
576 except formencode.Invalid as errors:
512 except formencode.Invalid as errors:
577 defaults = errors.value
513 defaults = errors.value
578 c.user = user
514 c.user = user
579 return htmlfill.render(
515 return htmlfill.render(
580 render('admin/users/user_edit.mako'),
516 render('admin/users/user_edit.mako'),
581 defaults=defaults,
517 defaults=defaults,
582 errors=errors.error_dict or {},
518 errors=errors.error_dict or {},
583 prefix_error=False,
519 prefix_error=False,
584 encoding="UTF-8",
520 encoding="UTF-8",
585 force_defaults=False)
521 force_defaults=False)
586 except Exception:
522 except Exception:
587 log.exception("Exception during permissions saving")
523 log.exception("Exception during permissions saving")
588 h.flash(_('An error occurred during permissions saving'),
524 h.flash(_('An error occurred during permissions saving'),
589 category='error')
525 category='error')
590 return redirect(url('edit_user_global_perms', user_id=user_id))
526 return redirect(url('edit_user_global_perms', user_id=user_id))
591
527
592 @HasPermissionAllDecorator('hg.admin')
528 @HasPermissionAllDecorator('hg.admin')
593 def edit_perms_summary(self, user_id):
529 def edit_perms_summary(self, user_id):
594 user_id = safe_int(user_id)
530 user_id = safe_int(user_id)
595 c.user = User.get_or_404(user_id)
531 c.user = User.get_or_404(user_id)
596 if c.user.username == User.DEFAULT_USER:
532 if c.user.username == User.DEFAULT_USER:
597 h.flash(_("You can't edit this user"), category='warning')
533 h.flash(_("You can't edit this user"), category='warning')
598 return redirect(url('users'))
534 return redirect(url('users'))
599
535
600 c.active = 'perms_summary'
536 c.active = 'perms_summary'
601 c.perm_user = AuthUser(user_id=user_id, ip_addr=self.ip_addr)
537 c.perm_user = AuthUser(user_id=user_id, ip_addr=self.ip_addr)
602
538
603 return render('admin/users/user_edit.mako')
539 return render('admin/users/user_edit.mako')
604
540
605 @HasPermissionAllDecorator('hg.admin')
541 @HasPermissionAllDecorator('hg.admin')
606 def edit_emails(self, user_id):
542 def edit_emails(self, user_id):
607 user_id = safe_int(user_id)
543 user_id = safe_int(user_id)
608 c.user = User.get_or_404(user_id)
544 c.user = User.get_or_404(user_id)
609 if c.user.username == User.DEFAULT_USER:
545 if c.user.username == User.DEFAULT_USER:
610 h.flash(_("You can't edit this user"), category='warning')
546 h.flash(_("You can't edit this user"), category='warning')
611 return redirect(url('users'))
547 return redirect(url('users'))
612
548
613 c.active = 'emails'
549 c.active = 'emails'
614 c.user_email_map = UserEmailMap.query() \
550 c.user_email_map = UserEmailMap.query() \
615 .filter(UserEmailMap.user == c.user).all()
551 .filter(UserEmailMap.user == c.user).all()
616
552
617 defaults = c.user.get_dict()
553 defaults = c.user.get_dict()
618 return htmlfill.render(
554 return htmlfill.render(
619 render('admin/users/user_edit.mako'),
555 render('admin/users/user_edit.mako'),
620 defaults=defaults,
556 defaults=defaults,
621 encoding="UTF-8",
557 encoding="UTF-8",
622 force_defaults=False)
558 force_defaults=False)
623
559
624 @HasPermissionAllDecorator('hg.admin')
560 @HasPermissionAllDecorator('hg.admin')
625 @auth.CSRFRequired()
561 @auth.CSRFRequired()
626 def add_email(self, user_id):
562 def add_email(self, user_id):
627 """POST /user_emails:Add an existing item"""
563 """POST /user_emails:Add an existing item"""
628 # url('user_emails', user_id=ID, method='put')
564 # url('user_emails', user_id=ID, method='put')
629 user_id = safe_int(user_id)
565 user_id = safe_int(user_id)
630 c.user = User.get_or_404(user_id)
566 c.user = User.get_or_404(user_id)
631
567
632 email = request.POST.get('new_email')
568 email = request.POST.get('new_email')
633 user_model = UserModel()
569 user_model = UserModel()
634
570
635 try:
571 try:
636 user_model.add_extra_email(user_id, email)
572 user_model.add_extra_email(user_id, email)
637 Session().commit()
573 Session().commit()
638 h.flash(_("Added new email address `%s` for user account") % email,
574 h.flash(_("Added new email address `%s` for user account") % email,
639 category='success')
575 category='success')
640 except formencode.Invalid as error:
576 except formencode.Invalid as error:
641 msg = error.error_dict['email']
577 msg = error.error_dict['email']
642 h.flash(msg, category='error')
578 h.flash(msg, category='error')
643 except Exception:
579 except Exception:
644 log.exception("Exception during email saving")
580 log.exception("Exception during email saving")
645 h.flash(_('An error occurred during email saving'),
581 h.flash(_('An error occurred during email saving'),
646 category='error')
582 category='error')
647 return redirect(url('edit_user_emails', user_id=user_id))
583 return redirect(url('edit_user_emails', user_id=user_id))
648
584
649 @HasPermissionAllDecorator('hg.admin')
585 @HasPermissionAllDecorator('hg.admin')
650 @auth.CSRFRequired()
586 @auth.CSRFRequired()
651 def delete_email(self, user_id):
587 def delete_email(self, user_id):
652 """DELETE /user_emails_delete/user_id: Delete an existing item"""
588 """DELETE /user_emails_delete/user_id: Delete an existing item"""
653 # url('user_emails_delete', user_id=ID, method='delete')
589 # url('user_emails_delete', user_id=ID, method='delete')
654 user_id = safe_int(user_id)
590 user_id = safe_int(user_id)
655 c.user = User.get_or_404(user_id)
591 c.user = User.get_or_404(user_id)
656 email_id = request.POST.get('del_email_id')
592 email_id = request.POST.get('del_email_id')
657 user_model = UserModel()
593 user_model = UserModel()
658 user_model.delete_extra_email(user_id, email_id)
594 user_model.delete_extra_email(user_id, email_id)
659 Session().commit()
595 Session().commit()
660 h.flash(_("Removed email address from user account"), category='success')
596 h.flash(_("Removed email address from user account"), category='success')
661 return redirect(url('edit_user_emails', user_id=user_id))
597 return redirect(url('edit_user_emails', user_id=user_id))
662
598
663 @HasPermissionAllDecorator('hg.admin')
599 @HasPermissionAllDecorator('hg.admin')
664 def edit_ips(self, user_id):
600 def edit_ips(self, user_id):
665 user_id = safe_int(user_id)
601 user_id = safe_int(user_id)
666 c.user = User.get_or_404(user_id)
602 c.user = User.get_or_404(user_id)
667 if c.user.username == User.DEFAULT_USER:
603 if c.user.username == User.DEFAULT_USER:
668 h.flash(_("You can't edit this user"), category='warning')
604 h.flash(_("You can't edit this user"), category='warning')
669 return redirect(url('users'))
605 return redirect(url('users'))
670
606
671 c.active = 'ips'
607 c.active = 'ips'
672 c.user_ip_map = UserIpMap.query() \
608 c.user_ip_map = UserIpMap.query() \
673 .filter(UserIpMap.user == c.user).all()
609 .filter(UserIpMap.user == c.user).all()
674
610
675 c.inherit_default_ips = c.user.inherit_default_permissions
611 c.inherit_default_ips = c.user.inherit_default_permissions
676 c.default_user_ip_map = UserIpMap.query() \
612 c.default_user_ip_map = UserIpMap.query() \
677 .filter(UserIpMap.user == User.get_default_user()).all()
613 .filter(UserIpMap.user == User.get_default_user()).all()
678
614
679 defaults = c.user.get_dict()
615 defaults = c.user.get_dict()
680 return htmlfill.render(
616 return htmlfill.render(
681 render('admin/users/user_edit.mako'),
617 render('admin/users/user_edit.mako'),
682 defaults=defaults,
618 defaults=defaults,
683 encoding="UTF-8",
619 encoding="UTF-8",
684 force_defaults=False)
620 force_defaults=False)
685
621
686 @HasPermissionAllDecorator('hg.admin')
622 @HasPermissionAllDecorator('hg.admin')
687 @auth.CSRFRequired()
623 @auth.CSRFRequired()
688 def add_ip(self, user_id):
624 def add_ip(self, user_id):
689 """POST /user_ips:Add an existing item"""
625 """POST /user_ips:Add an existing item"""
690 # url('user_ips', user_id=ID, method='put')
626 # url('user_ips', user_id=ID, method='put')
691
627
692 user_id = safe_int(user_id)
628 user_id = safe_int(user_id)
693 c.user = User.get_or_404(user_id)
629 c.user = User.get_or_404(user_id)
694 user_model = UserModel()
630 user_model = UserModel()
695 try:
631 try:
696 ip_list = user_model.parse_ip_range(request.POST.get('new_ip'))
632 ip_list = user_model.parse_ip_range(request.POST.get('new_ip'))
697 except Exception as e:
633 except Exception as e:
698 ip_list = []
634 ip_list = []
699 log.exception("Exception during ip saving")
635 log.exception("Exception during ip saving")
700 h.flash(_('An error occurred during ip saving:%s' % (e,)),
636 h.flash(_('An error occurred during ip saving:%s' % (e,)),
701 category='error')
637 category='error')
702
638
703 desc = request.POST.get('description')
639 desc = request.POST.get('description')
704 added = []
640 added = []
705 for ip in ip_list:
641 for ip in ip_list:
706 try:
642 try:
707 user_model.add_extra_ip(user_id, ip, desc)
643 user_model.add_extra_ip(user_id, ip, desc)
708 Session().commit()
644 Session().commit()
709 added.append(ip)
645 added.append(ip)
710 except formencode.Invalid as error:
646 except formencode.Invalid as error:
711 msg = error.error_dict['ip']
647 msg = error.error_dict['ip']
712 h.flash(msg, category='error')
648 h.flash(msg, category='error')
713 except Exception:
649 except Exception:
714 log.exception("Exception during ip saving")
650 log.exception("Exception during ip saving")
715 h.flash(_('An error occurred during ip saving'),
651 h.flash(_('An error occurred during ip saving'),
716 category='error')
652 category='error')
717 if added:
653 if added:
718 h.flash(
654 h.flash(
719 _("Added ips %s to user whitelist") % (', '.join(ip_list), ),
655 _("Added ips %s to user whitelist") % (', '.join(ip_list), ),
720 category='success')
656 category='success')
721 if 'default_user' in request.POST:
657 if 'default_user' in request.POST:
722 return redirect(url('admin_permissions_ips'))
658 return redirect(url('admin_permissions_ips'))
723 return redirect(url('edit_user_ips', user_id=user_id))
659 return redirect(url('edit_user_ips', user_id=user_id))
724
660
725 @HasPermissionAllDecorator('hg.admin')
661 @HasPermissionAllDecorator('hg.admin')
726 @auth.CSRFRequired()
662 @auth.CSRFRequired()
727 def delete_ip(self, user_id):
663 def delete_ip(self, user_id):
728 """DELETE /user_ips_delete/user_id: Delete an existing item"""
664 """DELETE /user_ips_delete/user_id: Delete an existing item"""
729 # url('user_ips_delete', user_id=ID, method='delete')
665 # url('user_ips_delete', user_id=ID, method='delete')
730 user_id = safe_int(user_id)
666 user_id = safe_int(user_id)
731 c.user = User.get_or_404(user_id)
667 c.user = User.get_or_404(user_id)
732
668
733 ip_id = request.POST.get('del_ip_id')
669 ip_id = request.POST.get('del_ip_id')
734 user_model = UserModel()
670 user_model = UserModel()
735 user_model.delete_extra_ip(user_id, ip_id)
671 user_model.delete_extra_ip(user_id, ip_id)
736 Session().commit()
672 Session().commit()
737 h.flash(_("Removed ip address from user whitelist"), category='success')
673 h.flash(_("Removed ip address from user whitelist"), category='success')
738
674
739 if 'default_user' in request.POST:
675 if 'default_user' in request.POST:
740 return redirect(url('admin_permissions_ips'))
676 return redirect(url('admin_permissions_ips'))
741 return redirect(url('edit_user_ips', user_id=user_id))
677 return redirect(url('edit_user_ips', user_id=user_id))
@@ -1,158 +1,160 b''
1 <div class="panel panel-default">
1 <div class="panel panel-default">
2 <div class="panel-heading">
2 <div class="panel-heading">
3 <h3 class="panel-title">${_('Authentication Tokens')}</h3>
3 <h3 class="panel-title">${_('Authentication Tokens')}</h3>
4 </div>
4 </div>
5 <div class="panel-body">
5 <div class="panel-body">
6 <div class="apikeys_wrap">
6 <p>
7 <p>
7 ${_('Each token can have a role. Token with a role can be used only in given context, '
8 ${_('Each token can have a role. Token with a role can be used only in given context, '
8 'e.g. VCS tokens can be used together with the authtoken auth plugin for git/hg/svn operations only.')}
9 'e.g. VCS tokens can be used together with the authtoken auth plugin for git/hg/svn operations only.')}
9 </p>
10 </p>
10 <table class="rctable auth_tokens">
11 <table class="rctable auth_tokens">
11 %if c.user_auth_tokens:
12 <tr>
12 <tr>
13 <th>${_('Token')}</th>
13 <th>${_('Token')}</th>
14 <th>${_('Scope')}</th>
14 <th>${_('Scope')}</th>
15 <th>${_('Description')}</th>
15 <th>${_('Description')}</th>
16 <th>${_('Role')}</th>
16 <th>${_('Role')}</th>
17 <th>${_('Expiration')}</th>
17 <th>${_('Expiration')}</th>
18 <th>${_('Action')}</th>
18 <th>${_('Action')}</th>
19 </tr>
19 </tr>
20 %if c.user_auth_tokens:
20 %for auth_token in c.user_auth_tokens:
21 %for auth_token in c.user_auth_tokens:
21 <tr class="${'expired' if auth_token.expired else ''}">
22 <tr class="${'expired' if auth_token.expired else ''}">
22 <td class="truncate-wrap td-authtoken">
23 <td class="truncate-wrap td-authtoken">
23 <div class="user_auth_tokens truncate autoexpand">
24 <div class="user_auth_tokens truncate autoexpand">
24 <code>${auth_token.api_key}</code>
25 <code>${auth_token.api_key}</code>
25 </div>
26 </div>
26 </td>
27 </td>
27 <td class="td">${auth_token.scope_humanized}</td>
28 <td class="td">${auth_token.scope_humanized}</td>
28 <td class="td-wrap">${auth_token.description}</td>
29 <td class="td-wrap">${auth_token.description}</td>
29 <td class="td-tags">
30 <td class="td-tags">
30 <span class="tag disabled">${auth_token.role_humanized}</span>
31 <span class="tag disabled">${auth_token.role_humanized}</span>
31 </td>
32 </td>
32 <td class="td-exp">
33 <td class="td-exp">
33 %if auth_token.expires == -1:
34 %if auth_token.expires == -1:
34 ${_('never')}
35 ${_('never')}
35 %else:
36 %else:
36 %if auth_token.expired:
37 %if auth_token.expired:
37 <span style="text-decoration: line-through">${h.age_component(h.time_to_utcdatetime(auth_token.expires))}</span>
38 <span style="text-decoration: line-through">${h.age_component(h.time_to_utcdatetime(auth_token.expires))}</span>
38 %else:
39 %else:
39 ${h.age_component(h.time_to_utcdatetime(auth_token.expires))}
40 ${h.age_component(h.time_to_utcdatetime(auth_token.expires))}
40 %endif
41 %endif
41 %endif
42 %endif
42 </td>
43 </td>
43 <td class="td-action">
44 <td class="td-action">
44 ${h.secure_form(h.route_path('my_account_auth_tokens_delete'), method='post')}
45 ${h.secure_form(h.route_path('my_account_auth_tokens_delete'), method='post')}
45 ${h.hidden('del_auth_token',auth_token.api_key)}
46 ${h.hidden('del_auth_token',auth_token.api_key)}
46 <button class="btn btn-link btn-danger" type="submit"
47 <button class="btn btn-link btn-danger" type="submit"
47 onclick="return confirm('${_('Confirm to remove this auth token: %s') % auth_token.api_key}');">
48 onclick="return confirm('${_('Confirm to remove this auth token: %s') % auth_token.api_key}');">
48 ${_('Delete')}
49 ${_('Delete')}
49 </button>
50 </button>
50 ${h.end_form()}
51 ${h.end_form()}
51 </td>
52 </td>
52 </tr>
53 </tr>
53 %endfor
54 %endfor
54 %else:
55 %else:
55 <tr><td><div class="ip">${_('No additional auth token specified')}</div></td></tr>
56 <tr><td><div class="ip">${_('No additional auth tokens specified')}</div></td></tr>
56 %endif
57 %endif
57 </table>
58 </table>
59 </div>
58
60
59 <div class="user_auth_tokens">
61 <div class="user_auth_tokens">
60 ${h.secure_form(h.route_path('my_account_auth_tokens_add'), method='post')}
62 ${h.secure_form(h.route_path('my_account_auth_tokens_add'), method='post')}
61 <div class="form form-vertical">
63 <div class="form form-vertical">
62 <!-- fields -->
64 <!-- fields -->
63 <div class="fields">
65 <div class="fields">
64 <div class="field">
66 <div class="field">
65 <div class="label">
67 <div class="label">
66 <label for="new_email">${_('New authentication token')}:</label>
68 <label for="new_email">${_('New authentication token')}:</label>
67 </div>
69 </div>
68 <div class="input">
70 <div class="input">
69 ${h.text('description', placeholder=_('Description'))}
71 ${h.text('description', class_='medium', placeholder=_('Description'))}
70 ${h.select('lifetime', '', c.lifetime_options)}
72 ${h.select('lifetime', '', c.lifetime_options)}
71 ${h.select('role', '', c.role_options)}
73 ${h.select('role', '', c.role_options)}
72
74
73 % if c.allow_scoped_tokens:
75 % if c.allow_scoped_tokens:
74 ${h.hidden('scope_repo_id')}
76 ${h.hidden('scope_repo_id')}
75 % else:
77 % else:
76 ${h.select('scope_repo_id_disabled', '', ['Scopes available in EE edition'], disabled='disabled')}
78 ${h.select('scope_repo_id_disabled', '', ['Scopes available in EE edition'], disabled='disabled')}
77 % endif
79 % endif
78 </div>
80 </div>
79 <p class="help-block">
81 <p class="help-block">
80 ${_('Repository scope works only with tokens with VCS type.')}
82 ${_('Repository scope works only with tokens with VCS type.')}
81 </p>
83 </p>
82 </div>
84 </div>
83 <div class="buttons">
85 <div class="buttons">
84 ${h.submit('save',_('Add'),class_="btn")}
86 ${h.submit('save',_('Add'),class_="btn")}
85 ${h.reset('reset',_('Reset'),class_="btn")}
87 ${h.reset('reset',_('Reset'),class_="btn")}
86 </div>
88 </div>
87 </div>
89 </div>
88 </div>
90 </div>
89 ${h.end_form()}
91 ${h.end_form()}
90 </div>
92 </div>
91 </div>
93 </div>
92 </div>
94 </div>
93 <script>
95 <script>
94 $(document).ready(function(){
96 $(document).ready(function(){
95
97
96 var select2Options = {
98 var select2Options = {
97 'containerCssClass': "drop-menu",
99 'containerCssClass': "drop-menu",
98 'dropdownCssClass': "drop-menu-dropdown",
100 'dropdownCssClass': "drop-menu-dropdown",
99 'dropdownAutoWidth': true
101 'dropdownAutoWidth': true
100 };
102 };
101 $("#lifetime").select2(select2Options);
103 $("#lifetime").select2(select2Options);
102 $("#role").select2(select2Options);
104 $("#role").select2(select2Options);
103
105
104 var repoFilter = function(data) {
106 var repoFilter = function(data) {
105 var results = [];
107 var results = [];
106
108
107 if (!data.results[0]) {
109 if (!data.results[0]) {
108 return data
110 return data
109 }
111 }
110
112
111 $.each(data.results[0].children, function() {
113 $.each(data.results[0].children, function() {
112 // replace name to ID for submision
114 // replace name to ID for submision
113 this.id = this.obj.repo_id;
115 this.id = this.obj.repo_id;
114 results.push(this);
116 results.push(this);
115 });
117 });
116
118
117 data.results[0].children = results;
119 data.results[0].children = results;
118 return data;
120 return data;
119 };
121 };
120
122
121 $("#scope_repo_id_disabled").select2(select2Options);
123 $("#scope_repo_id_disabled").select2(select2Options);
122
124
123 $("#scope_repo_id").select2({
125 $("#scope_repo_id").select2({
124 cachedDataSource: {},
126 cachedDataSource: {},
125 minimumInputLength: 2,
127 minimumInputLength: 2,
126 placeholder: "${_('repository scope')}",
128 placeholder: "${_('repository scope')}",
127 dropdownAutoWidth: true,
129 dropdownAutoWidth: true,
128 containerCssClass: "drop-menu",
130 containerCssClass: "drop-menu",
129 dropdownCssClass: "drop-menu-dropdown",
131 dropdownCssClass: "drop-menu-dropdown",
130 formatResult: formatResult,
132 formatResult: formatResult,
131 query: $.debounce(250, function(query){
133 query: $.debounce(250, function(query){
132 self = this;
134 self = this;
133 var cacheKey = query.term;
135 var cacheKey = query.term;
134 var cachedData = self.cachedDataSource[cacheKey];
136 var cachedData = self.cachedDataSource[cacheKey];
135
137
136 if (cachedData) {
138 if (cachedData) {
137 query.callback({results: cachedData.results});
139 query.callback({results: cachedData.results});
138 } else {
140 } else {
139 $.ajax({
141 $.ajax({
140 url: "${h.url('repo_list_data')}",
142 url: "${h.url('repo_list_data')}",
141 data: {'query': query.term},
143 data: {'query': query.term},
142 dataType: 'json',
144 dataType: 'json',
143 type: 'GET',
145 type: 'GET',
144 success: function(data) {
146 success: function(data) {
145 data = repoFilter(data);
147 data = repoFilter(data);
146 self.cachedDataSource[cacheKey] = data;
148 self.cachedDataSource[cacheKey] = data;
147 query.callback({results: data.results});
149 query.callback({results: data.results});
148 },
150 },
149 error: function(data, textStatus, errorThrown) {
151 error: function(data, textStatus, errorThrown) {
150 alert("Error while fetching entries.\nError code {0} ({1}).".format(data.status, data.statusText));
152 alert("Error while fetching entries.\nError code {0} ({1}).".format(data.status, data.statusText));
151 }
153 }
152 })
154 })
153 }
155 }
154 })
156 })
155 });
157 });
156
158
157 });
159 });
158 </script>
160 </script>
@@ -1,49 +1,49 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 ${_('%s user settings') % c.user.username}
5 ${_('%s user settings') % c.user.username}
6 %if c.rhodecode_name:
6 %if c.rhodecode_name:
7 &middot; ${h.branding(c.rhodecode_name)}
7 &middot; ${h.branding(c.rhodecode_name)}
8 %endif
8 %endif
9 </%def>
9 </%def>
10
10
11 <%def name="breadcrumbs_links()">
11 <%def name="breadcrumbs_links()">
12 ${h.link_to(_('Admin'),h.url('admin_home'))}
12 ${h.link_to(_('Admin'),h.url('admin_home'))}
13 &raquo;
13 &raquo;
14 ${h.link_to(_('Users'),h.url('users'))}
14 ${h.link_to(_('Users'),h.url('users'))}
15 &raquo;
15 &raquo;
16 ${c.user.username}
16 ${c.user.username}
17 </%def>
17 </%def>
18
18
19 <%def name="menu_bar_nav()">
19 <%def name="menu_bar_nav()">
20 ${self.menu_items(active='admin')}
20 ${self.menu_items(active='admin')}
21 </%def>
21 </%def>
22
22
23 <%def name="main()">
23 <%def name="main()">
24 <div class="box user_settings">
24 <div class="box user_settings">
25 <div class="title">
25 <div class="title">
26 ${self.breadcrumbs()}
26 ${self.breadcrumbs()}
27 </div>
27 </div>
28
28
29 ##main
29 ##main
30 <div class="sidebar-col-wrapper">
30 <div class="sidebar-col-wrapper">
31 <div class="sidebar">
31 <div class="sidebar">
32 <ul class="nav nav-pills nav-stacked">
32 <ul class="nav nav-pills nav-stacked">
33 <li class="${'active' if c.active=='profile' else ''}"><a href="${h.url('edit_user', user_id=c.user.user_id)}">${_('User Profile')}</a></li>
33 <li class="${'active' if c.active=='profile' else ''}"><a href="${h.url('edit_user', user_id=c.user.user_id)}">${_('User Profile')}</a></li>
34 <li class="${'active' if c.active=='auth_tokens' else ''}"><a href="${h.url('edit_user_auth_tokens', user_id=c.user.user_id)}">${_('Auth tokens')}</a></li>
34 <li class="${'active' if c.active=='auth_tokens' else ''}"><a href="${h.route_path('edit_user_auth_tokens', user_id=c.user.user_id)}">${_('Auth tokens')}</a></li>
35 <li class="${'active' if c.active=='advanced' else ''}"><a href="${h.url('edit_user_advanced', user_id=c.user.user_id)}">${_('Advanced')}</a></li>
35 <li class="${'active' if c.active=='advanced' else ''}"><a href="${h.url('edit_user_advanced', user_id=c.user.user_id)}">${_('Advanced')}</a></li>
36 <li class="${'active' if c.active=='global_perms' else ''}"><a href="${h.url('edit_user_global_perms', user_id=c.user.user_id)}">${_('Global permissions')}</a></li>
36 <li class="${'active' if c.active=='global_perms' else ''}"><a href="${h.url('edit_user_global_perms', user_id=c.user.user_id)}">${_('Global permissions')}</a></li>
37 <li class="${'active' if c.active=='perms_summary' else ''}"><a href="${h.url('edit_user_perms_summary', user_id=c.user.user_id)}">${_('Permissions summary')}</a></li>
37 <li class="${'active' if c.active=='perms_summary' else ''}"><a href="${h.url('edit_user_perms_summary', user_id=c.user.user_id)}">${_('Permissions summary')}</a></li>
38 <li class="${'active' if c.active=='emails' else ''}"><a href="${h.url('edit_user_emails', user_id=c.user.user_id)}">${_('Emails')}</a></li>
38 <li class="${'active' if c.active=='emails' else ''}"><a href="${h.url('edit_user_emails', user_id=c.user.user_id)}">${_('Emails')}</a></li>
39 <li class="${'active' if c.active=='ips' else ''}"><a href="${h.url('edit_user_ips', user_id=c.user.user_id)}">${_('Ip Whitelist')}</a></li>
39 <li class="${'active' if c.active=='ips' else ''}"><a href="${h.url('edit_user_ips', user_id=c.user.user_id)}">${_('Ip Whitelist')}</a></li>
40 </ul>
40 </ul>
41 </div>
41 </div>
42
42
43 <div class="main-content-full-width">
43 <div class="main-content-full-width">
44 <%include file="/admin/users/user_edit_${c.active}.mako"/>
44 <%include file="/admin/users/user_edit_${c.active}.mako"/>
45 </div>
45 </div>
46 </div>
46 </div>
47 </div>
47 </div>
48
48
49 </%def>
49 </%def>
@@ -1,97 +1,157 b''
1 <div class="panel panel-default">
1 <div class="panel panel-default">
2 <div class="panel-heading">
2 <div class="panel-heading">
3 <h3 class="panel-title">${_('Authentication Access Tokens')}</h3>
3 <h3 class="panel-title">${_('Authentication Tokens')}</h3>
4 </div>
4 </div>
5 <div class="panel-body">
5 <div class="panel-body">
6 <div class="apikeys_wrap">
6 <div class="apikeys_wrap">
7 <p>
7 <p>
8 ${_('Each token can have a role. Token with a role can be used only in given context, '
8 ${_('Each token can have a role. Token with a role can be used only in given context, '
9 'e.g. VCS tokens can be used together with the authtoken auth plugin for git/hg/svn operations only.')}
9 'e.g. VCS tokens can be used together with the authtoken auth plugin for git/hg/svn operations only.')}
10 ${_('Additionally scope for VCS type token can narrow the use to chosen repository.')}
11 </p>
10 </p>
12 <table class="rctable auth_tokens">
11 <table class="rctable auth_tokens">
13 <tr>
12 <tr>
14 <th>${_('Token')}</th>
13 <th>${_('Token')}</th>
15 <th>${_('Scope')}</th>
14 <th>${_('Scope')}</th>
16 <th>${_('Description')}</th>
15 <th>${_('Description')}</th>
17 <th>${_('Role')}</th>
16 <th>${_('Role')}</th>
18 <th>${_('Expiration')}</th>
17 <th>${_('Expiration')}</th>
19 <th>${_('Action')}</th>
18 <th>${_('Action')}</th>
20 </tr>
19 </tr>
21 %if c.user_auth_tokens:
20 %if c.user_auth_tokens:
22 %for auth_token in c.user_auth_tokens:
21 %for auth_token in c.user_auth_tokens:
23 <tr class="${'expired' if auth_token.expired else ''}">
22 <tr class="${'expired' if auth_token.expired else ''}">
24 <td class="truncate-wrap td-authtoken"><div class="user_auth_tokens truncate autoexpand"><code>${auth_token.api_key}</code></div></td>
23 <td class="truncate-wrap td-authtoken"><div class="user_auth_tokens truncate autoexpand"><code>${auth_token.api_key}</code></div></td>
25 <td class="td">${auth_token.scope_humanized}</td>
24 <td class="td">${auth_token.scope_humanized}</td>
26 <td class="td-wrap">${auth_token.description}</td>
25 <td class="td-wrap">${auth_token.description}</td>
27 <td class="td-tags">
26 <td class="td-tags">
28 <span class="tag">${auth_token.role_humanized}</span>
27 <span class="tag disabled">${auth_token.role_humanized}</span>
29 </td>
28 </td>
30 <td class="td-exp">
29 <td class="td-exp">
31 %if auth_token.expires == -1:
30 %if auth_token.expires == -1:
32 ${_('never')}
31 ${_('never')}
33 %else:
32 %else:
34 %if auth_token.expired:
33 %if auth_token.expired:
35 <span style="text-decoration: line-through">${h.age_component(h.time_to_utcdatetime(auth_token.expires))}</span>
34 <span style="text-decoration: line-through">${h.age_component(h.time_to_utcdatetime(auth_token.expires))}</span>
36 %else:
35 %else:
37 ${h.age_component(h.time_to_utcdatetime(auth_token.expires))}
36 ${h.age_component(h.time_to_utcdatetime(auth_token.expires))}
38 %endif
37 %endif
39 %endif
38 %endif
40 </td>
39 </td>
41 <td>
40 <td class="td-action">
42 ${h.secure_form(url('edit_user_auth_tokens', user_id=c.user.user_id),method='delete')}
41 ${h.secure_form(h.route_path('edit_user_auth_tokens_delete', user_id=c.user.user_id), method='post')}
43 ${h.hidden('del_auth_token',auth_token.api_key)}
42 ${h.hidden('del_auth_token',auth_token.api_key)}
44 <button class="btn btn-link btn-danger" type="submit"
43 <button class="btn btn-link btn-danger" type="submit"
45 onclick="return confirm('${_('Confirm to remove this auth token: %s') % auth_token.api_key}');">
44 onclick="return confirm('${_('Confirm to remove this auth token: %s') % auth_token.api_key}');">
46 ${_('Delete')}
45 ${_('Delete')}
47 </button>
46 </button>
48 ${h.end_form()}
47 ${h.end_form()}
49 </td>
48 </td>
50 </tr>
49 </tr>
51 %endfor
50 %endfor
52 %else:
51 %else:
53 <tr><td><div class="ip">${_('No additional auth tokens specified')}</div></td></tr>
52 <tr><td><div class="ip">${_('No additional auth tokens specified')}</div></td></tr>
54 %endif
53 %endif
55 </table>
54 </table>
56 </div>
55 </div>
57
56
58 <div class="user_auth_tokens">
57 <div class="user_auth_tokens">
59 ${h.secure_form(url('edit_user_auth_tokens', user_id=c.user.user_id), method='put')}
58 ${h.secure_form(h.route_path('edit_user_auth_tokens_add', user_id=c.user.user_id), method='post')}
60 <div class="form form-vertical">
59 <div class="form form-vertical">
61 <!-- fields -->
60 <!-- fields -->
62 <div class="fields">
61 <div class="fields">
63 <div class="field">
62 <div class="field">
64 <div class="label">
63 <div class="label">
65 <label for="new_email">${_('New authentication token')}:</label>
64 <label for="new_email">${_('New authentication token')}:</label>
66 </div>
65 </div>
67 <div class="input">
66 <div class="input">
68 ${h.text('description', class_='medium', placeholder=_('Description'))}
67 ${h.text('description', class_='medium', placeholder=_('Description'))}
69 ${h.select('lifetime', '', c.lifetime_options)}
68 ${h.select('lifetime', '', c.lifetime_options)}
70 ${h.select('role', '', c.role_options)}
69 ${h.select('role', '', c.role_options)}
70
71 % if c.allow_scoped_tokens:
72 ${h.hidden('scope_repo_id')}
73 % else:
74 ${h.select('scope_repo_id_disabled', '', ['Scopes available in EE edition'], disabled='disabled')}
75 % endif
71 </div>
76 </div>
77 <p class="help-block">
78 ${_('Repository scope works only with tokens with VCS type.')}
79 </p>
72 </div>
80 </div>
73 <div class="buttons">
81 <div class="buttons">
74 ${h.submit('save',_('Add'),class_="btn btn-small")}
82 ${h.submit('save',_('Add'),class_="btn")}
75 ${h.reset('reset',_('Reset'),class_="btn btn-small")}
83 ${h.reset('reset',_('Reset'),class_="btn")}
76 </div>
84 </div>
77 </div>
85 </div>
78 </div>
86 </div>
79 ${h.end_form()}
87 ${h.end_form()}
80 </div>
88 </div>
81 </div>
89 </div>
82 </div>
90 </div>
83
91
84 <script>
92 <script>
93
85 $(document).ready(function(){
94 $(document).ready(function(){
86 $("#lifetime").select2({
95 var select2Options = {
87 'containerCssClass': "drop-menu",
96 'containerCssClass': "drop-menu",
88 'dropdownCssClass': "drop-menu-dropdown",
97 'dropdownCssClass': "drop-menu-dropdown",
89 'dropdownAutoWidth': true
98 'dropdownAutoWidth': true
99 };
100 $("#lifetime").select2(select2Options);
101 $("#role").select2(select2Options);
102
103 var repoFilter = function(data) {
104 var results = [];
105
106 if (!data.results[0]) {
107 return data
108 }
109
110 $.each(data.results[0].children, function() {
111 // replace name to ID for submision
112 this.id = this.obj.repo_id;
113 results.push(this);
90 });
114 });
91 $("#role").select2({
115
92 'containerCssClass': "drop-menu",
116 data.results[0].children = results;
93 'dropdownCssClass': "drop-menu-dropdown",
117 return data;
94 'dropdownAutoWidth': true
118 };
119
120 $("#scope_repo_id_disabled").select2(select2Options);
121
122 $("#scope_repo_id").select2({
123 cachedDataSource: {},
124 minimumInputLength: 2,
125 placeholder: "${_('repository scope')}",
126 dropdownAutoWidth: true,
127 containerCssClass: "drop-menu",
128 dropdownCssClass: "drop-menu-dropdown",
129 formatResult: formatResult,
130 query: $.debounce(250, function(query){
131 self = this;
132 var cacheKey = query.term;
133 var cachedData = self.cachedDataSource[cacheKey];
134
135 if (cachedData) {
136 query.callback({results: cachedData.results});
137 } else {
138 $.ajax({
139 url: "${h.url('repo_list_data')}",
140 data: {'query': query.term},
141 dataType: 'json',
142 type: 'GET',
143 success: function(data) {
144 data = repoFilter(data);
145 self.cachedDataSource[cacheKey] = data;
146 query.callback({results: data.results});
147 },
148 error: function(data, textStatus, errorThrown) {
149 alert("Error while fetching entries.\nError code {0} ({1}).".format(data.status, data.statusText));
150 }
151 })
152 }
153 })
95 });
154 });
96 })
155
156 });
97 </script>
157 </script>
General Comments 0
You need to be logged in to leave comments. Login now