##// END OF EJS Templates
apps: base app fixes for python3
super-admin -
r5086:62af941a default
parent child Browse files
Show More
@@ -1,59 +1,88 b''
1 # -*- coding: utf-8 -*-
2 1
3 2 # Copyright (C) 2010-2020 RhodeCode GmbH
4 3 #
5 4 # This program is free software: you can redistribute it and/or modify
6 5 # it under the terms of the GNU Affero General Public License, version 3
7 6 # (only), as published by the Free Software Foundation.
8 7 #
9 8 # This program is distributed in the hope that it will be useful,
10 9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 11 # GNU General Public License for more details.
13 12 #
14 13 # You should have received a copy of the GNU Affero General Public License
15 14 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 15 #
17 16 # This program is dual-licensed. If you wish to learn more about the
18 17 # RhodeCode Enterprise Edition, including its added features, Support services,
19 18 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 19
21 20 import os
22 21 import datetime
23 22 import collections
24 23
25 now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")
24 now = datetime.datetime.now()
25 now = now.strftime("%Y-%m-%d %H:%M:%S") + '.' + "{:03d}".format(int(now.microsecond/1000))
26 26
27 27 print(f'{now} Starting RhodeCode imports...')
28 28
29 29 VERSION = tuple(open(os.path.join(
30 30 os.path.dirname(__file__), 'VERSION')).read().split('.'))
31 31
32 32 BACKENDS = collections.OrderedDict()
33 33
34 34 BACKENDS['hg'] = 'Mercurial repository'
35 35 BACKENDS['git'] = 'Git repository'
36 36 BACKENDS['svn'] = 'Subversion repository'
37 37
38 38
39 39 CELERY_ENABLED = False
40 40 CELERY_EAGER = False
41 41
42 42 # link to config for pyramid
43 43 CONFIG = {}
44 44
45
46 class ConfigGet:
47 NotGiven = object()
48
49 def _get_val_or_missing(self, key, missing):
50 if key not in CONFIG:
51 if missing == self.NotGiven:
52 return missing
53 # we don't get key, we don't get missing value, return nothing similar as config.get(key)
54 return None
55 else:
56 val = CONFIG[key]
57 return val
58
59 def get_str(self, key, missing=NotGiven):
60 from rhodecode.lib.str_utils import safe_str
61 val = self._get_val_or_missing(key, missing)
62 return safe_str(val)
63
64 def get_int(self, key, missing=NotGiven):
65 from rhodecode.lib.str_utils import safe_int
66 val = self._get_val_or_missing(key, missing)
67 return safe_int(val)
68
69 def get_bool(self, key, missing=NotGiven):
70 from rhodecode.lib.type_utils import str2bool
71 val = self._get_val_or_missing(key, missing)
72 return str2bool(val)
73
45 74 # Populated with the settings dictionary from application init in
46 75 # rhodecode.conf.environment.load_pyramid_environment
47 76 PYRAMID_SETTINGS = {}
48 77
49 78 # Linked module for extensions
50 79 EXTENSIONS = {}
51 80
52 81 __version__ = ('.'.join((str(each) for each in VERSION[:3])))
53 82 __dbversion__ = 114 # defines current db version for migrations
54 83 __license__ = 'AGPLv3, and Commercial License'
55 84 __author__ = 'RhodeCode GmbH'
56 85 __url__ = 'https://code.rhodecode.com'
57 86
58 87 is_test = False
59 88 disable_error_handler = False
@@ -1,858 +1,860 b''
1 1
2 2
3 3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import time
22 22 import logging
23 23 import operator
24 24
25 25 from pyramid.httpexceptions import HTTPFound, HTTPForbidden, HTTPBadRequest
26 26
27 27 from rhodecode.lib import helpers as h, diffs, rc_cache
28 from rhodecode.lib.str_utils import safe_str
28 29 from rhodecode.lib.utils import repo_name_slug
29 30 from rhodecode.lib.utils2 import (
30 StrictAttributeDict, str2bool, safe_int, datetime_to_time, safe_unicode)
31 StrictAttributeDict, str2bool, safe_int, datetime_to_time)
31 32 from rhodecode.lib.markup_renderer import MarkupRenderer, relative_links
32 33 from rhodecode.lib.vcs.backends.base import EmptyCommit
33 34 from rhodecode.lib.vcs.exceptions import RepositoryRequirementError
34 35 from rhodecode.model import repo
35 36 from rhodecode.model import repo_group
36 37 from rhodecode.model import user_group
37 38 from rhodecode.model import user
38 39 from rhodecode.model.db import User
39 40 from rhodecode.model.scm import ScmModel
40 41 from rhodecode.model.settings import VcsSettingsModel, IssueTrackerSettingsModel
41 42 from rhodecode.model.repo import ReadmeFinder
42 43
43 44 log = logging.getLogger(__name__)
44 45
45 46
46 47 ADMIN_PREFIX = '/_admin'
47 48 STATIC_FILE_PREFIX = '/_static'
48 49
49 50 URL_NAME_REQUIREMENTS = {
50 51 # group name can have a slash in them, but they must not end with a slash
51 52 'group_name': r'.*?[^/]',
52 53 'repo_group_name': r'.*?[^/]',
53 54 # repo names can have a slash in them, but they must not end with a slash
54 55 'repo_name': r'.*?[^/]',
55 56 # file path eats up everything at the end
56 57 'f_path': r'.*',
57 58 # reference types
58 59 'source_ref_type': '(branch|book|tag|rev|\%\(source_ref_type\)s)',
59 60 'target_ref_type': '(branch|book|tag|rev|\%\(target_ref_type\)s)',
60 61 }
61 62
62 63
63 64 def add_route_with_slash(config,name, pattern, **kw):
64 65 config.add_route(name, pattern, **kw)
65 66 if not pattern.endswith('/'):
66 67 config.add_route(name + '_slash', pattern + '/', **kw)
67 68
68 69
69 70 def add_route_requirements(route_path, requirements=None):
70 71 """
71 72 Adds regex requirements to pyramid routes using a mapping dict
72 73 e.g::
73 74 add_route_requirements('{repo_name}/settings')
74 75 """
75 76 requirements = requirements or URL_NAME_REQUIREMENTS
76 for key, regex in requirements.items():
77 for key, regex in list(requirements.items()):
77 78 route_path = route_path.replace('{%s}' % key, '{%s:%s}' % (key, regex))
78 79 return route_path
79 80
80 81
81 82 def get_format_ref_id(repo):
82 83 """Returns a `repo` specific reference formatter function"""
83 84 if h.is_svn(repo):
84 85 return _format_ref_id_svn
85 86 else:
86 87 return _format_ref_id
87 88
88 89
89 90 def _format_ref_id(name, raw_id):
90 91 """Default formatting of a given reference `name`"""
91 92 return name
92 93
93 94
94 95 def _format_ref_id_svn(name, raw_id):
95 96 """Special way of formatting a reference for Subversion including path"""
96 97 return '%s@%s' % (name, raw_id)
97 98
98 99
99 100 class TemplateArgs(StrictAttributeDict):
100 101 pass
101 102
102 103
103 104 class BaseAppView(object):
104 105
105 106 def __init__(self, context, request):
106 107 self.request = request
107 108 self.context = context
108 109 self.session = request.session
109 110 if not hasattr(request, 'user'):
110 111 # NOTE(marcink): edge case, we ended up in matched route
111 112 # but probably of web-app context, e.g API CALL/VCS CALL
112 113 if hasattr(request, 'vcs_call') or hasattr(request, 'rpc_method'):
113 114 log.warning('Unable to process request `%s` in this scope', request)
114 115 raise HTTPBadRequest()
115 116
116 117 self._rhodecode_user = request.user # auth user
117 118 self._rhodecode_db_user = self._rhodecode_user.get_instance()
118 119 self._maybe_needs_password_change(
119 120 request.matched_route.name, self._rhodecode_db_user)
120 121
121 122 def _maybe_needs_password_change(self, view_name, user_obj):
122 123
123 124 dont_check_views = [
124 125 'channelstream_connect',
125 126 'ops_ping'
126 127 ]
127 128 if view_name in dont_check_views:
128 129 return
129 130
130 131 log.debug('Checking if user %s needs password change on view %s',
131 132 user_obj, view_name)
132 133
133 134 skip_user_views = [
134 135 'logout', 'login',
135 136 'my_account_password', 'my_account_password_update'
136 137 ]
137 138
138 139 if not user_obj:
139 140 return
140 141
141 142 if user_obj.username == User.DEFAULT_USER:
142 143 return
143 144
144 145 now = time.time()
145 146 should_change = user_obj.user_data.get('force_password_change')
146 147 change_after = safe_int(should_change) or 0
147 148 if should_change and now > change_after:
148 149 log.debug('User %s requires password change', user_obj)
149 150 h.flash('You are required to change your password', 'warning',
150 151 ignore_duplicate=True)
151 152
152 153 if view_name not in skip_user_views:
153 154 raise HTTPFound(
154 155 self.request.route_path('my_account_password'))
155 156
156 157 def _log_creation_exception(self, e, repo_name):
157 158 _ = self.request.translate
158 159 reason = None
159 160 if len(e.args) == 2:
160 161 reason = e.args[1]
161 162
162 163 if reason == 'INVALID_CERTIFICATE':
163 164 log.exception(
164 165 'Exception creating a repository: invalid certificate')
165 166 msg = (_('Error creating repository %s: invalid certificate')
166 167 % repo_name)
167 168 else:
168 169 log.exception("Exception creating a repository")
169 170 msg = (_('Error creating repository %s')
170 171 % repo_name)
171 172 return msg
172 173
173 174 def _get_local_tmpl_context(self, include_app_defaults=True):
174 175 c = TemplateArgs()
175 176 c.auth_user = self.request.user
176 177 # TODO(marcink): migrate the usage of c.rhodecode_user to c.auth_user
177 178 c.rhodecode_user = self.request.user
178 179
179 180 if include_app_defaults:
180 181 from rhodecode.lib.base import attach_context_attributes
181 182 attach_context_attributes(c, self.request, self.request.user.user_id)
182 183
183 184 c.is_super_admin = c.auth_user.is_admin
184 185
185 186 c.can_create_repo = c.is_super_admin
186 187 c.can_create_repo_group = c.is_super_admin
187 188 c.can_create_user_group = c.is_super_admin
188 189
189 190 c.is_delegated_admin = False
190 191
191 192 if not c.auth_user.is_default and not c.is_super_admin:
192 193 c.can_create_repo = h.HasPermissionAny('hg.create.repository')(
193 194 user=self.request.user)
194 195 repositories = c.auth_user.repositories_admin or c.can_create_repo
195 196
196 197 c.can_create_repo_group = h.HasPermissionAny('hg.repogroup.create.true')(
197 198 user=self.request.user)
198 199 repository_groups = c.auth_user.repository_groups_admin or c.can_create_repo_group
199 200
200 201 c.can_create_user_group = h.HasPermissionAny('hg.usergroup.create.true')(
201 202 user=self.request.user)
202 203 user_groups = c.auth_user.user_groups_admin or c.can_create_user_group
203 204 # delegated admin can create, or manage some objects
204 205 c.is_delegated_admin = repositories or repository_groups or user_groups
205 206 return c
206 207
207 208 def _get_template_context(self, tmpl_args, **kwargs):
208 209
209 210 local_tmpl_args = {
210 211 'defaults': {},
211 212 'errors': {},
212 213 'c': tmpl_args
213 214 }
214 215 local_tmpl_args.update(kwargs)
215 216 return local_tmpl_args
216 217
217 218 def load_default_context(self):
218 219 """
219 220 example:
220 221
221 222 def load_default_context(self):
222 223 c = self._get_local_tmpl_context()
223 224 c.custom_var = 'foobar'
224 225
225 226 return c
226 227 """
227 228 raise NotImplementedError('Needs implementation in view class')
228 229
229 230
230 231 class RepoAppView(BaseAppView):
231 232
232 233 def __init__(self, context, request):
233 234 super(RepoAppView, self).__init__(context, request)
234 235 self.db_repo = request.db_repo
235 236 self.db_repo_name = self.db_repo.repo_name
236 237 self.db_repo_pull_requests = ScmModel().get_pull_requests(self.db_repo)
237 238 self.db_repo_artifacts = ScmModel().get_artifacts(self.db_repo)
238 239 self.db_repo_patterns = IssueTrackerSettingsModel(repo=self.db_repo)
239 240
240 241 def _handle_missing_requirements(self, error):
241 242 log.error(
242 243 'Requirements are missing for repository %s: %s',
243 self.db_repo_name, safe_unicode(error))
244 self.db_repo_name, safe_str(error))
244 245
245 246 def _prepare_and_set_clone_url(self, c):
246 247 username = ''
247 248 if self._rhodecode_user.username != User.DEFAULT_USER:
248 249 username = self._rhodecode_user.username
249 250
250 251 _def_clone_uri = c.clone_uri_tmpl
251 252 _def_clone_uri_id = c.clone_uri_id_tmpl
252 253 _def_clone_uri_ssh = c.clone_uri_ssh_tmpl
253 254
254 255 c.clone_repo_url = self.db_repo.clone_url(
255 256 user=username, uri_tmpl=_def_clone_uri)
256 257 c.clone_repo_url_id = self.db_repo.clone_url(
257 258 user=username, uri_tmpl=_def_clone_uri_id)
258 259 c.clone_repo_url_ssh = self.db_repo.clone_url(
259 260 uri_tmpl=_def_clone_uri_ssh, ssh=True)
260 261
261 262 def _get_local_tmpl_context(self, include_app_defaults=True):
262 263 _ = self.request.translate
263 264 c = super(RepoAppView, self)._get_local_tmpl_context(
264 265 include_app_defaults=include_app_defaults)
265 266
266 267 # register common vars for this type of view
267 268 c.rhodecode_db_repo = self.db_repo
268 269 c.repo_name = self.db_repo_name
269 270 c.repository_pull_requests = self.db_repo_pull_requests
270 271 c.repository_artifacts = self.db_repo_artifacts
271 272 c.repository_is_user_following = ScmModel().is_following_repo(
272 273 self.db_repo_name, self._rhodecode_user.user_id)
273 274 self.path_filter = PathFilter(None)
274 275
275 276 c.repository_requirements_missing = {}
276 277 try:
277 278 self.rhodecode_vcs_repo = self.db_repo.scm_instance()
278 279 # NOTE(marcink):
279 280 # comparison to None since if it's an object __bool__ is expensive to
280 281 # calculate
281 282 if self.rhodecode_vcs_repo is not None:
282 283 path_perms = self.rhodecode_vcs_repo.get_path_permissions(
283 284 c.auth_user.username)
284 285 self.path_filter = PathFilter(path_perms)
285 286 except RepositoryRequirementError as e:
286 287 c.repository_requirements_missing = {'error': str(e)}
287 288 self._handle_missing_requirements(e)
288 289 self.rhodecode_vcs_repo = None
289 290
290 291 c.path_filter = self.path_filter # used by atom_feed_entry.mako
291 292
292 293 if self.rhodecode_vcs_repo is None:
293 294 # unable to fetch this repo as vcs instance, report back to user
294 295 log.debug('Repository was not found on filesystem, check if it exists or is not damaged')
295 296 h.flash(_(
296 297 "The repository `%(repo_name)s` cannot be loaded in filesystem. "
297 298 "Please check if it exist, or is not damaged.") %
298 299 {'repo_name': c.repo_name},
299 300 category='error', ignore_duplicate=True)
300 301 if c.repository_requirements_missing:
301 302 route = self.request.matched_route.name
302 303 if route.startswith(('edit_repo', 'repo_summary')):
303 304 # allow summary and edit repo on missing requirements
304 305 return c
305 306
306 307 raise HTTPFound(
307 308 h.route_path('repo_summary', repo_name=self.db_repo_name))
308 309
309 310 else: # redirect if we don't show missing requirements
310 311 raise HTTPFound(h.route_path('home'))
311 312
312 313 c.has_origin_repo_read_perm = False
313 314 if self.db_repo.fork:
314 315 c.has_origin_repo_read_perm = h.HasRepoPermissionAny(
315 316 'repository.write', 'repository.read', 'repository.admin')(
316 317 self.db_repo.fork.repo_name, 'summary fork link')
317 318
318 319 return c
319 320
320 321 def _get_f_path_unchecked(self, matchdict, default=None):
321 322 """
322 323 Should only be used by redirects, everything else should call _get_f_path
323 324 """
324 325 f_path = matchdict.get('f_path')
325 326 if f_path:
326 327 # fix for multiple initial slashes that causes errors for GIT
327 328 return f_path.lstrip('/')
328 329
329 330 return default
330 331
331 332 def _get_f_path(self, matchdict, default=None):
332 333 f_path_match = self._get_f_path_unchecked(matchdict, default)
333 334 return self.path_filter.assert_path_permissions(f_path_match)
334 335
335 336 def _get_general_setting(self, target_repo, settings_key, default=False):
336 337 settings_model = VcsSettingsModel(repo=target_repo)
337 338 settings = settings_model.get_general_settings()
338 339 return settings.get(settings_key, default)
339 340
340 341 def _get_repo_setting(self, target_repo, settings_key, default=False):
341 342 settings_model = VcsSettingsModel(repo=target_repo)
342 343 settings = settings_model.get_repo_settings_inherited()
343 344 return settings.get(settings_key, default)
344 345
345 346 def _get_readme_data(self, db_repo, renderer_type, commit_id=None, path='/'):
346 347 log.debug('Looking for README file at path %s', path)
347 348 if commit_id:
348 349 landing_commit_id = commit_id
349 350 else:
350 351 landing_commit = db_repo.get_landing_commit()
351 352 if isinstance(landing_commit, EmptyCommit):
352 353 return None, None
353 354 landing_commit_id = landing_commit.raw_id
354 355
355 cache_namespace_uid = 'cache_repo.{}'.format(db_repo.repo_id)
356 region = rc_cache.get_or_create_region('cache_repo', cache_namespace_uid)
356 cache_namespace_uid = 'repo.{}'.format(db_repo.repo_id)
357 region = rc_cache.get_or_create_region('cache_repo', cache_namespace_uid, use_async_runner=True)
357 358 start = time.time()
358 359
359 360 @region.conditional_cache_on_arguments(namespace=cache_namespace_uid)
360 361 def generate_repo_readme(repo_id, _commit_id, _repo_name, _readme_search_path, _renderer_type):
361 362 readme_data = None
362 363 readme_filename = None
363 364
364 365 commit = db_repo.get_commit(_commit_id)
365 366 log.debug("Searching for a README file at commit %s.", _commit_id)
366 367 readme_node = ReadmeFinder(_renderer_type).search(commit, path=_readme_search_path)
367 368
368 369 if readme_node:
369 370 log.debug('Found README node: %s', readme_node)
370 371 relative_urls = {
371 372 'raw': h.route_path(
372 373 'repo_file_raw', repo_name=_repo_name,
373 374 commit_id=commit.raw_id, f_path=readme_node.path),
374 375 'standard': h.route_path(
375 376 'repo_files', repo_name=_repo_name,
376 377 commit_id=commit.raw_id, f_path=readme_node.path),
377 378 }
378 379
379 380 readme_data = self._render_readme_or_none(commit, readme_node, relative_urls)
380 readme_filename = readme_node.unicode_path
381 readme_filename = readme_node.str_path
381 382
382 383 return readme_data, readme_filename
383 384
384 385 readme_data, readme_filename = generate_repo_readme(
385 386 db_repo.repo_id, landing_commit_id, db_repo.repo_name, path, renderer_type,)
386 387
387 388 compute_time = time.time() - start
388 389 log.debug('Repo README for path %s generated and computed in %.4fs',
389 390 path, compute_time)
390 391 return readme_data, readme_filename
391 392
392 393 def _render_readme_or_none(self, commit, readme_node, relative_urls):
393 394 log.debug('Found README file `%s` rendering...', readme_node.path)
394 395 renderer = MarkupRenderer()
395 396 try:
396 397 html_source = renderer.render(
397 readme_node.content, filename=readme_node.path)
398 readme_node.str_content, filename=readme_node.path)
398 399 if relative_urls:
399 400 return relative_links(html_source, relative_urls)
400 401 return html_source
401 402 except Exception:
402 log.exception(
403 "Exception while trying to render the README")
403 log.exception("Exception while trying to render the README")
404 404
405 405 def get_recache_flag(self):
406 406 for flag_name in ['force_recache', 'force-recache', 'no-cache']:
407 407 flag_val = self.request.GET.get(flag_name)
408 408 if str2bool(flag_val):
409 409 return True
410 410 return False
411 411
412 412 def get_commit_preload_attrs(cls):
413 413 pre_load = ['author', 'branch', 'date', 'message', 'parents',
414 414 'obsolete', 'phase', 'hidden']
415 415 return pre_load
416 416
417 417
418 418 class PathFilter(object):
419 419
420 420 # Expects and instance of BasePathPermissionChecker or None
421 421 def __init__(self, permission_checker):
422 422 self.permission_checker = permission_checker
423 423
424 424 def assert_path_permissions(self, path):
425 425 if self.path_access_allowed(path):
426 426 return path
427 427 raise HTTPForbidden()
428 428
429 429 def path_access_allowed(self, path):
430 430 log.debug('Checking ACL permissions for PathFilter for `%s`', path)
431 431 if self.permission_checker:
432 432 has_access = path and self.permission_checker.has_access(path)
433 433 log.debug('ACL Permissions checker enabled, ACL Check has_access: %s', has_access)
434 434 return has_access
435 435
436 436 log.debug('ACL permissions checker not enabled, skipping...')
437 437 return True
438 438
439 439 def filter_patchset(self, patchset):
440 440 if not self.permission_checker or not patchset:
441 441 return patchset, False
442 442 had_filtered = False
443 443 filtered_patchset = []
444 444 for patch in patchset:
445 445 filename = patch.get('filename', None)
446 446 if not filename or self.permission_checker.has_access(filename):
447 447 filtered_patchset.append(patch)
448 448 else:
449 449 had_filtered = True
450 450 if had_filtered:
451 451 if isinstance(patchset, diffs.LimitedDiffContainer):
452 452 filtered_patchset = diffs.LimitedDiffContainer(patchset.diff_limit, patchset.cur_diff_size, filtered_patchset)
453 453 return filtered_patchset, True
454 454 else:
455 455 return patchset, False
456 456
457 457 def render_patchset_filtered(self, diffset, patchset, source_ref=None, target_ref=None):
458
458 459 filtered_patchset, has_hidden_changes = self.filter_patchset(patchset)
459 460 result = diffset.render_patchset(
460 461 filtered_patchset, source_ref=source_ref, target_ref=target_ref)
461 462 result.has_hidden_changes = has_hidden_changes
462 463 return result
463 464
464 465 def get_raw_patch(self, diff_processor):
465 466 if self.permission_checker is None:
466 467 return diff_processor.as_raw()
467 468 elif self.permission_checker.has_full_access:
468 469 return diff_processor.as_raw()
469 470 else:
470 471 return '# Repository has user-specific filters, raw patch generation is disabled.'
471 472
472 473 @property
473 474 def is_enabled(self):
474 475 return self.permission_checker is not None
475 476
476 477
477 478 class RepoGroupAppView(BaseAppView):
478 479 def __init__(self, context, request):
479 480 super(RepoGroupAppView, self).__init__(context, request)
480 481 self.db_repo_group = request.db_repo_group
481 482 self.db_repo_group_name = self.db_repo_group.group_name
482 483
483 484 def _get_local_tmpl_context(self, include_app_defaults=True):
484 485 _ = self.request.translate
485 486 c = super(RepoGroupAppView, self)._get_local_tmpl_context(
486 487 include_app_defaults=include_app_defaults)
487 488 c.repo_group = self.db_repo_group
488 489 return c
489 490
490 491 def _revoke_perms_on_yourself(self, form_result):
491 _updates = filter(lambda u: self._rhodecode_user.user_id == int(u[0]),
492 form_result['perm_updates'])
493 _additions = filter(lambda u: self._rhodecode_user.user_id == int(u[0]),
494 form_result['perm_additions'])
495 _deletions = filter(lambda u: self._rhodecode_user.user_id == int(u[0]),
496 form_result['perm_deletions'])
492 _updates = [u for u in form_result['perm_updates'] if self._rhodecode_user.user_id == int(u[0])]
493 _additions = [u for u in form_result['perm_additions'] if self._rhodecode_user.user_id == int(u[0])]
494 _deletions = [u for u in form_result['perm_deletions'] if self._rhodecode_user.user_id == int(u[0])]
497 495 admin_perm = 'group.admin'
498 496 if _updates and _updates[0][1] != admin_perm or \
499 497 _additions and _additions[0][1] != admin_perm or \
500 498 _deletions and _deletions[0][1] != admin_perm:
501 499 return True
502 500 return False
503 501
504 502
505 503 class UserGroupAppView(BaseAppView):
506 504 def __init__(self, context, request):
507 505 super(UserGroupAppView, self).__init__(context, request)
508 506 self.db_user_group = request.db_user_group
509 507 self.db_user_group_name = self.db_user_group.users_group_name
510 508
511 509
512 510 class UserAppView(BaseAppView):
513 511 def __init__(self, context, request):
514 512 super(UserAppView, self).__init__(context, request)
515 513 self.db_user = request.db_user
516 514 self.db_user_id = self.db_user.user_id
517 515
518 516 _ = self.request.translate
519 517 if not request.db_user_supports_default:
520 518 if self.db_user.username == User.DEFAULT_USER:
521 519 h.flash(_("Editing user `{}` is disabled.".format(
522 520 User.DEFAULT_USER)), category='warning')
523 521 raise HTTPFound(h.route_path('users'))
524 522
525 523
526 524 class DataGridAppView(object):
527 525 """
528 526 Common class to have re-usable grid rendering components
529 527 """
530 528
531 529 def _extract_ordering(self, request, column_map=None):
532 530 column_map = column_map or {}
533 531 column_index = safe_int(request.GET.get('order[0][column]'))
534 532 order_dir = request.GET.get(
535 533 'order[0][dir]', 'desc')
536 534 order_by = request.GET.get(
537 535 'columns[%s][data][sort]' % column_index, 'name_raw')
538 536
539 537 # translate datatable to DB columns
540 538 order_by = column_map.get(order_by) or order_by
541 539
542 540 search_q = request.GET.get('search[value]')
543 541 return search_q, order_by, order_dir
544 542
545 543 def _extract_chunk(self, request):
546 544 start = safe_int(request.GET.get('start'), 0)
547 545 length = safe_int(request.GET.get('length'), 25)
548 546 draw = safe_int(request.GET.get('draw'))
549 547 return draw, start, length
550 548
551 549 def _get_order_col(self, order_by, model):
552 550 if isinstance(order_by, str):
553 551 try:
554 552 return operator.attrgetter(order_by)(model)
555 553 except AttributeError:
556 554 return None
557 555 else:
558 556 return order_by
559 557
560 558
561 559 class BaseReferencesView(RepoAppView):
562 560 """
563 561 Base for reference view for branches, tags and bookmarks.
564 562 """
565 563 def load_default_context(self):
566 564 c = self._get_local_tmpl_context()
567 565 return c
568 566
569 567 def load_refs_context(self, ref_items, partials_template):
570 568 _render = self.request.get_partial_renderer(partials_template)
571 569 pre_load = ["author", "date", "message", "parents"]
572 570
573 571 is_svn = h.is_svn(self.rhodecode_vcs_repo)
574 572 is_hg = h.is_hg(self.rhodecode_vcs_repo)
575 573
576 574 format_ref_id = get_format_ref_id(self.rhodecode_vcs_repo)
577 575
578 576 closed_refs = {}
579 577 if is_hg:
580 578 closed_refs = self.rhodecode_vcs_repo.branches_closed
581 579
582 580 data = []
583 581 for ref_name, commit_id in ref_items:
584 582 commit = self.rhodecode_vcs_repo.get_commit(
585 583 commit_id=commit_id, pre_load=pre_load)
586 584 closed = ref_name in closed_refs
587 585
588 586 # TODO: johbo: Unify generation of reference links
589 587 use_commit_id = '/' in ref_name or is_svn
590 588
591 589 if use_commit_id:
592 590 files_url = h.route_path(
593 591 'repo_files',
594 592 repo_name=self.db_repo_name,
595 593 f_path=ref_name if is_svn else '',
596 594 commit_id=commit_id,
597 595 _query=dict(at=ref_name)
598 596 )
599 597
600 598 else:
601 599 files_url = h.route_path(
602 600 'repo_files',
603 601 repo_name=self.db_repo_name,
604 602 f_path=ref_name if is_svn else '',
605 603 commit_id=ref_name,
606 604 _query=dict(at=ref_name)
607 605 )
608 606
609 607 data.append({
610 608 "name": _render('name', ref_name, files_url, closed),
611 609 "name_raw": ref_name,
612 610 "date": _render('date', commit.date),
613 611 "date_raw": datetime_to_time(commit.date),
614 612 "author": _render('author', commit.author),
615 613 "commit": _render(
616 614 'commit', commit.message, commit.raw_id, commit.idx),
617 615 "commit_raw": commit.idx,
618 616 "compare": _render(
619 617 'compare', format_ref_id(ref_name, commit.raw_id)),
620 618 })
621 619
622 620 return data
623 621
624 622
625 623 class RepoRoutePredicate(object):
626 624 def __init__(self, val, config):
627 625 self.val = val
628 626
629 627 def text(self):
630 return 'repo_route = %s' % self.val
628 return f'repo_route = {self.val}'
631 629
632 630 phash = text
633 631
634 632 def __call__(self, info, request):
635 633 if hasattr(request, 'vcs_call'):
636 634 # skip vcs calls
637 635 return
638 636
639 637 repo_name = info['match']['repo_name']
640 638
641 639 repo_name_parts = repo_name.split('/')
642 repo_slugs = [x for x in map(lambda x: repo_name_slug(x), repo_name_parts)]
640 repo_slugs = [x for x in (repo_name_slug(x) for x in repo_name_parts)]
643 641
644 642 if repo_name_parts != repo_slugs:
645 643 # short-skip if the repo-name doesn't follow slug rule
646 644 log.warning('repo_name: %s is different than slug %s', repo_name_parts, repo_slugs)
647 645 return False
648 646
649 647 repo_model = repo.RepoModel()
650 648
651 649 by_name_match = repo_model.get_by_repo_name(repo_name, cache=False)
652 650
653 651 def redirect_if_creating(route_info, db_repo):
654 652 skip_views = ['edit_repo_advanced_delete']
655 653 route = route_info['route']
656 654 # we should skip delete view so we can actually "remove" repositories
657 655 # if they get stuck in creating state.
658 656 if route.name in skip_views:
659 657 return
660 658
661 659 if db_repo.repo_state in [repo.Repository.STATE_PENDING]:
662 660 repo_creating_url = request.route_path(
663 661 'repo_creating', repo_name=db_repo.repo_name)
664 662 raise HTTPFound(repo_creating_url)
665 663
666 664 if by_name_match:
667 665 # register this as request object we can re-use later
668 666 request.db_repo = by_name_match
667 request.db_repo_name = request.db_repo.repo_name
668
669 669 redirect_if_creating(info, by_name_match)
670 670 return True
671 671
672 672 by_id_match = repo_model.get_repo_by_id(repo_name)
673 673 if by_id_match:
674 674 request.db_repo = by_id_match
675 request.db_repo_name = request.db_repo.repo_name
675 676 redirect_if_creating(info, by_id_match)
676 677 return True
677 678
678 679 return False
679 680
680 681
681 682 class RepoForbidArchivedRoutePredicate(object):
682 683 def __init__(self, val, config):
683 684 self.val = val
684 685
685 686 def text(self):
686 return 'repo_forbid_archived = %s' % self.val
687 return f'repo_forbid_archived = {self.val}'
687 688
688 689 phash = text
689 690
690 691 def __call__(self, info, request):
691 692 _ = request.translate
692 693 rhodecode_db_repo = request.db_repo
693 694
694 695 log.debug(
695 696 '%s checking if archived flag for repo for %s',
696 697 self.__class__.__name__, rhodecode_db_repo.repo_name)
697 698
698 699 if rhodecode_db_repo.archived:
699 700 log.warning('Current view is not supported for archived repo:%s',
700 701 rhodecode_db_repo.repo_name)
701 702
702 703 h.flash(
703 704 h.literal(_('Action not supported for archived repository.')),
704 705 category='warning')
705 706 summary_url = request.route_path(
706 707 'repo_summary', repo_name=rhodecode_db_repo.repo_name)
707 708 raise HTTPFound(summary_url)
708 709 return True
709 710
710 711
711 712 class RepoTypeRoutePredicate(object):
712 713 def __init__(self, val, config):
713 714 self.val = val or ['hg', 'git', 'svn']
714 715
715 716 def text(self):
716 return 'repo_accepted_type = %s' % self.val
717 return f'repo_accepted_type = {self.val}'
717 718
718 719 phash = text
719 720
720 721 def __call__(self, info, request):
721 722 if hasattr(request, 'vcs_call'):
722 723 # skip vcs calls
723 724 return
724 725
725 726 rhodecode_db_repo = request.db_repo
726 727
727 728 log.debug(
728 729 '%s checking repo type for %s in %s',
729 730 self.__class__.__name__, rhodecode_db_repo.repo_type, self.val)
730 731
731 732 if rhodecode_db_repo.repo_type in self.val:
732 733 return True
733 734 else:
734 735 log.warning('Current view is not supported for repo type:%s',
735 736 rhodecode_db_repo.repo_type)
736 737 return False
737 738
738 739
739 740 class RepoGroupRoutePredicate(object):
740 741 def __init__(self, val, config):
741 742 self.val = val
742 743
743 744 def text(self):
744 return 'repo_group_route = %s' % self.val
745 return f'repo_group_route = {self.val}'
745 746
746 747 phash = text
747 748
748 749 def __call__(self, info, request):
749 750 if hasattr(request, 'vcs_call'):
750 751 # skip vcs calls
751 752 return
752 753
753 754 repo_group_name = info['match']['repo_group_name']
754 755
755 756 repo_group_name_parts = repo_group_name.split('/')
756 repo_group_slugs = [x for x in map(lambda x: repo_name_slug(x), repo_group_name_parts)]
757 repo_group_slugs = [x for x in [repo_name_slug(x) for x in repo_group_name_parts]]
757 758 if repo_group_name_parts != repo_group_slugs:
758 759 # short-skip if the repo-name doesn't follow slug rule
759 760 log.warning('repo_group_name: %s is different than slug %s', repo_group_name_parts, repo_group_slugs)
760 761 return False
761 762
762 763 repo_group_model = repo_group.RepoGroupModel()
763 764 by_name_match = repo_group_model.get_by_group_name(repo_group_name, cache=False)
764 765
765 766 if by_name_match:
766 767 # register this as request object we can re-use later
767 768 request.db_repo_group = by_name_match
769 request.db_repo_group_name = request.db_repo_group.group_name
768 770 return True
769 771
770 772 return False
771 773
772 774
773 775 class UserGroupRoutePredicate(object):
774 776 def __init__(self, val, config):
775 777 self.val = val
776 778
777 779 def text(self):
778 return 'user_group_route = %s' % self.val
780 return f'user_group_route = {self.val}'
779 781
780 782 phash = text
781 783
782 784 def __call__(self, info, request):
783 785 if hasattr(request, 'vcs_call'):
784 786 # skip vcs calls
785 787 return
786 788
787 789 user_group_id = info['match']['user_group_id']
788 790 user_group_model = user_group.UserGroup()
789 791 by_id_match = user_group_model.get(user_group_id, cache=False)
790 792
791 793 if by_id_match:
792 794 # register this as request object we can re-use later
793 795 request.db_user_group = by_id_match
794 796 return True
795 797
796 798 return False
797 799
798 800
799 801 class UserRoutePredicateBase(object):
800 802 supports_default = None
801 803
802 804 def __init__(self, val, config):
803 805 self.val = val
804 806
805 807 def text(self):
806 808 raise NotImplementedError()
807 809
808 810 def __call__(self, info, request):
809 811 if hasattr(request, 'vcs_call'):
810 812 # skip vcs calls
811 813 return
812 814
813 815 user_id = info['match']['user_id']
814 816 user_model = user.User()
815 817 by_id_match = user_model.get(user_id, cache=False)
816 818
817 819 if by_id_match:
818 820 # register this as request object we can re-use later
819 821 request.db_user = by_id_match
820 822 request.db_user_supports_default = self.supports_default
821 823 return True
822 824
823 825 return False
824 826
825 827
826 828 class UserRoutePredicate(UserRoutePredicateBase):
827 829 supports_default = False
828 830
829 831 def text(self):
830 return 'user_route = %s' % self.val
832 return f'user_route = {self.val}'
831 833
832 834 phash = text
833 835
834 836
835 837 class UserRouteWithDefaultPredicate(UserRoutePredicateBase):
836 838 supports_default = True
837 839
838 840 def text(self):
839 return 'user_with_default_route = %s' % self.val
841 return f'user_with_default_route = {self.val}'
840 842
841 843 phash = text
842 844
843 845
844 846 def includeme(config):
845 847 config.add_route_predicate(
846 848 'repo_route', RepoRoutePredicate)
847 849 config.add_route_predicate(
848 850 'repo_accepted_types', RepoTypeRoutePredicate)
849 851 config.add_route_predicate(
850 852 'repo_forbid_when_archived', RepoForbidArchivedRoutePredicate)
851 853 config.add_route_predicate(
852 854 'repo_group_route', RepoGroupRoutePredicate)
853 855 config.add_route_predicate(
854 856 'user_group_route', UserGroupRoutePredicate)
855 857 config.add_route_predicate(
856 858 'user_route_with_default', UserRouteWithDefaultPredicate)
857 859 config.add_route_predicate(
858 860 'user_route', UserRoutePredicate)
General Comments 0
You need to be logged in to leave comments. Login now