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