##// END OF EJS Templates
core: moved repo_list data to pyramid.
marcink -
r1667:be1f7a06 default
parent child Browse files
Show More
@@ -0,0 +1,103 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 json
22
23 from . import assert_and_get_content
24 from rhodecode.tests import TestController
25 from rhodecode.tests.fixture import Fixture
26 from rhodecode.model.db import Repository
27
28 fixture = Fixture()
29
30
31 def route_path(name, params=None, **kwargs):
32 import urllib
33
34 base_url = {
35 'repo_list_data': '/_repos',
36 }[name].format(**kwargs)
37
38 if params:
39 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
40 return base_url
41
42
43 class TestRepoListData(TestController):
44
45 def test_returns_list_of_repos_and_groups(self, xhr_header):
46 self.log_user()
47
48 response = self.app.get(
49 route_path('repo_list_data'),
50 extra_environ=xhr_header, status=200)
51 result = json.loads(response.body)['results']
52
53 repos, groups, commits = assert_and_get_content(result)
54
55 assert len(repos) == len(Repository.get_all())
56 assert len(groups) == 0
57 assert len(commits) == 0
58
59 def test_returns_list_of_repos_and_groups_filtered(self, xhr_header):
60 self.log_user()
61
62 response = self.app.get(
63 route_path('repo_list_data'),
64 params={'query': 'vcs_test_git'},
65 extra_environ=xhr_header, status=200)
66 result = json.loads(response.body)['results']
67
68 repos, groups, commits = assert_and_get_content(result)
69
70 assert len(repos) == len(Repository.query().filter(
71 Repository.repo_name.ilike('%vcs_test_git%')).all())
72 assert len(groups) == 0
73 assert len(commits) == 0
74
75 def test_returns_list_of_repos_and_groups_filtered_with_type(self, xhr_header):
76 self.log_user()
77
78 response = self.app.get(
79 route_path('repo_list_data'),
80 params={'query': 'vcs_test_git', 'repo_type': 'git'},
81 extra_environ=xhr_header, status=200)
82 result = json.loads(response.body)['results']
83
84 repos, groups, commits = assert_and_get_content(result)
85
86 assert len(repos) == len(Repository.query().filter(
87 Repository.repo_name.ilike('%vcs_test_git%')).all())
88 assert len(groups) == 0
89 assert len(commits) == 0
90
91 def test_returns_list_of_repos_non_ascii_query(self, xhr_header):
92 self.log_user()
93 response = self.app.get(
94 route_path('repo_list_data'),
95 params={'query': 'ć_vcs_test_ą', 'repo_type': 'git'},
96 extra_environ=xhr_header, status=200)
97 result = json.loads(response.body)['results']
98
99 repos, groups, commits = assert_and_get_content(result)
100
101 assert len(repos) == 0
102 assert len(groups) == 0
103 assert len(commits) == 0
@@ -1,33 +1,37 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21
22 22 def includeme(config):
23 23
24 24 config.add_route(
25 25 name='user_autocomplete_data',
26 26 pattern='/_users')
27 27
28 28 config.add_route(
29 29 name='user_group_autocomplete_data',
30 30 pattern='/_user_groups')
31 31
32 config.add_route(
33 name='repo_list_data',
34 pattern='/_repos')
35
32 36 # Scan module for configuration decorators.
33 37 config.scan()
@@ -1,19 +1,40 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
21
22 def assert_and_get_content(result):
23 repos = []
24 groups = []
25 commits = []
26 for data in result:
27 for data_item in data['children']:
28 assert data_item['id']
29 assert data_item['text']
30 assert data_item['url']
31 if data_item['type'] == 'repo':
32 repos.append(data_item)
33 elif data_item['type'] == 'group':
34 groups.append(data_item)
35 elif data_item['type'] == 'commit':
36 commits.append(data_item)
37 else:
38 raise Exception('invalid type %s' % data_item['type'])
39
40 return repos, groups, commits No newline at end of file
@@ -1,81 +1,138 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import logging
22 22
23 23 from pyramid.view import view_config
24 24
25 25 from rhodecode.apps._base import BaseAppView
26 from rhodecode.lib import helpers as h
26 27 from rhodecode.lib.auth import LoginRequired, NotAnonymous
27 from rhodecode.lib.utils2 import str2bool
28 from rhodecode.lib.utils2 import safe_unicode, str2bool
29 from rhodecode.model.db import func, Repository
28 30 from rhodecode.model.repo import RepoModel
31 from rhodecode.model.scm import ScmModel
32
29 33
30 34 log = logging.getLogger(__name__)
31 35
32 36
33 37 class HomeView(BaseAppView):
34 38
35 39 def load_default_context(self):
36 40 c = self._get_local_tmpl_context()
37 41 c.user = c.auth_user.get_instance()
38 42 self._register_global_c(c)
39 43 return c
40 44
41 45 @LoginRequired()
42 46 @view_config(
43 47 route_name='user_autocomplete_data', request_method='GET',
44 48 renderer='json_ext', xhr=True)
45 49 def user_autocomplete_data(self):
46 50 query = self.request.GET.get('query')
47 51 active = str2bool(self.request.GET.get('active') or True)
48 52 include_groups = str2bool(self.request.GET.get('user_groups'))
49 53
50 54 log.debug('generating user list, query:%s, active:%s, with_groups:%s',
51 55 query, active, include_groups)
52 56
53 57 repo_model = RepoModel()
54 58 _users = repo_model.get_users(
55 59 name_contains=query, only_active=active)
56 60
57 61 if include_groups:
58 62 # extend with user groups
59 63 _user_groups = repo_model.get_user_groups(
60 64 name_contains=query, only_active=active)
61 65 _users = _users + _user_groups
62 66
63 67 return {'suggestions': _users}
64 68
65 69 @LoginRequired()
66 70 @NotAnonymous()
67 71 @view_config(
68 72 route_name='user_group_autocomplete_data', request_method='GET',
69 73 renderer='json_ext', xhr=True)
70 74 def user_group_autocomplete_data(self):
71 75 query = self.request.GET.get('query')
72 76 active = str2bool(self.request.GET.get('active') or True)
73 77 log.debug('generating user group list, query:%s, active:%s',
74 78 query, active)
75 79
76 80 repo_model = RepoModel()
77 81 _user_groups = repo_model.get_user_groups(
78 82 name_contains=query, only_active=active)
79 83 _user_groups = _user_groups
80 84
81 85 return {'suggestions': _user_groups}
86
87 def _get_repo_list(self, name_contains=None, repo_type=None, limit=20):
88 query = Repository.query()\
89 .order_by(func.length(Repository.repo_name))\
90 .order_by(Repository.repo_name)
91
92 if repo_type:
93 query = query.filter(Repository.repo_type == repo_type)
94
95 if name_contains:
96 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
97 query = query.filter(
98 Repository.repo_name.ilike(ilike_expression))
99 query = query.limit(limit)
100
101 all_repos = query.all()
102 # permission checks are inside this function
103 repo_iter = ScmModel().get_repos(all_repos)
104 return [
105 {
106 'id': obj['name'],
107 'text': obj['name'],
108 'type': 'repo',
109 'obj': obj['dbrepo'],
110 'url': h.url('summary_home', repo_name=obj['name'])
111 }
112 for obj in repo_iter]
113
114 @LoginRequired()
115 @view_config(
116 route_name='repo_list_data', request_method='GET',
117 renderer='json_ext', xhr=True)
118 def repo_list_data(self):
119 _ = self.request.translate
120
121 query = self.request.GET.get('query')
122 repo_type = self.request.GET.get('repo_type')
123 log.debug('generating repo list, query:%s, repo_type:%s',
124 query, repo_type)
125
126 res = []
127 repos = self._get_repo_list(query, repo_type=repo_type)
128 if repos:
129 res.append({
130 'text': _('Repositories'),
131 'children': repos
132 })
133
134 data = {
135 'more': False,
136 'results': res
137 }
138 return data
@@ -1,1148 +1,1146 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 """
22 22 Routes configuration
23 23
24 24 The more specific and detailed routes should be defined first so they
25 25 may take precedent over the more generic routes. For more information
26 26 refer to the routes manual at http://routes.groovie.org/docs/
27 27
28 28 IMPORTANT: if you change any routing here, make sure to take a look at lib/base.py
29 29 and _route_name variable which uses some of stored naming here to do redirects.
30 30 """
31 31 import os
32 32 import re
33 33 from routes import Mapper
34 34
35 35 from rhodecode.config import routing_links
36 36
37 37 # prefix for non repository related links needs to be prefixed with `/`
38 38 ADMIN_PREFIX = '/_admin'
39 39 STATIC_FILE_PREFIX = '/_static'
40 40
41 41 # Default requirements for URL parts
42 42 URL_NAME_REQUIREMENTS = {
43 43 # group name can have a slash in them, but they must not end with a slash
44 44 'group_name': r'.*?[^/]',
45 45 'repo_group_name': r'.*?[^/]',
46 46 # repo names can have a slash in them, but they must not end with a slash
47 47 'repo_name': r'.*?[^/]',
48 48 # file path eats up everything at the end
49 49 'f_path': r'.*',
50 50 # reference types
51 51 'source_ref_type': '(branch|book|tag|rev|\%\(source_ref_type\)s)',
52 52 'target_ref_type': '(branch|book|tag|rev|\%\(target_ref_type\)s)',
53 53 }
54 54
55 55
56 56 def add_route_requirements(route_path, requirements):
57 57 """
58 58 Adds regex requirements to pyramid routes using a mapping dict
59 59
60 60 >>> add_route_requirements('/{action}/{id}', {'id': r'\d+'})
61 61 '/{action}/{id:\d+}'
62 62
63 63 """
64 64 for key, regex in requirements.items():
65 65 route_path = route_path.replace('{%s}' % key, '{%s:%s}' % (key, regex))
66 66 return route_path
67 67
68 68
69 69 class JSRoutesMapper(Mapper):
70 70 """
71 71 Wrapper for routes.Mapper to make pyroutes compatible url definitions
72 72 """
73 73 _named_route_regex = re.compile(r'^[a-z-_0-9A-Z]+$')
74 74 _argument_prog = re.compile('\{(.*?)\}|:\((.*)\)')
75 75 def __init__(self, *args, **kw):
76 76 super(JSRoutesMapper, self).__init__(*args, **kw)
77 77 self._jsroutes = []
78 78
79 79 def connect(self, *args, **kw):
80 80 """
81 81 Wrapper for connect to take an extra argument jsroute=True
82 82
83 83 :param jsroute: boolean, if True will add the route to the pyroutes list
84 84 """
85 85 if kw.pop('jsroute', False):
86 86 if not self._named_route_regex.match(args[0]):
87 87 raise Exception('only named routes can be added to pyroutes')
88 88 self._jsroutes.append(args[0])
89 89
90 90 super(JSRoutesMapper, self).connect(*args, **kw)
91 91
92 92 def _extract_route_information(self, route):
93 93 """
94 94 Convert a route into tuple(name, path, args), eg:
95 95 ('show_user', '/profile/%(username)s', ['username'])
96 96 """
97 97 routepath = route.routepath
98 98 def replace(matchobj):
99 99 if matchobj.group(1):
100 100 return "%%(%s)s" % matchobj.group(1).split(':')[0]
101 101 else:
102 102 return "%%(%s)s" % matchobj.group(2)
103 103
104 104 routepath = self._argument_prog.sub(replace, routepath)
105 105 return (
106 106 route.name,
107 107 routepath,
108 108 [(arg[0].split(':')[0] if arg[0] != '' else arg[1])
109 109 for arg in self._argument_prog.findall(route.routepath)]
110 110 )
111 111
112 112 def jsroutes(self):
113 113 """
114 114 Return a list of pyroutes.js compatible routes
115 115 """
116 116 for route_name in self._jsroutes:
117 117 yield self._extract_route_information(self._routenames[route_name])
118 118
119 119
120 120 def make_map(config):
121 121 """Create, configure and return the routes Mapper"""
122 122 rmap = JSRoutesMapper(directory=config['pylons.paths']['controllers'],
123 123 always_scan=config['debug'])
124 124 rmap.minimization = False
125 125 rmap.explicit = False
126 126
127 127 from rhodecode.lib.utils2 import str2bool
128 128 from rhodecode.model import repo, repo_group
129 129
130 130 def check_repo(environ, match_dict):
131 131 """
132 132 check for valid repository for proper 404 handling
133 133
134 134 :param environ:
135 135 :param match_dict:
136 136 """
137 137 repo_name = match_dict.get('repo_name')
138 138
139 139 if match_dict.get('f_path'):
140 140 # fix for multiple initial slashes that causes errors
141 141 match_dict['f_path'] = match_dict['f_path'].lstrip('/')
142 142 repo_model = repo.RepoModel()
143 143 by_name_match = repo_model.get_by_repo_name(repo_name)
144 144 # if we match quickly from database, short circuit the operation,
145 145 # and validate repo based on the type.
146 146 if by_name_match:
147 147 return True
148 148
149 149 by_id_match = repo_model.get_repo_by_id(repo_name)
150 150 if by_id_match:
151 151 repo_name = by_id_match.repo_name
152 152 match_dict['repo_name'] = repo_name
153 153 return True
154 154
155 155 return False
156 156
157 157 def check_group(environ, match_dict):
158 158 """
159 159 check for valid repository group path for proper 404 handling
160 160
161 161 :param environ:
162 162 :param match_dict:
163 163 """
164 164 repo_group_name = match_dict.get('group_name')
165 165 repo_group_model = repo_group.RepoGroupModel()
166 166 by_name_match = repo_group_model.get_by_group_name(repo_group_name)
167 167 if by_name_match:
168 168 return True
169 169
170 170 return False
171 171
172 172 def check_user_group(environ, match_dict):
173 173 """
174 174 check for valid user group for proper 404 handling
175 175
176 176 :param environ:
177 177 :param match_dict:
178 178 """
179 179 return True
180 180
181 181 def check_int(environ, match_dict):
182 182 return match_dict.get('id').isdigit()
183 183
184 184
185 185 #==========================================================================
186 186 # CUSTOM ROUTES HERE
187 187 #==========================================================================
188 188
189 189 # MAIN PAGE
190 190 rmap.connect('home', '/', controller='home', action='index', jsroute=True)
191 191 rmap.connect('goto_switcher_data', '/_goto_data', controller='home',
192 192 action='goto_switcher_data')
193 rmap.connect('repo_list_data', '/_repos', controller='home',
194 action='repo_list_data')
195 193
196 194 # TODO: johbo: Static links, to be replaced by our redirection mechanism
197 195 rmap.connect('rst_help',
198 196 'http://docutils.sourceforge.net/docs/user/rst/quickref.html',
199 197 _static=True)
200 198 rmap.connect('markdown_help',
201 199 'http://daringfireball.net/projects/markdown/syntax',
202 200 _static=True)
203 201 rmap.connect('rhodecode_official', 'https://rhodecode.com', _static=True)
204 202 rmap.connect('rhodecode_support', 'https://rhodecode.com/help/', _static=True)
205 203 rmap.connect('rhodecode_translations', 'https://rhodecode.com/translate/enterprise', _static=True)
206 204 # TODO: anderson - making this a static link since redirect won't play
207 205 # nice with POST requests
208 206 rmap.connect('enterprise_license_convert_from_old',
209 207 'https://rhodecode.com/u/license-upgrade',
210 208 _static=True)
211 209
212 210 routing_links.connect_redirection_links(rmap)
213 211
214 212 rmap.connect('ping', '%s/ping' % (ADMIN_PREFIX,), controller='home', action='ping')
215 213 rmap.connect('error_test', '%s/error_test' % (ADMIN_PREFIX,), controller='home', action='error_test')
216 214
217 215 # ADMIN REPOSITORY ROUTES
218 216 with rmap.submapper(path_prefix=ADMIN_PREFIX,
219 217 controller='admin/repos') as m:
220 218 m.connect('repos', '/repos',
221 219 action='create', conditions={'method': ['POST']})
222 220 m.connect('repos', '/repos',
223 221 action='index', conditions={'method': ['GET']})
224 222 m.connect('new_repo', '/create_repository', jsroute=True,
225 223 action='create_repository', conditions={'method': ['GET']})
226 224 m.connect('/repos/{repo_name}',
227 225 action='update', conditions={'method': ['PUT'],
228 226 'function': check_repo},
229 227 requirements=URL_NAME_REQUIREMENTS)
230 228 m.connect('delete_repo', '/repos/{repo_name}',
231 229 action='delete', conditions={'method': ['DELETE']},
232 230 requirements=URL_NAME_REQUIREMENTS)
233 231 m.connect('repo', '/repos/{repo_name}',
234 232 action='show', conditions={'method': ['GET'],
235 233 'function': check_repo},
236 234 requirements=URL_NAME_REQUIREMENTS)
237 235
238 236 # ADMIN REPOSITORY GROUPS ROUTES
239 237 with rmap.submapper(path_prefix=ADMIN_PREFIX,
240 238 controller='admin/repo_groups') as m:
241 239 m.connect('repo_groups', '/repo_groups',
242 240 action='create', conditions={'method': ['POST']})
243 241 m.connect('repo_groups', '/repo_groups',
244 242 action='index', conditions={'method': ['GET']})
245 243 m.connect('new_repo_group', '/repo_groups/new',
246 244 action='new', conditions={'method': ['GET']})
247 245 m.connect('update_repo_group', '/repo_groups/{group_name}',
248 246 action='update', conditions={'method': ['PUT'],
249 247 'function': check_group},
250 248 requirements=URL_NAME_REQUIREMENTS)
251 249
252 250 # EXTRAS REPO GROUP ROUTES
253 251 m.connect('edit_repo_group', '/repo_groups/{group_name}/edit',
254 252 action='edit',
255 253 conditions={'method': ['GET'], 'function': check_group},
256 254 requirements=URL_NAME_REQUIREMENTS)
257 255 m.connect('edit_repo_group', '/repo_groups/{group_name}/edit',
258 256 action='edit',
259 257 conditions={'method': ['PUT'], 'function': check_group},
260 258 requirements=URL_NAME_REQUIREMENTS)
261 259
262 260 m.connect('edit_repo_group_advanced', '/repo_groups/{group_name}/edit/advanced',
263 261 action='edit_repo_group_advanced',
264 262 conditions={'method': ['GET'], 'function': check_group},
265 263 requirements=URL_NAME_REQUIREMENTS)
266 264 m.connect('edit_repo_group_advanced', '/repo_groups/{group_name}/edit/advanced',
267 265 action='edit_repo_group_advanced',
268 266 conditions={'method': ['PUT'], 'function': check_group},
269 267 requirements=URL_NAME_REQUIREMENTS)
270 268
271 269 m.connect('edit_repo_group_perms', '/repo_groups/{group_name}/edit/permissions',
272 270 action='edit_repo_group_perms',
273 271 conditions={'method': ['GET'], 'function': check_group},
274 272 requirements=URL_NAME_REQUIREMENTS)
275 273 m.connect('edit_repo_group_perms', '/repo_groups/{group_name}/edit/permissions',
276 274 action='update_perms',
277 275 conditions={'method': ['PUT'], 'function': check_group},
278 276 requirements=URL_NAME_REQUIREMENTS)
279 277
280 278 m.connect('delete_repo_group', '/repo_groups/{group_name}',
281 279 action='delete', conditions={'method': ['DELETE'],
282 280 'function': check_group},
283 281 requirements=URL_NAME_REQUIREMENTS)
284 282
285 283 # ADMIN USER ROUTES
286 284 with rmap.submapper(path_prefix=ADMIN_PREFIX,
287 285 controller='admin/users') as m:
288 286 m.connect('users', '/users',
289 287 action='create', conditions={'method': ['POST']})
290 288 m.connect('new_user', '/users/new',
291 289 action='new', conditions={'method': ['GET']})
292 290 m.connect('update_user', '/users/{user_id}',
293 291 action='update', conditions={'method': ['PUT']})
294 292 m.connect('delete_user', '/users/{user_id}',
295 293 action='delete', conditions={'method': ['DELETE']})
296 294 m.connect('edit_user', '/users/{user_id}/edit',
297 295 action='edit', conditions={'method': ['GET']}, jsroute=True)
298 296 m.connect('user', '/users/{user_id}',
299 297 action='show', conditions={'method': ['GET']})
300 298 m.connect('force_password_reset_user', '/users/{user_id}/password_reset',
301 299 action='reset_password', conditions={'method': ['POST']})
302 300 m.connect('create_personal_repo_group', '/users/{user_id}/create_repo_group',
303 301 action='create_personal_repo_group', conditions={'method': ['POST']})
304 302
305 303 # EXTRAS USER ROUTES
306 304 m.connect('edit_user_advanced', '/users/{user_id}/edit/advanced',
307 305 action='edit_advanced', conditions={'method': ['GET']})
308 306 m.connect('edit_user_advanced', '/users/{user_id}/edit/advanced',
309 307 action='update_advanced', conditions={'method': ['PUT']})
310 308
311 309 m.connect('edit_user_global_perms', '/users/{user_id}/edit/global_permissions',
312 310 action='edit_global_perms', conditions={'method': ['GET']})
313 311 m.connect('edit_user_global_perms', '/users/{user_id}/edit/global_permissions',
314 312 action='update_global_perms', conditions={'method': ['PUT']})
315 313
316 314 m.connect('edit_user_perms_summary', '/users/{user_id}/edit/permissions_summary',
317 315 action='edit_perms_summary', conditions={'method': ['GET']})
318 316
319 317 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
320 318 action='edit_emails', conditions={'method': ['GET']})
321 319 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
322 320 action='add_email', conditions={'method': ['PUT']})
323 321 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
324 322 action='delete_email', conditions={'method': ['DELETE']})
325 323
326 324 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
327 325 action='edit_ips', conditions={'method': ['GET']})
328 326 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
329 327 action='add_ip', conditions={'method': ['PUT']})
330 328 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
331 329 action='delete_ip', conditions={'method': ['DELETE']})
332 330
333 331 # ADMIN USER GROUPS REST ROUTES
334 332 with rmap.submapper(path_prefix=ADMIN_PREFIX,
335 333 controller='admin/user_groups') as m:
336 334 m.connect('users_groups', '/user_groups',
337 335 action='create', conditions={'method': ['POST']})
338 336 m.connect('users_groups', '/user_groups',
339 337 action='index', conditions={'method': ['GET']})
340 338 m.connect('new_users_group', '/user_groups/new',
341 339 action='new', conditions={'method': ['GET']})
342 340 m.connect('update_users_group', '/user_groups/{user_group_id}',
343 341 action='update', conditions={'method': ['PUT']})
344 342 m.connect('delete_users_group', '/user_groups/{user_group_id}',
345 343 action='delete', conditions={'method': ['DELETE']})
346 344 m.connect('edit_users_group', '/user_groups/{user_group_id}/edit',
347 345 action='edit', conditions={'method': ['GET']},
348 346 function=check_user_group)
349 347
350 348 # EXTRAS USER GROUP ROUTES
351 349 m.connect('edit_user_group_global_perms',
352 350 '/user_groups/{user_group_id}/edit/global_permissions',
353 351 action='edit_global_perms', conditions={'method': ['GET']})
354 352 m.connect('edit_user_group_global_perms',
355 353 '/user_groups/{user_group_id}/edit/global_permissions',
356 354 action='update_global_perms', conditions={'method': ['PUT']})
357 355 m.connect('edit_user_group_perms_summary',
358 356 '/user_groups/{user_group_id}/edit/permissions_summary',
359 357 action='edit_perms_summary', conditions={'method': ['GET']})
360 358
361 359 m.connect('edit_user_group_perms',
362 360 '/user_groups/{user_group_id}/edit/permissions',
363 361 action='edit_perms', conditions={'method': ['GET']})
364 362 m.connect('edit_user_group_perms',
365 363 '/user_groups/{user_group_id}/edit/permissions',
366 364 action='update_perms', conditions={'method': ['PUT']})
367 365
368 366 m.connect('edit_user_group_advanced',
369 367 '/user_groups/{user_group_id}/edit/advanced',
370 368 action='edit_advanced', conditions={'method': ['GET']})
371 369
372 370 m.connect('edit_user_group_advanced_sync',
373 371 '/user_groups/{user_group_id}/edit/advanced/sync',
374 372 action='edit_advanced_set_synchronization', conditions={'method': ['POST']})
375 373
376 374 m.connect('edit_user_group_members',
377 375 '/user_groups/{user_group_id}/edit/members', jsroute=True,
378 376 action='user_group_members', conditions={'method': ['GET']})
379 377
380 378 # ADMIN PERMISSIONS ROUTES
381 379 with rmap.submapper(path_prefix=ADMIN_PREFIX,
382 380 controller='admin/permissions') as m:
383 381 m.connect('admin_permissions_application', '/permissions/application',
384 382 action='permission_application_update', conditions={'method': ['POST']})
385 383 m.connect('admin_permissions_application', '/permissions/application',
386 384 action='permission_application', conditions={'method': ['GET']})
387 385
388 386 m.connect('admin_permissions_global', '/permissions/global',
389 387 action='permission_global_update', conditions={'method': ['POST']})
390 388 m.connect('admin_permissions_global', '/permissions/global',
391 389 action='permission_global', conditions={'method': ['GET']})
392 390
393 391 m.connect('admin_permissions_object', '/permissions/object',
394 392 action='permission_objects_update', conditions={'method': ['POST']})
395 393 m.connect('admin_permissions_object', '/permissions/object',
396 394 action='permission_objects', conditions={'method': ['GET']})
397 395
398 396 m.connect('admin_permissions_ips', '/permissions/ips',
399 397 action='permission_ips', conditions={'method': ['POST']})
400 398 m.connect('admin_permissions_ips', '/permissions/ips',
401 399 action='permission_ips', conditions={'method': ['GET']})
402 400
403 401 m.connect('admin_permissions_overview', '/permissions/overview',
404 402 action='permission_perms', conditions={'method': ['GET']})
405 403
406 404 # ADMIN DEFAULTS REST ROUTES
407 405 with rmap.submapper(path_prefix=ADMIN_PREFIX,
408 406 controller='admin/defaults') as m:
409 407 m.connect('admin_defaults_repositories', '/defaults/repositories',
410 408 action='update_repository_defaults', conditions={'method': ['POST']})
411 409 m.connect('admin_defaults_repositories', '/defaults/repositories',
412 410 action='index', conditions={'method': ['GET']})
413 411
414 412 # ADMIN DEBUG STYLE ROUTES
415 413 if str2bool(config.get('debug_style')):
416 414 with rmap.submapper(path_prefix=ADMIN_PREFIX + '/debug_style',
417 415 controller='debug_style') as m:
418 416 m.connect('debug_style_home', '',
419 417 action='index', conditions={'method': ['GET']})
420 418 m.connect('debug_style_template', '/t/{t_path}',
421 419 action='template', conditions={'method': ['GET']})
422 420
423 421 # ADMIN SETTINGS ROUTES
424 422 with rmap.submapper(path_prefix=ADMIN_PREFIX,
425 423 controller='admin/settings') as m:
426 424
427 425 # default
428 426 m.connect('admin_settings', '/settings',
429 427 action='settings_global_update',
430 428 conditions={'method': ['POST']})
431 429 m.connect('admin_settings', '/settings',
432 430 action='settings_global', conditions={'method': ['GET']})
433 431
434 432 m.connect('admin_settings_vcs', '/settings/vcs',
435 433 action='settings_vcs_update',
436 434 conditions={'method': ['POST']})
437 435 m.connect('admin_settings_vcs', '/settings/vcs',
438 436 action='settings_vcs',
439 437 conditions={'method': ['GET']})
440 438 m.connect('admin_settings_vcs', '/settings/vcs',
441 439 action='delete_svn_pattern',
442 440 conditions={'method': ['DELETE']})
443 441
444 442 m.connect('admin_settings_mapping', '/settings/mapping',
445 443 action='settings_mapping_update',
446 444 conditions={'method': ['POST']})
447 445 m.connect('admin_settings_mapping', '/settings/mapping',
448 446 action='settings_mapping', conditions={'method': ['GET']})
449 447
450 448 m.connect('admin_settings_global', '/settings/global',
451 449 action='settings_global_update',
452 450 conditions={'method': ['POST']})
453 451 m.connect('admin_settings_global', '/settings/global',
454 452 action='settings_global', conditions={'method': ['GET']})
455 453
456 454 m.connect('admin_settings_visual', '/settings/visual',
457 455 action='settings_visual_update',
458 456 conditions={'method': ['POST']})
459 457 m.connect('admin_settings_visual', '/settings/visual',
460 458 action='settings_visual', conditions={'method': ['GET']})
461 459
462 460 m.connect('admin_settings_issuetracker',
463 461 '/settings/issue-tracker', action='settings_issuetracker',
464 462 conditions={'method': ['GET']})
465 463 m.connect('admin_settings_issuetracker_save',
466 464 '/settings/issue-tracker/save',
467 465 action='settings_issuetracker_save',
468 466 conditions={'method': ['POST']})
469 467 m.connect('admin_issuetracker_test', '/settings/issue-tracker/test',
470 468 action='settings_issuetracker_test',
471 469 conditions={'method': ['POST']})
472 470 m.connect('admin_issuetracker_delete',
473 471 '/settings/issue-tracker/delete',
474 472 action='settings_issuetracker_delete',
475 473 conditions={'method': ['DELETE']})
476 474
477 475 m.connect('admin_settings_email', '/settings/email',
478 476 action='settings_email_update',
479 477 conditions={'method': ['POST']})
480 478 m.connect('admin_settings_email', '/settings/email',
481 479 action='settings_email', conditions={'method': ['GET']})
482 480
483 481 m.connect('admin_settings_hooks', '/settings/hooks',
484 482 action='settings_hooks_update',
485 483 conditions={'method': ['POST', 'DELETE']})
486 484 m.connect('admin_settings_hooks', '/settings/hooks',
487 485 action='settings_hooks', conditions={'method': ['GET']})
488 486
489 487 m.connect('admin_settings_search', '/settings/search',
490 488 action='settings_search', conditions={'method': ['GET']})
491 489
492 490 m.connect('admin_settings_supervisor', '/settings/supervisor',
493 491 action='settings_supervisor', conditions={'method': ['GET']})
494 492 m.connect('admin_settings_supervisor_log', '/settings/supervisor/{procid}/log',
495 493 action='settings_supervisor_log', conditions={'method': ['GET']})
496 494
497 495 m.connect('admin_settings_labs', '/settings/labs',
498 496 action='settings_labs_update',
499 497 conditions={'method': ['POST']})
500 498 m.connect('admin_settings_labs', '/settings/labs',
501 499 action='settings_labs', conditions={'method': ['GET']})
502 500
503 501 # ADMIN MY ACCOUNT
504 502 with rmap.submapper(path_prefix=ADMIN_PREFIX,
505 503 controller='admin/my_account') as m:
506 504
507 505 m.connect('my_account_edit', '/my_account/edit',
508 506 action='my_account_edit', conditions={'method': ['GET']})
509 507 m.connect('my_account', '/my_account/update',
510 508 action='my_account_update', conditions={'method': ['POST']})
511 509
512 510 # NOTE(marcink): this needs to be kept for password force flag to be
513 511 # handler, remove after migration to pyramid
514 512 m.connect('my_account_password', '/my_account/password',
515 513 action='my_account_password', conditions={'method': ['GET']})
516 514
517 515 m.connect('my_account_repos', '/my_account/repos',
518 516 action='my_account_repos', conditions={'method': ['GET']})
519 517
520 518 m.connect('my_account_watched', '/my_account/watched',
521 519 action='my_account_watched', conditions={'method': ['GET']})
522 520
523 521 m.connect('my_account_pullrequests', '/my_account/pull_requests',
524 522 action='my_account_pullrequests', conditions={'method': ['GET']})
525 523
526 524 m.connect('my_account_perms', '/my_account/perms',
527 525 action='my_account_perms', conditions={'method': ['GET']})
528 526
529 527 m.connect('my_account_emails', '/my_account/emails',
530 528 action='my_account_emails', conditions={'method': ['GET']})
531 529 m.connect('my_account_emails', '/my_account/emails',
532 530 action='my_account_emails_add', conditions={'method': ['POST']})
533 531 m.connect('my_account_emails', '/my_account/emails',
534 532 action='my_account_emails_delete', conditions={'method': ['DELETE']})
535 533
536 534 m.connect('my_account_notifications', '/my_account/notifications',
537 535 action='my_notifications',
538 536 conditions={'method': ['GET']})
539 537 m.connect('my_account_notifications_toggle_visibility',
540 538 '/my_account/toggle_visibility',
541 539 action='my_notifications_toggle_visibility',
542 540 conditions={'method': ['POST']})
543 541 m.connect('my_account_notifications_test_channelstream',
544 542 '/my_account/test_channelstream',
545 543 action='my_account_notifications_test_channelstream',
546 544 conditions={'method': ['POST']})
547 545
548 546 # NOTIFICATION REST ROUTES
549 547 with rmap.submapper(path_prefix=ADMIN_PREFIX,
550 548 controller='admin/notifications') as m:
551 549 m.connect('notifications', '/notifications',
552 550 action='index', conditions={'method': ['GET']})
553 551 m.connect('notifications_mark_all_read', '/notifications/mark_all_read',
554 552 action='mark_all_read', conditions={'method': ['POST']})
555 553 m.connect('/notifications/{notification_id}',
556 554 action='update', conditions={'method': ['PUT']})
557 555 m.connect('/notifications/{notification_id}',
558 556 action='delete', conditions={'method': ['DELETE']})
559 557 m.connect('notification', '/notifications/{notification_id}',
560 558 action='show', conditions={'method': ['GET']})
561 559
562 560 # ADMIN GIST
563 561 with rmap.submapper(path_prefix=ADMIN_PREFIX,
564 562 controller='admin/gists') as m:
565 563 m.connect('gists', '/gists',
566 564 action='create', conditions={'method': ['POST']})
567 565 m.connect('gists', '/gists', jsroute=True,
568 566 action='index', conditions={'method': ['GET']})
569 567 m.connect('new_gist', '/gists/new', jsroute=True,
570 568 action='new', conditions={'method': ['GET']})
571 569
572 570 m.connect('/gists/{gist_id}',
573 571 action='delete', conditions={'method': ['DELETE']})
574 572 m.connect('edit_gist', '/gists/{gist_id}/edit',
575 573 action='edit_form', conditions={'method': ['GET']})
576 574 m.connect('edit_gist', '/gists/{gist_id}/edit',
577 575 action='edit', conditions={'method': ['POST']})
578 576 m.connect(
579 577 'edit_gist_check_revision', '/gists/{gist_id}/edit/check_revision',
580 578 action='check_revision', conditions={'method': ['GET']})
581 579
582 580 m.connect('gist', '/gists/{gist_id}',
583 581 action='show', conditions={'method': ['GET']})
584 582 m.connect('gist_rev', '/gists/{gist_id}/{revision}',
585 583 revision='tip',
586 584 action='show', conditions={'method': ['GET']})
587 585 m.connect('formatted_gist', '/gists/{gist_id}/{revision}/{format}',
588 586 revision='tip',
589 587 action='show', conditions={'method': ['GET']})
590 588 m.connect('formatted_gist_file', '/gists/{gist_id}/{revision}/{format}/{f_path}',
591 589 revision='tip',
592 590 action='show', conditions={'method': ['GET']},
593 591 requirements=URL_NAME_REQUIREMENTS)
594 592
595 593 # ADMIN MAIN PAGES
596 594 with rmap.submapper(path_prefix=ADMIN_PREFIX,
597 595 controller='admin/admin') as m:
598 596 m.connect('admin_home', '', action='index')
599 597 m.connect('admin_add_repo', '/add_repo/{new_repo:[a-z0-9\. _-]*}',
600 598 action='add_repo')
601 599 m.connect(
602 600 'pull_requests_global_0', '/pull_requests/{pull_request_id:[0-9]+}',
603 601 action='pull_requests')
604 602 m.connect(
605 603 'pull_requests_global_1', '/pull-requests/{pull_request_id:[0-9]+}',
606 604 action='pull_requests')
607 605 m.connect(
608 606 'pull_requests_global', '/pull-request/{pull_request_id:[0-9]+}',
609 607 action='pull_requests')
610 608
611 609 # USER JOURNAL
612 610 rmap.connect('journal', '%s/journal' % (ADMIN_PREFIX,),
613 611 controller='journal', action='index')
614 612 rmap.connect('journal_rss', '%s/journal/rss' % (ADMIN_PREFIX,),
615 613 controller='journal', action='journal_rss')
616 614 rmap.connect('journal_atom', '%s/journal/atom' % (ADMIN_PREFIX,),
617 615 controller='journal', action='journal_atom')
618 616
619 617 rmap.connect('public_journal', '%s/public_journal' % (ADMIN_PREFIX,),
620 618 controller='journal', action='public_journal')
621 619
622 620 rmap.connect('public_journal_rss', '%s/public_journal/rss' % (ADMIN_PREFIX,),
623 621 controller='journal', action='public_journal_rss')
624 622
625 623 rmap.connect('public_journal_rss_old', '%s/public_journal_rss' % (ADMIN_PREFIX,),
626 624 controller='journal', action='public_journal_rss')
627 625
628 626 rmap.connect('public_journal_atom',
629 627 '%s/public_journal/atom' % (ADMIN_PREFIX,), controller='journal',
630 628 action='public_journal_atom')
631 629
632 630 rmap.connect('public_journal_atom_old',
633 631 '%s/public_journal_atom' % (ADMIN_PREFIX,), controller='journal',
634 632 action='public_journal_atom')
635 633
636 634 rmap.connect('toggle_following', '%s/toggle_following' % (ADMIN_PREFIX,),
637 635 controller='journal', action='toggle_following', jsroute=True,
638 636 conditions={'method': ['POST']})
639 637
640 638 # FULL TEXT SEARCH
641 639 rmap.connect('search', '%s/search' % (ADMIN_PREFIX,),
642 640 controller='search')
643 641 rmap.connect('search_repo_home', '/{repo_name}/search',
644 642 controller='search',
645 643 action='index',
646 644 conditions={'function': check_repo},
647 645 requirements=URL_NAME_REQUIREMENTS)
648 646
649 647 # FEEDS
650 648 rmap.connect('rss_feed_home', '/{repo_name}/feed/rss',
651 649 controller='feed', action='rss',
652 650 conditions={'function': check_repo},
653 651 requirements=URL_NAME_REQUIREMENTS)
654 652
655 653 rmap.connect('atom_feed_home', '/{repo_name}/feed/atom',
656 654 controller='feed', action='atom',
657 655 conditions={'function': check_repo},
658 656 requirements=URL_NAME_REQUIREMENTS)
659 657
660 658 #==========================================================================
661 659 # REPOSITORY ROUTES
662 660 #==========================================================================
663 661
664 662 rmap.connect('repo_creating_home', '/{repo_name}/repo_creating',
665 663 controller='admin/repos', action='repo_creating',
666 664 requirements=URL_NAME_REQUIREMENTS)
667 665 rmap.connect('repo_check_home', '/{repo_name}/crepo_check',
668 666 controller='admin/repos', action='repo_check',
669 667 requirements=URL_NAME_REQUIREMENTS)
670 668
671 669 rmap.connect('repo_stats', '/{repo_name}/repo_stats/{commit_id}',
672 670 controller='summary', action='repo_stats',
673 671 conditions={'function': check_repo},
674 672 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
675 673
676 674 rmap.connect('repo_refs_data', '/{repo_name}/refs-data',
677 675 controller='summary', action='repo_refs_data',
678 676 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
679 677 rmap.connect('repo_refs_changelog_data', '/{repo_name}/refs-data-changelog',
680 678 controller='summary', action='repo_refs_changelog_data',
681 679 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
682 680 rmap.connect('repo_default_reviewers_data', '/{repo_name}/default-reviewers',
683 681 controller='summary', action='repo_default_reviewers_data',
684 682 jsroute=True, requirements=URL_NAME_REQUIREMENTS)
685 683
686 684 rmap.connect('changeset_home', '/{repo_name}/changeset/{revision}',
687 685 controller='changeset', revision='tip',
688 686 conditions={'function': check_repo},
689 687 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
690 688 rmap.connect('changeset_children', '/{repo_name}/changeset_children/{revision}',
691 689 controller='changeset', revision='tip', action='changeset_children',
692 690 conditions={'function': check_repo},
693 691 requirements=URL_NAME_REQUIREMENTS)
694 692 rmap.connect('changeset_parents', '/{repo_name}/changeset_parents/{revision}',
695 693 controller='changeset', revision='tip', action='changeset_parents',
696 694 conditions={'function': check_repo},
697 695 requirements=URL_NAME_REQUIREMENTS)
698 696
699 697 # repo edit options
700 698 rmap.connect('edit_repo', '/{repo_name}/settings', jsroute=True,
701 699 controller='admin/repos', action='edit',
702 700 conditions={'method': ['GET'], 'function': check_repo},
703 701 requirements=URL_NAME_REQUIREMENTS)
704 702
705 703 rmap.connect('edit_repo_perms', '/{repo_name}/settings/permissions',
706 704 jsroute=True,
707 705 controller='admin/repos', action='edit_permissions',
708 706 conditions={'method': ['GET'], 'function': check_repo},
709 707 requirements=URL_NAME_REQUIREMENTS)
710 708 rmap.connect('edit_repo_perms_update', '/{repo_name}/settings/permissions',
711 709 controller='admin/repos', action='edit_permissions_update',
712 710 conditions={'method': ['PUT'], 'function': check_repo},
713 711 requirements=URL_NAME_REQUIREMENTS)
714 712
715 713 rmap.connect('edit_repo_fields', '/{repo_name}/settings/fields',
716 714 controller='admin/repos', action='edit_fields',
717 715 conditions={'method': ['GET'], 'function': check_repo},
718 716 requirements=URL_NAME_REQUIREMENTS)
719 717 rmap.connect('create_repo_fields', '/{repo_name}/settings/fields/new',
720 718 controller='admin/repos', action='create_repo_field',
721 719 conditions={'method': ['PUT'], 'function': check_repo},
722 720 requirements=URL_NAME_REQUIREMENTS)
723 721 rmap.connect('delete_repo_fields', '/{repo_name}/settings/fields/{field_id}',
724 722 controller='admin/repos', action='delete_repo_field',
725 723 conditions={'method': ['DELETE'], 'function': check_repo},
726 724 requirements=URL_NAME_REQUIREMENTS)
727 725
728 726 rmap.connect('edit_repo_advanced', '/{repo_name}/settings/advanced',
729 727 controller='admin/repos', action='edit_advanced',
730 728 conditions={'method': ['GET'], 'function': check_repo},
731 729 requirements=URL_NAME_REQUIREMENTS)
732 730
733 731 rmap.connect('edit_repo_advanced_locking', '/{repo_name}/settings/advanced/locking',
734 732 controller='admin/repos', action='edit_advanced_locking',
735 733 conditions={'method': ['PUT'], 'function': check_repo},
736 734 requirements=URL_NAME_REQUIREMENTS)
737 735 rmap.connect('toggle_locking', '/{repo_name}/settings/advanced/locking_toggle',
738 736 controller='admin/repos', action='toggle_locking',
739 737 conditions={'method': ['GET'], 'function': check_repo},
740 738 requirements=URL_NAME_REQUIREMENTS)
741 739
742 740 rmap.connect('edit_repo_advanced_journal', '/{repo_name}/settings/advanced/journal',
743 741 controller='admin/repos', action='edit_advanced_journal',
744 742 conditions={'method': ['PUT'], 'function': check_repo},
745 743 requirements=URL_NAME_REQUIREMENTS)
746 744
747 745 rmap.connect('edit_repo_advanced_fork', '/{repo_name}/settings/advanced/fork',
748 746 controller='admin/repos', action='edit_advanced_fork',
749 747 conditions={'method': ['PUT'], 'function': check_repo},
750 748 requirements=URL_NAME_REQUIREMENTS)
751 749
752 750 rmap.connect('edit_repo_caches', '/{repo_name}/settings/caches',
753 751 controller='admin/repos', action='edit_caches_form',
754 752 conditions={'method': ['GET'], 'function': check_repo},
755 753 requirements=URL_NAME_REQUIREMENTS)
756 754 rmap.connect('edit_repo_caches', '/{repo_name}/settings/caches',
757 755 controller='admin/repos', action='edit_caches',
758 756 conditions={'method': ['PUT'], 'function': check_repo},
759 757 requirements=URL_NAME_REQUIREMENTS)
760 758
761 759 rmap.connect('edit_repo_remote', '/{repo_name}/settings/remote',
762 760 controller='admin/repos', action='edit_remote_form',
763 761 conditions={'method': ['GET'], 'function': check_repo},
764 762 requirements=URL_NAME_REQUIREMENTS)
765 763 rmap.connect('edit_repo_remote', '/{repo_name}/settings/remote',
766 764 controller='admin/repos', action='edit_remote',
767 765 conditions={'method': ['PUT'], 'function': check_repo},
768 766 requirements=URL_NAME_REQUIREMENTS)
769 767
770 768 rmap.connect('edit_repo_statistics', '/{repo_name}/settings/statistics',
771 769 controller='admin/repos', action='edit_statistics_form',
772 770 conditions={'method': ['GET'], 'function': check_repo},
773 771 requirements=URL_NAME_REQUIREMENTS)
774 772 rmap.connect('edit_repo_statistics', '/{repo_name}/settings/statistics',
775 773 controller='admin/repos', action='edit_statistics',
776 774 conditions={'method': ['PUT'], 'function': check_repo},
777 775 requirements=URL_NAME_REQUIREMENTS)
778 776 rmap.connect('repo_settings_issuetracker',
779 777 '/{repo_name}/settings/issue-tracker',
780 778 controller='admin/repos', action='repo_issuetracker',
781 779 conditions={'method': ['GET'], 'function': check_repo},
782 780 requirements=URL_NAME_REQUIREMENTS)
783 781 rmap.connect('repo_issuetracker_test',
784 782 '/{repo_name}/settings/issue-tracker/test',
785 783 controller='admin/repos', action='repo_issuetracker_test',
786 784 conditions={'method': ['POST'], 'function': check_repo},
787 785 requirements=URL_NAME_REQUIREMENTS)
788 786 rmap.connect('repo_issuetracker_delete',
789 787 '/{repo_name}/settings/issue-tracker/delete',
790 788 controller='admin/repos', action='repo_issuetracker_delete',
791 789 conditions={'method': ['DELETE'], 'function': check_repo},
792 790 requirements=URL_NAME_REQUIREMENTS)
793 791 rmap.connect('repo_issuetracker_save',
794 792 '/{repo_name}/settings/issue-tracker/save',
795 793 controller='admin/repos', action='repo_issuetracker_save',
796 794 conditions={'method': ['POST'], 'function': check_repo},
797 795 requirements=URL_NAME_REQUIREMENTS)
798 796 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
799 797 controller='admin/repos', action='repo_settings_vcs_update',
800 798 conditions={'method': ['POST'], 'function': check_repo},
801 799 requirements=URL_NAME_REQUIREMENTS)
802 800 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
803 801 controller='admin/repos', action='repo_settings_vcs',
804 802 conditions={'method': ['GET'], 'function': check_repo},
805 803 requirements=URL_NAME_REQUIREMENTS)
806 804 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
807 805 controller='admin/repos', action='repo_delete_svn_pattern',
808 806 conditions={'method': ['DELETE'], 'function': check_repo},
809 807 requirements=URL_NAME_REQUIREMENTS)
810 808 rmap.connect('repo_pullrequest_settings', '/{repo_name}/settings/pullrequest',
811 809 controller='admin/repos', action='repo_settings_pullrequest',
812 810 conditions={'method': ['GET', 'POST'], 'function': check_repo},
813 811 requirements=URL_NAME_REQUIREMENTS)
814 812
815 813 # still working url for backward compat.
816 814 rmap.connect('raw_changeset_home_depraced',
817 815 '/{repo_name}/raw-changeset/{revision}',
818 816 controller='changeset', action='changeset_raw',
819 817 revision='tip', conditions={'function': check_repo},
820 818 requirements=URL_NAME_REQUIREMENTS)
821 819
822 820 # new URLs
823 821 rmap.connect('changeset_raw_home',
824 822 '/{repo_name}/changeset-diff/{revision}',
825 823 controller='changeset', action='changeset_raw',
826 824 revision='tip', conditions={'function': check_repo},
827 825 requirements=URL_NAME_REQUIREMENTS)
828 826
829 827 rmap.connect('changeset_patch_home',
830 828 '/{repo_name}/changeset-patch/{revision}',
831 829 controller='changeset', action='changeset_patch',
832 830 revision='tip', conditions={'function': check_repo},
833 831 requirements=URL_NAME_REQUIREMENTS)
834 832
835 833 rmap.connect('changeset_download_home',
836 834 '/{repo_name}/changeset-download/{revision}',
837 835 controller='changeset', action='changeset_download',
838 836 revision='tip', conditions={'function': check_repo},
839 837 requirements=URL_NAME_REQUIREMENTS)
840 838
841 839 rmap.connect('changeset_comment',
842 840 '/{repo_name}/changeset/{revision}/comment', jsroute=True,
843 841 controller='changeset', revision='tip', action='comment',
844 842 conditions={'function': check_repo},
845 843 requirements=URL_NAME_REQUIREMENTS)
846 844
847 845 rmap.connect('changeset_comment_preview',
848 846 '/{repo_name}/changeset/comment/preview', jsroute=True,
849 847 controller='changeset', action='preview_comment',
850 848 conditions={'function': check_repo, 'method': ['POST']},
851 849 requirements=URL_NAME_REQUIREMENTS)
852 850
853 851 rmap.connect('changeset_comment_delete',
854 852 '/{repo_name}/changeset/comment/{comment_id}/delete',
855 853 controller='changeset', action='delete_comment',
856 854 conditions={'function': check_repo, 'method': ['DELETE']},
857 855 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
858 856
859 857 rmap.connect('changeset_info', '/{repo_name}/changeset_info/{revision}',
860 858 controller='changeset', action='changeset_info',
861 859 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
862 860
863 861 rmap.connect('compare_home',
864 862 '/{repo_name}/compare',
865 863 controller='compare', action='index',
866 864 conditions={'function': check_repo},
867 865 requirements=URL_NAME_REQUIREMENTS)
868 866
869 867 rmap.connect('compare_url',
870 868 '/{repo_name}/compare/{source_ref_type}@{source_ref:.*?}...{target_ref_type}@{target_ref:.*?}',
871 869 controller='compare', action='compare',
872 870 conditions={'function': check_repo},
873 871 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
874 872
875 873 rmap.connect('pullrequest_home',
876 874 '/{repo_name}/pull-request/new', controller='pullrequests',
877 875 action='index', conditions={'function': check_repo,
878 876 'method': ['GET']},
879 877 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
880 878
881 879 rmap.connect('pullrequest',
882 880 '/{repo_name}/pull-request/new', controller='pullrequests',
883 881 action='create', conditions={'function': check_repo,
884 882 'method': ['POST']},
885 883 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
886 884
887 885 rmap.connect('pullrequest_repo_refs',
888 886 '/{repo_name}/pull-request/refs/{target_repo_name:.*?[^/]}',
889 887 controller='pullrequests',
890 888 action='get_repo_refs',
891 889 conditions={'function': check_repo, 'method': ['GET']},
892 890 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
893 891
894 892 rmap.connect('pullrequest_repo_destinations',
895 893 '/{repo_name}/pull-request/repo-destinations',
896 894 controller='pullrequests',
897 895 action='get_repo_destinations',
898 896 conditions={'function': check_repo, 'method': ['GET']},
899 897 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
900 898
901 899 rmap.connect('pullrequest_show',
902 900 '/{repo_name}/pull-request/{pull_request_id}',
903 901 controller='pullrequests',
904 902 action='show', conditions={'function': check_repo,
905 903 'method': ['GET']},
906 904 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
907 905
908 906 rmap.connect('pullrequest_update',
909 907 '/{repo_name}/pull-request/{pull_request_id}',
910 908 controller='pullrequests',
911 909 action='update', conditions={'function': check_repo,
912 910 'method': ['PUT']},
913 911 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
914 912
915 913 rmap.connect('pullrequest_merge',
916 914 '/{repo_name}/pull-request/{pull_request_id}',
917 915 controller='pullrequests',
918 916 action='merge', conditions={'function': check_repo,
919 917 'method': ['POST']},
920 918 requirements=URL_NAME_REQUIREMENTS)
921 919
922 920 rmap.connect('pullrequest_delete',
923 921 '/{repo_name}/pull-request/{pull_request_id}',
924 922 controller='pullrequests',
925 923 action='delete', conditions={'function': check_repo,
926 924 'method': ['DELETE']},
927 925 requirements=URL_NAME_REQUIREMENTS)
928 926
929 927 rmap.connect('pullrequest_show_all',
930 928 '/{repo_name}/pull-request',
931 929 controller='pullrequests',
932 930 action='show_all', conditions={'function': check_repo,
933 931 'method': ['GET']},
934 932 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
935 933
936 934 rmap.connect('pullrequest_comment',
937 935 '/{repo_name}/pull-request-comment/{pull_request_id}',
938 936 controller='pullrequests',
939 937 action='comment', conditions={'function': check_repo,
940 938 'method': ['POST']},
941 939 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
942 940
943 941 rmap.connect('pullrequest_comment_delete',
944 942 '/{repo_name}/pull-request-comment/{comment_id}/delete',
945 943 controller='pullrequests', action='delete_comment',
946 944 conditions={'function': check_repo, 'method': ['DELETE']},
947 945 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
948 946
949 947 rmap.connect('summary_home_explicit', '/{repo_name}/summary',
950 948 controller='summary', conditions={'function': check_repo},
951 949 requirements=URL_NAME_REQUIREMENTS)
952 950
953 951 rmap.connect('branches_home', '/{repo_name}/branches',
954 952 controller='branches', conditions={'function': check_repo},
955 953 requirements=URL_NAME_REQUIREMENTS)
956 954
957 955 rmap.connect('tags_home', '/{repo_name}/tags',
958 956 controller='tags', conditions={'function': check_repo},
959 957 requirements=URL_NAME_REQUIREMENTS)
960 958
961 959 rmap.connect('bookmarks_home', '/{repo_name}/bookmarks',
962 960 controller='bookmarks', conditions={'function': check_repo},
963 961 requirements=URL_NAME_REQUIREMENTS)
964 962
965 963 rmap.connect('changelog_home', '/{repo_name}/changelog', jsroute=True,
966 964 controller='changelog', conditions={'function': check_repo},
967 965 requirements=URL_NAME_REQUIREMENTS)
968 966
969 967 rmap.connect('changelog_summary_home', '/{repo_name}/changelog_summary',
970 968 controller='changelog', action='changelog_summary',
971 969 conditions={'function': check_repo},
972 970 requirements=URL_NAME_REQUIREMENTS)
973 971
974 972 rmap.connect('changelog_file_home',
975 973 '/{repo_name}/changelog/{revision}/{f_path}',
976 974 controller='changelog', f_path=None,
977 975 conditions={'function': check_repo},
978 976 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
979 977
980 978 rmap.connect('changelog_elements', '/{repo_name}/changelog_details',
981 979 controller='changelog', action='changelog_elements',
982 980 conditions={'function': check_repo},
983 981 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
984 982
985 983 rmap.connect('files_home', '/{repo_name}/files/{revision}/{f_path}',
986 984 controller='files', revision='tip', f_path='',
987 985 conditions={'function': check_repo},
988 986 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
989 987
990 988 rmap.connect('files_home_simple_catchrev',
991 989 '/{repo_name}/files/{revision}',
992 990 controller='files', revision='tip', f_path='',
993 991 conditions={'function': check_repo},
994 992 requirements=URL_NAME_REQUIREMENTS)
995 993
996 994 rmap.connect('files_home_simple_catchall',
997 995 '/{repo_name}/files',
998 996 controller='files', revision='tip', f_path='',
999 997 conditions={'function': check_repo},
1000 998 requirements=URL_NAME_REQUIREMENTS)
1001 999
1002 1000 rmap.connect('files_history_home',
1003 1001 '/{repo_name}/history/{revision}/{f_path}',
1004 1002 controller='files', action='history', revision='tip', f_path='',
1005 1003 conditions={'function': check_repo},
1006 1004 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1007 1005
1008 1006 rmap.connect('files_authors_home',
1009 1007 '/{repo_name}/authors/{revision}/{f_path}',
1010 1008 controller='files', action='authors', revision='tip', f_path='',
1011 1009 conditions={'function': check_repo},
1012 1010 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1013 1011
1014 1012 rmap.connect('files_diff_home', '/{repo_name}/diff/{f_path}',
1015 1013 controller='files', action='diff', f_path='',
1016 1014 conditions={'function': check_repo},
1017 1015 requirements=URL_NAME_REQUIREMENTS)
1018 1016
1019 1017 rmap.connect('files_diff_2way_home',
1020 1018 '/{repo_name}/diff-2way/{f_path}',
1021 1019 controller='files', action='diff_2way', f_path='',
1022 1020 conditions={'function': check_repo},
1023 1021 requirements=URL_NAME_REQUIREMENTS)
1024 1022
1025 1023 rmap.connect('files_rawfile_home',
1026 1024 '/{repo_name}/rawfile/{revision}/{f_path}',
1027 1025 controller='files', action='rawfile', revision='tip',
1028 1026 f_path='', conditions={'function': check_repo},
1029 1027 requirements=URL_NAME_REQUIREMENTS)
1030 1028
1031 1029 rmap.connect('files_raw_home',
1032 1030 '/{repo_name}/raw/{revision}/{f_path}',
1033 1031 controller='files', action='raw', revision='tip', f_path='',
1034 1032 conditions={'function': check_repo},
1035 1033 requirements=URL_NAME_REQUIREMENTS)
1036 1034
1037 1035 rmap.connect('files_render_home',
1038 1036 '/{repo_name}/render/{revision}/{f_path}',
1039 1037 controller='files', action='index', revision='tip', f_path='',
1040 1038 rendered=True, conditions={'function': check_repo},
1041 1039 requirements=URL_NAME_REQUIREMENTS)
1042 1040
1043 1041 rmap.connect('files_annotate_home',
1044 1042 '/{repo_name}/annotate/{revision}/{f_path}',
1045 1043 controller='files', action='index', revision='tip',
1046 1044 f_path='', annotate=True, conditions={'function': check_repo},
1047 1045 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1048 1046
1049 1047 rmap.connect('files_annotate_previous',
1050 1048 '/{repo_name}/annotate-previous/{revision}/{f_path}',
1051 1049 controller='files', action='annotate_previous', revision='tip',
1052 1050 f_path='', annotate=True, conditions={'function': check_repo},
1053 1051 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1054 1052
1055 1053 rmap.connect('files_edit',
1056 1054 '/{repo_name}/edit/{revision}/{f_path}',
1057 1055 controller='files', action='edit', revision='tip',
1058 1056 f_path='',
1059 1057 conditions={'function': check_repo, 'method': ['POST']},
1060 1058 requirements=URL_NAME_REQUIREMENTS)
1061 1059
1062 1060 rmap.connect('files_edit_home',
1063 1061 '/{repo_name}/edit/{revision}/{f_path}',
1064 1062 controller='files', action='edit_home', revision='tip',
1065 1063 f_path='', conditions={'function': check_repo},
1066 1064 requirements=URL_NAME_REQUIREMENTS)
1067 1065
1068 1066 rmap.connect('files_add',
1069 1067 '/{repo_name}/add/{revision}/{f_path}',
1070 1068 controller='files', action='add', revision='tip',
1071 1069 f_path='',
1072 1070 conditions={'function': check_repo, 'method': ['POST']},
1073 1071 requirements=URL_NAME_REQUIREMENTS)
1074 1072
1075 1073 rmap.connect('files_add_home',
1076 1074 '/{repo_name}/add/{revision}/{f_path}',
1077 1075 controller='files', action='add_home', revision='tip',
1078 1076 f_path='', conditions={'function': check_repo},
1079 1077 requirements=URL_NAME_REQUIREMENTS)
1080 1078
1081 1079 rmap.connect('files_delete',
1082 1080 '/{repo_name}/delete/{revision}/{f_path}',
1083 1081 controller='files', action='delete', revision='tip',
1084 1082 f_path='',
1085 1083 conditions={'function': check_repo, 'method': ['POST']},
1086 1084 requirements=URL_NAME_REQUIREMENTS)
1087 1085
1088 1086 rmap.connect('files_delete_home',
1089 1087 '/{repo_name}/delete/{revision}/{f_path}',
1090 1088 controller='files', action='delete_home', revision='tip',
1091 1089 f_path='', conditions={'function': check_repo},
1092 1090 requirements=URL_NAME_REQUIREMENTS)
1093 1091
1094 1092 rmap.connect('files_archive_home', '/{repo_name}/archive/{fname}',
1095 1093 controller='files', action='archivefile',
1096 1094 conditions={'function': check_repo},
1097 1095 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1098 1096
1099 1097 rmap.connect('files_nodelist_home',
1100 1098 '/{repo_name}/nodelist/{revision}/{f_path}',
1101 1099 controller='files', action='nodelist',
1102 1100 conditions={'function': check_repo},
1103 1101 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1104 1102
1105 1103 rmap.connect('files_nodetree_full',
1106 1104 '/{repo_name}/nodetree_full/{commit_id}/{f_path}',
1107 1105 controller='files', action='nodetree_full',
1108 1106 conditions={'function': check_repo},
1109 1107 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1110 1108
1111 1109 rmap.connect('repo_fork_create_home', '/{repo_name}/fork',
1112 1110 controller='forks', action='fork_create',
1113 1111 conditions={'function': check_repo, 'method': ['POST']},
1114 1112 requirements=URL_NAME_REQUIREMENTS)
1115 1113
1116 1114 rmap.connect('repo_fork_home', '/{repo_name}/fork',
1117 1115 controller='forks', action='fork',
1118 1116 conditions={'function': check_repo},
1119 1117 requirements=URL_NAME_REQUIREMENTS)
1120 1118
1121 1119 rmap.connect('repo_forks_home', '/{repo_name}/forks',
1122 1120 controller='forks', action='forks',
1123 1121 conditions={'function': check_repo},
1124 1122 requirements=URL_NAME_REQUIREMENTS)
1125 1123
1126 1124 # must be here for proper group/repo catching pattern
1127 1125 _connect_with_slash(
1128 1126 rmap, 'repo_group_home', '/{group_name}',
1129 1127 controller='home', action='index_repo_group',
1130 1128 conditions={'function': check_group},
1131 1129 requirements=URL_NAME_REQUIREMENTS)
1132 1130
1133 1131 # catch all, at the end
1134 1132 _connect_with_slash(
1135 1133 rmap, 'summary_home', '/{repo_name}', jsroute=True,
1136 1134 controller='summary', action='index',
1137 1135 conditions={'function': check_repo},
1138 1136 requirements=URL_NAME_REQUIREMENTS)
1139 1137
1140 1138 return rmap
1141 1139
1142 1140
1143 1141 def _connect_with_slash(mapper, name, path, *args, **kwargs):
1144 1142 """
1145 1143 Connect a route with an optional trailing slash in `path`.
1146 1144 """
1147 1145 mapper.connect(name + '_slash', path + '/', *args, **kwargs)
1148 1146 mapper.connect(name, path, *args, **kwargs)
@@ -1,257 +1,235 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 """
22 22 Home controller for RhodeCode Enterprise
23 23 """
24 24
25 25 import logging
26 26 import time
27 27 import re
28 28
29 29 from pylons import tmpl_context as c, request, url, config
30 30 from pylons.i18n.translation import _
31 31 from sqlalchemy.sql import func
32 32
33 33 from rhodecode.lib.auth import (
34 34 LoginRequired, HasPermissionAllDecorator, AuthUser,
35 35 HasRepoGroupPermissionAnyDecorator, XHRRequired)
36 36 from rhodecode.lib.base import BaseController, render
37 37 from rhodecode.lib.index import searcher_from_config
38 38 from rhodecode.lib.ext_json import json
39 39 from rhodecode.lib.utils import jsonify
40 40 from rhodecode.lib.utils2 import safe_unicode, str2bool
41 41 from rhodecode.model.db import Repository, RepoGroup
42 42 from rhodecode.model.repo import RepoModel
43 43 from rhodecode.model.repo_group import RepoGroupModel
44 44 from rhodecode.model.scm import RepoList, RepoGroupList
45 45
46 46
47 47 log = logging.getLogger(__name__)
48 48
49 49
50 50 class HomeController(BaseController):
51 51 def __before__(self):
52 52 super(HomeController, self).__before__()
53 53
54 54 def ping(self):
55 55 """
56 56 Ping, doesn't require login, good for checking out the platform
57 57 """
58 58 instance_id = getattr(c, 'rhodecode_instanceid', '')
59 59 return 'pong[%s] => %s' % (instance_id, self.ip_addr,)
60 60
61 61 @LoginRequired()
62 62 @HasPermissionAllDecorator('hg.admin')
63 63 def error_test(self):
64 64 """
65 65 Test exception handling and emails on errors
66 66 """
67 67 class TestException(Exception):
68 68 pass
69 69
70 70 msg = ('RhodeCode Enterprise %s test exception. Generation time: %s'
71 71 % (c.rhodecode_name, time.time()))
72 72 raise TestException(msg)
73 73
74 74 def _get_groups_and_repos(self, repo_group_id=None):
75 75 # repo groups groups
76 76 repo_group_list = RepoGroup.get_all_repo_groups(group_id=repo_group_id)
77 77 _perms = ['group.read', 'group.write', 'group.admin']
78 78 repo_group_list_acl = RepoGroupList(repo_group_list, perm_set=_perms)
79 79 repo_group_data = RepoGroupModel().get_repo_groups_as_dict(
80 80 repo_group_list=repo_group_list_acl, admin=False)
81 81
82 82 # repositories
83 83 repo_list = Repository.get_all_repos(group_id=repo_group_id)
84 84 _perms = ['repository.read', 'repository.write', 'repository.admin']
85 85 repo_list_acl = RepoList(repo_list, perm_set=_perms)
86 86 repo_data = RepoModel().get_repos_as_dict(
87 87 repo_list=repo_list_acl, admin=False)
88 88
89 89 return repo_data, repo_group_data
90 90
91 91 @LoginRequired()
92 92 def index(self):
93 93 c.repo_group = None
94 94
95 95 repo_data, repo_group_data = self._get_groups_and_repos()
96 96 # json used to render the grids
97 97 c.repos_data = json.dumps(repo_data)
98 98 c.repo_groups_data = json.dumps(repo_group_data)
99 99
100 100 return render('/index.mako')
101 101
102 102 @LoginRequired()
103 103 @HasRepoGroupPermissionAnyDecorator('group.read', 'group.write',
104 104 'group.admin')
105 105 def index_repo_group(self, group_name):
106 106 """GET /repo_group_name: Show a specific item"""
107 107 c.repo_group = RepoGroupModel()._get_repo_group(group_name)
108 108 repo_data, repo_group_data = self._get_groups_and_repos(
109 109 c.repo_group.group_id)
110 110
111 111 # json used to render the grids
112 112 c.repos_data = json.dumps(repo_data)
113 113 c.repo_groups_data = json.dumps(repo_group_data)
114 114
115 115 return render('index_repo_group.mako')
116 116
117 117 def _get_repo_list(self, name_contains=None, repo_type=None, limit=20):
118 118 query = Repository.query()\
119 119 .order_by(func.length(Repository.repo_name))\
120 120 .order_by(Repository.repo_name)
121 121
122 122 if repo_type:
123 123 query = query.filter(Repository.repo_type == repo_type)
124 124
125 125 if name_contains:
126 126 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
127 127 query = query.filter(
128 128 Repository.repo_name.ilike(ilike_expression))
129 129 query = query.limit(limit)
130 130
131 131 all_repos = query.all()
132 132 repo_iter = self.scm_model.get_repos(all_repos)
133 133 return [
134 134 {
135 135 'id': obj['name'],
136 136 'text': obj['name'],
137 137 'type': 'repo',
138 138 'obj': obj['dbrepo'],
139 139 'url': url('summary_home', repo_name=obj['name'])
140 140 }
141 141 for obj in repo_iter]
142 142
143 143 def _get_repo_group_list(self, name_contains=None, limit=20):
144 144 query = RepoGroup.query()\
145 145 .order_by(func.length(RepoGroup.group_name))\
146 146 .order_by(RepoGroup.group_name)
147 147
148 148 if name_contains:
149 149 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
150 150 query = query.filter(
151 151 RepoGroup.group_name.ilike(ilike_expression))
152 152 query = query.limit(limit)
153 153
154 154 all_groups = query.all()
155 155 repo_groups_iter = self.scm_model.get_repo_groups(all_groups)
156 156 return [
157 157 {
158 158 'id': obj.group_name,
159 159 'text': obj.group_name,
160 160 'type': 'group',
161 161 'obj': {},
162 162 'url': url('repo_group_home', group_name=obj.group_name)
163 163 }
164 164 for obj in repo_groups_iter]
165 165
166 166 def _get_hash_commit_list(self, hash_starts_with=None, limit=20):
167 167 if not hash_starts_with or len(hash_starts_with) < 3:
168 168 return []
169 169
170 170 commit_hashes = re.compile('([0-9a-f]{2,40})').findall(hash_starts_with)
171 171
172 172 if len(commit_hashes) != 1:
173 173 return []
174 174
175 175 commit_hash_prefix = commit_hashes[0]
176 176
177 177 auth_user = AuthUser(
178 178 user_id=c.rhodecode_user.user_id, ip_addr=self.ip_addr)
179 179 searcher = searcher_from_config(config)
180 180 result = searcher.search(
181 181 'commit_id:%s*' % commit_hash_prefix, 'commit', auth_user,
182 182 raise_on_exc=False)
183 183
184 184 return [
185 185 {
186 186 'id': entry['commit_id'],
187 187 'text': entry['commit_id'],
188 188 'type': 'commit',
189 189 'obj': {'repo': entry['repository']},
190 190 'url': url('changeset_home',
191 191 repo_name=entry['repository'],
192 192 revision=entry['commit_id'])
193 193 }
194 194 for entry in result['results']]
195 195
196 196 @LoginRequired()
197 197 @XHRRequired()
198 198 @jsonify
199 199 def goto_switcher_data(self):
200 200 query = request.GET.get('query')
201 201 log.debug('generating goto switcher list, query %s', query)
202 202
203 203 res = []
204 204 repo_groups = self._get_repo_group_list(query)
205 205 if repo_groups:
206 206 res.append({
207 207 'text': _('Groups'),
208 208 'children': repo_groups
209 209 })
210 210
211 211 repos = self._get_repo_list(query)
212 212 if repos:
213 213 res.append({
214 214 'text': _('Repositories'),
215 215 'children': repos
216 216 })
217 217
218 218 commits = self._get_hash_commit_list(query)
219 219 if commits:
220 220 unique_repos = {}
221 221 for commit in commits:
222 222 unique_repos.setdefault(commit['obj']['repo'], []
223 223 ).append(commit)
224 224
225 225 for repo in unique_repos:
226 226 res.append({
227 227 'text': _('Commits in %(repo)s') % {'repo': repo},
228 228 'children': unique_repos[repo]
229 229 })
230 230
231 231 data = {
232 232 'more': False,
233 233 'results': res
234 234 }
235 235 return data
236
237 @LoginRequired()
238 @XHRRequired()
239 @jsonify
240 def repo_list_data(self):
241 query = request.GET.get('query')
242 repo_type = request.GET.get('repo_type')
243 log.debug('generating repo list, query:%s', query)
244
245 res = []
246 repos = self._get_repo_list(query, repo_type=repo_type)
247 if repos:
248 res.append({
249 'text': _('Repositories'),
250 'children': repos
251 })
252
253 data = {
254 'more': False,
255 'results': res
256 }
257 return data
@@ -1,111 +1,112 b''
1 1
2 2 /******************************************************************************
3 3 * *
4 4 * DO NOT CHANGE THIS FILE MANUALLY *
5 5 * *
6 6 * *
7 7 * This file is automatically generated when the app starts up with *
8 8 * generate_js_files = true *
9 9 * *
10 10 * To add a route here pass jsroute=True to the route definition in the app *
11 11 * *
12 12 ******************************************************************************/
13 13 function registerRCRoutes() {
14 14 // routes registration
15 15 pyroutes.register('home', '/', []);
16 pyroutes.register('user_autocomplete_data', '/_users', []);
17 pyroutes.register('user_group_autocomplete_data', '/_user_groups', []);
18 16 pyroutes.register('new_repo', '/_admin/create_repository', []);
19 17 pyroutes.register('edit_user', '/_admin/users/%(user_id)s/edit', ['user_id']);
20 18 pyroutes.register('edit_user_group_members', '/_admin/user_groups/%(user_group_id)s/edit/members', ['user_group_id']);
21 19 pyroutes.register('gists', '/_admin/gists', []);
22 20 pyroutes.register('new_gist', '/_admin/gists/new', []);
23 21 pyroutes.register('toggle_following', '/_admin/toggle_following', []);
24 22 pyroutes.register('repo_stats', '/%(repo_name)s/repo_stats/%(commit_id)s', ['repo_name', 'commit_id']);
25 23 pyroutes.register('repo_refs_data', '/%(repo_name)s/refs-data', ['repo_name']);
26 24 pyroutes.register('repo_refs_changelog_data', '/%(repo_name)s/refs-data-changelog', ['repo_name']);
27 25 pyroutes.register('repo_default_reviewers_data', '/%(repo_name)s/default-reviewers', ['repo_name']);
28 26 pyroutes.register('changeset_home', '/%(repo_name)s/changeset/%(revision)s', ['repo_name', 'revision']);
29 27 pyroutes.register('edit_repo', '/%(repo_name)s/settings', ['repo_name']);
30 28 pyroutes.register('edit_repo_perms', '/%(repo_name)s/settings/permissions', ['repo_name']);
31 29 pyroutes.register('changeset_comment', '/%(repo_name)s/changeset/%(revision)s/comment', ['repo_name', 'revision']);
32 30 pyroutes.register('changeset_comment_preview', '/%(repo_name)s/changeset/comment/preview', ['repo_name']);
33 31 pyroutes.register('changeset_comment_delete', '/%(repo_name)s/changeset/comment/%(comment_id)s/delete', ['repo_name', 'comment_id']);
34 32 pyroutes.register('changeset_info', '/%(repo_name)s/changeset_info/%(revision)s', ['repo_name', 'revision']);
35 33 pyroutes.register('compare_url', '/%(repo_name)s/compare/%(source_ref_type)s@%(source_ref)s...%(target_ref_type)s@%(target_ref)s', ['repo_name', 'source_ref_type', 'source_ref', 'target_ref_type', 'target_ref']);
36 34 pyroutes.register('pullrequest_home', '/%(repo_name)s/pull-request/new', ['repo_name']);
37 35 pyroutes.register('pullrequest', '/%(repo_name)s/pull-request/new', ['repo_name']);
38 36 pyroutes.register('pullrequest_repo_refs', '/%(repo_name)s/pull-request/refs/%(target_repo_name)s', ['repo_name', 'target_repo_name']);
39 37 pyroutes.register('pullrequest_repo_destinations', '/%(repo_name)s/pull-request/repo-destinations', ['repo_name']);
40 38 pyroutes.register('pullrequest_show', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
41 39 pyroutes.register('pullrequest_update', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
42 40 pyroutes.register('pullrequest_show_all', '/%(repo_name)s/pull-request', ['repo_name']);
43 41 pyroutes.register('pullrequest_comment', '/%(repo_name)s/pull-request-comment/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
44 42 pyroutes.register('pullrequest_comment_delete', '/%(repo_name)s/pull-request-comment/%(comment_id)s/delete', ['repo_name', 'comment_id']);
45 43 pyroutes.register('changelog_home', '/%(repo_name)s/changelog', ['repo_name']);
46 44 pyroutes.register('changelog_file_home', '/%(repo_name)s/changelog/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
47 45 pyroutes.register('changelog_elements', '/%(repo_name)s/changelog_details', ['repo_name']);
48 46 pyroutes.register('files_home', '/%(repo_name)s/files/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
49 47 pyroutes.register('files_history_home', '/%(repo_name)s/history/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
50 48 pyroutes.register('files_authors_home', '/%(repo_name)s/authors/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
51 49 pyroutes.register('files_annotate_home', '/%(repo_name)s/annotate/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
52 50 pyroutes.register('files_annotate_previous', '/%(repo_name)s/annotate-previous/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
53 51 pyroutes.register('files_archive_home', '/%(repo_name)s/archive/%(fname)s', ['repo_name', 'fname']);
54 52 pyroutes.register('files_nodelist_home', '/%(repo_name)s/nodelist/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
55 53 pyroutes.register('files_nodetree_full', '/%(repo_name)s/nodetree_full/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
56 54 pyroutes.register('summary_home_slash', '/%(repo_name)s/', ['repo_name']);
57 55 pyroutes.register('summary_home', '/%(repo_name)s', ['repo_name']);
58 56 pyroutes.register('favicon', '/favicon.ico', []);
59 57 pyroutes.register('robots', '/robots.txt', []);
60 58 pyroutes.register('auth_home', '/_admin/auth*traverse', []);
61 59 pyroutes.register('global_integrations_new', '/_admin/integrations/new', []);
62 60 pyroutes.register('global_integrations_home', '/_admin/integrations', []);
63 61 pyroutes.register('global_integrations_list', '/_admin/integrations/%(integration)s', ['integration']);
64 62 pyroutes.register('global_integrations_create', '/_admin/integrations/%(integration)s/new', ['integration']);
65 63 pyroutes.register('global_integrations_edit', '/_admin/integrations/%(integration)s/%(integration_id)s', ['integration', 'integration_id']);
66 64 pyroutes.register('repo_group_integrations_home', '%(repo_group_name)s/settings/integrations', ['repo_group_name']);
67 65 pyroutes.register('repo_group_integrations_list', '%(repo_group_name)s/settings/integrations/%(integration)s', ['repo_group_name', 'integration']);
68 66 pyroutes.register('repo_group_integrations_new', '%(repo_group_name)s/settings/integrations/new', ['repo_group_name']);
69 67 pyroutes.register('repo_group_integrations_create', '%(repo_group_name)s/settings/integrations/%(integration)s/new', ['repo_group_name', 'integration']);
70 68 pyroutes.register('repo_group_integrations_edit', '%(repo_group_name)s/settings/integrations/%(integration)s/%(integration_id)s', ['repo_group_name', 'integration', 'integration_id']);
71 69 pyroutes.register('repo_integrations_home', '%(repo_name)s/settings/integrations', ['repo_name']);
72 70 pyroutes.register('repo_integrations_list', '%(repo_name)s/settings/integrations/%(integration)s', ['repo_name', 'integration']);
73 71 pyroutes.register('repo_integrations_new', '%(repo_name)s/settings/integrations/new', ['repo_name']);
74 72 pyroutes.register('repo_integrations_create', '%(repo_name)s/settings/integrations/%(integration)s/new', ['repo_name', 'integration']);
75 73 pyroutes.register('repo_integrations_edit', '%(repo_name)s/settings/integrations/%(integration)s/%(integration_id)s', ['repo_name', 'integration', 'integration_id']);
76 74 pyroutes.register('admin_settings_open_source', '_admin/settings/open_source', []);
77 75 pyroutes.register('admin_settings_vcs_svn_generate_cfg', '_admin/settings/vcs/svn_generate_cfg', []);
78 76 pyroutes.register('admin_settings_system', '_admin/settings/system', []);
79 77 pyroutes.register('admin_settings_system_update', '_admin/settings/system/updates', []);
80 78 pyroutes.register('admin_settings_sessions', '_admin/settings/sessions', []);
81 79 pyroutes.register('admin_settings_sessions_cleanup', '_admin/settings/sessions/cleanup', []);
82 80 pyroutes.register('users', '_admin/users', []);
83 81 pyroutes.register('users_data', '_admin/users_data', []);
84 82 pyroutes.register('edit_user_auth_tokens', '_admin/users/%(user_id)s/edit/auth_tokens', ['user_id']);
85 83 pyroutes.register('edit_user_auth_tokens_add', '_admin/users/%(user_id)s/edit/auth_tokens/new', ['user_id']);
86 84 pyroutes.register('edit_user_auth_tokens_delete', '_admin/users/%(user_id)s/edit/auth_tokens/delete', ['user_id']);
87 85 pyroutes.register('edit_user_groups_management', '_admin/users/%(user_id)s/edit/groups_management', ['user_id']);
88 86 pyroutes.register('edit_user_groups_management_updates', '_admin/users/%(user_id)s/edit/edit_user_groups_management/updates', ['user_id']);
89 87 pyroutes.register('edit_user_audit_logs', '_admin/users/%(user_id)s/edit/audit', ['user_id']);
90 88 pyroutes.register('channelstream_connect', '/_admin/channelstream/connect', []);
91 89 pyroutes.register('channelstream_subscribe', '/_admin/channelstream/subscribe', []);
92 90 pyroutes.register('channelstream_proxy', '/_channelstream', []);
93 91 pyroutes.register('login', '/_admin/login', []);
94 92 pyroutes.register('logout', '/_admin/logout', []);
95 93 pyroutes.register('register', '/_admin/register', []);
96 94 pyroutes.register('reset_password', '/_admin/password_reset', []);
97 95 pyroutes.register('reset_password_confirmation', '/_admin/password_reset_confirmation', []);
96 pyroutes.register('user_autocomplete_data', '/_users', []);
97 pyroutes.register('user_group_autocomplete_data', '/_user_groups', []);
98 pyroutes.register('repo_list_data', '/_repos', []);
98 99 pyroutes.register('repo_maintenance', '/%(repo_name)s/maintenance', ['repo_name']);
99 100 pyroutes.register('repo_maintenance_execute', '/%(repo_name)s/maintenance/execute', ['repo_name']);
100 101 pyroutes.register('strip', '/%(repo_name)s/strip', ['repo_name']);
101 102 pyroutes.register('strip_check', '/%(repo_name)s/strip_check', ['repo_name']);
102 103 pyroutes.register('strip_execute', '/%(repo_name)s/strip_execute', ['repo_name']);
103 104 pyroutes.register('user_profile', '/_profiles/%(username)s', ['username']);
104 105 pyroutes.register('my_account_profile', '/_admin/my_account/profile', []);
105 106 pyroutes.register('my_account_password', '/_admin/my_account/password', []);
106 107 pyroutes.register('my_account_password_update', '/_admin/my_account/password', []);
107 108 pyroutes.register('my_account_auth_tokens', '/_admin/my_account/auth_tokens', []);
108 109 pyroutes.register('my_account_auth_tokens_add', '/_admin/my_account/auth_tokens/new', []);
109 110 pyroutes.register('my_account_auth_tokens_delete', '/_admin/my_account/auth_tokens/delete', []);
110 111 pyroutes.register('apiv2', '/_admin/api', []);
111 112 }
@@ -1,160 +1,160 b''
1 1 <div class="panel panel-default">
2 2 <div class="panel-heading">
3 3 <h3 class="panel-title">${_('Authentication Tokens')}</h3>
4 4 </div>
5 5 <div class="panel-body">
6 6 <div class="apikeys_wrap">
7 7 <p>
8 8 ${_('Each token can have a role. Token with a role can be used only in given context, '
9 9 'e.g. VCS tokens can be used together with the authtoken auth plugin for git/hg/svn operations only.')}
10 10 </p>
11 11 <table class="rctable auth_tokens">
12 12 <tr>
13 13 <th>${_('Token')}</th>
14 14 <th>${_('Scope')}</th>
15 15 <th>${_('Description')}</th>
16 16 <th>${_('Role')}</th>
17 17 <th>${_('Expiration')}</th>
18 18 <th>${_('Action')}</th>
19 19 </tr>
20 20 %if c.user_auth_tokens:
21 21 %for auth_token in c.user_auth_tokens:
22 22 <tr class="${'expired' if auth_token.expired else ''}">
23 23 <td class="truncate-wrap td-authtoken">
24 24 <div class="user_auth_tokens truncate autoexpand">
25 25 <code>${auth_token.api_key}</code>
26 26 </div>
27 27 </td>
28 28 <td class="td">${auth_token.scope_humanized}</td>
29 29 <td class="td-wrap">${auth_token.description}</td>
30 30 <td class="td-tags">
31 31 <span class="tag disabled">${auth_token.role_humanized}</span>
32 32 </td>
33 33 <td class="td-exp">
34 34 %if auth_token.expires == -1:
35 35 ${_('never')}
36 36 %else:
37 37 %if auth_token.expired:
38 38 <span style="text-decoration: line-through">${h.age_component(h.time_to_utcdatetime(auth_token.expires))}</span>
39 39 %else:
40 40 ${h.age_component(h.time_to_utcdatetime(auth_token.expires))}
41 41 %endif
42 42 %endif
43 43 </td>
44 44 <td class="td-action">
45 45 ${h.secure_form(h.route_path('my_account_auth_tokens_delete'), method='post')}
46 46 ${h.hidden('del_auth_token',auth_token.api_key)}
47 47 <button class="btn btn-link btn-danger" type="submit"
48 48 onclick="return confirm('${_('Confirm to remove this auth token: %s') % auth_token.api_key}');">
49 49 ${_('Delete')}
50 50 </button>
51 51 ${h.end_form()}
52 52 </td>
53 53 </tr>
54 54 %endfor
55 55 %else:
56 56 <tr><td><div class="ip">${_('No additional auth tokens specified')}</div></td></tr>
57 57 %endif
58 58 </table>
59 59 </div>
60 60
61 61 <div class="user_auth_tokens">
62 62 ${h.secure_form(h.route_path('my_account_auth_tokens_add'), method='post')}
63 63 <div class="form form-vertical">
64 64 <!-- fields -->
65 65 <div class="fields">
66 66 <div class="field">
67 67 <div class="label">
68 68 <label for="new_email">${_('New authentication token')}:</label>
69 69 </div>
70 70 <div class="input">
71 71 ${h.text('description', class_='medium', placeholder=_('Description'))}
72 72 ${h.select('lifetime', '', c.lifetime_options)}
73 73 ${h.select('role', '', c.role_options)}
74 74
75 75 % if c.allow_scoped_tokens:
76 76 ${h.hidden('scope_repo_id')}
77 77 % else:
78 78 ${h.select('scope_repo_id_disabled', '', ['Scopes available in EE edition'], disabled='disabled')}
79 79 % endif
80 80 </div>
81 81 <p class="help-block">
82 82 ${_('Repository scope works only with tokens with VCS type.')}
83 83 </p>
84 84 </div>
85 85 <div class="buttons">
86 86 ${h.submit('save',_('Add'),class_="btn")}
87 87 ${h.reset('reset',_('Reset'),class_="btn")}
88 88 </div>
89 89 </div>
90 90 </div>
91 91 ${h.end_form()}
92 92 </div>
93 93 </div>
94 94 </div>
95 95 <script>
96 96 $(document).ready(function(){
97 97
98 98 var select2Options = {
99 99 'containerCssClass': "drop-menu",
100 100 'dropdownCssClass': "drop-menu-dropdown",
101 101 'dropdownAutoWidth': true
102 102 };
103 103 $("#lifetime").select2(select2Options);
104 104 $("#role").select2(select2Options);
105 105
106 106 var repoFilter = function(data) {
107 107 var results = [];
108 108
109 109 if (!data.results[0]) {
110 110 return data
111 111 }
112 112
113 113 $.each(data.results[0].children, function() {
114 114 // replace name to ID for submision
115 115 this.id = this.obj.repo_id;
116 116 results.push(this);
117 117 });
118 118
119 119 data.results[0].children = results;
120 120 return data;
121 121 };
122 122
123 123 $("#scope_repo_id_disabled").select2(select2Options);
124 124
125 125 $("#scope_repo_id").select2({
126 126 cachedDataSource: {},
127 127 minimumInputLength: 2,
128 128 placeholder: "${_('repository scope')}",
129 129 dropdownAutoWidth: true,
130 130 containerCssClass: "drop-menu",
131 131 dropdownCssClass: "drop-menu-dropdown",
132 132 formatResult: formatResult,
133 133 query: $.debounce(250, function(query){
134 134 self = this;
135 135 var cacheKey = query.term;
136 136 var cachedData = self.cachedDataSource[cacheKey];
137 137
138 138 if (cachedData) {
139 139 query.callback({results: cachedData.results});
140 140 } else {
141 141 $.ajax({
142 url: "${h.url('repo_list_data')}",
142 url: pyroutes.url('repo_list_data'),
143 143 data: {'query': query.term},
144 144 dataType: 'json',
145 145 type: 'GET',
146 146 success: function(data) {
147 147 data = repoFilter(data);
148 148 self.cachedDataSource[cacheKey] = data;
149 149 query.callback({results: data.results});
150 150 },
151 151 error: function(data, textStatus, errorThrown) {
152 152 alert("Error while fetching entries.\nError code {0} ({1}).".format(data.status, data.statusText));
153 153 }
154 154 })
155 155 }
156 156 })
157 157 });
158 158
159 159 });
160 160 </script>
@@ -1,211 +1,211 b''
1 1 <%namespace name="base" file="/base/base.mako"/>
2 2
3 3 <%
4 4 elems = [
5 5 (_('Owner'), lambda:base.gravatar_with_user(c.repo_info.user.email), '', ''),
6 6 (_('Created on'), h.format_date(c.repo_info.created_on), '', ''),
7 7 (_('Updated on'), h.format_date(c.repo_info.updated_on), '', ''),
8 8 (_('Cached Commit id'), lambda: h.link_to(c.repo_info.changeset_cache.get('short_id'), h.url('changeset_home',repo_name=c.repo_name,revision=c.repo_info.changeset_cache.get('raw_id'))), '', ''),
9 9 ]
10 10 %>
11 11
12 12 <div class="panel panel-default">
13 13 <div class="panel-heading">
14 14 <h3 class="panel-title">${_('Repository: %s') % c.repo_info.repo_name}</h3>
15 15 </div>
16 16 <div class="panel-body">
17 17 ${base.dt_info_panel(elems)}
18 18 </div>
19 19 </div>
20 20
21 21
22 22 <div class="panel panel-default">
23 23 <div class="panel-heading">
24 24 <h3 class="panel-title">${_('Fork Reference')}</h3>
25 25 </div>
26 26 <div class="panel-body">
27 27 ${h.secure_form(url('edit_repo_advanced_fork', repo_name=c.repo_info.repo_name), method='put')}
28 28
29 29 % if c.repo_info.fork:
30 30 <div class="panel-body-title-text">${h.literal(_('This repository is a fork of %(repo_link)s') % {'repo_link': h.link_to_if(c.has_origin_repo_read_perm,c.repo_info.fork.repo_name, h.url('summary_home', repo_name=c.repo_info.fork.repo_name))})}
31 31 | <button class="btn btn-link btn-danger" type="submit">Remove fork reference</button></div>
32 32 % endif
33 33
34 34 <div class="field">
35 35 ${h.hidden('id_fork_of')}
36 36 ${h.submit('set_as_fork_%s' % c.repo_info.repo_name,_('Set'),class_="btn btn-small",)}
37 37 </div>
38 38 <div class="field">
39 39 <span class="help-block">${_('Manually set this repository as a fork of another from the list')}</span>
40 40 </div>
41 41 ${h.end_form()}
42 42 </div>
43 43 </div>
44 44
45 45
46 46 <div class="panel panel-default">
47 47 <div class="panel-heading">
48 48 <h3 class="panel-title">${_('Public Journal Visibility')}</h3>
49 49 </div>
50 50 <div class="panel-body">
51 51 ${h.secure_form(url('edit_repo_advanced_journal', repo_name=c.repo_info.repo_name), method='put')}
52 52 <div class="field">
53 53 %if c.in_public_journal:
54 54 <button class="btn btn-small" type="submit">
55 55 <i class="icon-minus"></i>
56 56 ${_('Remove from Public Journal')}
57 57 </button>
58 58 %else:
59 59 <button class="btn btn-small" type="submit">
60 60 ${_('Add to Public Journal')}
61 61 </button>
62 62 %endif
63 63 </div>
64 64 <div class="field" >
65 65 <span class="help-block">${_('All actions made on this repository will be visible to everyone following the public journal.')}</span>
66 66 </div>
67 67 ${h.end_form()}
68 68 </div>
69 69 </div>
70 70
71 71
72 72 <div class="panel panel-default">
73 73 <div class="panel-heading">
74 74 <h3 class="panel-title">${_('Locking state')}</h3>
75 75 </div>
76 76 <div class="panel-body">
77 77 ${h.secure_form(url('edit_repo_advanced_locking', repo_name=c.repo_info.repo_name), method='put')}
78 78
79 79 %if c.repo_info.locked[0]:
80 80 <div class="panel-body-title-text">${'Locked by %s on %s. Lock reason: %s' % (h.person_by_id(c.repo_info.locked[0]),
81 81 h.format_date(h. time_to_datetime(c.repo_info.locked[1])), c.repo_info.locked[2])}</div>
82 82 %else:
83 83 <div class="panel-body-title-text">${_('This Repository is not currently locked.')}</div>
84 84 %endif
85 85
86 86 <div class="field" >
87 87 %if c.repo_info.locked[0]:
88 88 ${h.hidden('set_unlock', '1')}
89 89 <button class="btn btn-small" type="submit"
90 90 onclick="return confirm('${_('Confirm to unlock repository.')}');">
91 91 <i class="icon-unlock"></i>
92 92 ${_('Unlock repository')}
93 93 </button>
94 94 %else:
95 95 ${h.hidden('set_lock', '1')}
96 96 <button class="btn btn-small" type="submit"
97 97 onclick="return confirm('${_('Confirm to lock repository.')}');">
98 98 <i class="icon-lock"></i>
99 99 ${_('Lock Repository')}
100 100 </button>
101 101 %endif
102 102 </div>
103 103 <div class="field" >
104 104 <span class="help-block">
105 105 ${_('Force repository locking. This only works when anonymous access is disabled. Pulling from the repository locks the repository to that user until the same user pushes to that repository again.')}
106 106 </span>
107 107 </div>
108 108 ${h.end_form()}
109 109 </div>
110 110 </div>
111 111
112 112 <div class="panel panel-danger">
113 113 <div class="panel-heading">
114 114 <h3 class="panel-title">${_('Delete repository')}</h3>
115 115 </div>
116 116 <div class="panel-body">
117 117 ${h.secure_form(url('repo', repo_name=c.repo_name),method='delete')}
118 118 <table class="display">
119 119 <tr>
120 120 <td>
121 121 ${ungettext('This repository has %s fork.', 'This repository has %s forks.', c.repo_info. forks.count()) % c.repo_info.forks.count()}
122 122 </td>
123 123 <td>
124 124 %if c.repo_info.forks.count():
125 125 <input type="radio" name="forks" value="detach_forks" checked="checked"/> <label for="forks">${_('Detach forks')}</label>
126 126 %endif
127 127 </td>
128 128 <td>
129 129 %if c.repo_info.forks.count():
130 130 <input type="radio" name="forks" value="delete_forks"/> <label for="forks">${_('Delete forks')}</label>
131 131 %endif
132 132 </td>
133 133 </tr>
134 134 </table>
135 135 <div style="margin: 0 0 20px 0" class="fake-space"></div>
136 136
137 137 <div class="field">
138 138 <button class="btn btn-small btn-danger" type="submit"
139 139 onclick="return confirm('${_('Confirm to delete this repository: %s') % c.repo_name}');">
140 140 <i class="icon-remove-sign"></i>
141 141 ${_('Delete This Repository')}
142 142 </button>
143 143 </div>
144 144 <div class="field">
145 145 <span class="help-block">
146 146 ${_('This repository will be renamed in a special way in order to make it inaccessible to RhodeCode Enterprise and its VCS systems. If you need to fully delete it from the file system, please do it manually, or with rhodecode-cleanup-repos command.')}
147 147 </span>
148 148 </div>
149 149
150 150 ${h.end_form()}
151 151 </div>
152 152 </div>
153 153
154 154
155 155 <script>
156 156
157 157 var currentRepoId = ${c.repo_info.repo_id};
158 158
159 159 var repoTypeFilter = function(data) {
160 160 var results = [];
161 161
162 162 if (!data.results[0]) {
163 163 return data
164 164 }
165 165
166 166 $.each(data.results[0].children, function() {
167 167 // filter out the SAME repo, it cannot be used as fork of itself
168 168 if (this.obj.repo_id != currentRepoId) {
169 169 this.id = this.obj.repo_id;
170 170 results.push(this)
171 171 }
172 172 });
173 173 data.results[0].children = results;
174 174 return data;
175 175 };
176 176
177 177 $("#id_fork_of").select2({
178 178 cachedDataSource: {},
179 179 minimumInputLength: 2,
180 180 placeholder: "${_('Change repository') if c.repo_info.fork else _('Pick repository')}",
181 181 dropdownAutoWidth: true,
182 182 containerCssClass: "drop-menu",
183 183 dropdownCssClass: "drop-menu-dropdown",
184 184 formatResult: formatResult,
185 185 query: $.debounce(250, function(query){
186 186 self = this;
187 187 var cacheKey = query.term;
188 188 var cachedData = self.cachedDataSource[cacheKey];
189 189
190 190 if (cachedData) {
191 191 query.callback({results: cachedData.results});
192 192 } else {
193 193 $.ajax({
194 url: "${h.url('repo_list_data')}",
194 url: pyroutes.url('repo_list_data'),
195 195 data: {'query': query.term, repo_type: '${c.repo_info.repo_type}'},
196 196 dataType: 'json',
197 197 type: 'GET',
198 198 success: function(data) {
199 199 data = repoTypeFilter(data);
200 200 self.cachedDataSource[cacheKey] = data;
201 201 query.callback({results: data.results});
202 202 },
203 203 error: function(data, textStatus, errorThrown) {
204 204 alert("Error while fetching entries.\nError code {0} ({1}).".format(data.status, data.statusText));
205 205 }
206 206 })
207 207 }
208 208 })
209 209 });
210 210 </script>
211 211
@@ -1,157 +1,157 b''
1 1 <div class="panel panel-default">
2 2 <div class="panel-heading">
3 3 <h3 class="panel-title">${_('Authentication Tokens')}</h3>
4 4 </div>
5 5 <div class="panel-body">
6 6 <div class="apikeys_wrap">
7 7 <p>
8 8 ${_('Each token can have a role. Token with a role can be used only in given context, '
9 9 'e.g. VCS tokens can be used together with the authtoken auth plugin for git/hg/svn operations only.')}
10 10 </p>
11 11 <table class="rctable auth_tokens">
12 12 <tr>
13 13 <th>${_('Token')}</th>
14 14 <th>${_('Scope')}</th>
15 15 <th>${_('Description')}</th>
16 16 <th>${_('Role')}</th>
17 17 <th>${_('Expiration')}</th>
18 18 <th>${_('Action')}</th>
19 19 </tr>
20 20 %if c.user_auth_tokens:
21 21 %for auth_token in c.user_auth_tokens:
22 22 <tr class="${'expired' if auth_token.expired else ''}">
23 23 <td class="truncate-wrap td-authtoken"><div class="user_auth_tokens truncate autoexpand"><code>${auth_token.api_key}</code></div></td>
24 24 <td class="td">${auth_token.scope_humanized}</td>
25 25 <td class="td-wrap">${auth_token.description}</td>
26 26 <td class="td-tags">
27 27 <span class="tag disabled">${auth_token.role_humanized}</span>
28 28 </td>
29 29 <td class="td-exp">
30 30 %if auth_token.expires == -1:
31 31 ${_('never')}
32 32 %else:
33 33 %if auth_token.expired:
34 34 <span style="text-decoration: line-through">${h.age_component(h.time_to_utcdatetime(auth_token.expires))}</span>
35 35 %else:
36 36 ${h.age_component(h.time_to_utcdatetime(auth_token.expires))}
37 37 %endif
38 38 %endif
39 39 </td>
40 40 <td class="td-action">
41 41 ${h.secure_form(h.route_path('edit_user_auth_tokens_delete', user_id=c.user.user_id), method='post')}
42 42 ${h.hidden('del_auth_token',auth_token.api_key)}
43 43 <button class="btn btn-link btn-danger" type="submit"
44 44 onclick="return confirm('${_('Confirm to remove this auth token: %s') % auth_token.api_key}');">
45 45 ${_('Delete')}
46 46 </button>
47 47 ${h.end_form()}
48 48 </td>
49 49 </tr>
50 50 %endfor
51 51 %else:
52 52 <tr><td><div class="ip">${_('No additional auth tokens specified')}</div></td></tr>
53 53 %endif
54 54 </table>
55 55 </div>
56 56
57 57 <div class="user_auth_tokens">
58 58 ${h.secure_form(h.route_path('edit_user_auth_tokens_add', user_id=c.user.user_id), method='post')}
59 59 <div class="form form-vertical">
60 60 <!-- fields -->
61 61 <div class="fields">
62 62 <div class="field">
63 63 <div class="label">
64 64 <label for="new_email">${_('New authentication token')}:</label>
65 65 </div>
66 66 <div class="input">
67 67 ${h.text('description', class_='medium', placeholder=_('Description'))}
68 68 ${h.select('lifetime', '', c.lifetime_options)}
69 69 ${h.select('role', '', c.role_options)}
70 70
71 71 % if c.allow_scoped_tokens:
72 72 ${h.hidden('scope_repo_id')}
73 73 % else:
74 74 ${h.select('scope_repo_id_disabled', '', ['Scopes available in EE edition'], disabled='disabled')}
75 75 % endif
76 76 </div>
77 77 <p class="help-block">
78 78 ${_('Repository scope works only with tokens with VCS type.')}
79 79 </p>
80 80 </div>
81 81 <div class="buttons">
82 82 ${h.submit('save',_('Add'),class_="btn")}
83 83 ${h.reset('reset',_('Reset'),class_="btn")}
84 84 </div>
85 85 </div>
86 86 </div>
87 87 ${h.end_form()}
88 88 </div>
89 89 </div>
90 90 </div>
91 91
92 92 <script>
93 93
94 94 $(document).ready(function(){
95 95 var select2Options = {
96 96 'containerCssClass': "drop-menu",
97 97 'dropdownCssClass': "drop-menu-dropdown",
98 98 'dropdownAutoWidth': true
99 99 };
100 100 $("#lifetime").select2(select2Options);
101 101 $("#role").select2(select2Options);
102 102
103 103 var repoFilter = function(data) {
104 104 var results = [];
105 105
106 106 if (!data.results[0]) {
107 107 return data
108 108 }
109 109
110 110 $.each(data.results[0].children, function() {
111 111 // replace name to ID for submision
112 112 this.id = this.obj.repo_id;
113 113 results.push(this);
114 114 });
115 115
116 116 data.results[0].children = results;
117 117 return data;
118 118 };
119 119
120 120 $("#scope_repo_id_disabled").select2(select2Options);
121 121
122 122 $("#scope_repo_id").select2({
123 123 cachedDataSource: {},
124 124 minimumInputLength: 2,
125 125 placeholder: "${_('repository scope')}",
126 126 dropdownAutoWidth: true,
127 127 containerCssClass: "drop-menu",
128 128 dropdownCssClass: "drop-menu-dropdown",
129 129 formatResult: formatResult,
130 130 query: $.debounce(250, function(query){
131 131 self = this;
132 132 var cacheKey = query.term;
133 133 var cachedData = self.cachedDataSource[cacheKey];
134 134
135 135 if (cachedData) {
136 136 query.callback({results: cachedData.results});
137 137 } else {
138 138 $.ajax({
139 url: "${h.url('repo_list_data')}",
139 url: pyroutes.url('repo_list_data'),
140 140 data: {'query': query.term},
141 141 dataType: 'json',
142 142 type: 'GET',
143 143 success: function(data) {
144 144 data = repoFilter(data);
145 145 self.cachedDataSource[cacheKey] = data;
146 146 query.callback({results: data.results});
147 147 },
148 148 error: function(data, textStatus, errorThrown) {
149 149 alert("Error while fetching entries.\nError code {0} ({1}).".format(data.status, data.statusText));
150 150 }
151 151 })
152 152 }
153 153 })
154 154 });
155 155
156 156 });
157 157 </script>
@@ -1,319 +1,257 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import json
22 22
23 23 from mock import patch
24 24 import pytest
25 25 from pylons import tmpl_context as c
26 26
27 27 import rhodecode
28 28 from rhodecode.lib.utils import map_groups
29 29 from rhodecode.model.db import Repository, User, RepoGroup
30 30 from rhodecode.model.meta import Session
31 31 from rhodecode.model.repo import RepoModel
32 32 from rhodecode.model.repo_group import RepoGroupModel
33 33 from rhodecode.model.settings import SettingsModel
34 34 from rhodecode.tests import TestController, url, TEST_USER_ADMIN_LOGIN
35 35 from rhodecode.tests.fixture import Fixture
36 36
37 37
38 38 fixture = Fixture()
39 39
40 40
41 41 class TestHomeController(TestController):
42 42
43 43 def test_index(self):
44 44 self.log_user()
45 45 response = self.app.get(url(controller='home', action='index'))
46 46 # if global permission is set
47 47 response.mustcontain('Add Repository')
48 48
49 49 # search for objects inside the JavaScript JSON
50 50 for repo in Repository.getAll():
51 51 response.mustcontain('"name_raw": "%s"' % repo.repo_name)
52 52
53 53 def test_index_contains_statics_with_ver(self):
54 54 self.log_user()
55 55 response = self.app.get(url(controller='home', action='index'))
56 56
57 57 rhodecode_version_hash = c.rhodecode_version_hash
58 58 response.mustcontain('style.css?ver={0}'.format(rhodecode_version_hash))
59 59 response.mustcontain('rhodecode-components.js?ver={0}'.format(rhodecode_version_hash))
60 60
61 61 def test_index_contains_backend_specific_details(self, backend):
62 62 self.log_user()
63 63 response = self.app.get(url(controller='home', action='index'))
64 64 tip = backend.repo.get_commit().raw_id
65 65
66 66 # html in javascript variable:
67 67 response.mustcontain(r'<i class=\"icon-%s\"' % (backend.alias, ))
68 68 response.mustcontain(r'href=\"/%s\"' % (backend.repo_name, ))
69 69
70 70 response.mustcontain("""/%s/changeset/%s""" % (backend.repo_name, tip))
71 71 response.mustcontain("""Added a symlink""")
72 72
73 73 def test_index_with_anonymous_access_disabled(self):
74 74 with fixture.anon_access(False):
75 75 response = self.app.get(url(controller='home', action='index'),
76 76 status=302)
77 77 assert 'login' in response.location
78 78
79 79 def test_index_page_on_groups(self, autologin_user, repo_group):
80 80 response = self.app.get(url('repo_group_home', group_name='gr1'))
81 81 response.mustcontain("gr1/repo_in_group")
82 82
83 83 def test_index_page_on_group_with_trailing_slash(
84 84 self, autologin_user, repo_group):
85 85 response = self.app.get(url('repo_group_home', group_name='gr1') + '/')
86 86 response.mustcontain("gr1/repo_in_group")
87 87
88 88 @pytest.fixture(scope='class')
89 89 def repo_group(self, request):
90 90 gr = fixture.create_repo_group('gr1')
91 91 fixture.create_repo(name='gr1/repo_in_group', repo_group=gr)
92 92
93 93 @request.addfinalizer
94 94 def cleanup():
95 95 RepoModel().delete('gr1/repo_in_group')
96 96 RepoGroupModel().delete(repo_group='gr1', force_delete=True)
97 97 Session().commit()
98 98
99 99 def test_index_with_name_with_tags(self, autologin_user):
100 100 user = User.get_by_username('test_admin')
101 101 user.name = '<img src="/image1" onload="alert(\'Hello, World!\');">'
102 102 user.lastname = (
103 103 '<img src="/image2" onload="alert(\'Hello, World!\');">')
104 104 Session().add(user)
105 105 Session().commit()
106 106
107 107 response = self.app.get(url(controller='home', action='index'))
108 108 response.mustcontain(
109 109 '&lt;img src=&#34;/image1&#34; onload=&#34;'
110 110 'alert(&#39;Hello, World!&#39;);&#34;&gt;')
111 111 response.mustcontain(
112 112 '&lt;img src=&#34;/image2&#34; onload=&#34;'
113 113 'alert(&#39;Hello, World!&#39;);&#34;&gt;')
114 114
115 115 @pytest.mark.parametrize("name, state", [
116 116 ('Disabled', False),
117 117 ('Enabled', True),
118 118 ])
119 119 def test_index_show_version(self, autologin_user, name, state):
120 120 version_string = 'RhodeCode Enterprise %s' % rhodecode.__version__
121 121
122 122 sett = SettingsModel().create_or_update_setting(
123 123 'show_version', state, 'bool')
124 124 Session().add(sett)
125 125 Session().commit()
126 126 SettingsModel().invalidate_settings_cache()
127 127
128 128 response = self.app.get(url(controller='home', action='index'))
129 129 if state is True:
130 130 response.mustcontain(version_string)
131 131 if state is False:
132 132 response.mustcontain(no=[version_string])
133 133
134 134
135 135 def assert_and_get_content(result):
136 136 repos = []
137 137 groups = []
138 138 commits = []
139 139 for data in result:
140 140 for data_item in data['children']:
141 141 assert data_item['id']
142 142 assert data_item['text']
143 143 assert data_item['url']
144 144 if data_item['type'] == 'repo':
145 145 repos.append(data_item)
146 146 elif data_item['type'] == 'group':
147 147 groups.append(data_item)
148 148 elif data_item['type'] == 'commit':
149 149 commits.append(data_item)
150 150 else:
151 151 raise Exception('invalid type %s' % data_item['type'])
152 152
153 153 return repos, groups, commits
154 154
155 155
156 156 class TestGotoSwitcherData(TestController):
157 157 required_repos_with_groups = [
158 158 'abc',
159 159 'abc-fork',
160 160 'forks/abcd',
161 161 'abcd',
162 162 'abcde',
163 163 'a/abc',
164 164 'aa/abc',
165 165 'aaa/abc',
166 166 'aaaa/abc',
167 167 'repos_abc/aaa/abc',
168 168 'abc_repos/abc',
169 169 'abc_repos/abcd',
170 170 'xxx/xyz',
171 171 'forked-abc/a/abc'
172 172 ]
173 173
174 174 @pytest.fixture(autouse=True, scope='class')
175 175 def prepare(self, request, pylonsapp):
176 176 for repo_and_group in self.required_repos_with_groups:
177 177 # create structure of groups and return the last group
178 178
179 179 repo_group = map_groups(repo_and_group)
180 180
181 181 RepoModel()._create_repo(
182 182 repo_and_group, 'hg', 'test-ac', TEST_USER_ADMIN_LOGIN,
183 183 repo_group=getattr(repo_group, 'group_id', None))
184 184
185 185 Session().commit()
186 186
187 187 request.addfinalizer(self.cleanup)
188 188
189 189 def cleanup(self):
190 190 # first delete all repos
191 191 for repo_and_groups in self.required_repos_with_groups:
192 192 repo = Repository.get_by_repo_name(repo_and_groups)
193 193 if repo:
194 194 RepoModel().delete(repo)
195 195 Session().commit()
196 196
197 197 # then delete all empty groups
198 198 for repo_and_groups in self.required_repos_with_groups:
199 199 if '/' in repo_and_groups:
200 200 r_group = repo_and_groups.rsplit('/', 1)[0]
201 201 repo_group = RepoGroup.get_by_group_name(r_group)
202 202 if not repo_group:
203 203 continue
204 204 parents = repo_group.parents
205 205 RepoGroupModel().delete(repo_group, force_delete=True)
206 206 Session().commit()
207 207
208 208 for el in reversed(parents):
209 209 RepoGroupModel().delete(el, force_delete=True)
210 210 Session().commit()
211 211
212 212 def test_returns_list_of_repos_and_groups(self):
213 213 self.log_user()
214 214
215 215 response = self.app.get(
216 216 url(controller='home', action='goto_switcher_data'),
217 217 headers={'X-REQUESTED-WITH': 'XMLHttpRequest', }, status=200)
218 218 result = json.loads(response.body)['results']
219 219
220 220 repos, groups, commits = assert_and_get_content(result)
221 221
222 222 assert len(repos) == len(Repository.get_all())
223 223 assert len(groups) == len(RepoGroup.get_all())
224 224 assert len(commits) == 0
225 225
226 226 def test_returns_list_of_repos_and_groups_filtered(self):
227 227 self.log_user()
228 228
229 229 response = self.app.get(
230 230 url(controller='home', action='goto_switcher_data'),
231 231 headers={'X-REQUESTED-WITH': 'XMLHttpRequest', },
232 232 params={'query': 'abc'}, status=200)
233 233 result = json.loads(response.body)['results']
234 234
235 235 repos, groups, commits = assert_and_get_content(result)
236 236
237 237 assert len(repos) == 13
238 238 assert len(groups) == 5
239 239 assert len(commits) == 0
240 240
241 241 def test_returns_list_of_properly_sorted_and_filtered(self):
242 242 self.log_user()
243 243
244 244 response = self.app.get(
245 245 url(controller='home', action='goto_switcher_data'),
246 246 headers={'X-REQUESTED-WITH': 'XMLHttpRequest', },
247 247 params={'query': 'abc'}, status=200)
248 248 result = json.loads(response.body)['results']
249 249
250 250 repos, groups, commits = assert_and_get_content(result)
251 251
252 252 test_repos = [x['text'] for x in repos[:4]]
253 253 assert ['abc', 'abcd', 'a/abc', 'abcde'] == test_repos
254 254
255 255 test_groups = [x['text'] for x in groups[:4]]
256 256 assert ['abc_repos', 'repos_abc',
257 257 'forked-abc', 'forked-abc/a'] == test_groups
258
259
260 class TestRepoListData(TestController):
261 def test_returns_list_of_repos_and_groups(self, user_util):
262 self.log_user()
263
264 response = self.app.get(
265 url(controller='home', action='repo_list_data'),
266 headers={'X-REQUESTED-WITH': 'XMLHttpRequest', }, status=200)
267 result = json.loads(response.body)['results']
268
269 repos, groups, commits = assert_and_get_content(result)
270
271 assert len(repos) == len(Repository.get_all())
272 assert len(groups) == 0
273 assert len(commits) == 0
274
275 def test_returns_list_of_repos_and_groups_filtered(self):
276 self.log_user()
277
278 response = self.app.get(
279 url(controller='home', action='repo_list_data'),
280 headers={'X-REQUESTED-WITH': 'XMLHttpRequest', },
281 params={'query': 'vcs_test_git'}, status=200)
282 result = json.loads(response.body)['results']
283
284 repos, groups, commits = assert_and_get_content(result)
285
286 assert len(repos) == len(Repository.query().filter(
287 Repository.repo_name.ilike('%vcs_test_git%')).all())
288 assert len(groups) == 0
289 assert len(commits) == 0
290
291 def test_returns_list_of_repos_and_groups_filtered_with_type(self):
292 self.log_user()
293
294 response = self.app.get(
295 url(controller='home', action='repo_list_data'),
296 headers={'X-REQUESTED-WITH': 'XMLHttpRequest', },
297 params={'query': 'vcs_test_git', 'repo_type': 'git'}, status=200)
298 result = json.loads(response.body)['results']
299
300 repos, groups, commits = assert_and_get_content(result)
301
302 assert len(repos) == len(Repository.query().filter(
303 Repository.repo_name.ilike('%vcs_test_git%')).all())
304 assert len(groups) == 0
305 assert len(commits) == 0
306
307 def test_returns_list_of_repos_non_ascii_query(self):
308 self.log_user()
309 response = self.app.get(
310 url(controller='home', action='repo_list_data'),
311 headers={'X-REQUESTED-WITH': 'XMLHttpRequest', },
312 params={'query': 'ć_vcs_test_ą', 'repo_type': 'git'}, status=200)
313 result = json.loads(response.body)['results']
314
315 repos, groups, commits = assert_and_get_content(result)
316
317 assert len(repos) == 0
318 assert len(groups) == 0
319 assert len(commits) == 0
General Comments 0
You need to be logged in to leave comments. Login now