##// END OF EJS Templates
core: handle edge case requesting matched routes but with hg/svn/git or api context....
marcink -
r3036:bfb27900 stable
parent child Browse files
Show More
@@ -1,645 +1,652 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2018 RhodeCode GmbH
3 # Copyright (C) 2016-2018 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import time
21 import time
22 import logging
22 import logging
23 import operator
23 import operator
24
24
25 from pyramid.httpexceptions import HTTPFound, HTTPForbidden
25 from pyramid.httpexceptions import HTTPFound, HTTPForbidden, HTTPBadRequest
26
26
27 from rhodecode.lib import helpers as h, diffs
27 from rhodecode.lib import helpers as h, diffs
28 from rhodecode.lib.utils2 import StrictAttributeDict, safe_int, datetime_to_time
28 from rhodecode.lib.utils2 import StrictAttributeDict, safe_int, datetime_to_time
29 from rhodecode.lib.vcs.exceptions import RepositoryRequirementError
29 from rhodecode.lib.vcs.exceptions import RepositoryRequirementError
30 from rhodecode.model import repo
30 from rhodecode.model import repo
31 from rhodecode.model import repo_group
31 from rhodecode.model import repo_group
32 from rhodecode.model import user_group
32 from rhodecode.model import user_group
33 from rhodecode.model import user
33 from rhodecode.model import user
34 from rhodecode.model.db import User
34 from rhodecode.model.db import User
35 from rhodecode.model.scm import ScmModel
35 from rhodecode.model.scm import ScmModel
36 from rhodecode.model.settings import VcsSettingsModel
36 from rhodecode.model.settings import VcsSettingsModel
37
37
38 log = logging.getLogger(__name__)
38 log = logging.getLogger(__name__)
39
39
40
40
41 ADMIN_PREFIX = '/_admin'
41 ADMIN_PREFIX = '/_admin'
42 STATIC_FILE_PREFIX = '/_static'
42 STATIC_FILE_PREFIX = '/_static'
43
43
44 URL_NAME_REQUIREMENTS = {
44 URL_NAME_REQUIREMENTS = {
45 # group name can have a slash in them, but they must not end with a slash
45 # group name can have a slash in them, but they must not end with a slash
46 'group_name': r'.*?[^/]',
46 'group_name': r'.*?[^/]',
47 'repo_group_name': r'.*?[^/]',
47 'repo_group_name': r'.*?[^/]',
48 # repo names can have a slash in them, but they must not end with a slash
48 # repo names can have a slash in them, but they must not end with a slash
49 'repo_name': r'.*?[^/]',
49 'repo_name': r'.*?[^/]',
50 # file path eats up everything at the end
50 # file path eats up everything at the end
51 'f_path': r'.*',
51 'f_path': r'.*',
52 # reference types
52 # reference types
53 'source_ref_type': '(branch|book|tag|rev|\%\(source_ref_type\)s)',
53 'source_ref_type': '(branch|book|tag|rev|\%\(source_ref_type\)s)',
54 'target_ref_type': '(branch|book|tag|rev|\%\(target_ref_type\)s)',
54 'target_ref_type': '(branch|book|tag|rev|\%\(target_ref_type\)s)',
55 }
55 }
56
56
57
57
58 def add_route_with_slash(config,name, pattern, **kw):
58 def add_route_with_slash(config,name, pattern, **kw):
59 config.add_route(name, pattern, **kw)
59 config.add_route(name, pattern, **kw)
60 if not pattern.endswith('/'):
60 if not pattern.endswith('/'):
61 config.add_route(name + '_slash', pattern + '/', **kw)
61 config.add_route(name + '_slash', pattern + '/', **kw)
62
62
63
63
64 def add_route_requirements(route_path, requirements=URL_NAME_REQUIREMENTS):
64 def add_route_requirements(route_path, requirements=URL_NAME_REQUIREMENTS):
65 """
65 """
66 Adds regex requirements to pyramid routes using a mapping dict
66 Adds regex requirements to pyramid routes using a mapping dict
67 e.g::
67 e.g::
68 add_route_requirements('{repo_name}/settings')
68 add_route_requirements('{repo_name}/settings')
69 """
69 """
70 for key, regex in requirements.items():
70 for key, regex in requirements.items():
71 route_path = route_path.replace('{%s}' % key, '{%s:%s}' % (key, regex))
71 route_path = route_path.replace('{%s}' % key, '{%s:%s}' % (key, regex))
72 return route_path
72 return route_path
73
73
74
74
75 def get_format_ref_id(repo):
75 def get_format_ref_id(repo):
76 """Returns a `repo` specific reference formatter function"""
76 """Returns a `repo` specific reference formatter function"""
77 if h.is_svn(repo):
77 if h.is_svn(repo):
78 return _format_ref_id_svn
78 return _format_ref_id_svn
79 else:
79 else:
80 return _format_ref_id
80 return _format_ref_id
81
81
82
82
83 def _format_ref_id(name, raw_id):
83 def _format_ref_id(name, raw_id):
84 """Default formatting of a given reference `name`"""
84 """Default formatting of a given reference `name`"""
85 return name
85 return name
86
86
87
87
88 def _format_ref_id_svn(name, raw_id):
88 def _format_ref_id_svn(name, raw_id):
89 """Special way of formatting a reference for Subversion including path"""
89 """Special way of formatting a reference for Subversion including path"""
90 return '%s@%s' % (name, raw_id)
90 return '%s@%s' % (name, raw_id)
91
91
92
92
93 class TemplateArgs(StrictAttributeDict):
93 class TemplateArgs(StrictAttributeDict):
94 pass
94 pass
95
95
96
96
97 class BaseAppView(object):
97 class BaseAppView(object):
98
98
99 def __init__(self, context, request):
99 def __init__(self, context, request):
100 self.request = request
100 self.request = request
101 self.context = context
101 self.context = context
102 self.session = request.session
102 self.session = request.session
103 if not hasattr(request, 'user'):
104 # NOTE(marcink): edge case, we ended up in matched route
105 # but probably of web-app context, e.g API CALL/VCS CALL
106 if hasattr(request, 'vcs_call') or hasattr(request, 'rpc_method'):
107 log.warning('Unable to process request `%s` in this scope', request)
108 raise HTTPBadRequest()
109
103 self._rhodecode_user = request.user # auth user
110 self._rhodecode_user = request.user # auth user
104 self._rhodecode_db_user = self._rhodecode_user.get_instance()
111 self._rhodecode_db_user = self._rhodecode_user.get_instance()
105 self._maybe_needs_password_change(
112 self._maybe_needs_password_change(
106 request.matched_route.name, self._rhodecode_db_user)
113 request.matched_route.name, self._rhodecode_db_user)
107
114
108 def _maybe_needs_password_change(self, view_name, user_obj):
115 def _maybe_needs_password_change(self, view_name, user_obj):
109 log.debug('Checking if user %s needs password change on view %s',
116 log.debug('Checking if user %s needs password change on view %s',
110 user_obj, view_name)
117 user_obj, view_name)
111 skip_user_views = [
118 skip_user_views = [
112 'logout', 'login',
119 'logout', 'login',
113 'my_account_password', 'my_account_password_update'
120 'my_account_password', 'my_account_password_update'
114 ]
121 ]
115
122
116 if not user_obj:
123 if not user_obj:
117 return
124 return
118
125
119 if user_obj.username == User.DEFAULT_USER:
126 if user_obj.username == User.DEFAULT_USER:
120 return
127 return
121
128
122 now = time.time()
129 now = time.time()
123 should_change = user_obj.user_data.get('force_password_change')
130 should_change = user_obj.user_data.get('force_password_change')
124 change_after = safe_int(should_change) or 0
131 change_after = safe_int(should_change) or 0
125 if should_change and now > change_after:
132 if should_change and now > change_after:
126 log.debug('User %s requires password change', user_obj)
133 log.debug('User %s requires password change', user_obj)
127 h.flash('You are required to change your password', 'warning',
134 h.flash('You are required to change your password', 'warning',
128 ignore_duplicate=True)
135 ignore_duplicate=True)
129
136
130 if view_name not in skip_user_views:
137 if view_name not in skip_user_views:
131 raise HTTPFound(
138 raise HTTPFound(
132 self.request.route_path('my_account_password'))
139 self.request.route_path('my_account_password'))
133
140
134 def _log_creation_exception(self, e, repo_name):
141 def _log_creation_exception(self, e, repo_name):
135 _ = self.request.translate
142 _ = self.request.translate
136 reason = None
143 reason = None
137 if len(e.args) == 2:
144 if len(e.args) == 2:
138 reason = e.args[1]
145 reason = e.args[1]
139
146
140 if reason == 'INVALID_CERTIFICATE':
147 if reason == 'INVALID_CERTIFICATE':
141 log.exception(
148 log.exception(
142 'Exception creating a repository: invalid certificate')
149 'Exception creating a repository: invalid certificate')
143 msg = (_('Error creating repository %s: invalid certificate')
150 msg = (_('Error creating repository %s: invalid certificate')
144 % repo_name)
151 % repo_name)
145 else:
152 else:
146 log.exception("Exception creating a repository")
153 log.exception("Exception creating a repository")
147 msg = (_('Error creating repository %s')
154 msg = (_('Error creating repository %s')
148 % repo_name)
155 % repo_name)
149 return msg
156 return msg
150
157
151 def _get_local_tmpl_context(self, include_app_defaults=True):
158 def _get_local_tmpl_context(self, include_app_defaults=True):
152 c = TemplateArgs()
159 c = TemplateArgs()
153 c.auth_user = self.request.user
160 c.auth_user = self.request.user
154 # TODO(marcink): migrate the usage of c.rhodecode_user to c.auth_user
161 # TODO(marcink): migrate the usage of c.rhodecode_user to c.auth_user
155 c.rhodecode_user = self.request.user
162 c.rhodecode_user = self.request.user
156
163
157 if include_app_defaults:
164 if include_app_defaults:
158 from rhodecode.lib.base import attach_context_attributes
165 from rhodecode.lib.base import attach_context_attributes
159 attach_context_attributes(c, self.request, self.request.user.user_id)
166 attach_context_attributes(c, self.request, self.request.user.user_id)
160
167
161 return c
168 return c
162
169
163 def _get_template_context(self, tmpl_args, **kwargs):
170 def _get_template_context(self, tmpl_args, **kwargs):
164
171
165 local_tmpl_args = {
172 local_tmpl_args = {
166 'defaults': {},
173 'defaults': {},
167 'errors': {},
174 'errors': {},
168 'c': tmpl_args
175 'c': tmpl_args
169 }
176 }
170 local_tmpl_args.update(kwargs)
177 local_tmpl_args.update(kwargs)
171 return local_tmpl_args
178 return local_tmpl_args
172
179
173 def load_default_context(self):
180 def load_default_context(self):
174 """
181 """
175 example:
182 example:
176
183
177 def load_default_context(self):
184 def load_default_context(self):
178 c = self._get_local_tmpl_context()
185 c = self._get_local_tmpl_context()
179 c.custom_var = 'foobar'
186 c.custom_var = 'foobar'
180
187
181 return c
188 return c
182 """
189 """
183 raise NotImplementedError('Needs implementation in view class')
190 raise NotImplementedError('Needs implementation in view class')
184
191
185
192
186 class RepoAppView(BaseAppView):
193 class RepoAppView(BaseAppView):
187
194
188 def __init__(self, context, request):
195 def __init__(self, context, request):
189 super(RepoAppView, self).__init__(context, request)
196 super(RepoAppView, self).__init__(context, request)
190 self.db_repo = request.db_repo
197 self.db_repo = request.db_repo
191 self.db_repo_name = self.db_repo.repo_name
198 self.db_repo_name = self.db_repo.repo_name
192 self.db_repo_pull_requests = ScmModel().get_pull_requests(self.db_repo)
199 self.db_repo_pull_requests = ScmModel().get_pull_requests(self.db_repo)
193
200
194 def _handle_missing_requirements(self, error):
201 def _handle_missing_requirements(self, error):
195 log.error(
202 log.error(
196 'Requirements are missing for repository %s: %s',
203 'Requirements are missing for repository %s: %s',
197 self.db_repo_name, error.message)
204 self.db_repo_name, error.message)
198
205
199 def _get_local_tmpl_context(self, include_app_defaults=True):
206 def _get_local_tmpl_context(self, include_app_defaults=True):
200 _ = self.request.translate
207 _ = self.request.translate
201 c = super(RepoAppView, self)._get_local_tmpl_context(
208 c = super(RepoAppView, self)._get_local_tmpl_context(
202 include_app_defaults=include_app_defaults)
209 include_app_defaults=include_app_defaults)
203
210
204 # register common vars for this type of view
211 # register common vars for this type of view
205 c.rhodecode_db_repo = self.db_repo
212 c.rhodecode_db_repo = self.db_repo
206 c.repo_name = self.db_repo_name
213 c.repo_name = self.db_repo_name
207 c.repository_pull_requests = self.db_repo_pull_requests
214 c.repository_pull_requests = self.db_repo_pull_requests
208 self.path_filter = PathFilter(None)
215 self.path_filter = PathFilter(None)
209
216
210 c.repository_requirements_missing = {}
217 c.repository_requirements_missing = {}
211 try:
218 try:
212 self.rhodecode_vcs_repo = self.db_repo.scm_instance()
219 self.rhodecode_vcs_repo = self.db_repo.scm_instance()
213 if self.rhodecode_vcs_repo:
220 if self.rhodecode_vcs_repo:
214 path_perms = self.rhodecode_vcs_repo.get_path_permissions(
221 path_perms = self.rhodecode_vcs_repo.get_path_permissions(
215 c.auth_user.username)
222 c.auth_user.username)
216 self.path_filter = PathFilter(path_perms)
223 self.path_filter = PathFilter(path_perms)
217 except RepositoryRequirementError as e:
224 except RepositoryRequirementError as e:
218 c.repository_requirements_missing = {'error': str(e)}
225 c.repository_requirements_missing = {'error': str(e)}
219 self._handle_missing_requirements(e)
226 self._handle_missing_requirements(e)
220 self.rhodecode_vcs_repo = None
227 self.rhodecode_vcs_repo = None
221
228
222 c.path_filter = self.path_filter # used by atom_feed_entry.mako
229 c.path_filter = self.path_filter # used by atom_feed_entry.mako
223
230
224 if self.rhodecode_vcs_repo is None:
231 if self.rhodecode_vcs_repo is None:
225 # unable to fetch this repo as vcs instance, report back to user
232 # unable to fetch this repo as vcs instance, report back to user
226 h.flash(_(
233 h.flash(_(
227 "The repository `%(repo_name)s` cannot be loaded in filesystem. "
234 "The repository `%(repo_name)s` cannot be loaded in filesystem. "
228 "Please check if it exist, or is not damaged.") %
235 "Please check if it exist, or is not damaged.") %
229 {'repo_name': c.repo_name},
236 {'repo_name': c.repo_name},
230 category='error', ignore_duplicate=True)
237 category='error', ignore_duplicate=True)
231 if c.repository_requirements_missing:
238 if c.repository_requirements_missing:
232 route = self.request.matched_route.name
239 route = self.request.matched_route.name
233 if route.startswith(('edit_repo', 'repo_summary')):
240 if route.startswith(('edit_repo', 'repo_summary')):
234 # allow summary and edit repo on missing requirements
241 # allow summary and edit repo on missing requirements
235 return c
242 return c
236
243
237 raise HTTPFound(
244 raise HTTPFound(
238 h.route_path('repo_summary', repo_name=self.db_repo_name))
245 h.route_path('repo_summary', repo_name=self.db_repo_name))
239
246
240 else: # redirect if we don't show missing requirements
247 else: # redirect if we don't show missing requirements
241 raise HTTPFound(h.route_path('home'))
248 raise HTTPFound(h.route_path('home'))
242
249
243 return c
250 return c
244
251
245 def _get_f_path_unchecked(self, matchdict, default=None):
252 def _get_f_path_unchecked(self, matchdict, default=None):
246 """
253 """
247 Should only be used by redirects, everything else should call _get_f_path
254 Should only be used by redirects, everything else should call _get_f_path
248 """
255 """
249 f_path = matchdict.get('f_path')
256 f_path = matchdict.get('f_path')
250 if f_path:
257 if f_path:
251 # fix for multiple initial slashes that causes errors for GIT
258 # fix for multiple initial slashes that causes errors for GIT
252 return f_path.lstrip('/')
259 return f_path.lstrip('/')
253
260
254 return default
261 return default
255
262
256 def _get_f_path(self, matchdict, default=None):
263 def _get_f_path(self, matchdict, default=None):
257 f_path_match = self._get_f_path_unchecked(matchdict, default)
264 f_path_match = self._get_f_path_unchecked(matchdict, default)
258 return self.path_filter.assert_path_permissions(f_path_match)
265 return self.path_filter.assert_path_permissions(f_path_match)
259
266
260 def _get_general_setting(self, target_repo, settings_key, default=False):
267 def _get_general_setting(self, target_repo, settings_key, default=False):
261 settings_model = VcsSettingsModel(repo=target_repo)
268 settings_model = VcsSettingsModel(repo=target_repo)
262 settings = settings_model.get_general_settings()
269 settings = settings_model.get_general_settings()
263 return settings.get(settings_key, default)
270 return settings.get(settings_key, default)
264
271
265
272
266 class PathFilter(object):
273 class PathFilter(object):
267
274
268 # Expects and instance of BasePathPermissionChecker or None
275 # Expects and instance of BasePathPermissionChecker or None
269 def __init__(self, permission_checker):
276 def __init__(self, permission_checker):
270 self.permission_checker = permission_checker
277 self.permission_checker = permission_checker
271
278
272 def assert_path_permissions(self, path):
279 def assert_path_permissions(self, path):
273 if path and self.permission_checker and not self.permission_checker.has_access(path):
280 if path and self.permission_checker and not self.permission_checker.has_access(path):
274 raise HTTPForbidden()
281 raise HTTPForbidden()
275 return path
282 return path
276
283
277 def filter_patchset(self, patchset):
284 def filter_patchset(self, patchset):
278 if not self.permission_checker or not patchset:
285 if not self.permission_checker or not patchset:
279 return patchset, False
286 return patchset, False
280 had_filtered = False
287 had_filtered = False
281 filtered_patchset = []
288 filtered_patchset = []
282 for patch in patchset:
289 for patch in patchset:
283 filename = patch.get('filename', None)
290 filename = patch.get('filename', None)
284 if not filename or self.permission_checker.has_access(filename):
291 if not filename or self.permission_checker.has_access(filename):
285 filtered_patchset.append(patch)
292 filtered_patchset.append(patch)
286 else:
293 else:
287 had_filtered = True
294 had_filtered = True
288 if had_filtered:
295 if had_filtered:
289 if isinstance(patchset, diffs.LimitedDiffContainer):
296 if isinstance(patchset, diffs.LimitedDiffContainer):
290 filtered_patchset = diffs.LimitedDiffContainer(patchset.diff_limit, patchset.cur_diff_size, filtered_patchset)
297 filtered_patchset = diffs.LimitedDiffContainer(patchset.diff_limit, patchset.cur_diff_size, filtered_patchset)
291 return filtered_patchset, True
298 return filtered_patchset, True
292 else:
299 else:
293 return patchset, False
300 return patchset, False
294
301
295 def render_patchset_filtered(self, diffset, patchset, source_ref=None, target_ref=None):
302 def render_patchset_filtered(self, diffset, patchset, source_ref=None, target_ref=None):
296 filtered_patchset, has_hidden_changes = self.filter_patchset(patchset)
303 filtered_patchset, has_hidden_changes = self.filter_patchset(patchset)
297 result = diffset.render_patchset(filtered_patchset, source_ref=source_ref, target_ref=target_ref)
304 result = diffset.render_patchset(filtered_patchset, source_ref=source_ref, target_ref=target_ref)
298 result.has_hidden_changes = has_hidden_changes
305 result.has_hidden_changes = has_hidden_changes
299 return result
306 return result
300
307
301 def get_raw_patch(self, diff_processor):
308 def get_raw_patch(self, diff_processor):
302 if self.permission_checker is None:
309 if self.permission_checker is None:
303 return diff_processor.as_raw()
310 return diff_processor.as_raw()
304 elif self.permission_checker.has_full_access:
311 elif self.permission_checker.has_full_access:
305 return diff_processor.as_raw()
312 return diff_processor.as_raw()
306 else:
313 else:
307 return '# Repository has user-specific filters, raw patch generation is disabled.'
314 return '# Repository has user-specific filters, raw patch generation is disabled.'
308
315
309 @property
316 @property
310 def is_enabled(self):
317 def is_enabled(self):
311 return self.permission_checker is not None
318 return self.permission_checker is not None
312
319
313
320
314 class RepoGroupAppView(BaseAppView):
321 class RepoGroupAppView(BaseAppView):
315 def __init__(self, context, request):
322 def __init__(self, context, request):
316 super(RepoGroupAppView, self).__init__(context, request)
323 super(RepoGroupAppView, self).__init__(context, request)
317 self.db_repo_group = request.db_repo_group
324 self.db_repo_group = request.db_repo_group
318 self.db_repo_group_name = self.db_repo_group.group_name
325 self.db_repo_group_name = self.db_repo_group.group_name
319
326
320 def _revoke_perms_on_yourself(self, form_result):
327 def _revoke_perms_on_yourself(self, form_result):
321 _updates = filter(lambda u: self._rhodecode_user.user_id == int(u[0]),
328 _updates = filter(lambda u: self._rhodecode_user.user_id == int(u[0]),
322 form_result['perm_updates'])
329 form_result['perm_updates'])
323 _additions = filter(lambda u: self._rhodecode_user.user_id == int(u[0]),
330 _additions = filter(lambda u: self._rhodecode_user.user_id == int(u[0]),
324 form_result['perm_additions'])
331 form_result['perm_additions'])
325 _deletions = filter(lambda u: self._rhodecode_user.user_id == int(u[0]),
332 _deletions = filter(lambda u: self._rhodecode_user.user_id == int(u[0]),
326 form_result['perm_deletions'])
333 form_result['perm_deletions'])
327 admin_perm = 'group.admin'
334 admin_perm = 'group.admin'
328 if _updates and _updates[0][1] != admin_perm or \
335 if _updates and _updates[0][1] != admin_perm or \
329 _additions and _additions[0][1] != admin_perm or \
336 _additions and _additions[0][1] != admin_perm or \
330 _deletions and _deletions[0][1] != admin_perm:
337 _deletions and _deletions[0][1] != admin_perm:
331 return True
338 return True
332 return False
339 return False
333
340
334
341
335 class UserGroupAppView(BaseAppView):
342 class UserGroupAppView(BaseAppView):
336 def __init__(self, context, request):
343 def __init__(self, context, request):
337 super(UserGroupAppView, self).__init__(context, request)
344 super(UserGroupAppView, self).__init__(context, request)
338 self.db_user_group = request.db_user_group
345 self.db_user_group = request.db_user_group
339 self.db_user_group_name = self.db_user_group.users_group_name
346 self.db_user_group_name = self.db_user_group.users_group_name
340
347
341
348
342 class UserAppView(BaseAppView):
349 class UserAppView(BaseAppView):
343 def __init__(self, context, request):
350 def __init__(self, context, request):
344 super(UserAppView, self).__init__(context, request)
351 super(UserAppView, self).__init__(context, request)
345 self.db_user = request.db_user
352 self.db_user = request.db_user
346 self.db_user_id = self.db_user.user_id
353 self.db_user_id = self.db_user.user_id
347
354
348 _ = self.request.translate
355 _ = self.request.translate
349 if not request.db_user_supports_default:
356 if not request.db_user_supports_default:
350 if self.db_user.username == User.DEFAULT_USER:
357 if self.db_user.username == User.DEFAULT_USER:
351 h.flash(_("Editing user `{}` is disabled.".format(
358 h.flash(_("Editing user `{}` is disabled.".format(
352 User.DEFAULT_USER)), category='warning')
359 User.DEFAULT_USER)), category='warning')
353 raise HTTPFound(h.route_path('users'))
360 raise HTTPFound(h.route_path('users'))
354
361
355
362
356 class DataGridAppView(object):
363 class DataGridAppView(object):
357 """
364 """
358 Common class to have re-usable grid rendering components
365 Common class to have re-usable grid rendering components
359 """
366 """
360
367
361 def _extract_ordering(self, request, column_map=None):
368 def _extract_ordering(self, request, column_map=None):
362 column_map = column_map or {}
369 column_map = column_map or {}
363 column_index = safe_int(request.GET.get('order[0][column]'))
370 column_index = safe_int(request.GET.get('order[0][column]'))
364 order_dir = request.GET.get(
371 order_dir = request.GET.get(
365 'order[0][dir]', 'desc')
372 'order[0][dir]', 'desc')
366 order_by = request.GET.get(
373 order_by = request.GET.get(
367 'columns[%s][data][sort]' % column_index, 'name_raw')
374 'columns[%s][data][sort]' % column_index, 'name_raw')
368
375
369 # translate datatable to DB columns
376 # translate datatable to DB columns
370 order_by = column_map.get(order_by) or order_by
377 order_by = column_map.get(order_by) or order_by
371
378
372 search_q = request.GET.get('search[value]')
379 search_q = request.GET.get('search[value]')
373 return search_q, order_by, order_dir
380 return search_q, order_by, order_dir
374
381
375 def _extract_chunk(self, request):
382 def _extract_chunk(self, request):
376 start = safe_int(request.GET.get('start'), 0)
383 start = safe_int(request.GET.get('start'), 0)
377 length = safe_int(request.GET.get('length'), 25)
384 length = safe_int(request.GET.get('length'), 25)
378 draw = safe_int(request.GET.get('draw'))
385 draw = safe_int(request.GET.get('draw'))
379 return draw, start, length
386 return draw, start, length
380
387
381 def _get_order_col(self, order_by, model):
388 def _get_order_col(self, order_by, model):
382 if isinstance(order_by, basestring):
389 if isinstance(order_by, basestring):
383 try:
390 try:
384 return operator.attrgetter(order_by)(model)
391 return operator.attrgetter(order_by)(model)
385 except AttributeError:
392 except AttributeError:
386 return None
393 return None
387 else:
394 else:
388 return order_by
395 return order_by
389
396
390
397
391 class BaseReferencesView(RepoAppView):
398 class BaseReferencesView(RepoAppView):
392 """
399 """
393 Base for reference view for branches, tags and bookmarks.
400 Base for reference view for branches, tags and bookmarks.
394 """
401 """
395 def load_default_context(self):
402 def load_default_context(self):
396 c = self._get_local_tmpl_context()
403 c = self._get_local_tmpl_context()
397
404
398
405
399 return c
406 return c
400
407
401 def load_refs_context(self, ref_items, partials_template):
408 def load_refs_context(self, ref_items, partials_template):
402 _render = self.request.get_partial_renderer(partials_template)
409 _render = self.request.get_partial_renderer(partials_template)
403 pre_load = ["author", "date", "message"]
410 pre_load = ["author", "date", "message"]
404
411
405 is_svn = h.is_svn(self.rhodecode_vcs_repo)
412 is_svn = h.is_svn(self.rhodecode_vcs_repo)
406 is_hg = h.is_hg(self.rhodecode_vcs_repo)
413 is_hg = h.is_hg(self.rhodecode_vcs_repo)
407
414
408 format_ref_id = get_format_ref_id(self.rhodecode_vcs_repo)
415 format_ref_id = get_format_ref_id(self.rhodecode_vcs_repo)
409
416
410 closed_refs = {}
417 closed_refs = {}
411 if is_hg:
418 if is_hg:
412 closed_refs = self.rhodecode_vcs_repo.branches_closed
419 closed_refs = self.rhodecode_vcs_repo.branches_closed
413
420
414 data = []
421 data = []
415 for ref_name, commit_id in ref_items:
422 for ref_name, commit_id in ref_items:
416 commit = self.rhodecode_vcs_repo.get_commit(
423 commit = self.rhodecode_vcs_repo.get_commit(
417 commit_id=commit_id, pre_load=pre_load)
424 commit_id=commit_id, pre_load=pre_load)
418 closed = ref_name in closed_refs
425 closed = ref_name in closed_refs
419
426
420 # TODO: johbo: Unify generation of reference links
427 # TODO: johbo: Unify generation of reference links
421 use_commit_id = '/' in ref_name or is_svn
428 use_commit_id = '/' in ref_name or is_svn
422
429
423 if use_commit_id:
430 if use_commit_id:
424 files_url = h.route_path(
431 files_url = h.route_path(
425 'repo_files',
432 'repo_files',
426 repo_name=self.db_repo_name,
433 repo_name=self.db_repo_name,
427 f_path=ref_name if is_svn else '',
434 f_path=ref_name if is_svn else '',
428 commit_id=commit_id)
435 commit_id=commit_id)
429
436
430 else:
437 else:
431 files_url = h.route_path(
438 files_url = h.route_path(
432 'repo_files',
439 'repo_files',
433 repo_name=self.db_repo_name,
440 repo_name=self.db_repo_name,
434 f_path=ref_name if is_svn else '',
441 f_path=ref_name if is_svn else '',
435 commit_id=ref_name,
442 commit_id=ref_name,
436 _query=dict(at=ref_name))
443 _query=dict(at=ref_name))
437
444
438 data.append({
445 data.append({
439 "name": _render('name', ref_name, files_url, closed),
446 "name": _render('name', ref_name, files_url, closed),
440 "name_raw": ref_name,
447 "name_raw": ref_name,
441 "date": _render('date', commit.date),
448 "date": _render('date', commit.date),
442 "date_raw": datetime_to_time(commit.date),
449 "date_raw": datetime_to_time(commit.date),
443 "author": _render('author', commit.author),
450 "author": _render('author', commit.author),
444 "commit": _render(
451 "commit": _render(
445 'commit', commit.message, commit.raw_id, commit.idx),
452 'commit', commit.message, commit.raw_id, commit.idx),
446 "commit_raw": commit.idx,
453 "commit_raw": commit.idx,
447 "compare": _render(
454 "compare": _render(
448 'compare', format_ref_id(ref_name, commit.raw_id)),
455 'compare', format_ref_id(ref_name, commit.raw_id)),
449 })
456 })
450
457
451 return data
458 return data
452
459
453
460
454 class RepoRoutePredicate(object):
461 class RepoRoutePredicate(object):
455 def __init__(self, val, config):
462 def __init__(self, val, config):
456 self.val = val
463 self.val = val
457
464
458 def text(self):
465 def text(self):
459 return 'repo_route = %s' % self.val
466 return 'repo_route = %s' % self.val
460
467
461 phash = text
468 phash = text
462
469
463 def __call__(self, info, request):
470 def __call__(self, info, request):
464 if hasattr(request, 'vcs_call'):
471 if hasattr(request, 'vcs_call'):
465 # skip vcs calls
472 # skip vcs calls
466 return
473 return
467
474
468 repo_name = info['match']['repo_name']
475 repo_name = info['match']['repo_name']
469 repo_model = repo.RepoModel()
476 repo_model = repo.RepoModel()
470
477
471 by_name_match = repo_model.get_by_repo_name(repo_name, cache=False)
478 by_name_match = repo_model.get_by_repo_name(repo_name, cache=False)
472
479
473 def redirect_if_creating(route_info, db_repo):
480 def redirect_if_creating(route_info, db_repo):
474 skip_views = ['edit_repo_advanced_delete']
481 skip_views = ['edit_repo_advanced_delete']
475 route = route_info['route']
482 route = route_info['route']
476 # we should skip delete view so we can actually "remove" repositories
483 # we should skip delete view so we can actually "remove" repositories
477 # if they get stuck in creating state.
484 # if they get stuck in creating state.
478 if route.name in skip_views:
485 if route.name in skip_views:
479 return
486 return
480
487
481 if db_repo.repo_state in [repo.Repository.STATE_PENDING]:
488 if db_repo.repo_state in [repo.Repository.STATE_PENDING]:
482 repo_creating_url = request.route_path(
489 repo_creating_url = request.route_path(
483 'repo_creating', repo_name=db_repo.repo_name)
490 'repo_creating', repo_name=db_repo.repo_name)
484 raise HTTPFound(repo_creating_url)
491 raise HTTPFound(repo_creating_url)
485
492
486 if by_name_match:
493 if by_name_match:
487 # register this as request object we can re-use later
494 # register this as request object we can re-use later
488 request.db_repo = by_name_match
495 request.db_repo = by_name_match
489 redirect_if_creating(info, by_name_match)
496 redirect_if_creating(info, by_name_match)
490 return True
497 return True
491
498
492 by_id_match = repo_model.get_repo_by_id(repo_name)
499 by_id_match = repo_model.get_repo_by_id(repo_name)
493 if by_id_match:
500 if by_id_match:
494 request.db_repo = by_id_match
501 request.db_repo = by_id_match
495 redirect_if_creating(info, by_id_match)
502 redirect_if_creating(info, by_id_match)
496 return True
503 return True
497
504
498 return False
505 return False
499
506
500
507
501 class RepoTypeRoutePredicate(object):
508 class RepoTypeRoutePredicate(object):
502 def __init__(self, val, config):
509 def __init__(self, val, config):
503 self.val = val or ['hg', 'git', 'svn']
510 self.val = val or ['hg', 'git', 'svn']
504
511
505 def text(self):
512 def text(self):
506 return 'repo_accepted_type = %s' % self.val
513 return 'repo_accepted_type = %s' % self.val
507
514
508 phash = text
515 phash = text
509
516
510 def __call__(self, info, request):
517 def __call__(self, info, request):
511 if hasattr(request, 'vcs_call'):
518 if hasattr(request, 'vcs_call'):
512 # skip vcs calls
519 # skip vcs calls
513 return
520 return
514
521
515 rhodecode_db_repo = request.db_repo
522 rhodecode_db_repo = request.db_repo
516
523
517 log.debug(
524 log.debug(
518 '%s checking repo type for %s in %s',
525 '%s checking repo type for %s in %s',
519 self.__class__.__name__, rhodecode_db_repo.repo_type, self.val)
526 self.__class__.__name__, rhodecode_db_repo.repo_type, self.val)
520
527
521 if rhodecode_db_repo.repo_type in self.val:
528 if rhodecode_db_repo.repo_type in self.val:
522 return True
529 return True
523 else:
530 else:
524 log.warning('Current view is not supported for repo type:%s',
531 log.warning('Current view is not supported for repo type:%s',
525 rhodecode_db_repo.repo_type)
532 rhodecode_db_repo.repo_type)
526
533
527 # h.flash(h.literal(
534 # h.flash(h.literal(
528 # _('Action not supported for %s.' % rhodecode_repo.alias)),
535 # _('Action not supported for %s.' % rhodecode_repo.alias)),
529 # category='warning')
536 # category='warning')
530 # return redirect(
537 # return redirect(
531 # route_path('repo_summary', repo_name=cls.rhodecode_db_repo.repo_name))
538 # route_path('repo_summary', repo_name=cls.rhodecode_db_repo.repo_name))
532
539
533 return False
540 return False
534
541
535
542
536 class RepoGroupRoutePredicate(object):
543 class RepoGroupRoutePredicate(object):
537 def __init__(self, val, config):
544 def __init__(self, val, config):
538 self.val = val
545 self.val = val
539
546
540 def text(self):
547 def text(self):
541 return 'repo_group_route = %s' % self.val
548 return 'repo_group_route = %s' % self.val
542
549
543 phash = text
550 phash = text
544
551
545 def __call__(self, info, request):
552 def __call__(self, info, request):
546 if hasattr(request, 'vcs_call'):
553 if hasattr(request, 'vcs_call'):
547 # skip vcs calls
554 # skip vcs calls
548 return
555 return
549
556
550 repo_group_name = info['match']['repo_group_name']
557 repo_group_name = info['match']['repo_group_name']
551 repo_group_model = repo_group.RepoGroupModel()
558 repo_group_model = repo_group.RepoGroupModel()
552 by_name_match = repo_group_model.get_by_group_name(repo_group_name, cache=False)
559 by_name_match = repo_group_model.get_by_group_name(repo_group_name, cache=False)
553
560
554 if by_name_match:
561 if by_name_match:
555 # register this as request object we can re-use later
562 # register this as request object we can re-use later
556 request.db_repo_group = by_name_match
563 request.db_repo_group = by_name_match
557 return True
564 return True
558
565
559 return False
566 return False
560
567
561
568
562 class UserGroupRoutePredicate(object):
569 class UserGroupRoutePredicate(object):
563 def __init__(self, val, config):
570 def __init__(self, val, config):
564 self.val = val
571 self.val = val
565
572
566 def text(self):
573 def text(self):
567 return 'user_group_route = %s' % self.val
574 return 'user_group_route = %s' % self.val
568
575
569 phash = text
576 phash = text
570
577
571 def __call__(self, info, request):
578 def __call__(self, info, request):
572 if hasattr(request, 'vcs_call'):
579 if hasattr(request, 'vcs_call'):
573 # skip vcs calls
580 # skip vcs calls
574 return
581 return
575
582
576 user_group_id = info['match']['user_group_id']
583 user_group_id = info['match']['user_group_id']
577 user_group_model = user_group.UserGroup()
584 user_group_model = user_group.UserGroup()
578 by_id_match = user_group_model.get(user_group_id, cache=False)
585 by_id_match = user_group_model.get(user_group_id, cache=False)
579
586
580 if by_id_match:
587 if by_id_match:
581 # register this as request object we can re-use later
588 # register this as request object we can re-use later
582 request.db_user_group = by_id_match
589 request.db_user_group = by_id_match
583 return True
590 return True
584
591
585 return False
592 return False
586
593
587
594
588 class UserRoutePredicateBase(object):
595 class UserRoutePredicateBase(object):
589 supports_default = None
596 supports_default = None
590
597
591 def __init__(self, val, config):
598 def __init__(self, val, config):
592 self.val = val
599 self.val = val
593
600
594 def text(self):
601 def text(self):
595 raise NotImplementedError()
602 raise NotImplementedError()
596
603
597 def __call__(self, info, request):
604 def __call__(self, info, request):
598 if hasattr(request, 'vcs_call'):
605 if hasattr(request, 'vcs_call'):
599 # skip vcs calls
606 # skip vcs calls
600 return
607 return
601
608
602 user_id = info['match']['user_id']
609 user_id = info['match']['user_id']
603 user_model = user.User()
610 user_model = user.User()
604 by_id_match = user_model.get(user_id, cache=False)
611 by_id_match = user_model.get(user_id, cache=False)
605
612
606 if by_id_match:
613 if by_id_match:
607 # register this as request object we can re-use later
614 # register this as request object we can re-use later
608 request.db_user = by_id_match
615 request.db_user = by_id_match
609 request.db_user_supports_default = self.supports_default
616 request.db_user_supports_default = self.supports_default
610 return True
617 return True
611
618
612 return False
619 return False
613
620
614
621
615 class UserRoutePredicate(UserRoutePredicateBase):
622 class UserRoutePredicate(UserRoutePredicateBase):
616 supports_default = False
623 supports_default = False
617
624
618 def text(self):
625 def text(self):
619 return 'user_route = %s' % self.val
626 return 'user_route = %s' % self.val
620
627
621 phash = text
628 phash = text
622
629
623
630
624 class UserRouteWithDefaultPredicate(UserRoutePredicateBase):
631 class UserRouteWithDefaultPredicate(UserRoutePredicateBase):
625 supports_default = True
632 supports_default = True
626
633
627 def text(self):
634 def text(self):
628 return 'user_with_default_route = %s' % self.val
635 return 'user_with_default_route = %s' % self.val
629
636
630 phash = text
637 phash = text
631
638
632
639
633 def includeme(config):
640 def includeme(config):
634 config.add_route_predicate(
641 config.add_route_predicate(
635 'repo_route', RepoRoutePredicate)
642 'repo_route', RepoRoutePredicate)
636 config.add_route_predicate(
643 config.add_route_predicate(
637 'repo_accepted_types', RepoTypeRoutePredicate)
644 'repo_accepted_types', RepoTypeRoutePredicate)
638 config.add_route_predicate(
645 config.add_route_predicate(
639 'repo_group_route', RepoGroupRoutePredicate)
646 'repo_group_route', RepoGroupRoutePredicate)
640 config.add_route_predicate(
647 config.add_route_predicate(
641 'user_group_route', UserGroupRoutePredicate)
648 'user_group_route', UserGroupRoutePredicate)
642 config.add_route_predicate(
649 config.add_route_predicate(
643 'user_route_with_default', UserRouteWithDefaultPredicate)
650 'user_route_with_default', UserRouteWithDefaultPredicate)
644 config.add_route_predicate(
651 config.add_route_predicate(
645 'user_route', UserRoutePredicate) No newline at end of file
652 'user_route', UserRoutePredicate)
@@ -1,53 +1,73 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2018 RhodeCode GmbH
3 # Copyright (C) 2016-2018 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 from rhodecode.config import routing_links
20 from rhodecode.config import routing_links
21
21
22
22
23 class VCSCallPredicate(object):
24 def __init__(self, val, config):
25 self.val = val
26
27 def text(self):
28 return 'vcs_call route = %s' % self.val
29
30 phash = text
31
32 def __call__(self, info, request):
33 if hasattr(request, 'vcs_call'):
34 # skip vcs calls
35 return False
36
37 return True
38
39
23 def includeme(config):
40 def includeme(config):
24
41
25 config.add_route(
42 config.add_route(
26 name='home',
43 name='home',
27 pattern='/')
44 pattern='/')
28
45
29 config.add_route(
46 config.add_route(
30 name='user_autocomplete_data',
47 name='user_autocomplete_data',
31 pattern='/_users')
48 pattern='/_users')
32
49
33 config.add_route(
50 config.add_route(
34 name='user_group_autocomplete_data',
51 name='user_group_autocomplete_data',
35 pattern='/_user_groups')
52 pattern='/_user_groups')
36
53
37 config.add_route(
54 config.add_route(
38 name='repo_list_data',
55 name='repo_list_data',
39 pattern='/_repos')
56 pattern='/_repos')
40
57
41 config.add_route(
58 config.add_route(
42 name='goto_switcher_data',
59 name='goto_switcher_data',
43 pattern='/_goto_data')
60 pattern='/_goto_data')
44
61
45 config.add_route(
62 config.add_route(
46 name='markup_preview',
63 name='markup_preview',
47 pattern='/_markup_preview')
64 pattern='/_markup_preview')
48
65
49 # register our static links via redirection mechanism
66 # register our static links via redirection mechanism
50 routing_links.connect_redirection_links(config)
67 routing_links.connect_redirection_links(config)
51
68
52 # Scan module for configuration decorators.
69 # Scan module for configuration decorators.
53 config.scan('.views', ignore='.tests')
70 config.scan('.views', ignore='.tests')
71
72 config.add_route_predicate(
73 'skip_vcs_call', VCSCallPredicate)
General Comments 0
You need to be logged in to leave comments. Login now