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