##// END OF EJS Templates
core: moved goto_switcher views into pyramid.
marcink -
r1668:6a5ff1d6 default
parent child Browse files
Show More
@@ -0,0 +1,151 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 import pytest
24
25 from . import assert_and_get_content
26 from rhodecode.tests import TestController, TEST_USER_ADMIN_LOGIN
27 from rhodecode.tests.fixture import Fixture
28
29 from rhodecode.lib.utils import map_groups
30 from rhodecode.model.repo import RepoModel
31 from rhodecode.model.repo_group import RepoGroupModel
32 from rhodecode.model.db import Session, Repository, RepoGroup
33
34 fixture = Fixture()
35
36
37 def route_path(name, params=None, **kwargs):
38 import urllib
39
40 base_url = {
41 'goto_switcher_data': '/_goto_data',
42 }[name].format(**kwargs)
43
44 if params:
45 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
46 return base_url
47
48
49 class TestGotoSwitcherData(TestController):
50
51 required_repos_with_groups = [
52 'abc',
53 'abc-fork',
54 'forks/abcd',
55 'abcd',
56 'abcde',
57 'a/abc',
58 'aa/abc',
59 'aaa/abc',
60 'aaaa/abc',
61 'repos_abc/aaa/abc',
62 'abc_repos/abc',
63 'abc_repos/abcd',
64 'xxx/xyz',
65 'forked-abc/a/abc'
66 ]
67
68 @pytest.fixture(autouse=True, scope='class')
69 def prepare(self, request, pylonsapp):
70 for repo_and_group in self.required_repos_with_groups:
71 # create structure of groups and return the last group
72
73 repo_group = map_groups(repo_and_group)
74
75 RepoModel()._create_repo(
76 repo_and_group, 'hg', 'test-ac', TEST_USER_ADMIN_LOGIN,
77 repo_group=getattr(repo_group, 'group_id', None))
78
79 Session().commit()
80
81 request.addfinalizer(self.cleanup)
82
83 def cleanup(self):
84 # first delete all repos
85 for repo_and_groups in self.required_repos_with_groups:
86 repo = Repository.get_by_repo_name(repo_and_groups)
87 if repo:
88 RepoModel().delete(repo)
89 Session().commit()
90
91 # then delete all empty groups
92 for repo_and_groups in self.required_repos_with_groups:
93 if '/' in repo_and_groups:
94 r_group = repo_and_groups.rsplit('/', 1)[0]
95 repo_group = RepoGroup.get_by_group_name(r_group)
96 if not repo_group:
97 continue
98 parents = repo_group.parents
99 RepoGroupModel().delete(repo_group, force_delete=True)
100 Session().commit()
101
102 for el in reversed(parents):
103 RepoGroupModel().delete(el, force_delete=True)
104 Session().commit()
105
106 def test_returns_list_of_repos_and_groups(self, xhr_header):
107 self.log_user()
108
109 response = self.app.get(
110 route_path('goto_switcher_data'),
111 extra_environ=xhr_header, status=200)
112 result = json.loads(response.body)['results']
113
114 repos, groups, commits = assert_and_get_content(result)
115
116 assert len(repos) == len(Repository.get_all())
117 assert len(groups) == len(RepoGroup.get_all())
118 assert len(commits) == 0
119
120 def test_returns_list_of_repos_and_groups_filtered(self, xhr_header):
121 self.log_user()
122
123 response = self.app.get(
124 route_path('goto_switcher_data'),
125 params={'query': 'abc'},
126 extra_environ=xhr_header, status=200)
127 result = json.loads(response.body)['results']
128
129 repos, groups, commits = assert_and_get_content(result)
130
131 assert len(repos) == 13
132 assert len(groups) == 5
133 assert len(commits) == 0
134
135 def test_returns_list_of_properly_sorted_and_filtered(self, xhr_header):
136 self.log_user()
137
138 response = self.app.get(
139 route_path('goto_switcher_data'),
140 params={'query': 'abc'},
141 extra_environ=xhr_header, status=200)
142 result = json.loads(response.body)['results']
143
144 repos, groups, commits = assert_and_get_content(result)
145
146 test_repos = [x['text'] for x in repos[:4]]
147 assert ['abc', 'abcd', 'a/abc', 'abcde'] == test_repos
148
149 test_groups = [x['text'] for x in groups[:4]]
150 assert ['abc_repos', 'repos_abc',
151 'forked-abc', 'forked-abc/a'] == test_groups
@@ -1,37 +1,41 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 32 config.add_route(
33 33 name='repo_list_data',
34 34 pattern='/_repos')
35 35
36 config.add_route(
37 name='goto_switcher_data',
38 pattern='/_goto_data')
39
36 40 # Scan module for configuration decorators.
37 41 config.scan()
@@ -1,138 +1,237 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 import re
21 22 import logging
22 23
23 24 from pyramid.view import view_config
24 25
25 26 from rhodecode.apps._base import BaseAppView
26 27 from rhodecode.lib import helpers as h
27 28 from rhodecode.lib.auth import LoginRequired, NotAnonymous
29 from rhodecode.lib.index import searcher_from_config
28 30 from rhodecode.lib.utils2 import safe_unicode, str2bool
29 from rhodecode.model.db import func, Repository
31 from rhodecode.model.db import func, Repository, RepoGroup
30 32 from rhodecode.model.repo import RepoModel
31 33 from rhodecode.model.scm import ScmModel
32 34
33 35
34 36 log = logging.getLogger(__name__)
35 37
36 38
37 39 class HomeView(BaseAppView):
38 40
39 41 def load_default_context(self):
40 42 c = self._get_local_tmpl_context()
41 43 c.user = c.auth_user.get_instance()
42 44 self._register_global_c(c)
43 45 return c
44 46
45 47 @LoginRequired()
46 48 @view_config(
47 49 route_name='user_autocomplete_data', request_method='GET',
48 50 renderer='json_ext', xhr=True)
49 51 def user_autocomplete_data(self):
50 52 query = self.request.GET.get('query')
51 53 active = str2bool(self.request.GET.get('active') or True)
52 54 include_groups = str2bool(self.request.GET.get('user_groups'))
53 55
54 56 log.debug('generating user list, query:%s, active:%s, with_groups:%s',
55 57 query, active, include_groups)
56 58
57 59 repo_model = RepoModel()
58 60 _users = repo_model.get_users(
59 61 name_contains=query, only_active=active)
60 62
61 63 if include_groups:
62 64 # extend with user groups
63 65 _user_groups = repo_model.get_user_groups(
64 66 name_contains=query, only_active=active)
65 67 _users = _users + _user_groups
66 68
67 69 return {'suggestions': _users}
68 70
69 71 @LoginRequired()
70 72 @NotAnonymous()
71 73 @view_config(
72 74 route_name='user_group_autocomplete_data', request_method='GET',
73 75 renderer='json_ext', xhr=True)
74 76 def user_group_autocomplete_data(self):
75 77 query = self.request.GET.get('query')
76 78 active = str2bool(self.request.GET.get('active') or True)
77 79 log.debug('generating user group list, query:%s, active:%s',
78 80 query, active)
79 81
80 82 repo_model = RepoModel()
81 83 _user_groups = repo_model.get_user_groups(
82 84 name_contains=query, only_active=active)
83 85 _user_groups = _user_groups
84 86
85 87 return {'suggestions': _user_groups}
86 88
87 89 def _get_repo_list(self, name_contains=None, repo_type=None, limit=20):
88 90 query = Repository.query()\
89 91 .order_by(func.length(Repository.repo_name))\
90 92 .order_by(Repository.repo_name)
91 93
92 94 if repo_type:
93 95 query = query.filter(Repository.repo_type == repo_type)
94 96
95 97 if name_contains:
96 98 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
97 99 query = query.filter(
98 100 Repository.repo_name.ilike(ilike_expression))
99 101 query = query.limit(limit)
100 102
101 103 all_repos = query.all()
102 104 # permission checks are inside this function
103 105 repo_iter = ScmModel().get_repos(all_repos)
104 106 return [
105 107 {
106 108 'id': obj['name'],
107 109 'text': obj['name'],
108 110 'type': 'repo',
109 111 'obj': obj['dbrepo'],
110 112 'url': h.url('summary_home', repo_name=obj['name'])
111 113 }
112 114 for obj in repo_iter]
113 115
116 def _get_repo_group_list(self, name_contains=None, limit=20):
117 query = RepoGroup.query()\
118 .order_by(func.length(RepoGroup.group_name))\
119 .order_by(RepoGroup.group_name)
120
121 if name_contains:
122 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
123 query = query.filter(
124 RepoGroup.group_name.ilike(ilike_expression))
125 query = query.limit(limit)
126
127 all_groups = query.all()
128 repo_groups_iter = ScmModel().get_repo_groups(all_groups)
129 return [
130 {
131 'id': obj.group_name,
132 'text': obj.group_name,
133 'type': 'group',
134 'obj': {},
135 'url': h.url('repo_group_home', group_name=obj.group_name)
136 }
137 for obj in repo_groups_iter]
138
139 def _get_hash_commit_list(self, auth_user, hash_starts_with=None):
140 if not hash_starts_with or len(hash_starts_with) < 3:
141 return []
142
143 commit_hashes = re.compile('([0-9a-f]{2,40})').findall(hash_starts_with)
144
145 if len(commit_hashes) != 1:
146 return []
147
148 commit_hash_prefix = commit_hashes[0]
149
150 searcher = searcher_from_config(self.request.registry.settings)
151 result = searcher.search(
152 'commit_id:%s*' % commit_hash_prefix, 'commit', auth_user,
153 raise_on_exc=False)
154
155 return [
156 {
157 'id': entry['commit_id'],
158 'text': entry['commit_id'],
159 'type': 'commit',
160 'obj': {'repo': entry['repository']},
161 'url': h.url('changeset_home',
162 repo_name=entry['repository'],
163 revision=entry['commit_id'])
164 }
165 for entry in result['results']]
166
114 167 @LoginRequired()
115 168 @view_config(
116 169 route_name='repo_list_data', request_method='GET',
117 170 renderer='json_ext', xhr=True)
118 171 def repo_list_data(self):
119 172 _ = self.request.translate
120 173
121 174 query = self.request.GET.get('query')
122 175 repo_type = self.request.GET.get('repo_type')
123 176 log.debug('generating repo list, query:%s, repo_type:%s',
124 177 query, repo_type)
125 178
126 179 res = []
127 180 repos = self._get_repo_list(query, repo_type=repo_type)
128 181 if repos:
129 182 res.append({
130 183 'text': _('Repositories'),
131 184 'children': repos
132 185 })
133 186
134 187 data = {
135 188 'more': False,
136 189 'results': res
137 190 }
138 191 return data
192
193 @LoginRequired()
194 @view_config(
195 route_name='goto_switcher_data', request_method='GET',
196 renderer='json_ext', xhr=True)
197 def goto_switcher_data(self):
198 c = self.load_default_context()
199
200 _ = self.request.translate
201
202 query = self.request.GET.get('query')
203 log.debug('generating goto switcher list, query %s', query)
204
205 res = []
206 repo_groups = self._get_repo_group_list(query)
207 if repo_groups:
208 res.append({
209 'text': _('Groups'),
210 'children': repo_groups
211 })
212
213 repos = self._get_repo_list(query)
214 if repos:
215 res.append({
216 'text': _('Repositories'),
217 'children': repos
218 })
219
220 commits = self._get_hash_commit_list(c.auth_user, query)
221 if commits:
222 unique_repos = {}
223 for commit in commits:
224 unique_repos.setdefault(commit['obj']['repo'], []
225 ).append(commit)
226
227 for repo in unique_repos:
228 res.append({
229 'text': _('Commits in %(repo)s') % {'repo': repo},
230 'children': unique_repos[repo]
231 })
232
233 data = {
234 'more': False,
235 'results': res
236 }
237 return data
@@ -1,1146 +1,1144 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 rmap.connect('goto_switcher_data', '/_goto_data', controller='home',
192 action='goto_switcher_data')
193 191
194 192 # TODO: johbo: Static links, to be replaced by our redirection mechanism
195 193 rmap.connect('rst_help',
196 194 'http://docutils.sourceforge.net/docs/user/rst/quickref.html',
197 195 _static=True)
198 196 rmap.connect('markdown_help',
199 197 'http://daringfireball.net/projects/markdown/syntax',
200 198 _static=True)
201 199 rmap.connect('rhodecode_official', 'https://rhodecode.com', _static=True)
202 200 rmap.connect('rhodecode_support', 'https://rhodecode.com/help/', _static=True)
203 201 rmap.connect('rhodecode_translations', 'https://rhodecode.com/translate/enterprise', _static=True)
204 202 # TODO: anderson - making this a static link since redirect won't play
205 203 # nice with POST requests
206 204 rmap.connect('enterprise_license_convert_from_old',
207 205 'https://rhodecode.com/u/license-upgrade',
208 206 _static=True)
209 207
210 208 routing_links.connect_redirection_links(rmap)
211 209
212 210 rmap.connect('ping', '%s/ping' % (ADMIN_PREFIX,), controller='home', action='ping')
213 211 rmap.connect('error_test', '%s/error_test' % (ADMIN_PREFIX,), controller='home', action='error_test')
214 212
215 213 # ADMIN REPOSITORY ROUTES
216 214 with rmap.submapper(path_prefix=ADMIN_PREFIX,
217 215 controller='admin/repos') as m:
218 216 m.connect('repos', '/repos',
219 217 action='create', conditions={'method': ['POST']})
220 218 m.connect('repos', '/repos',
221 219 action='index', conditions={'method': ['GET']})
222 220 m.connect('new_repo', '/create_repository', jsroute=True,
223 221 action='create_repository', conditions={'method': ['GET']})
224 222 m.connect('/repos/{repo_name}',
225 223 action='update', conditions={'method': ['PUT'],
226 224 'function': check_repo},
227 225 requirements=URL_NAME_REQUIREMENTS)
228 226 m.connect('delete_repo', '/repos/{repo_name}',
229 227 action='delete', conditions={'method': ['DELETE']},
230 228 requirements=URL_NAME_REQUIREMENTS)
231 229 m.connect('repo', '/repos/{repo_name}',
232 230 action='show', conditions={'method': ['GET'],
233 231 'function': check_repo},
234 232 requirements=URL_NAME_REQUIREMENTS)
235 233
236 234 # ADMIN REPOSITORY GROUPS ROUTES
237 235 with rmap.submapper(path_prefix=ADMIN_PREFIX,
238 236 controller='admin/repo_groups') as m:
239 237 m.connect('repo_groups', '/repo_groups',
240 238 action='create', conditions={'method': ['POST']})
241 239 m.connect('repo_groups', '/repo_groups',
242 240 action='index', conditions={'method': ['GET']})
243 241 m.connect('new_repo_group', '/repo_groups/new',
244 242 action='new', conditions={'method': ['GET']})
245 243 m.connect('update_repo_group', '/repo_groups/{group_name}',
246 244 action='update', conditions={'method': ['PUT'],
247 245 'function': check_group},
248 246 requirements=URL_NAME_REQUIREMENTS)
249 247
250 248 # EXTRAS REPO GROUP ROUTES
251 249 m.connect('edit_repo_group', '/repo_groups/{group_name}/edit',
252 250 action='edit',
253 251 conditions={'method': ['GET'], 'function': check_group},
254 252 requirements=URL_NAME_REQUIREMENTS)
255 253 m.connect('edit_repo_group', '/repo_groups/{group_name}/edit',
256 254 action='edit',
257 255 conditions={'method': ['PUT'], 'function': check_group},
258 256 requirements=URL_NAME_REQUIREMENTS)
259 257
260 258 m.connect('edit_repo_group_advanced', '/repo_groups/{group_name}/edit/advanced',
261 259 action='edit_repo_group_advanced',
262 260 conditions={'method': ['GET'], 'function': check_group},
263 261 requirements=URL_NAME_REQUIREMENTS)
264 262 m.connect('edit_repo_group_advanced', '/repo_groups/{group_name}/edit/advanced',
265 263 action='edit_repo_group_advanced',
266 264 conditions={'method': ['PUT'], 'function': check_group},
267 265 requirements=URL_NAME_REQUIREMENTS)
268 266
269 267 m.connect('edit_repo_group_perms', '/repo_groups/{group_name}/edit/permissions',
270 268 action='edit_repo_group_perms',
271 269 conditions={'method': ['GET'], 'function': check_group},
272 270 requirements=URL_NAME_REQUIREMENTS)
273 271 m.connect('edit_repo_group_perms', '/repo_groups/{group_name}/edit/permissions',
274 272 action='update_perms',
275 273 conditions={'method': ['PUT'], 'function': check_group},
276 274 requirements=URL_NAME_REQUIREMENTS)
277 275
278 276 m.connect('delete_repo_group', '/repo_groups/{group_name}',
279 277 action='delete', conditions={'method': ['DELETE'],
280 278 'function': check_group},
281 279 requirements=URL_NAME_REQUIREMENTS)
282 280
283 281 # ADMIN USER ROUTES
284 282 with rmap.submapper(path_prefix=ADMIN_PREFIX,
285 283 controller='admin/users') as m:
286 284 m.connect('users', '/users',
287 285 action='create', conditions={'method': ['POST']})
288 286 m.connect('new_user', '/users/new',
289 287 action='new', conditions={'method': ['GET']})
290 288 m.connect('update_user', '/users/{user_id}',
291 289 action='update', conditions={'method': ['PUT']})
292 290 m.connect('delete_user', '/users/{user_id}',
293 291 action='delete', conditions={'method': ['DELETE']})
294 292 m.connect('edit_user', '/users/{user_id}/edit',
295 293 action='edit', conditions={'method': ['GET']}, jsroute=True)
296 294 m.connect('user', '/users/{user_id}',
297 295 action='show', conditions={'method': ['GET']})
298 296 m.connect('force_password_reset_user', '/users/{user_id}/password_reset',
299 297 action='reset_password', conditions={'method': ['POST']})
300 298 m.connect('create_personal_repo_group', '/users/{user_id}/create_repo_group',
301 299 action='create_personal_repo_group', conditions={'method': ['POST']})
302 300
303 301 # EXTRAS USER ROUTES
304 302 m.connect('edit_user_advanced', '/users/{user_id}/edit/advanced',
305 303 action='edit_advanced', conditions={'method': ['GET']})
306 304 m.connect('edit_user_advanced', '/users/{user_id}/edit/advanced',
307 305 action='update_advanced', conditions={'method': ['PUT']})
308 306
309 307 m.connect('edit_user_global_perms', '/users/{user_id}/edit/global_permissions',
310 308 action='edit_global_perms', conditions={'method': ['GET']})
311 309 m.connect('edit_user_global_perms', '/users/{user_id}/edit/global_permissions',
312 310 action='update_global_perms', conditions={'method': ['PUT']})
313 311
314 312 m.connect('edit_user_perms_summary', '/users/{user_id}/edit/permissions_summary',
315 313 action='edit_perms_summary', conditions={'method': ['GET']})
316 314
317 315 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
318 316 action='edit_emails', conditions={'method': ['GET']})
319 317 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
320 318 action='add_email', conditions={'method': ['PUT']})
321 319 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
322 320 action='delete_email', conditions={'method': ['DELETE']})
323 321
324 322 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
325 323 action='edit_ips', conditions={'method': ['GET']})
326 324 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
327 325 action='add_ip', conditions={'method': ['PUT']})
328 326 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
329 327 action='delete_ip', conditions={'method': ['DELETE']})
330 328
331 329 # ADMIN USER GROUPS REST ROUTES
332 330 with rmap.submapper(path_prefix=ADMIN_PREFIX,
333 331 controller='admin/user_groups') as m:
334 332 m.connect('users_groups', '/user_groups',
335 333 action='create', conditions={'method': ['POST']})
336 334 m.connect('users_groups', '/user_groups',
337 335 action='index', conditions={'method': ['GET']})
338 336 m.connect('new_users_group', '/user_groups/new',
339 337 action='new', conditions={'method': ['GET']})
340 338 m.connect('update_users_group', '/user_groups/{user_group_id}',
341 339 action='update', conditions={'method': ['PUT']})
342 340 m.connect('delete_users_group', '/user_groups/{user_group_id}',
343 341 action='delete', conditions={'method': ['DELETE']})
344 342 m.connect('edit_users_group', '/user_groups/{user_group_id}/edit',
345 343 action='edit', conditions={'method': ['GET']},
346 344 function=check_user_group)
347 345
348 346 # EXTRAS USER GROUP ROUTES
349 347 m.connect('edit_user_group_global_perms',
350 348 '/user_groups/{user_group_id}/edit/global_permissions',
351 349 action='edit_global_perms', conditions={'method': ['GET']})
352 350 m.connect('edit_user_group_global_perms',
353 351 '/user_groups/{user_group_id}/edit/global_permissions',
354 352 action='update_global_perms', conditions={'method': ['PUT']})
355 353 m.connect('edit_user_group_perms_summary',
356 354 '/user_groups/{user_group_id}/edit/permissions_summary',
357 355 action='edit_perms_summary', conditions={'method': ['GET']})
358 356
359 357 m.connect('edit_user_group_perms',
360 358 '/user_groups/{user_group_id}/edit/permissions',
361 359 action='edit_perms', conditions={'method': ['GET']})
362 360 m.connect('edit_user_group_perms',
363 361 '/user_groups/{user_group_id}/edit/permissions',
364 362 action='update_perms', conditions={'method': ['PUT']})
365 363
366 364 m.connect('edit_user_group_advanced',
367 365 '/user_groups/{user_group_id}/edit/advanced',
368 366 action='edit_advanced', conditions={'method': ['GET']})
369 367
370 368 m.connect('edit_user_group_advanced_sync',
371 369 '/user_groups/{user_group_id}/edit/advanced/sync',
372 370 action='edit_advanced_set_synchronization', conditions={'method': ['POST']})
373 371
374 372 m.connect('edit_user_group_members',
375 373 '/user_groups/{user_group_id}/edit/members', jsroute=True,
376 374 action='user_group_members', conditions={'method': ['GET']})
377 375
378 376 # ADMIN PERMISSIONS ROUTES
379 377 with rmap.submapper(path_prefix=ADMIN_PREFIX,
380 378 controller='admin/permissions') as m:
381 379 m.connect('admin_permissions_application', '/permissions/application',
382 380 action='permission_application_update', conditions={'method': ['POST']})
383 381 m.connect('admin_permissions_application', '/permissions/application',
384 382 action='permission_application', conditions={'method': ['GET']})
385 383
386 384 m.connect('admin_permissions_global', '/permissions/global',
387 385 action='permission_global_update', conditions={'method': ['POST']})
388 386 m.connect('admin_permissions_global', '/permissions/global',
389 387 action='permission_global', conditions={'method': ['GET']})
390 388
391 389 m.connect('admin_permissions_object', '/permissions/object',
392 390 action='permission_objects_update', conditions={'method': ['POST']})
393 391 m.connect('admin_permissions_object', '/permissions/object',
394 392 action='permission_objects', conditions={'method': ['GET']})
395 393
396 394 m.connect('admin_permissions_ips', '/permissions/ips',
397 395 action='permission_ips', conditions={'method': ['POST']})
398 396 m.connect('admin_permissions_ips', '/permissions/ips',
399 397 action='permission_ips', conditions={'method': ['GET']})
400 398
401 399 m.connect('admin_permissions_overview', '/permissions/overview',
402 400 action='permission_perms', conditions={'method': ['GET']})
403 401
404 402 # ADMIN DEFAULTS REST ROUTES
405 403 with rmap.submapper(path_prefix=ADMIN_PREFIX,
406 404 controller='admin/defaults') as m:
407 405 m.connect('admin_defaults_repositories', '/defaults/repositories',
408 406 action='update_repository_defaults', conditions={'method': ['POST']})
409 407 m.connect('admin_defaults_repositories', '/defaults/repositories',
410 408 action='index', conditions={'method': ['GET']})
411 409
412 410 # ADMIN DEBUG STYLE ROUTES
413 411 if str2bool(config.get('debug_style')):
414 412 with rmap.submapper(path_prefix=ADMIN_PREFIX + '/debug_style',
415 413 controller='debug_style') as m:
416 414 m.connect('debug_style_home', '',
417 415 action='index', conditions={'method': ['GET']})
418 416 m.connect('debug_style_template', '/t/{t_path}',
419 417 action='template', conditions={'method': ['GET']})
420 418
421 419 # ADMIN SETTINGS ROUTES
422 420 with rmap.submapper(path_prefix=ADMIN_PREFIX,
423 421 controller='admin/settings') as m:
424 422
425 423 # default
426 424 m.connect('admin_settings', '/settings',
427 425 action='settings_global_update',
428 426 conditions={'method': ['POST']})
429 427 m.connect('admin_settings', '/settings',
430 428 action='settings_global', conditions={'method': ['GET']})
431 429
432 430 m.connect('admin_settings_vcs', '/settings/vcs',
433 431 action='settings_vcs_update',
434 432 conditions={'method': ['POST']})
435 433 m.connect('admin_settings_vcs', '/settings/vcs',
436 434 action='settings_vcs',
437 435 conditions={'method': ['GET']})
438 436 m.connect('admin_settings_vcs', '/settings/vcs',
439 437 action='delete_svn_pattern',
440 438 conditions={'method': ['DELETE']})
441 439
442 440 m.connect('admin_settings_mapping', '/settings/mapping',
443 441 action='settings_mapping_update',
444 442 conditions={'method': ['POST']})
445 443 m.connect('admin_settings_mapping', '/settings/mapping',
446 444 action='settings_mapping', conditions={'method': ['GET']})
447 445
448 446 m.connect('admin_settings_global', '/settings/global',
449 447 action='settings_global_update',
450 448 conditions={'method': ['POST']})
451 449 m.connect('admin_settings_global', '/settings/global',
452 450 action='settings_global', conditions={'method': ['GET']})
453 451
454 452 m.connect('admin_settings_visual', '/settings/visual',
455 453 action='settings_visual_update',
456 454 conditions={'method': ['POST']})
457 455 m.connect('admin_settings_visual', '/settings/visual',
458 456 action='settings_visual', conditions={'method': ['GET']})
459 457
460 458 m.connect('admin_settings_issuetracker',
461 459 '/settings/issue-tracker', action='settings_issuetracker',
462 460 conditions={'method': ['GET']})
463 461 m.connect('admin_settings_issuetracker_save',
464 462 '/settings/issue-tracker/save',
465 463 action='settings_issuetracker_save',
466 464 conditions={'method': ['POST']})
467 465 m.connect('admin_issuetracker_test', '/settings/issue-tracker/test',
468 466 action='settings_issuetracker_test',
469 467 conditions={'method': ['POST']})
470 468 m.connect('admin_issuetracker_delete',
471 469 '/settings/issue-tracker/delete',
472 470 action='settings_issuetracker_delete',
473 471 conditions={'method': ['DELETE']})
474 472
475 473 m.connect('admin_settings_email', '/settings/email',
476 474 action='settings_email_update',
477 475 conditions={'method': ['POST']})
478 476 m.connect('admin_settings_email', '/settings/email',
479 477 action='settings_email', conditions={'method': ['GET']})
480 478
481 479 m.connect('admin_settings_hooks', '/settings/hooks',
482 480 action='settings_hooks_update',
483 481 conditions={'method': ['POST', 'DELETE']})
484 482 m.connect('admin_settings_hooks', '/settings/hooks',
485 483 action='settings_hooks', conditions={'method': ['GET']})
486 484
487 485 m.connect('admin_settings_search', '/settings/search',
488 486 action='settings_search', conditions={'method': ['GET']})
489 487
490 488 m.connect('admin_settings_supervisor', '/settings/supervisor',
491 489 action='settings_supervisor', conditions={'method': ['GET']})
492 490 m.connect('admin_settings_supervisor_log', '/settings/supervisor/{procid}/log',
493 491 action='settings_supervisor_log', conditions={'method': ['GET']})
494 492
495 493 m.connect('admin_settings_labs', '/settings/labs',
496 494 action='settings_labs_update',
497 495 conditions={'method': ['POST']})
498 496 m.connect('admin_settings_labs', '/settings/labs',
499 497 action='settings_labs', conditions={'method': ['GET']})
500 498
501 499 # ADMIN MY ACCOUNT
502 500 with rmap.submapper(path_prefix=ADMIN_PREFIX,
503 501 controller='admin/my_account') as m:
504 502
505 503 m.connect('my_account_edit', '/my_account/edit',
506 504 action='my_account_edit', conditions={'method': ['GET']})
507 505 m.connect('my_account', '/my_account/update',
508 506 action='my_account_update', conditions={'method': ['POST']})
509 507
510 508 # NOTE(marcink): this needs to be kept for password force flag to be
511 509 # handler, remove after migration to pyramid
512 510 m.connect('my_account_password', '/my_account/password',
513 511 action='my_account_password', conditions={'method': ['GET']})
514 512
515 513 m.connect('my_account_repos', '/my_account/repos',
516 514 action='my_account_repos', conditions={'method': ['GET']})
517 515
518 516 m.connect('my_account_watched', '/my_account/watched',
519 517 action='my_account_watched', conditions={'method': ['GET']})
520 518
521 519 m.connect('my_account_pullrequests', '/my_account/pull_requests',
522 520 action='my_account_pullrequests', conditions={'method': ['GET']})
523 521
524 522 m.connect('my_account_perms', '/my_account/perms',
525 523 action='my_account_perms', conditions={'method': ['GET']})
526 524
527 525 m.connect('my_account_emails', '/my_account/emails',
528 526 action='my_account_emails', conditions={'method': ['GET']})
529 527 m.connect('my_account_emails', '/my_account/emails',
530 528 action='my_account_emails_add', conditions={'method': ['POST']})
531 529 m.connect('my_account_emails', '/my_account/emails',
532 530 action='my_account_emails_delete', conditions={'method': ['DELETE']})
533 531
534 532 m.connect('my_account_notifications', '/my_account/notifications',
535 533 action='my_notifications',
536 534 conditions={'method': ['GET']})
537 535 m.connect('my_account_notifications_toggle_visibility',
538 536 '/my_account/toggle_visibility',
539 537 action='my_notifications_toggle_visibility',
540 538 conditions={'method': ['POST']})
541 539 m.connect('my_account_notifications_test_channelstream',
542 540 '/my_account/test_channelstream',
543 541 action='my_account_notifications_test_channelstream',
544 542 conditions={'method': ['POST']})
545 543
546 544 # NOTIFICATION REST ROUTES
547 545 with rmap.submapper(path_prefix=ADMIN_PREFIX,
548 546 controller='admin/notifications') as m:
549 547 m.connect('notifications', '/notifications',
550 548 action='index', conditions={'method': ['GET']})
551 549 m.connect('notifications_mark_all_read', '/notifications/mark_all_read',
552 550 action='mark_all_read', conditions={'method': ['POST']})
553 551 m.connect('/notifications/{notification_id}',
554 552 action='update', conditions={'method': ['PUT']})
555 553 m.connect('/notifications/{notification_id}',
556 554 action='delete', conditions={'method': ['DELETE']})
557 555 m.connect('notification', '/notifications/{notification_id}',
558 556 action='show', conditions={'method': ['GET']})
559 557
560 558 # ADMIN GIST
561 559 with rmap.submapper(path_prefix=ADMIN_PREFIX,
562 560 controller='admin/gists') as m:
563 561 m.connect('gists', '/gists',
564 562 action='create', conditions={'method': ['POST']})
565 563 m.connect('gists', '/gists', jsroute=True,
566 564 action='index', conditions={'method': ['GET']})
567 565 m.connect('new_gist', '/gists/new', jsroute=True,
568 566 action='new', conditions={'method': ['GET']})
569 567
570 568 m.connect('/gists/{gist_id}',
571 569 action='delete', conditions={'method': ['DELETE']})
572 570 m.connect('edit_gist', '/gists/{gist_id}/edit',
573 571 action='edit_form', conditions={'method': ['GET']})
574 572 m.connect('edit_gist', '/gists/{gist_id}/edit',
575 573 action='edit', conditions={'method': ['POST']})
576 574 m.connect(
577 575 'edit_gist_check_revision', '/gists/{gist_id}/edit/check_revision',
578 576 action='check_revision', conditions={'method': ['GET']})
579 577
580 578 m.connect('gist', '/gists/{gist_id}',
581 579 action='show', conditions={'method': ['GET']})
582 580 m.connect('gist_rev', '/gists/{gist_id}/{revision}',
583 581 revision='tip',
584 582 action='show', conditions={'method': ['GET']})
585 583 m.connect('formatted_gist', '/gists/{gist_id}/{revision}/{format}',
586 584 revision='tip',
587 585 action='show', conditions={'method': ['GET']})
588 586 m.connect('formatted_gist_file', '/gists/{gist_id}/{revision}/{format}/{f_path}',
589 587 revision='tip',
590 588 action='show', conditions={'method': ['GET']},
591 589 requirements=URL_NAME_REQUIREMENTS)
592 590
593 591 # ADMIN MAIN PAGES
594 592 with rmap.submapper(path_prefix=ADMIN_PREFIX,
595 593 controller='admin/admin') as m:
596 594 m.connect('admin_home', '', action='index')
597 595 m.connect('admin_add_repo', '/add_repo/{new_repo:[a-z0-9\. _-]*}',
598 596 action='add_repo')
599 597 m.connect(
600 598 'pull_requests_global_0', '/pull_requests/{pull_request_id:[0-9]+}',
601 599 action='pull_requests')
602 600 m.connect(
603 601 'pull_requests_global_1', '/pull-requests/{pull_request_id:[0-9]+}',
604 602 action='pull_requests')
605 603 m.connect(
606 604 'pull_requests_global', '/pull-request/{pull_request_id:[0-9]+}',
607 605 action='pull_requests')
608 606
609 607 # USER JOURNAL
610 608 rmap.connect('journal', '%s/journal' % (ADMIN_PREFIX,),
611 609 controller='journal', action='index')
612 610 rmap.connect('journal_rss', '%s/journal/rss' % (ADMIN_PREFIX,),
613 611 controller='journal', action='journal_rss')
614 612 rmap.connect('journal_atom', '%s/journal/atom' % (ADMIN_PREFIX,),
615 613 controller='journal', action='journal_atom')
616 614
617 615 rmap.connect('public_journal', '%s/public_journal' % (ADMIN_PREFIX,),
618 616 controller='journal', action='public_journal')
619 617
620 618 rmap.connect('public_journal_rss', '%s/public_journal/rss' % (ADMIN_PREFIX,),
621 619 controller='journal', action='public_journal_rss')
622 620
623 621 rmap.connect('public_journal_rss_old', '%s/public_journal_rss' % (ADMIN_PREFIX,),
624 622 controller='journal', action='public_journal_rss')
625 623
626 624 rmap.connect('public_journal_atom',
627 625 '%s/public_journal/atom' % (ADMIN_PREFIX,), controller='journal',
628 626 action='public_journal_atom')
629 627
630 628 rmap.connect('public_journal_atom_old',
631 629 '%s/public_journal_atom' % (ADMIN_PREFIX,), controller='journal',
632 630 action='public_journal_atom')
633 631
634 632 rmap.connect('toggle_following', '%s/toggle_following' % (ADMIN_PREFIX,),
635 633 controller='journal', action='toggle_following', jsroute=True,
636 634 conditions={'method': ['POST']})
637 635
638 636 # FULL TEXT SEARCH
639 637 rmap.connect('search', '%s/search' % (ADMIN_PREFIX,),
640 638 controller='search')
641 639 rmap.connect('search_repo_home', '/{repo_name}/search',
642 640 controller='search',
643 641 action='index',
644 642 conditions={'function': check_repo},
645 643 requirements=URL_NAME_REQUIREMENTS)
646 644
647 645 # FEEDS
648 646 rmap.connect('rss_feed_home', '/{repo_name}/feed/rss',
649 647 controller='feed', action='rss',
650 648 conditions={'function': check_repo},
651 649 requirements=URL_NAME_REQUIREMENTS)
652 650
653 651 rmap.connect('atom_feed_home', '/{repo_name}/feed/atom',
654 652 controller='feed', action='atom',
655 653 conditions={'function': check_repo},
656 654 requirements=URL_NAME_REQUIREMENTS)
657 655
658 656 #==========================================================================
659 657 # REPOSITORY ROUTES
660 658 #==========================================================================
661 659
662 660 rmap.connect('repo_creating_home', '/{repo_name}/repo_creating',
663 661 controller='admin/repos', action='repo_creating',
664 662 requirements=URL_NAME_REQUIREMENTS)
665 663 rmap.connect('repo_check_home', '/{repo_name}/crepo_check',
666 664 controller='admin/repos', action='repo_check',
667 665 requirements=URL_NAME_REQUIREMENTS)
668 666
669 667 rmap.connect('repo_stats', '/{repo_name}/repo_stats/{commit_id}',
670 668 controller='summary', action='repo_stats',
671 669 conditions={'function': check_repo},
672 670 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
673 671
674 672 rmap.connect('repo_refs_data', '/{repo_name}/refs-data',
675 673 controller='summary', action='repo_refs_data',
676 674 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
677 675 rmap.connect('repo_refs_changelog_data', '/{repo_name}/refs-data-changelog',
678 676 controller='summary', action='repo_refs_changelog_data',
679 677 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
680 678 rmap.connect('repo_default_reviewers_data', '/{repo_name}/default-reviewers',
681 679 controller='summary', action='repo_default_reviewers_data',
682 680 jsroute=True, requirements=URL_NAME_REQUIREMENTS)
683 681
684 682 rmap.connect('changeset_home', '/{repo_name}/changeset/{revision}',
685 683 controller='changeset', revision='tip',
686 684 conditions={'function': check_repo},
687 685 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
688 686 rmap.connect('changeset_children', '/{repo_name}/changeset_children/{revision}',
689 687 controller='changeset', revision='tip', action='changeset_children',
690 688 conditions={'function': check_repo},
691 689 requirements=URL_NAME_REQUIREMENTS)
692 690 rmap.connect('changeset_parents', '/{repo_name}/changeset_parents/{revision}',
693 691 controller='changeset', revision='tip', action='changeset_parents',
694 692 conditions={'function': check_repo},
695 693 requirements=URL_NAME_REQUIREMENTS)
696 694
697 695 # repo edit options
698 696 rmap.connect('edit_repo', '/{repo_name}/settings', jsroute=True,
699 697 controller='admin/repos', action='edit',
700 698 conditions={'method': ['GET'], 'function': check_repo},
701 699 requirements=URL_NAME_REQUIREMENTS)
702 700
703 701 rmap.connect('edit_repo_perms', '/{repo_name}/settings/permissions',
704 702 jsroute=True,
705 703 controller='admin/repos', action='edit_permissions',
706 704 conditions={'method': ['GET'], 'function': check_repo},
707 705 requirements=URL_NAME_REQUIREMENTS)
708 706 rmap.connect('edit_repo_perms_update', '/{repo_name}/settings/permissions',
709 707 controller='admin/repos', action='edit_permissions_update',
710 708 conditions={'method': ['PUT'], 'function': check_repo},
711 709 requirements=URL_NAME_REQUIREMENTS)
712 710
713 711 rmap.connect('edit_repo_fields', '/{repo_name}/settings/fields',
714 712 controller='admin/repos', action='edit_fields',
715 713 conditions={'method': ['GET'], 'function': check_repo},
716 714 requirements=URL_NAME_REQUIREMENTS)
717 715 rmap.connect('create_repo_fields', '/{repo_name}/settings/fields/new',
718 716 controller='admin/repos', action='create_repo_field',
719 717 conditions={'method': ['PUT'], 'function': check_repo},
720 718 requirements=URL_NAME_REQUIREMENTS)
721 719 rmap.connect('delete_repo_fields', '/{repo_name}/settings/fields/{field_id}',
722 720 controller='admin/repos', action='delete_repo_field',
723 721 conditions={'method': ['DELETE'], 'function': check_repo},
724 722 requirements=URL_NAME_REQUIREMENTS)
725 723
726 724 rmap.connect('edit_repo_advanced', '/{repo_name}/settings/advanced',
727 725 controller='admin/repos', action='edit_advanced',
728 726 conditions={'method': ['GET'], 'function': check_repo},
729 727 requirements=URL_NAME_REQUIREMENTS)
730 728
731 729 rmap.connect('edit_repo_advanced_locking', '/{repo_name}/settings/advanced/locking',
732 730 controller='admin/repos', action='edit_advanced_locking',
733 731 conditions={'method': ['PUT'], 'function': check_repo},
734 732 requirements=URL_NAME_REQUIREMENTS)
735 733 rmap.connect('toggle_locking', '/{repo_name}/settings/advanced/locking_toggle',
736 734 controller='admin/repos', action='toggle_locking',
737 735 conditions={'method': ['GET'], 'function': check_repo},
738 736 requirements=URL_NAME_REQUIREMENTS)
739 737
740 738 rmap.connect('edit_repo_advanced_journal', '/{repo_name}/settings/advanced/journal',
741 739 controller='admin/repos', action='edit_advanced_journal',
742 740 conditions={'method': ['PUT'], 'function': check_repo},
743 741 requirements=URL_NAME_REQUIREMENTS)
744 742
745 743 rmap.connect('edit_repo_advanced_fork', '/{repo_name}/settings/advanced/fork',
746 744 controller='admin/repos', action='edit_advanced_fork',
747 745 conditions={'method': ['PUT'], 'function': check_repo},
748 746 requirements=URL_NAME_REQUIREMENTS)
749 747
750 748 rmap.connect('edit_repo_caches', '/{repo_name}/settings/caches',
751 749 controller='admin/repos', action='edit_caches_form',
752 750 conditions={'method': ['GET'], 'function': check_repo},
753 751 requirements=URL_NAME_REQUIREMENTS)
754 752 rmap.connect('edit_repo_caches', '/{repo_name}/settings/caches',
755 753 controller='admin/repos', action='edit_caches',
756 754 conditions={'method': ['PUT'], 'function': check_repo},
757 755 requirements=URL_NAME_REQUIREMENTS)
758 756
759 757 rmap.connect('edit_repo_remote', '/{repo_name}/settings/remote',
760 758 controller='admin/repos', action='edit_remote_form',
761 759 conditions={'method': ['GET'], 'function': check_repo},
762 760 requirements=URL_NAME_REQUIREMENTS)
763 761 rmap.connect('edit_repo_remote', '/{repo_name}/settings/remote',
764 762 controller='admin/repos', action='edit_remote',
765 763 conditions={'method': ['PUT'], 'function': check_repo},
766 764 requirements=URL_NAME_REQUIREMENTS)
767 765
768 766 rmap.connect('edit_repo_statistics', '/{repo_name}/settings/statistics',
769 767 controller='admin/repos', action='edit_statistics_form',
770 768 conditions={'method': ['GET'], 'function': check_repo},
771 769 requirements=URL_NAME_REQUIREMENTS)
772 770 rmap.connect('edit_repo_statistics', '/{repo_name}/settings/statistics',
773 771 controller='admin/repos', action='edit_statistics',
774 772 conditions={'method': ['PUT'], 'function': check_repo},
775 773 requirements=URL_NAME_REQUIREMENTS)
776 774 rmap.connect('repo_settings_issuetracker',
777 775 '/{repo_name}/settings/issue-tracker',
778 776 controller='admin/repos', action='repo_issuetracker',
779 777 conditions={'method': ['GET'], 'function': check_repo},
780 778 requirements=URL_NAME_REQUIREMENTS)
781 779 rmap.connect('repo_issuetracker_test',
782 780 '/{repo_name}/settings/issue-tracker/test',
783 781 controller='admin/repos', action='repo_issuetracker_test',
784 782 conditions={'method': ['POST'], 'function': check_repo},
785 783 requirements=URL_NAME_REQUIREMENTS)
786 784 rmap.connect('repo_issuetracker_delete',
787 785 '/{repo_name}/settings/issue-tracker/delete',
788 786 controller='admin/repos', action='repo_issuetracker_delete',
789 787 conditions={'method': ['DELETE'], 'function': check_repo},
790 788 requirements=URL_NAME_REQUIREMENTS)
791 789 rmap.connect('repo_issuetracker_save',
792 790 '/{repo_name}/settings/issue-tracker/save',
793 791 controller='admin/repos', action='repo_issuetracker_save',
794 792 conditions={'method': ['POST'], 'function': check_repo},
795 793 requirements=URL_NAME_REQUIREMENTS)
796 794 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
797 795 controller='admin/repos', action='repo_settings_vcs_update',
798 796 conditions={'method': ['POST'], 'function': check_repo},
799 797 requirements=URL_NAME_REQUIREMENTS)
800 798 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
801 799 controller='admin/repos', action='repo_settings_vcs',
802 800 conditions={'method': ['GET'], 'function': check_repo},
803 801 requirements=URL_NAME_REQUIREMENTS)
804 802 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
805 803 controller='admin/repos', action='repo_delete_svn_pattern',
806 804 conditions={'method': ['DELETE'], 'function': check_repo},
807 805 requirements=URL_NAME_REQUIREMENTS)
808 806 rmap.connect('repo_pullrequest_settings', '/{repo_name}/settings/pullrequest',
809 807 controller='admin/repos', action='repo_settings_pullrequest',
810 808 conditions={'method': ['GET', 'POST'], 'function': check_repo},
811 809 requirements=URL_NAME_REQUIREMENTS)
812 810
813 811 # still working url for backward compat.
814 812 rmap.connect('raw_changeset_home_depraced',
815 813 '/{repo_name}/raw-changeset/{revision}',
816 814 controller='changeset', action='changeset_raw',
817 815 revision='tip', conditions={'function': check_repo},
818 816 requirements=URL_NAME_REQUIREMENTS)
819 817
820 818 # new URLs
821 819 rmap.connect('changeset_raw_home',
822 820 '/{repo_name}/changeset-diff/{revision}',
823 821 controller='changeset', action='changeset_raw',
824 822 revision='tip', conditions={'function': check_repo},
825 823 requirements=URL_NAME_REQUIREMENTS)
826 824
827 825 rmap.connect('changeset_patch_home',
828 826 '/{repo_name}/changeset-patch/{revision}',
829 827 controller='changeset', action='changeset_patch',
830 828 revision='tip', conditions={'function': check_repo},
831 829 requirements=URL_NAME_REQUIREMENTS)
832 830
833 831 rmap.connect('changeset_download_home',
834 832 '/{repo_name}/changeset-download/{revision}',
835 833 controller='changeset', action='changeset_download',
836 834 revision='tip', conditions={'function': check_repo},
837 835 requirements=URL_NAME_REQUIREMENTS)
838 836
839 837 rmap.connect('changeset_comment',
840 838 '/{repo_name}/changeset/{revision}/comment', jsroute=True,
841 839 controller='changeset', revision='tip', action='comment',
842 840 conditions={'function': check_repo},
843 841 requirements=URL_NAME_REQUIREMENTS)
844 842
845 843 rmap.connect('changeset_comment_preview',
846 844 '/{repo_name}/changeset/comment/preview', jsroute=True,
847 845 controller='changeset', action='preview_comment',
848 846 conditions={'function': check_repo, 'method': ['POST']},
849 847 requirements=URL_NAME_REQUIREMENTS)
850 848
851 849 rmap.connect('changeset_comment_delete',
852 850 '/{repo_name}/changeset/comment/{comment_id}/delete',
853 851 controller='changeset', action='delete_comment',
854 852 conditions={'function': check_repo, 'method': ['DELETE']},
855 853 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
856 854
857 855 rmap.connect('changeset_info', '/{repo_name}/changeset_info/{revision}',
858 856 controller='changeset', action='changeset_info',
859 857 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
860 858
861 859 rmap.connect('compare_home',
862 860 '/{repo_name}/compare',
863 861 controller='compare', action='index',
864 862 conditions={'function': check_repo},
865 863 requirements=URL_NAME_REQUIREMENTS)
866 864
867 865 rmap.connect('compare_url',
868 866 '/{repo_name}/compare/{source_ref_type}@{source_ref:.*?}...{target_ref_type}@{target_ref:.*?}',
869 867 controller='compare', action='compare',
870 868 conditions={'function': check_repo},
871 869 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
872 870
873 871 rmap.connect('pullrequest_home',
874 872 '/{repo_name}/pull-request/new', controller='pullrequests',
875 873 action='index', conditions={'function': check_repo,
876 874 'method': ['GET']},
877 875 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
878 876
879 877 rmap.connect('pullrequest',
880 878 '/{repo_name}/pull-request/new', controller='pullrequests',
881 879 action='create', conditions={'function': check_repo,
882 880 'method': ['POST']},
883 881 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
884 882
885 883 rmap.connect('pullrequest_repo_refs',
886 884 '/{repo_name}/pull-request/refs/{target_repo_name:.*?[^/]}',
887 885 controller='pullrequests',
888 886 action='get_repo_refs',
889 887 conditions={'function': check_repo, 'method': ['GET']},
890 888 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
891 889
892 890 rmap.connect('pullrequest_repo_destinations',
893 891 '/{repo_name}/pull-request/repo-destinations',
894 892 controller='pullrequests',
895 893 action='get_repo_destinations',
896 894 conditions={'function': check_repo, 'method': ['GET']},
897 895 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
898 896
899 897 rmap.connect('pullrequest_show',
900 898 '/{repo_name}/pull-request/{pull_request_id}',
901 899 controller='pullrequests',
902 900 action='show', conditions={'function': check_repo,
903 901 'method': ['GET']},
904 902 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
905 903
906 904 rmap.connect('pullrequest_update',
907 905 '/{repo_name}/pull-request/{pull_request_id}',
908 906 controller='pullrequests',
909 907 action='update', conditions={'function': check_repo,
910 908 'method': ['PUT']},
911 909 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
912 910
913 911 rmap.connect('pullrequest_merge',
914 912 '/{repo_name}/pull-request/{pull_request_id}',
915 913 controller='pullrequests',
916 914 action='merge', conditions={'function': check_repo,
917 915 'method': ['POST']},
918 916 requirements=URL_NAME_REQUIREMENTS)
919 917
920 918 rmap.connect('pullrequest_delete',
921 919 '/{repo_name}/pull-request/{pull_request_id}',
922 920 controller='pullrequests',
923 921 action='delete', conditions={'function': check_repo,
924 922 'method': ['DELETE']},
925 923 requirements=URL_NAME_REQUIREMENTS)
926 924
927 925 rmap.connect('pullrequest_show_all',
928 926 '/{repo_name}/pull-request',
929 927 controller='pullrequests',
930 928 action='show_all', conditions={'function': check_repo,
931 929 'method': ['GET']},
932 930 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
933 931
934 932 rmap.connect('pullrequest_comment',
935 933 '/{repo_name}/pull-request-comment/{pull_request_id}',
936 934 controller='pullrequests',
937 935 action='comment', conditions={'function': check_repo,
938 936 'method': ['POST']},
939 937 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
940 938
941 939 rmap.connect('pullrequest_comment_delete',
942 940 '/{repo_name}/pull-request-comment/{comment_id}/delete',
943 941 controller='pullrequests', action='delete_comment',
944 942 conditions={'function': check_repo, 'method': ['DELETE']},
945 943 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
946 944
947 945 rmap.connect('summary_home_explicit', '/{repo_name}/summary',
948 946 controller='summary', conditions={'function': check_repo},
949 947 requirements=URL_NAME_REQUIREMENTS)
950 948
951 949 rmap.connect('branches_home', '/{repo_name}/branches',
952 950 controller='branches', conditions={'function': check_repo},
953 951 requirements=URL_NAME_REQUIREMENTS)
954 952
955 953 rmap.connect('tags_home', '/{repo_name}/tags',
956 954 controller='tags', conditions={'function': check_repo},
957 955 requirements=URL_NAME_REQUIREMENTS)
958 956
959 957 rmap.connect('bookmarks_home', '/{repo_name}/bookmarks',
960 958 controller='bookmarks', conditions={'function': check_repo},
961 959 requirements=URL_NAME_REQUIREMENTS)
962 960
963 961 rmap.connect('changelog_home', '/{repo_name}/changelog', jsroute=True,
964 962 controller='changelog', conditions={'function': check_repo},
965 963 requirements=URL_NAME_REQUIREMENTS)
966 964
967 965 rmap.connect('changelog_summary_home', '/{repo_name}/changelog_summary',
968 966 controller='changelog', action='changelog_summary',
969 967 conditions={'function': check_repo},
970 968 requirements=URL_NAME_REQUIREMENTS)
971 969
972 970 rmap.connect('changelog_file_home',
973 971 '/{repo_name}/changelog/{revision}/{f_path}',
974 972 controller='changelog', f_path=None,
975 973 conditions={'function': check_repo},
976 974 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
977 975
978 976 rmap.connect('changelog_elements', '/{repo_name}/changelog_details',
979 977 controller='changelog', action='changelog_elements',
980 978 conditions={'function': check_repo},
981 979 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
982 980
983 981 rmap.connect('files_home', '/{repo_name}/files/{revision}/{f_path}',
984 982 controller='files', revision='tip', f_path='',
985 983 conditions={'function': check_repo},
986 984 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
987 985
988 986 rmap.connect('files_home_simple_catchrev',
989 987 '/{repo_name}/files/{revision}',
990 988 controller='files', revision='tip', f_path='',
991 989 conditions={'function': check_repo},
992 990 requirements=URL_NAME_REQUIREMENTS)
993 991
994 992 rmap.connect('files_home_simple_catchall',
995 993 '/{repo_name}/files',
996 994 controller='files', revision='tip', f_path='',
997 995 conditions={'function': check_repo},
998 996 requirements=URL_NAME_REQUIREMENTS)
999 997
1000 998 rmap.connect('files_history_home',
1001 999 '/{repo_name}/history/{revision}/{f_path}',
1002 1000 controller='files', action='history', revision='tip', f_path='',
1003 1001 conditions={'function': check_repo},
1004 1002 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1005 1003
1006 1004 rmap.connect('files_authors_home',
1007 1005 '/{repo_name}/authors/{revision}/{f_path}',
1008 1006 controller='files', action='authors', revision='tip', f_path='',
1009 1007 conditions={'function': check_repo},
1010 1008 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1011 1009
1012 1010 rmap.connect('files_diff_home', '/{repo_name}/diff/{f_path}',
1013 1011 controller='files', action='diff', f_path='',
1014 1012 conditions={'function': check_repo},
1015 1013 requirements=URL_NAME_REQUIREMENTS)
1016 1014
1017 1015 rmap.connect('files_diff_2way_home',
1018 1016 '/{repo_name}/diff-2way/{f_path}',
1019 1017 controller='files', action='diff_2way', f_path='',
1020 1018 conditions={'function': check_repo},
1021 1019 requirements=URL_NAME_REQUIREMENTS)
1022 1020
1023 1021 rmap.connect('files_rawfile_home',
1024 1022 '/{repo_name}/rawfile/{revision}/{f_path}',
1025 1023 controller='files', action='rawfile', revision='tip',
1026 1024 f_path='', conditions={'function': check_repo},
1027 1025 requirements=URL_NAME_REQUIREMENTS)
1028 1026
1029 1027 rmap.connect('files_raw_home',
1030 1028 '/{repo_name}/raw/{revision}/{f_path}',
1031 1029 controller='files', action='raw', revision='tip', f_path='',
1032 1030 conditions={'function': check_repo},
1033 1031 requirements=URL_NAME_REQUIREMENTS)
1034 1032
1035 1033 rmap.connect('files_render_home',
1036 1034 '/{repo_name}/render/{revision}/{f_path}',
1037 1035 controller='files', action='index', revision='tip', f_path='',
1038 1036 rendered=True, conditions={'function': check_repo},
1039 1037 requirements=URL_NAME_REQUIREMENTS)
1040 1038
1041 1039 rmap.connect('files_annotate_home',
1042 1040 '/{repo_name}/annotate/{revision}/{f_path}',
1043 1041 controller='files', action='index', revision='tip',
1044 1042 f_path='', annotate=True, conditions={'function': check_repo},
1045 1043 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1046 1044
1047 1045 rmap.connect('files_annotate_previous',
1048 1046 '/{repo_name}/annotate-previous/{revision}/{f_path}',
1049 1047 controller='files', action='annotate_previous', revision='tip',
1050 1048 f_path='', annotate=True, conditions={'function': check_repo},
1051 1049 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1052 1050
1053 1051 rmap.connect('files_edit',
1054 1052 '/{repo_name}/edit/{revision}/{f_path}',
1055 1053 controller='files', action='edit', revision='tip',
1056 1054 f_path='',
1057 1055 conditions={'function': check_repo, 'method': ['POST']},
1058 1056 requirements=URL_NAME_REQUIREMENTS)
1059 1057
1060 1058 rmap.connect('files_edit_home',
1061 1059 '/{repo_name}/edit/{revision}/{f_path}',
1062 1060 controller='files', action='edit_home', revision='tip',
1063 1061 f_path='', conditions={'function': check_repo},
1064 1062 requirements=URL_NAME_REQUIREMENTS)
1065 1063
1066 1064 rmap.connect('files_add',
1067 1065 '/{repo_name}/add/{revision}/{f_path}',
1068 1066 controller='files', action='add', revision='tip',
1069 1067 f_path='',
1070 1068 conditions={'function': check_repo, 'method': ['POST']},
1071 1069 requirements=URL_NAME_REQUIREMENTS)
1072 1070
1073 1071 rmap.connect('files_add_home',
1074 1072 '/{repo_name}/add/{revision}/{f_path}',
1075 1073 controller='files', action='add_home', revision='tip',
1076 1074 f_path='', conditions={'function': check_repo},
1077 1075 requirements=URL_NAME_REQUIREMENTS)
1078 1076
1079 1077 rmap.connect('files_delete',
1080 1078 '/{repo_name}/delete/{revision}/{f_path}',
1081 1079 controller='files', action='delete', revision='tip',
1082 1080 f_path='',
1083 1081 conditions={'function': check_repo, 'method': ['POST']},
1084 1082 requirements=URL_NAME_REQUIREMENTS)
1085 1083
1086 1084 rmap.connect('files_delete_home',
1087 1085 '/{repo_name}/delete/{revision}/{f_path}',
1088 1086 controller='files', action='delete_home', revision='tip',
1089 1087 f_path='', conditions={'function': check_repo},
1090 1088 requirements=URL_NAME_REQUIREMENTS)
1091 1089
1092 1090 rmap.connect('files_archive_home', '/{repo_name}/archive/{fname}',
1093 1091 controller='files', action='archivefile',
1094 1092 conditions={'function': check_repo},
1095 1093 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1096 1094
1097 1095 rmap.connect('files_nodelist_home',
1098 1096 '/{repo_name}/nodelist/{revision}/{f_path}',
1099 1097 controller='files', action='nodelist',
1100 1098 conditions={'function': check_repo},
1101 1099 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1102 1100
1103 1101 rmap.connect('files_nodetree_full',
1104 1102 '/{repo_name}/nodetree_full/{commit_id}/{f_path}',
1105 1103 controller='files', action='nodetree_full',
1106 1104 conditions={'function': check_repo},
1107 1105 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1108 1106
1109 1107 rmap.connect('repo_fork_create_home', '/{repo_name}/fork',
1110 1108 controller='forks', action='fork_create',
1111 1109 conditions={'function': check_repo, 'method': ['POST']},
1112 1110 requirements=URL_NAME_REQUIREMENTS)
1113 1111
1114 1112 rmap.connect('repo_fork_home', '/{repo_name}/fork',
1115 1113 controller='forks', action='fork',
1116 1114 conditions={'function': check_repo},
1117 1115 requirements=URL_NAME_REQUIREMENTS)
1118 1116
1119 1117 rmap.connect('repo_forks_home', '/{repo_name}/forks',
1120 1118 controller='forks', action='forks',
1121 1119 conditions={'function': check_repo},
1122 1120 requirements=URL_NAME_REQUIREMENTS)
1123 1121
1124 1122 # must be here for proper group/repo catching pattern
1125 1123 _connect_with_slash(
1126 1124 rmap, 'repo_group_home', '/{group_name}',
1127 1125 controller='home', action='index_repo_group',
1128 1126 conditions={'function': check_group},
1129 1127 requirements=URL_NAME_REQUIREMENTS)
1130 1128
1131 1129 # catch all, at the end
1132 1130 _connect_with_slash(
1133 1131 rmap, 'summary_home', '/{repo_name}', jsroute=True,
1134 1132 controller='summary', action='index',
1135 1133 conditions={'function': check_repo},
1136 1134 requirements=URL_NAME_REQUIREMENTS)
1137 1135
1138 1136 return rmap
1139 1137
1140 1138
1141 1139 def _connect_with_slash(mapper, name, path, *args, **kwargs):
1142 1140 """
1143 1141 Connect a route with an optional trailing slash in `path`.
1144 1142 """
1145 1143 mapper.connect(name + '_slash', path + '/', *args, **kwargs)
1146 1144 mapper.connect(name, path, *args, **kwargs)
@@ -1,235 +1,110 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 import re
28 27
29 from pylons import tmpl_context as c, request, url, config
30 from pylons.i18n.translation import _
31 from sqlalchemy.sql import func
28 from pylons import tmpl_context as c
32 29
33 30 from rhodecode.lib.auth import (
34 LoginRequired, HasPermissionAllDecorator, AuthUser,
35 HasRepoGroupPermissionAnyDecorator, XHRRequired)
31 LoginRequired, HasPermissionAllDecorator,
32 HasRepoGroupPermissionAnyDecorator)
36 33 from rhodecode.lib.base import BaseController, render
37 from rhodecode.lib.index import searcher_from_config
34
38 35 from rhodecode.lib.ext_json import json
39 from rhodecode.lib.utils import jsonify
40 from rhodecode.lib.utils2 import safe_unicode, str2bool
41 36 from rhodecode.model.db import Repository, RepoGroup
42 37 from rhodecode.model.repo import RepoModel
43 38 from rhodecode.model.repo_group import RepoGroupModel
44 39 from rhodecode.model.scm import RepoList, RepoGroupList
45 40
46 41
47 42 log = logging.getLogger(__name__)
48 43
49 44
50 45 class HomeController(BaseController):
51 46 def __before__(self):
52 47 super(HomeController, self).__before__()
53 48
54 49 def ping(self):
55 50 """
56 51 Ping, doesn't require login, good for checking out the platform
57 52 """
58 53 instance_id = getattr(c, 'rhodecode_instanceid', '')
59 54 return 'pong[%s] => %s' % (instance_id, self.ip_addr,)
60 55
61 56 @LoginRequired()
62 57 @HasPermissionAllDecorator('hg.admin')
63 58 def error_test(self):
64 59 """
65 60 Test exception handling and emails on errors
66 61 """
67 62 class TestException(Exception):
68 63 pass
69 64
70 65 msg = ('RhodeCode Enterprise %s test exception. Generation time: %s'
71 66 % (c.rhodecode_name, time.time()))
72 67 raise TestException(msg)
73 68
74 69 def _get_groups_and_repos(self, repo_group_id=None):
75 70 # repo groups groups
76 71 repo_group_list = RepoGroup.get_all_repo_groups(group_id=repo_group_id)
77 72 _perms = ['group.read', 'group.write', 'group.admin']
78 73 repo_group_list_acl = RepoGroupList(repo_group_list, perm_set=_perms)
79 74 repo_group_data = RepoGroupModel().get_repo_groups_as_dict(
80 75 repo_group_list=repo_group_list_acl, admin=False)
81 76
82 77 # repositories
83 78 repo_list = Repository.get_all_repos(group_id=repo_group_id)
84 79 _perms = ['repository.read', 'repository.write', 'repository.admin']
85 80 repo_list_acl = RepoList(repo_list, perm_set=_perms)
86 81 repo_data = RepoModel().get_repos_as_dict(
87 82 repo_list=repo_list_acl, admin=False)
88 83
89 84 return repo_data, repo_group_data
90 85
91 86 @LoginRequired()
92 87 def index(self):
93 88 c.repo_group = None
94 89
95 90 repo_data, repo_group_data = self._get_groups_and_repos()
96 91 # json used to render the grids
97 92 c.repos_data = json.dumps(repo_data)
98 93 c.repo_groups_data = json.dumps(repo_group_data)
99 94
100 95 return render('/index.mako')
101 96
102 97 @LoginRequired()
103 98 @HasRepoGroupPermissionAnyDecorator('group.read', 'group.write',
104 99 'group.admin')
105 100 def index_repo_group(self, group_name):
106 101 """GET /repo_group_name: Show a specific item"""
107 102 c.repo_group = RepoGroupModel()._get_repo_group(group_name)
108 103 repo_data, repo_group_data = self._get_groups_and_repos(
109 104 c.repo_group.group_id)
110 105
111 106 # json used to render the grids
112 107 c.repos_data = json.dumps(repo_data)
113 108 c.repo_groups_data = json.dumps(repo_group_data)
114 109
115 110 return render('index_repo_group.mako')
116
117 def _get_repo_list(self, name_contains=None, repo_type=None, limit=20):
118 query = Repository.query()\
119 .order_by(func.length(Repository.repo_name))\
120 .order_by(Repository.repo_name)
121
122 if repo_type:
123 query = query.filter(Repository.repo_type == repo_type)
124
125 if name_contains:
126 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
127 query = query.filter(
128 Repository.repo_name.ilike(ilike_expression))
129 query = query.limit(limit)
130
131 all_repos = query.all()
132 repo_iter = self.scm_model.get_repos(all_repos)
133 return [
134 {
135 'id': obj['name'],
136 'text': obj['name'],
137 'type': 'repo',
138 'obj': obj['dbrepo'],
139 'url': url('summary_home', repo_name=obj['name'])
140 }
141 for obj in repo_iter]
142
143 def _get_repo_group_list(self, name_contains=None, limit=20):
144 query = RepoGroup.query()\
145 .order_by(func.length(RepoGroup.group_name))\
146 .order_by(RepoGroup.group_name)
147
148 if name_contains:
149 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
150 query = query.filter(
151 RepoGroup.group_name.ilike(ilike_expression))
152 query = query.limit(limit)
153
154 all_groups = query.all()
155 repo_groups_iter = self.scm_model.get_repo_groups(all_groups)
156 return [
157 {
158 'id': obj.group_name,
159 'text': obj.group_name,
160 'type': 'group',
161 'obj': {},
162 'url': url('repo_group_home', group_name=obj.group_name)
163 }
164 for obj in repo_groups_iter]
165
166 def _get_hash_commit_list(self, hash_starts_with=None, limit=20):
167 if not hash_starts_with or len(hash_starts_with) < 3:
168 return []
169
170 commit_hashes = re.compile('([0-9a-f]{2,40})').findall(hash_starts_with)
171
172 if len(commit_hashes) != 1:
173 return []
174
175 commit_hash_prefix = commit_hashes[0]
176
177 auth_user = AuthUser(
178 user_id=c.rhodecode_user.user_id, ip_addr=self.ip_addr)
179 searcher = searcher_from_config(config)
180 result = searcher.search(
181 'commit_id:%s*' % commit_hash_prefix, 'commit', auth_user,
182 raise_on_exc=False)
183
184 return [
185 {
186 'id': entry['commit_id'],
187 'text': entry['commit_id'],
188 'type': 'commit',
189 'obj': {'repo': entry['repository']},
190 'url': url('changeset_home',
191 repo_name=entry['repository'],
192 revision=entry['commit_id'])
193 }
194 for entry in result['results']]
195
196 @LoginRequired()
197 @XHRRequired()
198 @jsonify
199 def goto_switcher_data(self):
200 query = request.GET.get('query')
201 log.debug('generating goto switcher list, query %s', query)
202
203 res = []
204 repo_groups = self._get_repo_group_list(query)
205 if repo_groups:
206 res.append({
207 'text': _('Groups'),
208 'children': repo_groups
209 })
210
211 repos = self._get_repo_list(query)
212 if repos:
213 res.append({
214 'text': _('Repositories'),
215 'children': repos
216 })
217
218 commits = self._get_hash_commit_list(query)
219 if commits:
220 unique_repos = {}
221 for commit in commits:
222 unique_repos.setdefault(commit['obj']['repo'], []
223 ).append(commit)
224
225 for repo in unique_repos:
226 res.append({
227 'text': _('Commits in %(repo)s') % {'repo': repo},
228 'children': unique_repos[repo]
229 })
230
231 data = {
232 'more': False,
233 'results': res
234 }
235 return data
@@ -1,112 +1,113 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 16 pyroutes.register('new_repo', '/_admin/create_repository', []);
17 17 pyroutes.register('edit_user', '/_admin/users/%(user_id)s/edit', ['user_id']);
18 18 pyroutes.register('edit_user_group_members', '/_admin/user_groups/%(user_group_id)s/edit/members', ['user_group_id']);
19 19 pyroutes.register('gists', '/_admin/gists', []);
20 20 pyroutes.register('new_gist', '/_admin/gists/new', []);
21 21 pyroutes.register('toggle_following', '/_admin/toggle_following', []);
22 22 pyroutes.register('repo_stats', '/%(repo_name)s/repo_stats/%(commit_id)s', ['repo_name', 'commit_id']);
23 23 pyroutes.register('repo_refs_data', '/%(repo_name)s/refs-data', ['repo_name']);
24 24 pyroutes.register('repo_refs_changelog_data', '/%(repo_name)s/refs-data-changelog', ['repo_name']);
25 25 pyroutes.register('repo_default_reviewers_data', '/%(repo_name)s/default-reviewers', ['repo_name']);
26 26 pyroutes.register('changeset_home', '/%(repo_name)s/changeset/%(revision)s', ['repo_name', 'revision']);
27 27 pyroutes.register('edit_repo', '/%(repo_name)s/settings', ['repo_name']);
28 28 pyroutes.register('edit_repo_perms', '/%(repo_name)s/settings/permissions', ['repo_name']);
29 29 pyroutes.register('changeset_comment', '/%(repo_name)s/changeset/%(revision)s/comment', ['repo_name', 'revision']);
30 30 pyroutes.register('changeset_comment_preview', '/%(repo_name)s/changeset/comment/preview', ['repo_name']);
31 31 pyroutes.register('changeset_comment_delete', '/%(repo_name)s/changeset/comment/%(comment_id)s/delete', ['repo_name', 'comment_id']);
32 32 pyroutes.register('changeset_info', '/%(repo_name)s/changeset_info/%(revision)s', ['repo_name', 'revision']);
33 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']);
34 34 pyroutes.register('pullrequest_home', '/%(repo_name)s/pull-request/new', ['repo_name']);
35 35 pyroutes.register('pullrequest', '/%(repo_name)s/pull-request/new', ['repo_name']);
36 36 pyroutes.register('pullrequest_repo_refs', '/%(repo_name)s/pull-request/refs/%(target_repo_name)s', ['repo_name', 'target_repo_name']);
37 37 pyroutes.register('pullrequest_repo_destinations', '/%(repo_name)s/pull-request/repo-destinations', ['repo_name']);
38 38 pyroutes.register('pullrequest_show', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
39 39 pyroutes.register('pullrequest_update', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
40 40 pyroutes.register('pullrequest_show_all', '/%(repo_name)s/pull-request', ['repo_name']);
41 41 pyroutes.register('pullrequest_comment', '/%(repo_name)s/pull-request-comment/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
42 42 pyroutes.register('pullrequest_comment_delete', '/%(repo_name)s/pull-request-comment/%(comment_id)s/delete', ['repo_name', 'comment_id']);
43 43 pyroutes.register('changelog_home', '/%(repo_name)s/changelog', ['repo_name']);
44 44 pyroutes.register('changelog_file_home', '/%(repo_name)s/changelog/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
45 45 pyroutes.register('changelog_elements', '/%(repo_name)s/changelog_details', ['repo_name']);
46 46 pyroutes.register('files_home', '/%(repo_name)s/files/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
47 47 pyroutes.register('files_history_home', '/%(repo_name)s/history/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
48 48 pyroutes.register('files_authors_home', '/%(repo_name)s/authors/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
49 49 pyroutes.register('files_annotate_home', '/%(repo_name)s/annotate/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
50 50 pyroutes.register('files_annotate_previous', '/%(repo_name)s/annotate-previous/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
51 51 pyroutes.register('files_archive_home', '/%(repo_name)s/archive/%(fname)s', ['repo_name', 'fname']);
52 52 pyroutes.register('files_nodelist_home', '/%(repo_name)s/nodelist/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
53 53 pyroutes.register('files_nodetree_full', '/%(repo_name)s/nodetree_full/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
54 54 pyroutes.register('summary_home_slash', '/%(repo_name)s/', ['repo_name']);
55 55 pyroutes.register('summary_home', '/%(repo_name)s', ['repo_name']);
56 56 pyroutes.register('favicon', '/favicon.ico', []);
57 57 pyroutes.register('robots', '/robots.txt', []);
58 58 pyroutes.register('auth_home', '/_admin/auth*traverse', []);
59 59 pyroutes.register('global_integrations_new', '/_admin/integrations/new', []);
60 60 pyroutes.register('global_integrations_home', '/_admin/integrations', []);
61 61 pyroutes.register('global_integrations_list', '/_admin/integrations/%(integration)s', ['integration']);
62 62 pyroutes.register('global_integrations_create', '/_admin/integrations/%(integration)s/new', ['integration']);
63 63 pyroutes.register('global_integrations_edit', '/_admin/integrations/%(integration)s/%(integration_id)s', ['integration', 'integration_id']);
64 64 pyroutes.register('repo_group_integrations_home', '%(repo_group_name)s/settings/integrations', ['repo_group_name']);
65 65 pyroutes.register('repo_group_integrations_list', '%(repo_group_name)s/settings/integrations/%(integration)s', ['repo_group_name', 'integration']);
66 66 pyroutes.register('repo_group_integrations_new', '%(repo_group_name)s/settings/integrations/new', ['repo_group_name']);
67 67 pyroutes.register('repo_group_integrations_create', '%(repo_group_name)s/settings/integrations/%(integration)s/new', ['repo_group_name', 'integration']);
68 68 pyroutes.register('repo_group_integrations_edit', '%(repo_group_name)s/settings/integrations/%(integration)s/%(integration_id)s', ['repo_group_name', 'integration', 'integration_id']);
69 69 pyroutes.register('repo_integrations_home', '%(repo_name)s/settings/integrations', ['repo_name']);
70 70 pyroutes.register('repo_integrations_list', '%(repo_name)s/settings/integrations/%(integration)s', ['repo_name', 'integration']);
71 71 pyroutes.register('repo_integrations_new', '%(repo_name)s/settings/integrations/new', ['repo_name']);
72 72 pyroutes.register('repo_integrations_create', '%(repo_name)s/settings/integrations/%(integration)s/new', ['repo_name', 'integration']);
73 73 pyroutes.register('repo_integrations_edit', '%(repo_name)s/settings/integrations/%(integration)s/%(integration_id)s', ['repo_name', 'integration', 'integration_id']);
74 74 pyroutes.register('admin_settings_open_source', '_admin/settings/open_source', []);
75 75 pyroutes.register('admin_settings_vcs_svn_generate_cfg', '_admin/settings/vcs/svn_generate_cfg', []);
76 76 pyroutes.register('admin_settings_system', '_admin/settings/system', []);
77 77 pyroutes.register('admin_settings_system_update', '_admin/settings/system/updates', []);
78 78 pyroutes.register('admin_settings_sessions', '_admin/settings/sessions', []);
79 79 pyroutes.register('admin_settings_sessions_cleanup', '_admin/settings/sessions/cleanup', []);
80 80 pyroutes.register('users', '_admin/users', []);
81 81 pyroutes.register('users_data', '_admin/users_data', []);
82 82 pyroutes.register('edit_user_auth_tokens', '_admin/users/%(user_id)s/edit/auth_tokens', ['user_id']);
83 83 pyroutes.register('edit_user_auth_tokens_add', '_admin/users/%(user_id)s/edit/auth_tokens/new', ['user_id']);
84 84 pyroutes.register('edit_user_auth_tokens_delete', '_admin/users/%(user_id)s/edit/auth_tokens/delete', ['user_id']);
85 85 pyroutes.register('edit_user_groups_management', '_admin/users/%(user_id)s/edit/groups_management', ['user_id']);
86 86 pyroutes.register('edit_user_groups_management_updates', '_admin/users/%(user_id)s/edit/edit_user_groups_management/updates', ['user_id']);
87 87 pyroutes.register('edit_user_audit_logs', '_admin/users/%(user_id)s/edit/audit', ['user_id']);
88 88 pyroutes.register('channelstream_connect', '/_admin/channelstream/connect', []);
89 89 pyroutes.register('channelstream_subscribe', '/_admin/channelstream/subscribe', []);
90 90 pyroutes.register('channelstream_proxy', '/_channelstream', []);
91 91 pyroutes.register('login', '/_admin/login', []);
92 92 pyroutes.register('logout', '/_admin/logout', []);
93 93 pyroutes.register('register', '/_admin/register', []);
94 94 pyroutes.register('reset_password', '/_admin/password_reset', []);
95 95 pyroutes.register('reset_password_confirmation', '/_admin/password_reset_confirmation', []);
96 96 pyroutes.register('user_autocomplete_data', '/_users', []);
97 97 pyroutes.register('user_group_autocomplete_data', '/_user_groups', []);
98 98 pyroutes.register('repo_list_data', '/_repos', []);
99 pyroutes.register('goto_switcher_data', '/_goto_data', []);
99 100 pyroutes.register('repo_maintenance', '/%(repo_name)s/maintenance', ['repo_name']);
100 101 pyroutes.register('repo_maintenance_execute', '/%(repo_name)s/maintenance/execute', ['repo_name']);
101 102 pyroutes.register('strip', '/%(repo_name)s/strip', ['repo_name']);
102 103 pyroutes.register('strip_check', '/%(repo_name)s/strip_check', ['repo_name']);
103 104 pyroutes.register('strip_execute', '/%(repo_name)s/strip_execute', ['repo_name']);
104 105 pyroutes.register('user_profile', '/_profiles/%(username)s', ['username']);
105 106 pyroutes.register('my_account_profile', '/_admin/my_account/profile', []);
106 107 pyroutes.register('my_account_password', '/_admin/my_account/password', []);
107 108 pyroutes.register('my_account_password_update', '/_admin/my_account/password', []);
108 109 pyroutes.register('my_account_auth_tokens', '/_admin/my_account/auth_tokens', []);
109 110 pyroutes.register('my_account_auth_tokens_add', '/_admin/my_account/auth_tokens/new', []);
110 111 pyroutes.register('my_account_auth_tokens_delete', '/_admin/my_account/auth_tokens/delete', []);
111 112 pyroutes.register('apiv2', '/_admin/api', []);
112 113 }
@@ -1,601 +1,601 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <%inherit file="root.mako"/>
3 3
4 4 <div class="outerwrapper">
5 5 <!-- HEADER -->
6 6 <div class="header">
7 7 <div id="header-inner" class="wrapper">
8 8 <div id="logo">
9 9 <div class="logo-wrapper">
10 10 <a href="${h.url('home')}"><img src="${h.asset('images/rhodecode-logo-white-216x60.png')}" alt="RhodeCode"/></a>
11 11 </div>
12 12 %if c.rhodecode_name:
13 13 <div class="branding">- ${h.branding(c.rhodecode_name)}</div>
14 14 %endif
15 15 </div>
16 16 <!-- MENU BAR NAV -->
17 17 ${self.menu_bar_nav()}
18 18 <!-- END MENU BAR NAV -->
19 19 </div>
20 20 </div>
21 21 ${self.menu_bar_subnav()}
22 22 <!-- END HEADER -->
23 23
24 24 <!-- CONTENT -->
25 25 <div id="content" class="wrapper">
26 26
27 27 <rhodecode-toast id="notifications"></rhodecode-toast>
28 28
29 29 <div class="main">
30 30 ${next.main()}
31 31 </div>
32 32 </div>
33 33 <!-- END CONTENT -->
34 34
35 35 </div>
36 36 <!-- FOOTER -->
37 37 <div id="footer">
38 38 <div id="footer-inner" class="title wrapper">
39 39 <div>
40 40 <p class="footer-link-right">
41 41 % if c.visual.show_version:
42 42 RhodeCode Enterprise ${c.rhodecode_version} ${c.rhodecode_edition}
43 43 % endif
44 44 &copy; 2010-${h.datetime.today().year}, <a href="${h.url('rhodecode_official')}" target="_blank">RhodeCode GmbH</a>. All rights reserved.
45 45 % if c.visual.rhodecode_support_url:
46 46 <a href="${c.visual.rhodecode_support_url}" target="_blank">${_('Support')}</a>
47 47 % endif
48 48 </p>
49 49 <% sid = 'block' if request.GET.get('showrcid') else 'none' %>
50 50 <p class="server-instance" style="display:${sid}">
51 51 ## display hidden instance ID if specially defined
52 52 % if c.rhodecode_instanceid:
53 53 ${_('RhodeCode instance id: %s') % c.rhodecode_instanceid}
54 54 % endif
55 55 </p>
56 56 </div>
57 57 </div>
58 58 </div>
59 59
60 60 <!-- END FOOTER -->
61 61
62 62 ### MAKO DEFS ###
63 63
64 64 <%def name="menu_bar_subnav()">
65 65 </%def>
66 66
67 67 <%def name="breadcrumbs(class_='breadcrumbs')">
68 68 <div class="${class_}">
69 69 ${self.breadcrumbs_links()}
70 70 </div>
71 71 </%def>
72 72
73 73 <%def name="admin_menu()">
74 74 <ul class="admin_menu submenu">
75 75 <li><a href="${h.url('admin_home')}">${_('Admin journal')}</a></li>
76 76 <li><a href="${h.url('repos')}">${_('Repositories')}</a></li>
77 77 <li><a href="${h.url('repo_groups')}">${_('Repository groups')}</a></li>
78 78 <li><a href="${h.route_path('users')}">${_('Users')}</a></li>
79 79 <li><a href="${h.url('users_groups')}">${_('User groups')}</a></li>
80 80 <li><a href="${h.url('admin_permissions_application')}">${_('Permissions')}</a></li>
81 81 <li><a href="${h.route_path('auth_home', traverse='')}">${_('Authentication')}</a></li>
82 82 <li><a href="${h.route_path('global_integrations_home')}">${_('Integrations')}</a></li>
83 83 <li><a href="${h.url('admin_defaults_repositories')}">${_('Defaults')}</a></li>
84 84 <li class="last"><a href="${h.url('admin_settings')}">${_('Settings')}</a></li>
85 85 </ul>
86 86 </%def>
87 87
88 88
89 89 <%def name="dt_info_panel(elements)">
90 90 <dl class="dl-horizontal">
91 91 %for dt, dd, title, show_items in elements:
92 92 <dt>${dt}:</dt>
93 93 <dd title="${title}">
94 94 %if callable(dd):
95 95 ## allow lazy evaluation of elements
96 96 ${dd()}
97 97 %else:
98 98 ${dd}
99 99 %endif
100 100 %if show_items:
101 101 <span class="btn-collapse" data-toggle="item-${h.md5_safe(dt)[:6]}-details">${_('Show More')} </span>
102 102 %endif
103 103 </dd>
104 104
105 105 %if show_items:
106 106 <div class="collapsable-content" data-toggle="item-${h.md5_safe(dt)[:6]}-details" style="display: none">
107 107 %for item in show_items:
108 108 <dt></dt>
109 109 <dd>${item}</dd>
110 110 %endfor
111 111 </div>
112 112 %endif
113 113
114 114 %endfor
115 115 </dl>
116 116 </%def>
117 117
118 118
119 119 <%def name="gravatar(email, size=16)">
120 120 <%
121 121 if (size > 16):
122 122 gravatar_class = 'gravatar gravatar-large'
123 123 else:
124 124 gravatar_class = 'gravatar'
125 125 %>
126 126 <%doc>
127 127 TODO: johbo: For now we serve double size images to make it smooth
128 128 for retina. This is how it worked until now. Should be replaced
129 129 with a better solution at some point.
130 130 </%doc>
131 131 <img class="${gravatar_class}" src="${h.gravatar_url(email, size * 2)}" height="${size}" width="${size}">
132 132 </%def>
133 133
134 134
135 135 <%def name="gravatar_with_user(contact, size=16, show_disabled=False)">
136 136 <% email = h.email_or_none(contact) %>
137 137 <div class="rc-user tooltip" title="${h.author_string(email)}">
138 138 ${self.gravatar(email, size)}
139 139 <span class="${'user user-disabled' if show_disabled else 'user'}"> ${h.link_to_user(contact)}</span>
140 140 </div>
141 141 </%def>
142 142
143 143
144 144 ## admin menu used for people that have some admin resources
145 145 <%def name="admin_menu_simple(repositories=None, repository_groups=None, user_groups=None)">
146 146 <ul class="submenu">
147 147 %if repositories:
148 148 <li class="local-admin-repos"><a href="${h.url('repos')}">${_('Repositories')}</a></li>
149 149 %endif
150 150 %if repository_groups:
151 151 <li class="local-admin-repo-groups"><a href="${h.url('repo_groups')}">${_('Repository groups')}</a></li>
152 152 %endif
153 153 %if user_groups:
154 154 <li class="local-admin-user-groups"><a href="${h.url('users_groups')}">${_('User groups')}</a></li>
155 155 %endif
156 156 </ul>
157 157 </%def>
158 158
159 159 <%def name="repo_page_title(repo_instance)">
160 160 <div class="title-content">
161 161 <div class="title-main">
162 162 ## SVN/HG/GIT icons
163 163 %if h.is_hg(repo_instance):
164 164 <i class="icon-hg"></i>
165 165 %endif
166 166 %if h.is_git(repo_instance):
167 167 <i class="icon-git"></i>
168 168 %endif
169 169 %if h.is_svn(repo_instance):
170 170 <i class="icon-svn"></i>
171 171 %endif
172 172
173 173 ## public/private
174 174 %if repo_instance.private:
175 175 <i class="icon-repo-private"></i>
176 176 %else:
177 177 <i class="icon-repo-public"></i>
178 178 %endif
179 179
180 180 ## repo name with group name
181 181 ${h.breadcrumb_repo_link(c.rhodecode_db_repo)}
182 182
183 183 </div>
184 184
185 185 ## FORKED
186 186 %if repo_instance.fork:
187 187 <p>
188 188 <i class="icon-code-fork"></i> ${_('Fork of')}
189 189 <a href="${h.url('summary_home',repo_name=repo_instance.fork.repo_name)}">${repo_instance.fork.repo_name}</a>
190 190 </p>
191 191 %endif
192 192
193 193 ## IMPORTED FROM REMOTE
194 194 %if repo_instance.clone_uri:
195 195 <p>
196 196 <i class="icon-code-fork"></i> ${_('Clone from')}
197 197 <a href="${h.url(h.safe_str(h.hide_credentials(repo_instance.clone_uri)))}">${h.hide_credentials(repo_instance.clone_uri)}</a>
198 198 </p>
199 199 %endif
200 200
201 201 ## LOCKING STATUS
202 202 %if repo_instance.locked[0]:
203 203 <p class="locking_locked">
204 204 <i class="icon-repo-lock"></i>
205 205 ${_('Repository locked by %(user)s') % {'user': h.person_by_id(repo_instance.locked[0])}}
206 206 </p>
207 207 %elif repo_instance.enable_locking:
208 208 <p class="locking_unlocked">
209 209 <i class="icon-repo-unlock"></i>
210 210 ${_('Repository not locked. Pull repository to lock it.')}
211 211 </p>
212 212 %endif
213 213
214 214 </div>
215 215 </%def>
216 216
217 217 <%def name="repo_menu(active=None)">
218 218 <%
219 219 def is_active(selected):
220 220 if selected == active:
221 221 return "active"
222 222 %>
223 223
224 224 <!--- CONTEXT BAR -->
225 225 <div id="context-bar">
226 226 <div class="wrapper">
227 227 <ul id="context-pages" class="horizontal-list navigation">
228 228 <li class="${is_active('summary')}"><a class="menulink" href="${h.url('summary_home', repo_name=c.repo_name)}"><div class="menulabel">${_('Summary')}</div></a></li>
229 229 <li class="${is_active('changelog')}"><a class="menulink" href="${h.url('changelog_home', repo_name=c.repo_name)}"><div class="menulabel">${_('Changelog')}</div></a></li>
230 230 <li class="${is_active('files')}"><a class="menulink" href="${h.url('files_home', repo_name=c.repo_name, revision=c.rhodecode_db_repo.landing_rev[1])}"><div class="menulabel">${_('Files')}</div></a></li>
231 231 <li class="${is_active('compare')}">
232 232 <a class="menulink" href="${h.url('compare_home',repo_name=c.repo_name)}"><div class="menulabel">${_('Compare')}</div></a>
233 233 </li>
234 234 ## TODO: anderson: ideally it would have a function on the scm_instance "enable_pullrequest() and enable_fork()"
235 235 %if c.rhodecode_db_repo.repo_type in ['git','hg']:
236 236 <li class="${is_active('showpullrequest')}">
237 237 <a class="menulink" href="${h.url('pullrequest_show_all',repo_name=c.repo_name)}" title="${_('Show Pull Requests for %s') % c.repo_name}">
238 238 %if c.repository_pull_requests:
239 239 <span class="pr_notifications">${c.repository_pull_requests}</span>
240 240 %endif
241 241 <div class="menulabel">${_('Pull Requests')}</div>
242 242 </a>
243 243 </li>
244 244 %endif
245 245 <li class="${is_active('options')}">
246 246 <a class="menulink" href="#" class="dropdown"><div class="menulabel">${_('Options')} <div class="show_more"></div></div></a>
247 247 <ul class="submenu">
248 248 %if h.HasRepoPermissionAll('repository.admin')(c.repo_name):
249 249 <li><a href="${h.url('edit_repo',repo_name=c.repo_name)}">${_('Settings')}</a></li>
250 250 %endif
251 251 %if c.rhodecode_db_repo.fork:
252 252 <li><a href="${h.url('compare_url',repo_name=c.rhodecode_db_repo.fork.repo_name,source_ref_type=c.rhodecode_db_repo.landing_rev[0],source_ref=c.rhodecode_db_repo.landing_rev[1], target_repo=c.repo_name,target_ref_type='branch' if request.GET.get('branch') else c.rhodecode_db_repo.landing_rev[0],target_ref=request.GET.get('branch') or c.rhodecode_db_repo.landing_rev[1], merge=1)}">
253 253 ${_('Compare fork')}</a></li>
254 254 %endif
255 255
256 256 <li><a href="${h.url('search_repo_home',repo_name=c.repo_name)}">${_('Search')}</a></li>
257 257
258 258 %if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name) and c.rhodecode_db_repo.enable_locking:
259 259 %if c.rhodecode_db_repo.locked[0]:
260 260 <li><a class="locking_del" href="${h.url('toggle_locking',repo_name=c.repo_name)}">${_('Unlock')}</a></li>
261 261 %else:
262 262 <li><a class="locking_add" href="${h.url('toggle_locking',repo_name=c.repo_name)}">${_('Lock')}</a></li>
263 263 %endif
264 264 %endif
265 265 %if c.rhodecode_user.username != h.DEFAULT_USER:
266 266 %if c.rhodecode_db_repo.repo_type in ['git','hg']:
267 267 <li><a href="${h.url('repo_fork_home',repo_name=c.repo_name)}">${_('Fork')}</a></li>
268 268 <li><a href="${h.url('pullrequest_home',repo_name=c.repo_name)}">${_('Create Pull Request')}</a></li>
269 269 %endif
270 270 %endif
271 271 </ul>
272 272 </li>
273 273 </ul>
274 274 </div>
275 275 <div class="clear"></div>
276 276 </div>
277 277 <!--- END CONTEXT BAR -->
278 278
279 279 </%def>
280 280
281 281 <%def name="usermenu(active=False)">
282 282 ## USER MENU
283 283 <li id="quick_login_li" class="${'active' if active else ''}">
284 284 <a id="quick_login_link" class="menulink childs">
285 285 ${gravatar(c.rhodecode_user.email, 20)}
286 286 <span class="user">
287 287 %if c.rhodecode_user.username != h.DEFAULT_USER:
288 288 <span class="menu_link_user">${c.rhodecode_user.username}</span><div class="show_more"></div>
289 289 %else:
290 290 <span>${_('Sign in')}</span>
291 291 %endif
292 292 </span>
293 293 </a>
294 294
295 295 <div class="user-menu submenu">
296 296 <div id="quick_login">
297 297 %if c.rhodecode_user.username == h.DEFAULT_USER:
298 298 <h4>${_('Sign in to your account')}</h4>
299 299 ${h.form(h.route_path('login', _query={'came_from': h.url.current()}), needs_csrf_token=False)}
300 300 <div class="form form-vertical">
301 301 <div class="fields">
302 302 <div class="field">
303 303 <div class="label">
304 304 <label for="username">${_('Username')}:</label>
305 305 </div>
306 306 <div class="input">
307 307 ${h.text('username',class_='focus',tabindex=1)}
308 308 </div>
309 309
310 310 </div>
311 311 <div class="field">
312 312 <div class="label">
313 313 <label for="password">${_('Password')}:</label>
314 314 %if h.HasPermissionAny('hg.password_reset.enabled')():
315 315 <span class="forgot_password">${h.link_to(_('(Forgot password?)'),h.route_path('reset_password'), class_='pwd_reset')}</span>
316 316 %endif
317 317 </div>
318 318 <div class="input">
319 319 ${h.password('password',class_='focus',tabindex=2)}
320 320 </div>
321 321 </div>
322 322 <div class="buttons">
323 323 <div class="register">
324 324 %if h.HasPermissionAny('hg.admin', 'hg.register.auto_activate', 'hg.register.manual_activate')():
325 325 ${h.link_to(_("Don't have an account ?"),h.route_path('register'))}
326 326 %endif
327 327 </div>
328 328 <div class="submit">
329 329 ${h.submit('sign_in',_('Sign In'),class_="btn btn-small",tabindex=3)}
330 330 </div>
331 331 </div>
332 332 </div>
333 333 </div>
334 334 ${h.end_form()}
335 335 %else:
336 336 <div class="">
337 337 <div class="big_gravatar">${gravatar(c.rhodecode_user.email, 48)}</div>
338 338 <div class="full_name">${c.rhodecode_user.full_name_or_username}</div>
339 339 <div class="email">${c.rhodecode_user.email}</div>
340 340 </div>
341 341 <div class="">
342 342 <ol class="links">
343 343 <li>${h.link_to(_(u'My account'),h.route_path('my_account_profile'))}</li>
344 344 % if c.rhodecode_user.personal_repo_group:
345 345 <li>${h.link_to(_(u'My personal group'), h.url('repo_group_home', group_name=c.rhodecode_user.personal_repo_group.group_name))}</li>
346 346 % endif
347 347 <li class="logout">
348 348 ${h.secure_form(h.route_path('logout'))}
349 349 ${h.submit('log_out', _(u'Sign Out'),class_="btn btn-primary")}
350 350 ${h.end_form()}
351 351 </li>
352 352 </ol>
353 353 </div>
354 354 %endif
355 355 </div>
356 356 </div>
357 357 %if c.rhodecode_user.username != h.DEFAULT_USER:
358 358 <div class="pill_container">
359 359 % if c.unread_notifications == 0:
360 360 <a class="menu_link_notifications empty" href="${h.url('notifications')}">${c.unread_notifications}</a>
361 361 % else:
362 362 <a class="menu_link_notifications" href="${h.url('notifications')}">${c.unread_notifications}</a>
363 363 % endif
364 364 </div>
365 365 % endif
366 366 </li>
367 367 </%def>
368 368
369 369 <%def name="menu_items(active=None)">
370 370 <%
371 371 def is_active(selected):
372 372 if selected == active:
373 373 return "active"
374 374 return ""
375 375 %>
376 376 <ul id="quick" class="main_nav navigation horizontal-list">
377 377 <!-- repo switcher -->
378 378 <li class="${is_active('repositories')} repo_switcher_li has_select2">
379 379 <input id="repo_switcher" name="repo_switcher" type="hidden">
380 380 </li>
381 381
382 382 ## ROOT MENU
383 383 %if c.rhodecode_user.username != h.DEFAULT_USER:
384 384 <li class="${is_active('journal')}">
385 385 <a class="menulink" title="${_('Show activity journal')}" href="${h.url('journal')}">
386 386 <div class="menulabel">${_('Journal')}</div>
387 387 </a>
388 388 </li>
389 389 %else:
390 390 <li class="${is_active('journal')}">
391 391 <a class="menulink" title="${_('Show Public activity journal')}" href="${h.url('public_journal')}">
392 392 <div class="menulabel">${_('Public journal')}</div>
393 393 </a>
394 394 </li>
395 395 %endif
396 396 <li class="${is_active('gists')}">
397 397 <a class="menulink childs" title="${_('Show Gists')}" href="${h.url('gists')}">
398 398 <div class="menulabel">${_('Gists')}</div>
399 399 </a>
400 400 </li>
401 401 <li class="${is_active('search')}">
402 402 <a class="menulink" title="${_('Search in repositories you have access to')}" href="${h.url('search')}">
403 403 <div class="menulabel">${_('Search')}</div>
404 404 </a>
405 405 </li>
406 406 % if h.HasPermissionAll('hg.admin')('access admin main page'):
407 407 <li class="${is_active('admin')}">
408 408 <a class="menulink childs" title="${_('Admin settings')}" href="#" onclick="return false;">
409 409 <div class="menulabel">${_('Admin')} <div class="show_more"></div></div>
410 410 </a>
411 411 ${admin_menu()}
412 412 </li>
413 413 % elif c.rhodecode_user.repositories_admin or c.rhodecode_user.repository_groups_admin or c.rhodecode_user.user_groups_admin:
414 414 <li class="${is_active('admin')}">
415 415 <a class="menulink childs" title="${_('Delegated Admin settings')}">
416 416 <div class="menulabel">${_('Admin')} <div class="show_more"></div></div>
417 417 </a>
418 418 ${admin_menu_simple(c.rhodecode_user.repositories_admin,
419 419 c.rhodecode_user.repository_groups_admin,
420 420 c.rhodecode_user.user_groups_admin or h.HasPermissionAny('hg.usergroup.create.true')())}
421 421 </li>
422 422 % endif
423 423 % if c.debug_style:
424 424 <li class="${is_active('debug_style')}">
425 425 <a class="menulink" title="${_('Style')}" href="${h.url('debug_style_home')}">
426 426 <div class="menulabel">${_('Style')}</div>
427 427 </a>
428 428 </li>
429 429 % endif
430 430 ## render extra user menu
431 431 ${usermenu(active=(active=='my_account'))}
432 432 </ul>
433 433
434 434 <script type="text/javascript">
435 435 var visual_show_public_icon = "${c.visual.show_public_icon}" == "True";
436 436
437 437 /*format the look of items in the list*/
438 438 var format = function(state, escapeMarkup){
439 439 if (!state.id){
440 440 return state.text; // optgroup
441 441 }
442 442 var obj_dict = state.obj;
443 443 var tmpl = '';
444 444
445 445 if(obj_dict && state.type == 'repo'){
446 446 if(obj_dict['repo_type'] === 'hg'){
447 447 tmpl += '<i class="icon-hg"></i> ';
448 448 }
449 449 else if(obj_dict['repo_type'] === 'git'){
450 450 tmpl += '<i class="icon-git"></i> ';
451 451 }
452 452 else if(obj_dict['repo_type'] === 'svn'){
453 453 tmpl += '<i class="icon-svn"></i> ';
454 454 }
455 455 if(obj_dict['private']){
456 456 tmpl += '<i class="icon-lock" ></i> ';
457 457 }
458 458 else if(visual_show_public_icon){
459 459 tmpl += '<i class="icon-unlock-alt"></i> ';
460 460 }
461 461 }
462 462 if(obj_dict && state.type == 'commit') {
463 463 tmpl += '<i class="icon-tag"></i>';
464 464 }
465 465 if(obj_dict && state.type == 'group'){
466 466 tmpl += '<i class="icon-folder-close"></i> ';
467 467 }
468 468 tmpl += escapeMarkup(state.text);
469 469 return tmpl;
470 470 };
471 471
472 472 var formatResult = function(result, container, query, escapeMarkup) {
473 473 return format(result, escapeMarkup);
474 474 };
475 475
476 476 var formatSelection = function(data, container, escapeMarkup) {
477 477 return format(data, escapeMarkup);
478 478 };
479 479
480 480 $("#repo_switcher").select2({
481 481 cachedDataSource: {},
482 482 minimumInputLength: 2,
483 483 placeholder: '<div class="menulabel">${_('Go to')} <div class="show_more"></div></div>',
484 484 dropdownAutoWidth: true,
485 485 formatResult: formatResult,
486 486 formatSelection: formatSelection,
487 487 containerCssClass: "repo-switcher",
488 488 dropdownCssClass: "repo-switcher-dropdown",
489 489 escapeMarkup: function(m){
490 490 // don't escape our custom placeholder
491 491 if(m.substr(0,23) == '<div class="menulabel">'){
492 492 return m;
493 493 }
494 494
495 495 return Select2.util.escapeMarkup(m);
496 496 },
497 497 query: $.debounce(250, function(query){
498 498 self = this;
499 499 var cacheKey = query.term;
500 500 var cachedData = self.cachedDataSource[cacheKey];
501 501
502 502 if (cachedData) {
503 503 query.callback({results: cachedData.results});
504 504 } else {
505 505 $.ajax({
506 url: "${h.url('goto_switcher_data')}",
506 url: pyroutes.url('goto_switcher_data'),
507 507 data: {'query': query.term},
508 508 dataType: 'json',
509 509 type: 'GET',
510 510 success: function(data) {
511 511 self.cachedDataSource[cacheKey] = data;
512 512 query.callback({results: data.results});
513 513 },
514 514 error: function(data, textStatus, errorThrown) {
515 515 alert("Error while fetching entries.\nError code {0} ({1}).".format(data.status, data.statusText));
516 516 }
517 517 })
518 518 }
519 519 })
520 520 });
521 521
522 522 $("#repo_switcher").on('select2-selecting', function(e){
523 523 e.preventDefault();
524 524 window.location = e.choice.url;
525 525 });
526 526
527 527 </script>
528 528 <script src="${h.asset('js/rhodecode/base/keyboard-bindings.js', ver=c.rhodecode_version_hash)}"></script>
529 529 </%def>
530 530
531 531 <div class="modal" id="help_kb" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
532 532 <div class="modal-dialog">
533 533 <div class="modal-content">
534 534 <div class="modal-header">
535 535 <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
536 536 <h4 class="modal-title" id="myModalLabel">${_('Keyboard shortcuts')}</h4>
537 537 </div>
538 538 <div class="modal-body">
539 539 <div class="block-left">
540 540 <table class="keyboard-mappings">
541 541 <tbody>
542 542 <tr>
543 543 <th></th>
544 544 <th>${_('Site-wide shortcuts')}</th>
545 545 </tr>
546 546 <%
547 547 elems = [
548 548 ('/', 'Open quick search box'),
549 549 ('g h', 'Goto home page'),
550 550 ('g g', 'Goto my private gists page'),
551 551 ('g G', 'Goto my public gists page'),
552 552 ('n r', 'New repository page'),
553 553 ('n g', 'New gist page'),
554 554 ]
555 555 %>
556 556 %for key, desc in elems:
557 557 <tr>
558 558 <td class="keys">
559 559 <span class="key tag">${key}</span>
560 560 </td>
561 561 <td>${desc}</td>
562 562 </tr>
563 563 %endfor
564 564 </tbody>
565 565 </table>
566 566 </div>
567 567 <div class="block-left">
568 568 <table class="keyboard-mappings">
569 569 <tbody>
570 570 <tr>
571 571 <th></th>
572 572 <th>${_('Repositories')}</th>
573 573 </tr>
574 574 <%
575 575 elems = [
576 576 ('g s', 'Goto summary page'),
577 577 ('g c', 'Goto changelog page'),
578 578 ('g f', 'Goto files page'),
579 579 ('g F', 'Goto files page with file search activated'),
580 580 ('g p', 'Goto pull requests page'),
581 581 ('g o', 'Goto repository settings'),
582 582 ('g O', 'Goto repository permissions settings'),
583 583 ]
584 584 %>
585 585 %for key, desc in elems:
586 586 <tr>
587 587 <td class="keys">
588 588 <span class="key tag">${key}</span>
589 589 </td>
590 590 <td>${desc}</td>
591 591 </tr>
592 592 %endfor
593 593 </tbody>
594 594 </table>
595 595 </div>
596 596 </div>
597 597 <div class="modal-footer">
598 598 </div>
599 599 </div><!-- /.modal-content -->
600 600 </div><!-- /.modal-dialog -->
601 601 </div><!-- /.modal -->
@@ -1,257 +1,129 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 import json
22 21
23 from mock import patch
24 22 import pytest
25 23 from pylons import tmpl_context as c
26 24
27 25 import rhodecode
28 from rhodecode.lib.utils import map_groups
29 from rhodecode.model.db import Repository, User, RepoGroup
26 from rhodecode.model.db import Repository, User
30 27 from rhodecode.model.meta import Session
31 28 from rhodecode.model.repo import RepoModel
32 29 from rhodecode.model.repo_group import RepoGroupModel
33 30 from rhodecode.model.settings import SettingsModel
34 from rhodecode.tests import TestController, url, TEST_USER_ADMIN_LOGIN
31 from rhodecode.tests import TestController, url
35 32 from rhodecode.tests.fixture import Fixture
36 33
37 34
38 35 fixture = Fixture()
39 36
40 37
41 38 class TestHomeController(TestController):
42 39
43 40 def test_index(self):
44 41 self.log_user()
45 42 response = self.app.get(url(controller='home', action='index'))
46 43 # if global permission is set
47 44 response.mustcontain('Add Repository')
48 45
49 46 # search for objects inside the JavaScript JSON
50 47 for repo in Repository.getAll():
51 48 response.mustcontain('"name_raw": "%s"' % repo.repo_name)
52 49
53 50 def test_index_contains_statics_with_ver(self):
54 51 self.log_user()
55 52 response = self.app.get(url(controller='home', action='index'))
56 53
57 54 rhodecode_version_hash = c.rhodecode_version_hash
58 55 response.mustcontain('style.css?ver={0}'.format(rhodecode_version_hash))
59 56 response.mustcontain('rhodecode-components.js?ver={0}'.format(rhodecode_version_hash))
60 57
61 58 def test_index_contains_backend_specific_details(self, backend):
62 59 self.log_user()
63 60 response = self.app.get(url(controller='home', action='index'))
64 61 tip = backend.repo.get_commit().raw_id
65 62
66 63 # html in javascript variable:
67 64 response.mustcontain(r'<i class=\"icon-%s\"' % (backend.alias, ))
68 65 response.mustcontain(r'href=\"/%s\"' % (backend.repo_name, ))
69 66
70 67 response.mustcontain("""/%s/changeset/%s""" % (backend.repo_name, tip))
71 68 response.mustcontain("""Added a symlink""")
72 69
73 70 def test_index_with_anonymous_access_disabled(self):
74 71 with fixture.anon_access(False):
75 72 response = self.app.get(url(controller='home', action='index'),
76 73 status=302)
77 74 assert 'login' in response.location
78 75
79 76 def test_index_page_on_groups(self, autologin_user, repo_group):
80 77 response = self.app.get(url('repo_group_home', group_name='gr1'))
81 78 response.mustcontain("gr1/repo_in_group")
82 79
83 80 def test_index_page_on_group_with_trailing_slash(
84 81 self, autologin_user, repo_group):
85 82 response = self.app.get(url('repo_group_home', group_name='gr1') + '/')
86 83 response.mustcontain("gr1/repo_in_group")
87 84
88 85 @pytest.fixture(scope='class')
89 86 def repo_group(self, request):
90 87 gr = fixture.create_repo_group('gr1')
91 88 fixture.create_repo(name='gr1/repo_in_group', repo_group=gr)
92 89
93 90 @request.addfinalizer
94 91 def cleanup():
95 92 RepoModel().delete('gr1/repo_in_group')
96 93 RepoGroupModel().delete(repo_group='gr1', force_delete=True)
97 94 Session().commit()
98 95
99 96 def test_index_with_name_with_tags(self, autologin_user):
100 97 user = User.get_by_username('test_admin')
101 98 user.name = '<img src="/image1" onload="alert(\'Hello, World!\');">'
102 99 user.lastname = (
103 100 '<img src="/image2" onload="alert(\'Hello, World!\');">')
104 101 Session().add(user)
105 102 Session().commit()
106 103
107 104 response = self.app.get(url(controller='home', action='index'))
108 105 response.mustcontain(
109 106 '&lt;img src=&#34;/image1&#34; onload=&#34;'
110 107 'alert(&#39;Hello, World!&#39;);&#34;&gt;')
111 108 response.mustcontain(
112 109 '&lt;img src=&#34;/image2&#34; onload=&#34;'
113 110 'alert(&#39;Hello, World!&#39;);&#34;&gt;')
114 111
115 112 @pytest.mark.parametrize("name, state", [
116 113 ('Disabled', False),
117 114 ('Enabled', True),
118 115 ])
119 116 def test_index_show_version(self, autologin_user, name, state):
120 117 version_string = 'RhodeCode Enterprise %s' % rhodecode.__version__
121 118
122 119 sett = SettingsModel().create_or_update_setting(
123 120 'show_version', state, 'bool')
124 121 Session().add(sett)
125 122 Session().commit()
126 123 SettingsModel().invalidate_settings_cache()
127 124
128 125 response = self.app.get(url(controller='home', action='index'))
129 126 if state is True:
130 127 response.mustcontain(version_string)
131 128 if state is False:
132 129 response.mustcontain(no=[version_string])
133
134
135 def assert_and_get_content(result):
136 repos = []
137 groups = []
138 commits = []
139 for data in result:
140 for data_item in data['children']:
141 assert data_item['id']
142 assert data_item['text']
143 assert data_item['url']
144 if data_item['type'] == 'repo':
145 repos.append(data_item)
146 elif data_item['type'] == 'group':
147 groups.append(data_item)
148 elif data_item['type'] == 'commit':
149 commits.append(data_item)
150 else:
151 raise Exception('invalid type %s' % data_item['type'])
152
153 return repos, groups, commits
154
155
156 class TestGotoSwitcherData(TestController):
157 required_repos_with_groups = [
158 'abc',
159 'abc-fork',
160 'forks/abcd',
161 'abcd',
162 'abcde',
163 'a/abc',
164 'aa/abc',
165 'aaa/abc',
166 'aaaa/abc',
167 'repos_abc/aaa/abc',
168 'abc_repos/abc',
169 'abc_repos/abcd',
170 'xxx/xyz',
171 'forked-abc/a/abc'
172 ]
173
174 @pytest.fixture(autouse=True, scope='class')
175 def prepare(self, request, pylonsapp):
176 for repo_and_group in self.required_repos_with_groups:
177 # create structure of groups and return the last group
178
179 repo_group = map_groups(repo_and_group)
180
181 RepoModel()._create_repo(
182 repo_and_group, 'hg', 'test-ac', TEST_USER_ADMIN_LOGIN,
183 repo_group=getattr(repo_group, 'group_id', None))
184
185 Session().commit()
186
187 request.addfinalizer(self.cleanup)
188
189 def cleanup(self):
190 # first delete all repos
191 for repo_and_groups in self.required_repos_with_groups:
192 repo = Repository.get_by_repo_name(repo_and_groups)
193 if repo:
194 RepoModel().delete(repo)
195 Session().commit()
196
197 # then delete all empty groups
198 for repo_and_groups in self.required_repos_with_groups:
199 if '/' in repo_and_groups:
200 r_group = repo_and_groups.rsplit('/', 1)[0]
201 repo_group = RepoGroup.get_by_group_name(r_group)
202 if not repo_group:
203 continue
204 parents = repo_group.parents
205 RepoGroupModel().delete(repo_group, force_delete=True)
206 Session().commit()
207
208 for el in reversed(parents):
209 RepoGroupModel().delete(el, force_delete=True)
210 Session().commit()
211
212 def test_returns_list_of_repos_and_groups(self):
213 self.log_user()
214
215 response = self.app.get(
216 url(controller='home', action='goto_switcher_data'),
217 headers={'X-REQUESTED-WITH': 'XMLHttpRequest', }, status=200)
218 result = json.loads(response.body)['results']
219
220 repos, groups, commits = assert_and_get_content(result)
221
222 assert len(repos) == len(Repository.get_all())
223 assert len(groups) == len(RepoGroup.get_all())
224 assert len(commits) == 0
225
226 def test_returns_list_of_repos_and_groups_filtered(self):
227 self.log_user()
228
229 response = self.app.get(
230 url(controller='home', action='goto_switcher_data'),
231 headers={'X-REQUESTED-WITH': 'XMLHttpRequest', },
232 params={'query': 'abc'}, status=200)
233 result = json.loads(response.body)['results']
234
235 repos, groups, commits = assert_and_get_content(result)
236
237 assert len(repos) == 13
238 assert len(groups) == 5
239 assert len(commits) == 0
240
241 def test_returns_list_of_properly_sorted_and_filtered(self):
242 self.log_user()
243
244 response = self.app.get(
245 url(controller='home', action='goto_switcher_data'),
246 headers={'X-REQUESTED-WITH': 'XMLHttpRequest', },
247 params={'query': 'abc'}, status=200)
248 result = json.loads(response.body)['results']
249
250 repos, groups, commits = assert_and_get_content(result)
251
252 test_repos = [x['text'] for x in repos[:4]]
253 assert ['abc', 'abcd', 'a/abc', 'abcde'] == test_repos
254
255 test_groups = [x['text'] for x in groups[:4]]
256 assert ['abc_repos', 'repos_abc',
257 'forked-abc', 'forked-abc/a'] == test_groups
General Comments 0
You need to be logged in to leave comments. Login now