##// END OF EJS Templates
fix(file-caching): fixed cases when old cache was used before changes to operate on bytestrings
super-admin -
r5651:bad147da tip default
parent child Browse files
Show More
@@ -395,7 +395,7 class RepoAppView(BaseAppView):
395 395 settings = settings_model.get_repo_settings_inherited()
396 396 return settings.get(settings_key, default)
397 397
398 def _get_readme_data(self, db_repo, renderer_type, commit_id=None, path="/"):
398 def _get_readme_data(self, db_repo, renderer_type, commit_id=None, path="/", nodes=None):
399 399 log.debug("Looking for README file at path %s", path)
400 400 if commit_id:
401 401 landing_commit_id = commit_id
@@ -413,16 +413,14 class RepoAppView(BaseAppView):
413 413
414 414 @region.conditional_cache_on_arguments(namespace=cache_namespace_uid)
415 415 def generate_repo_readme(
416 repo_id, _commit_id, _repo_name, _readme_search_path, _renderer_type
416 _repo_id, _commit_id, _repo_name, _readme_search_path, _renderer_type
417 417 ):
418 readme_data = None
419 readme_filename = None
418 _readme_data = None
419 _readme_filename = None
420 420
421 421 commit = db_repo.get_commit(_commit_id)
422 422 log.debug("Searching for a README file at commit %s.", _commit_id)
423 readme_node = ReadmeFinder(_renderer_type).search(
424 commit, path=_readme_search_path
425 )
423 readme_node = ReadmeFinder(_renderer_type).search(commit, path=_readme_search_path, nodes=nodes)
426 424
427 425 if readme_node:
428 426 log.debug("Found README node: %s", readme_node)
@@ -442,19 +440,19 class RepoAppView(BaseAppView):
442 440 ),
443 441 }
444 442
445 readme_data = self._render_readme_or_none(
443 _readme_data = self._render_readme_or_none(
446 444 commit, readme_node, relative_urls
447 445 )
448 readme_filename = readme_node.str_path
446 _readme_filename = readme_node.str_path
449 447
450 return readme_data, readme_filename
448 return _readme_data, _readme_filename
451 449
452 450 readme_data, readme_filename = generate_repo_readme(
453 451 db_repo.repo_id,
454 452 landing_commit_id,
455 453 db_repo.repo_name,
456 454 path,
457 renderer_type,
455 renderer_type
458 456 )
459 457
460 458 compute_time = time.time() - start
This diff has been collapsed as it changes many lines, (995 lines changed) Show them Hide them
@@ -40,25 +40,34 from rhodecode.lib import diffs, helpers
40 40 from rhodecode.lib import audit_logger
41 41 from rhodecode.lib.hash_utils import sha1_safe
42 42 from rhodecode.lib.archive_cache import (
43 get_archival_cache_store, get_archival_config, ArchiveCacheGenerationLock, archive_iterator)
43 get_archival_cache_store,
44 get_archival_config,
45 ArchiveCacheGenerationLock,
46 archive_iterator,
47 )
44 48 from rhodecode.lib.str_utils import safe_bytes, convert_special_chars
45 49 from rhodecode.lib.view_utils import parse_path_ref
46 50 from rhodecode.lib.exceptions import NonRelativePathError
47 from rhodecode.lib.codeblocks import (
48 filenode_as_lines_tokens, filenode_as_annotated_lines_tokens)
51 from rhodecode.lib.codeblocks import filenode_as_lines_tokens, filenode_as_annotated_lines_tokens
49 52 from rhodecode.lib.utils2 import convert_line_endings, detect_mode
50 53 from rhodecode.lib.type_utils import str2bool
51 54 from rhodecode.lib.str_utils import safe_str, safe_int, header_safe_str
52 from rhodecode.lib.auth import (
53 LoginRequired, HasRepoPermissionAnyDecorator, CSRFRequired)
55 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator, CSRFRequired
54 56 from rhodecode.lib.vcs import path as vcspath
55 57 from rhodecode.lib.vcs.backends.base import EmptyCommit
56 58 from rhodecode.lib.vcs.conf import settings
57 59 from rhodecode.lib.vcs.nodes import FileNode
58 60 from rhodecode.lib.vcs.exceptions import (
59 RepositoryError, CommitDoesNotExistError, EmptyRepositoryError,
60 ImproperArchiveTypeError, VCSError, NodeAlreadyExistsError,
61 NodeDoesNotExistError, CommitError, NodeError)
61 RepositoryError,
62 CommitDoesNotExistError,
63 EmptyRepositoryError,
64 ImproperArchiveTypeError,
65 VCSError,
66 NodeAlreadyExistsError,
67 NodeDoesNotExistError,
68 CommitError,
69 NodeError,
70 )
62 71
63 72 from rhodecode.model.scm import ScmModel
64 73 from rhodecode.model.db import Repository
@@ -66,17 +75,17 from rhodecode.model.db import Repositor
66 75 log = logging.getLogger(__name__)
67 76
68 77
69 def get_archive_name(db_repo_id, db_repo_name, commit_sha, ext, subrepos=False, path_sha='', with_hash=True):
78 def get_archive_name(db_repo_id, db_repo_name, commit_sha, ext, subrepos=False, path_sha="", with_hash=True):
70 79 # original backward compat name of archive
71 clean_name = safe_str(convert_special_chars(db_repo_name).replace('/', '_'))
80 clean_name = safe_str(convert_special_chars(db_repo_name).replace("/", "_"))
72 81
73 82 # e.g vcsserver-id-abcd-sub-1-abcfdef-archive-all.zip
74 83 # vcsserver-id-abcd-sub-0-abcfdef-COMMIT_SHA-PATH_SHA.zip
75 84 id_sha = sha1_safe(str(db_repo_id))[:4]
76 sub_repo = 'sub-1' if subrepos else 'sub-0'
77 commit = commit_sha if with_hash else 'archive'
78 path_marker = (path_sha if with_hash else '') or 'all'
79 archive_name = f'{clean_name}-id-{id_sha}-{sub_repo}-{commit}-{path_marker}{ext}'
85 sub_repo = "sub-1" if subrepos else "sub-0"
86 commit = commit_sha if with_hash else "archive"
87 path_marker = (path_sha if with_hash else "") or "all"
88 archive_name = f"{clean_name}-id-{id_sha}-{sub_repo}-{commit}-{path_marker}{ext}"
80 89
81 90 return archive_name
82 91
@@ -86,16 +95,15 def get_path_sha(at_path):
86 95
87 96
88 97 def _get_archive_spec(fname):
89 log.debug('Detecting archive spec for: `%s`', fname)
98 log.debug("Detecting archive spec for: `%s`", fname)
90 99
91 100 fileformat = None
92 101 ext = None
93 102 content_type = None
94 103 for a_type, content_type, extension in settings.ARCHIVE_SPECS:
95
96 104 if fname.endswith(extension):
97 105 fileformat = a_type
98 log.debug('archive is of type: %s', fileformat)
106 log.debug("archive is of type: %s", fileformat)
99 107 ext = extension
100 108 break
101 109
@@ -103,13 +111,12 def _get_archive_spec(fname):
103 111 raise ValueError()
104 112
105 113 # left over part of whole fname is the commit
106 commit_id = fname[:-len(ext)]
114 commit_id = fname[: -len(ext)]
107 115
108 116 return commit_id, ext, fileformat, content_type
109 117
110 118
111 119 class RepoFilesView(RepoAppView):
112
113 120 @staticmethod
114 121 def adjust_file_path_for_svn(f_path, repo):
115 122 """
@@ -118,13 +125,11 class RepoFilesView(RepoAppView):
118 125 This is mainly based on prefix matching of the recognized tags and
119 126 branches in the underlying repository.
120 127 """
121 tags_and_branches = itertools.chain(
122 repo.branches.keys(),
123 repo.tags.keys())
128 tags_and_branches = itertools.chain(repo.branches.keys(), repo.tags.keys())
124 129 tags_and_branches = sorted(tags_and_branches, key=len, reverse=True)
125 130
126 131 for name in tags_and_branches:
127 if f_path.startswith(f'{name}/'):
132 if f_path.startswith(f"{name}/"):
128 133 f_path = vcspath.relpath(f_path, name)
129 134 break
130 135 return f_path
@@ -135,59 +140,52 class RepoFilesView(RepoAppView):
135 140 c.enable_downloads = self.db_repo.enable_downloads
136 141 return c
137 142
138 def _ensure_not_locked(self, commit_id='tip'):
143 def _ensure_not_locked(self, commit_id="tip"):
139 144 _ = self.request.translate
140 145
141 146 repo = self.db_repo
142 147 if repo.enable_locking and repo.locked[0]:
143 h.flash(_('This repository has been locked by %s on %s')
144 % (h.person_by_id(repo.locked[0]),
145 h.format_date(h.time_to_datetime(repo.locked[1]))),
146 'warning')
147 files_url = h.route_path(
148 'repo_files:default_path',
149 repo_name=self.db_repo_name, commit_id=commit_id)
148 h.flash(
149 _("This repository has been locked by %s on %s")
150 % (h.person_by_id(repo.locked[0]), h.format_date(h.time_to_datetime(repo.locked[1]))),
151 "warning",
152 )
153 files_url = h.route_path("repo_files:default_path", repo_name=self.db_repo_name, commit_id=commit_id)
150 154 raise HTTPFound(files_url)
151 155
152 def forbid_non_head(self, is_head, f_path, commit_id='tip', json_mode=False):
156 def forbid_non_head(self, is_head, f_path, commit_id="tip", json_mode=False):
153 157 _ = self.request.translate
154 158
155 159 if not is_head:
156 message = _('Cannot modify file. '
157 'Given commit `{}` is not head of a branch.').format(commit_id)
158 h.flash(message, category='warning')
160 message = _("Cannot modify file. " "Given commit `{}` is not head of a branch.").format(commit_id)
161 h.flash(message, category="warning")
159 162
160 163 if json_mode:
161 164 return message
162 165
163 files_url = h.route_path(
164 'repo_files', repo_name=self.db_repo_name, commit_id=commit_id,
165 f_path=f_path)
166 files_url = h.route_path("repo_files", repo_name=self.db_repo_name, commit_id=commit_id, f_path=f_path)
166 167 raise HTTPFound(files_url)
167 168
168 def check_branch_permission(self, branch_name, commit_id='tip', json_mode=False):
169 def check_branch_permission(self, branch_name, commit_id="tip", json_mode=False):
169 170 _ = self.request.translate
170 171
171 rule, branch_perm = self._rhodecode_user.get_rule_and_branch_permission(
172 self.db_repo_name, branch_name)
173 if branch_perm and branch_perm not in ['branch.push', 'branch.push_force']:
174 message = _('Branch `{}` changes forbidden by rule {}.').format(
175 h.escape(branch_name), h.escape(rule))
176 h.flash(message, 'warning')
172 rule, branch_perm = self._rhodecode_user.get_rule_and_branch_permission(self.db_repo_name, branch_name)
173 if branch_perm and branch_perm not in ["branch.push", "branch.push_force"]:
174 message = _("Branch `{}` changes forbidden by rule {}.").format(h.escape(branch_name), h.escape(rule))
175 h.flash(message, "warning")
177 176
178 177 if json_mode:
179 178 return message
180 179
181 files_url = h.route_path(
182 'repo_files:default_path', repo_name=self.db_repo_name, commit_id=commit_id)
180 files_url = h.route_path("repo_files:default_path", repo_name=self.db_repo_name, commit_id=commit_id)
183 181
184 182 raise HTTPFound(files_url)
185 183
186 184 def _get_commit_and_path(self):
187 185 default_commit_id = self.db_repo.landing_ref_name
188 default_f_path = '/'
186 default_f_path = "/"
189 187
190 commit_id = self.request.matchdict.get('commit_id', default_commit_id)
188 commit_id = self.request.matchdict.get("commit_id", default_commit_id)
191 189 f_path = self._get_f_path(self.request.matchdict, default_f_path)
192 190
193 191 bytes_path = safe_bytes(f_path)
@@ -195,8 +193,8 class RepoFilesView(RepoAppView):
195 193
196 194 @classmethod
197 195 def _get_default_encoding(cls, c):
198 enc_list = getattr(c, 'default_encodings', [])
199 return enc_list[0] if enc_list else 'UTF-8'
196 enc_list = getattr(c, "default_encodings", [])
197 return enc_list[0] if enc_list else "UTF-8"
200 198
201 199 def _get_commit_or_redirect(self, commit_id, redirect_after=True):
202 200 """
@@ -215,31 +213,25 class RepoFilesView(RepoAppView):
215 213 return None
216 214
217 215 add_new = upload_new = ""
218 if h.HasRepoPermissionAny(
219 'repository.write', 'repository.admin')(self.db_repo_name):
220 _url = h.route_path(
221 'repo_files_add_file',
222 repo_name=self.db_repo_name, commit_id=0, f_path='')
223 add_new = h.link_to(
224 _('add a new file'), _url, class_="alert-link")
216 if h.HasRepoPermissionAny("repository.write", "repository.admin")(self.db_repo_name):
217 _url = h.route_path("repo_files_add_file", repo_name=self.db_repo_name, commit_id=0, f_path="")
218 add_new = h.link_to(_("add a new file"), _url, class_="alert-link")
225 219
226 _url_upld = h.route_path(
227 'repo_files_upload_file',
228 repo_name=self.db_repo_name, commit_id=0, f_path='')
229 upload_new = h.link_to(
230 _('upload a new file'), _url_upld, class_="alert-link")
220 _url_upld = h.route_path("repo_files_upload_file", repo_name=self.db_repo_name, commit_id=0, f_path="")
221 upload_new = h.link_to(_("upload a new file"), _url_upld, class_="alert-link")
231 222
232 h.flash(h.literal(
233 _('There are no files yet. Click here to %s or %s.') % (add_new, upload_new)), category='warning')
234 raise HTTPFound(
235 h.route_path('repo_summary', repo_name=self.db_repo_name))
223 h.flash(
224 h.literal(_("There are no files yet. Click here to %s or %s.") % (add_new, upload_new)),
225 category="warning",
226 )
227 raise HTTPFound(h.route_path("repo_summary", repo_name=self.db_repo_name))
236 228
237 229 except (CommitDoesNotExistError, LookupError) as e:
238 msg = _('No such commit exists for this repository. Commit: {}').format(commit_id)
239 h.flash(msg, category='error')
230 msg = _("No such commit exists for this repository. Commit: {}").format(commit_id)
231 h.flash(msg, category="error")
240 232 raise HTTPNotFound()
241 233 except RepositoryError as e:
242 h.flash(h.escape(safe_str(e)), category='error')
234 h.flash(h.escape(safe_str(e)), category="error")
243 235 raise HTTPNotFound()
244 236
245 237 def _get_filenode_or_redirect(self, commit_obj, path, pre_load=None):
@@ -252,22 +244,22 class RepoFilesView(RepoAppView):
252 244 try:
253 245 file_node = commit_obj.get_node(path, pre_load=pre_load)
254 246 if file_node.is_dir():
255 raise RepositoryError('The given path is a directory')
247 raise RepositoryError("The given path is a directory")
256 248 except CommitDoesNotExistError:
257 log.exception('No such commit exists for this repository')
258 h.flash(_('No such commit exists for this repository'), category='error')
249 log.exception("No such commit exists for this repository")
250 h.flash(_("No such commit exists for this repository"), category="error")
259 251 raise HTTPNotFound()
260 252 except RepositoryError as e:
261 log.warning('Repository error while fetching filenode `%s`. Err:%s', path, e)
262 h.flash(h.escape(safe_str(e)), category='error')
253 log.warning("Repository error while fetching filenode `%s`. Err:%s", path, e)
254 h.flash(h.escape(safe_str(e)), category="error")
263 255 raise HTTPNotFound()
264 256
265 257 return file_node
266 258
267 259 def _is_valid_head(self, commit_id, repo, landing_ref):
268 branch_name = sha_commit_id = ''
260 branch_name = sha_commit_id = ""
269 261 is_head = False
270 log.debug('Checking if commit_id `%s` is a head for %s.', commit_id, repo)
262 log.debug("Checking if commit_id `%s` is a head for %s.", commit_id, repo)
271 263
272 264 for _branch_name, branch_commit_id in repo.branches.items():
273 265 # simple case we pass in branch name, it's a HEAD
@@ -303,39 +295,39 class RepoFilesView(RepoAppView):
303 295 return branch_name, sha_commit_id, is_head
304 296
305 297 def _get_tree_at_commit(self, c, commit_id, f_path, full_load=False, at_rev=None):
306
307 298 repo_id = self.db_repo.repo_id
308 299 force_recache = self.get_recache_flag()
309 300
310 cache_seconds = safe_int(
311 rhodecode.CONFIG.get('rc_cache.cache_repo.expiration_time'))
301 cache_seconds = rhodecode.ConfigGet().get_int("rc_cache.cache_repo.expiration_time")
312 302 cache_on = not force_recache and cache_seconds > 0
303
313 304 log.debug(
314 'Computing FILE TREE for repo_id %s commit_id `%s` and path `%s`'
315 'with caching: %s[TTL: %ss]' % (
316 repo_id, commit_id, f_path, cache_on, cache_seconds or 0))
305 "Computing FILE TREE for repo_id %s commit_id `%s` and path `%s`"
306 "with caching: %s[TTL: %ss]" % (repo_id, commit_id, f_path, cache_on, cache_seconds or 0)
307 )
317 308
318 cache_namespace_uid = f'repo.{rc_cache.FILE_TREE_CACHE_VER}.{repo_id}'
319 region = rc_cache.get_or_create_region('cache_repo', cache_namespace_uid)
309 cache_namespace_uid = f"repo.{rc_cache.FILE_TREE_CACHE_VER}.{repo_id}"
310 region = rc_cache.get_or_create_region("cache_repo", cache_namespace_uid)
320 311
321 312 @region.conditional_cache_on_arguments(namespace=cache_namespace_uid, condition=cache_on)
322 313 def compute_file_tree(_name_hash, _repo_id, _commit_id, _f_path, _full_load, _at_rev):
323 log.debug('Generating cached file tree at for repo_id: %s, %s, %s',
324 _repo_id, _commit_id, _f_path)
314 log.debug("Generating cached file tree at for repo_id: %s, %s, %s", _repo_id, _commit_id, _f_path)
325 315
326 316 c.full_load = _full_load
327 317 return render(
328 'rhodecode:templates/files/files_browser_tree.mako',
329 self._get_template_context(c), self.request, _at_rev)
318 "rhodecode:templates/files/files_browser_tree.mako",
319 self._get_template_context(c),
320 self.request,
321 _at_rev,
322 )
330 323
331 324 return compute_file_tree(
332 self.db_repo.repo_name_hash, self.db_repo.repo_id, commit_id, f_path, full_load, at_rev)
325 self.db_repo.repo_name_hash, self.db_repo.repo_id, commit_id, f_path, full_load, at_rev
326 )
333 327
334 328 def create_pure_path(self, *parts):
335 329 # Split paths and sanitize them, removing any ../ etc
336 sanitized_path = [
337 x for x in pathlib.PurePath(*parts).parts
338 if x not in ['.', '..']]
330 sanitized_path = [x for x in pathlib.PurePath(*parts).parts if x not in [".", ".."]]
339 331
340 332 pure_path = pathlib.PurePath(*sanitized_path)
341 333 return pure_path
@@ -343,10 +335,7 class RepoFilesView(RepoAppView):
343 335 def _is_lf_enabled(self, target_repo):
344 336 lf_enabled = False
345 337
346 lf_key_for_vcs_map = {
347 'hg': 'extensions_largefiles',
348 'git': 'vcs_git_lfs_enabled'
349 }
338 lf_key_for_vcs_map = {"hg": "extensions_largefiles", "git": "vcs_git_lfs_enabled"}
350 339
351 340 lf_key_for_vcs = lf_key_for_vcs_map.get(target_repo.repo_type)
352 341
@@ -356,66 +345,77 class RepoFilesView(RepoAppView):
356 345 return lf_enabled
357 346
358 347 @LoginRequired()
359 @HasRepoPermissionAnyDecorator(
360 'repository.read', 'repository.write', 'repository.admin')
348 @HasRepoPermissionAnyDecorator("repository.read", "repository.write", "repository.admin")
361 349 def repo_archivefile(self):
362 350 # archive cache config
363 351 from rhodecode import CONFIG
352
364 353 _ = self.request.translate
365 354 self.load_default_context()
366 355
367 subrepos = self.request.GET.get('subrepos') == 'true'
368 with_hash = str2bool(self.request.GET.get('with_hash', '1'))
356 subrepos = self.request.GET.get("subrepos") == "true"
357 with_hash = str2bool(self.request.GET.get("with_hash", "1"))
369 358
370 default_at_path = '/'
371 fname = self.request.matchdict['fname']
372 at_path = self.request.GET.get('at_path') or default_at_path
359 default_at_path = "/"
360 fname = self.request.matchdict["fname"]
361 at_path = self.request.GET.get("at_path") or default_at_path
373 362
374 363 if not self.db_repo.enable_downloads:
375 return Response(_('Downloads disabled'))
364 return Response(_("Downloads disabled"))
376 365
377 366 try:
378 367 commit_id, ext, file_format, content_type = _get_archive_spec(fname)
379 368 except ValueError:
380 return Response(_('Unknown archive type for: `{}`').format(h.escape(fname)))
369 return Response(_("Unknown archive type for: `{}`").format(h.escape(fname)))
381 370
382 371 try:
383 372 commit = self.rhodecode_vcs_repo.get_commit(commit_id)
384 373 except CommitDoesNotExistError:
385 return Response(_('Unknown commit_id {}').format(
386 h.escape(commit_id)))
374 return Response(_("Unknown commit_id {}").format(h.escape(commit_id)))
387 375 except EmptyRepositoryError:
388 return Response(_('Empty repository'))
376 return Response(_("Empty repository"))
389 377
390 378 # we used a ref, or a shorter version, lets redirect client ot use explicit hash
391 379 if commit_id != commit.raw_id:
392 fname=f'{commit.raw_id}{ext}'
380 fname = f"{commit.raw_id}{ext}"
393 381 raise HTTPFound(self.request.current_route_path(fname=fname))
394 382
395 383 try:
396 384 at_path = commit.get_node(safe_bytes(at_path)).path or default_at_path
397 385 except Exception:
398 return Response(_('No node at path {} for this repository').format(h.escape(at_path)))
386 return Response(_("No node at path {} for this repository").format(h.escape(at_path)))
399 387
400 388 path_sha = get_path_sha(at_path)
401 389
402 390 # used for cache etc, consistent unique archive name
403 391 archive_name_key = get_archive_name(
404 self.db_repo.repo_id, self.db_repo_name, commit_sha=commit.short_id, ext=ext, subrepos=subrepos,
405 path_sha=path_sha, with_hash=True)
392 self.db_repo.repo_id,
393 self.db_repo_name,
394 commit_sha=commit.short_id,
395 ext=ext,
396 subrepos=subrepos,
397 path_sha=path_sha,
398 with_hash=True,
399 )
406 400
407 401 if not with_hash:
408 path_sha = ''
402 path_sha = ""
409 403
410 404 # what end client gets served
411 405 response_archive_name = get_archive_name(
412 self.db_repo.repo_id, self.db_repo_name, commit_sha=commit.short_id, ext=ext, subrepos=subrepos,
413 path_sha=path_sha, with_hash=with_hash)
406 self.db_repo.repo_id,
407 self.db_repo_name,
408 commit_sha=commit.short_id,
409 ext=ext,
410 subrepos=subrepos,
411 path_sha=path_sha,
412 with_hash=with_hash,
413 )
414 414
415 415 # remove extension from our archive directory name
416 archive_dir_name = response_archive_name[:-len(ext)]
416 archive_dir_name = response_archive_name[: -len(ext)]
417 417
418 archive_cache_disable = self.request.GET.get('no_cache')
418 archive_cache_disable = self.request.GET.get("no_cache")
419 419
420 420 d_cache = get_archival_cache_store(config=CONFIG)
421 421
@@ -423,29 +423,38 class RepoFilesView(RepoAppView):
423 423 d_cache_conf = get_archival_config(config=CONFIG)
424 424
425 425 # This is also a cache key, and lock key
426 reentrant_lock_key = archive_name_key + '.lock'
426 reentrant_lock_key = archive_name_key + ".lock"
427 427
428 428 use_cached_archive = False
429 429 if not archive_cache_disable and archive_name_key in d_cache:
430 430 reader, metadata = d_cache.fetch(archive_name_key)
431 431
432 432 use_cached_archive = True
433 log.debug('Found cached archive as key=%s tag=%s, serving archive from cache reader=%s',
434 archive_name_key, metadata, reader.name)
433 log.debug(
434 "Found cached archive as key=%s tag=%s, serving archive from cache reader=%s",
435 archive_name_key,
436 metadata,
437 reader.name,
438 )
435 439 else:
436 440 reader = None
437 log.debug('Archive with key=%s is not yet cached, creating one now...', archive_name_key)
441 log.debug("Archive with key=%s is not yet cached, creating one now...", archive_name_key)
438 442
439 443 if not reader:
440 444 # generate new archive, as previous was not found in the cache
441 445 try:
442 446 with d_cache.get_lock(reentrant_lock_key):
443 447 try:
444 commit.archive_repo(archive_name_key, archive_dir_name=archive_dir_name,
445 kind=file_format, subrepos=subrepos,
446 archive_at_path=at_path, cache_config=d_cache_conf)
448 commit.archive_repo(
449 archive_name_key,
450 archive_dir_name=archive_dir_name,
451 kind=file_format,
452 subrepos=subrepos,
453 archive_at_path=at_path,
454 cache_config=d_cache_conf,
455 )
447 456 except ImproperArchiveTypeError:
448 return _('Unknown archive type')
457 return _("Unknown archive type")
449 458
450 459 except ArchiveCacheGenerationLock:
451 460 retry_after = round(random.uniform(0.3, 3.0), 1)
@@ -464,7 +473,7 class RepoFilesView(RepoAppView):
464 473 reader, metadata = d_cache.fetch(archive_name_key, retry=True, retry_attempts=30)
465 474
466 475 response = Response(app_iter=archive_iterator(reader))
467 response.content_disposition = f'attachment; filename={response_archive_name}'
476 response.content_disposition = f"attachment; filename={response_archive_name}"
468 477 response.content_type = str(content_type)
469 478
470 479 try:
@@ -472,23 +481,25 class RepoFilesView(RepoAppView):
472 481 finally:
473 482 # store download action
474 483 audit_logger.store_web(
475 'repo.archive.download', action_data={
476 'user_agent': self.request.user_agent,
477 'archive_name': archive_name_key,
478 'archive_spec': fname,
479 'archive_cached': use_cached_archive},
484 "repo.archive.download",
485 action_data={
486 "user_agent": self.request.user_agent,
487 "archive_name": archive_name_key,
488 "archive_spec": fname,
489 "archive_cached": use_cached_archive,
490 },
480 491 user=self._rhodecode_user,
481 492 repo=self.db_repo,
482 commit=True
493 commit=True,
483 494 )
484 495
485 496 def _get_file_node(self, commit_id, f_path):
486 if commit_id not in ['', None, 'None', '0' * 12, '0' * 40]:
497 if commit_id not in ["", None, "None", "0" * 12, "0" * 40]:
487 498 commit = self.rhodecode_vcs_repo.get_commit(commit_id=commit_id)
488 499 try:
489 500 node = commit.get_node(safe_bytes(f_path))
490 501 if node.is_dir():
491 raise NodeError(f'{node} path is a {type(node)} not a file')
502 raise NodeError(f"{node} path is a {type(node)} not a file")
492 503 except NodeDoesNotExistError:
493 504 commit = EmptyCommit(
494 505 commit_id=commit_id,
@@ -497,46 +508,43 class RepoFilesView(RepoAppView):
497 508 alias=commit.repository.alias,
498 509 message=commit.message,
499 510 author=commit.author,
500 date=commit.date)
501 node = FileNode(safe_bytes(f_path), b'', commit=commit)
511 date=commit.date,
512 )
513 node = FileNode(safe_bytes(f_path), b"", commit=commit)
502 514 else:
503 commit = EmptyCommit(
504 repo=self.rhodecode_vcs_repo,
505 alias=self.rhodecode_vcs_repo.alias)
506 node = FileNode(safe_bytes(f_path), b'', commit=commit)
515 commit = EmptyCommit(repo=self.rhodecode_vcs_repo, alias=self.rhodecode_vcs_repo.alias)
516 node = FileNode(safe_bytes(f_path), b"", commit=commit)
507 517 return node
508 518
509 519 @LoginRequired()
510 @HasRepoPermissionAnyDecorator(
511 'repository.read', 'repository.write', 'repository.admin')
520 @HasRepoPermissionAnyDecorator("repository.read", "repository.write", "repository.admin")
512 521 def repo_files_diff(self):
513 522 c = self.load_default_context()
514 523 f_path = self._get_f_path(self.request.matchdict)
515 diff1 = self.request.GET.get('diff1', '')
516 diff2 = self.request.GET.get('diff2', '')
524 diff1 = self.request.GET.get("diff1", "")
525 diff2 = self.request.GET.get("diff2", "")
517 526
518 527 path1, diff1 = parse_path_ref(diff1, default_path=f_path)
519 528
520 ignore_whitespace = str2bool(self.request.GET.get('ignorews'))
521 line_context = self.request.GET.get('context', 3)
529 ignore_whitespace = str2bool(self.request.GET.get("ignorews"))
530 line_context = self.request.GET.get("context", 3)
522 531
523 532 if not any((diff1, diff2)):
524 h.flash(
525 'Need query parameter "diff1" or "diff2" to generate a diff.',
526 category='error')
533 h.flash('Need query parameter "diff1" or "diff2" to generate a diff.', category="error")
527 534 raise HTTPBadRequest()
528 535
529 c.action = self.request.GET.get('diff')
530 if c.action not in ['download', 'raw']:
536 c.action = self.request.GET.get("diff")
537 if c.action not in ["download", "raw"]:
531 538 compare_url = h.route_path(
532 'repo_compare',
539 "repo_compare",
533 540 repo_name=self.db_repo_name,
534 source_ref_type='rev',
541 source_ref_type="rev",
535 542 source_ref=diff1,
536 543 target_repo=self.db_repo_name,
537 target_ref_type='rev',
544 target_ref_type="rev",
538 545 target_ref=diff2,
539 _query=dict(f_path=f_path))
546 _query=dict(f_path=f_path),
547 )
540 548 # redirect to new view if we render diff
541 549 raise HTTPFound(compare_url)
542 550
@@ -545,43 +553,34 class RepoFilesView(RepoAppView):
545 553 node2 = self._get_file_node(diff2, f_path)
546 554 except (RepositoryError, NodeError):
547 555 log.exception("Exception while trying to get node from repository")
548 raise HTTPFound(
549 h.route_path('repo_files', repo_name=self.db_repo_name,
550 commit_id='tip', f_path=f_path))
556 raise HTTPFound(h.route_path("repo_files", repo_name=self.db_repo_name, commit_id="tip", f_path=f_path))
551 557
552 if all(isinstance(node.commit, EmptyCommit)
553 for node in (node1, node2)):
558 if all(isinstance(node.commit, EmptyCommit) for node in (node1, node2)):
554 559 raise HTTPNotFound()
555 560
556 561 c.commit_1 = node1.commit
557 562 c.commit_2 = node2.commit
558 563
559 if c.action == 'download':
560 _diff = diffs.get_gitdiff(node1, node2,
561 ignore_whitespace=ignore_whitespace,
562 context=line_context)
564 if c.action == "download":
565 _diff = diffs.get_gitdiff(node1, node2, ignore_whitespace=ignore_whitespace, context=line_context)
563 566 # NOTE: this was using diff_format='gitdiff'
564 diff = diffs.DiffProcessor(_diff, diff_format='newdiff')
567 diff = diffs.DiffProcessor(_diff, diff_format="newdiff")
565 568
566 569 response = Response(self.path_filter.get_raw_patch(diff))
567 response.content_type = 'text/plain'
568 response.content_disposition = (
569 f'attachment; filename={f_path}_{diff1}_vs_{diff2}.diff'
570 )
570 response.content_type = "text/plain"
571 response.content_disposition = f"attachment; filename={f_path}_{diff1}_vs_{diff2}.diff"
571 572 charset = self._get_default_encoding(c)
572 573 if charset:
573 574 response.charset = charset
574 575 return response
575 576
576 elif c.action == 'raw':
577 _diff = diffs.get_gitdiff(node1, node2,
578 ignore_whitespace=ignore_whitespace,
579 context=line_context)
577 elif c.action == "raw":
578 _diff = diffs.get_gitdiff(node1, node2, ignore_whitespace=ignore_whitespace, context=line_context)
580 579 # NOTE: this was using diff_format='gitdiff'
581 diff = diffs.DiffProcessor(_diff, diff_format='newdiff')
580 diff = diffs.DiffProcessor(_diff, diff_format="newdiff")
582 581
583 582 response = Response(self.path_filter.get_raw_patch(diff))
584 response.content_type = 'text/plain'
583 response.content_type = "text/plain"
585 584 charset = self._get_default_encoding(c)
586 585 if charset:
587 586 response.charset = charset
@@ -591,31 +590,32 class RepoFilesView(RepoAppView):
591 590 raise HTTPNotFound()
592 591
593 592 @LoginRequired()
594 @HasRepoPermissionAnyDecorator(
595 'repository.read', 'repository.write', 'repository.admin')
593 @HasRepoPermissionAnyDecorator("repository.read", "repository.write", "repository.admin")
596 594 def repo_files_diff_2way_redirect(self):
597 595 """
598 596 Kept only to make OLD links work
599 597 """
600 598 f_path = self._get_f_path_unchecked(self.request.matchdict)
601 diff1 = self.request.GET.get('diff1', '')
602 diff2 = self.request.GET.get('diff2', '')
599 diff1 = self.request.GET.get("diff1", "")
600 diff2 = self.request.GET.get("diff2", "")
603 601
604 602 if not any((diff1, diff2)):
605 h.flash(
606 'Need query parameter "diff1" or "diff2" to generate a diff.',
607 category='error')
603 h.flash('Need query parameter "diff1" or "diff2" to generate a diff.', category="error")
608 604 raise HTTPBadRequest()
609 605
610 606 compare_url = h.route_path(
611 'repo_compare',
607 "repo_compare",
612 608 repo_name=self.db_repo_name,
613 source_ref_type='rev',
609 source_ref_type="rev",
614 610 source_ref=diff1,
615 target_ref_type='rev',
611 target_ref_type="rev",
616 612 target_ref=diff2,
617 _query=dict(f_path=f_path, diffmode='sideside',
618 target_repo=self.db_repo_name,))
613 _query=dict(
614 f_path=f_path,
615 diffmode="sideside",
616 target_repo=self.db_repo_name,
617 ),
618 )
619 619 raise HTTPFound(compare_url)
620 620
621 621 @LoginRequired()
@@ -629,138 +629,131 class RepoFilesView(RepoAppView):
629 629 landing_url = h.repo_files_by_ref_url(
630 630 c.rhodecode_db_repo.repo_name,
631 631 c.rhodecode_db_repo.repo_type,
632 f_path='',
632 f_path="",
633 633 ref_name=ref_name,
634 commit_id='tip',
635 query=dict(at=ref_name)
634 commit_id="tip",
635 query=dict(at=ref_name),
636 636 )
637 637
638 638 raise HTTPFound(landing_url)
639 639
640 640 @LoginRequired()
641 @HasRepoPermissionAnyDecorator(
642 'repository.read', 'repository.write', 'repository.admin')
641 @HasRepoPermissionAnyDecorator("repository.read", "repository.write", "repository.admin")
643 642 def repo_files(self):
644 643 c = self.load_default_context()
645 644
646 view_name = getattr(self.request.matched_route, 'name', None)
645 view_name = getattr(self.request.matched_route, "name", None)
647 646
648 c.annotate = view_name == 'repo_files:annotated'
647 c.annotate = view_name == "repo_files:annotated"
649 648 # default is false, but .rst/.md files later are auto rendered, we can
650 649 # overwrite auto rendering by setting this GET flag
651 c.renderer = view_name == 'repo_files:rendered' or not self.request.GET.get('no-render', False)
650 c.renderer = view_name == "repo_files:rendered" or not self.request.GET.get("no-render", False)
652 651
653 652 commit_id, f_path, bytes_path = self._get_commit_and_path()
654 653
655 654 c.commit = self._get_commit_or_redirect(commit_id)
656 c.branch = self.request.GET.get('branch', None)
655 c.branch = self.request.GET.get("branch", None)
657 656 c.f_path = f_path
658 at_rev = self.request.GET.get('at')
657 at_rev = self.request.GET.get("at")
659 658
660 659 # files or dirs
661 660 try:
662
663 c.file = c.commit.get_node(bytes_path, pre_load=['is_binary', 'size', 'data'])
661 c.file = c.commit.get_node(bytes_path, pre_load=["is_binary", "size", "data"])
664 662
665 663 c.file_author = True
666 c.file_tree = ''
664 c.file_tree = ""
667 665
668 666 # prev link
669 667 try:
670 668 prev_commit = c.commit.prev(c.branch)
671 669 c.prev_commit = prev_commit
672 c.url_prev = h.route_path('repo_files', repo_name=self.db_repo_name, commit_id=prev_commit.raw_id, f_path=f_path)
670 c.url_prev = h.route_path(
671 "repo_files", repo_name=self.db_repo_name, commit_id=prev_commit.raw_id, f_path=f_path
672 )
673 673 if c.branch:
674 c.url_prev += f'?branch={c.branch}'
674 c.url_prev += f"?branch={c.branch}"
675 675 except (CommitDoesNotExistError, VCSError):
676 c.url_prev = '#'
676 c.url_prev = "#"
677 677 c.prev_commit = EmptyCommit()
678 678
679 679 # next link
680 680 try:
681 681 next_commit = c.commit.next(c.branch)
682 682 c.next_commit = next_commit
683 c.url_next = h.route_path('repo_files', repo_name=self.db_repo_name, commit_id=next_commit.raw_id, f_path=f_path)
683 c.url_next = h.route_path(
684 "repo_files", repo_name=self.db_repo_name, commit_id=next_commit.raw_id, f_path=f_path
685 )
684 686 if c.branch:
685 c.url_next += f'?branch={c.branch}'
687 c.url_next += f"?branch={c.branch}"
686 688 except (CommitDoesNotExistError, VCSError):
687 c.url_next = '#'
689 c.url_next = "#"
688 690 c.next_commit = EmptyCommit()
689 691
690 692 # load file content
691 693 if c.file.is_file():
692
693 694 c.lf_node = {}
694 695
695 696 has_lf_enabled = self._is_lf_enabled(self.db_repo)
696 697 if has_lf_enabled:
697 698 c.lf_node = c.file.get_largefile_node()
698 699
699 c.file_source_page = 'true'
700 c.file_source_page = "true"
700 701 c.file_last_commit = c.file.last_commit
701 702
702 703 c.file_size_too_big = c.file.size > c.visual.cut_off_limit_file
703 704
704 705 if not (c.file_size_too_big or c.file.is_binary):
705 706 if c.annotate: # annotation has precedence over renderer
706 c.annotated_lines = filenode_as_annotated_lines_tokens(
707 c.file
708 )
707 c.annotated_lines = filenode_as_annotated_lines_tokens(c.file)
709 708 else:
710 c.renderer = (
711 c.renderer and h.renderer_from_filename(c.file.path)
712 )
709 c.renderer = c.renderer and h.renderer_from_filename(c.file.path)
713 710 if not c.renderer:
714 711 c.lines = filenode_as_lines_tokens(c.file)
715 712
716 _branch_name, _sha_commit_id, is_head = \
717 self._is_valid_head(commit_id, self.rhodecode_vcs_repo,
718 landing_ref=self.db_repo.landing_ref_name)
713 _branch_name, _sha_commit_id, is_head = self._is_valid_head(
714 commit_id, self.rhodecode_vcs_repo, landing_ref=self.db_repo.landing_ref_name
715 )
719 716 c.on_branch_head = is_head
720 717
721 branch = c.commit.branch if (
722 c.commit.branch and '/' not in c.commit.branch) else None
718 branch = c.commit.branch if (c.commit.branch and "/" not in c.commit.branch) else None
723 719 c.branch_or_raw_id = branch or c.commit.raw_id
724 720 c.branch_name = c.commit.branch or h.short_id(c.commit.raw_id)
725 721
726 722 author = c.file_last_commit.author
727 c.authors = [[
728 h.email(author),
729 h.person(author, 'username_or_name_or_email'),
730 1
731 ]]
723 c.authors = [[h.email(author), h.person(author, "username_or_name_or_email"), 1]]
732 724
733 else: # load tree content at path
734 c.file_source_page = 'false'
725 else: # load tree content (dir content) at path
726 c.file_source_page = "false"
735 727 c.authors = []
728
729 dir_node = c.file
730 c.file_nodes = dir_node.commit.get_nodes(dir_node.bytes_path, pre_load=dir_node.default_pre_load)
736 731 # this loads a simple tree without metadata to speed things up
737 732 # later via ajax we call repo_nodetree_full and fetch whole
738 733 c.file_tree = self._get_tree_at_commit(c, c.commit.raw_id, f_path, at_rev=at_rev)
739 734
740 c.readme_data, c.readme_file = \
741 self._get_readme_data(self.db_repo, c.visual.default_renderer, c.commit.raw_id, bytes_path)
735 c.readme_data, c.readme_file = self._get_readme_data(
736 self.db_repo, c.visual.default_renderer, c.commit.raw_id, bytes_path, nodes=c.file_nodes
737 )
742 738
743 739 except RepositoryError as e:
744 h.flash(h.escape(safe_str(e)), category='error')
740 h.flash(h.escape(safe_str(e)), category="error")
745 741 raise HTTPNotFound()
746 742
747 if self.request.environ.get('HTTP_X_PJAX'):
748 html = render('rhodecode:templates/files/files_pjax.mako',
749 self._get_template_context(c), self.request)
743 if self.request.environ.get("HTTP_X_PJAX"):
744 html = render("rhodecode:templates/files/files_pjax.mako", self._get_template_context(c), self.request)
750 745 else:
751 html = render('rhodecode:templates/files/files.mako',
752 self._get_template_context(c), self.request)
746 html = render("rhodecode:templates/files/files.mako", self._get_template_context(c), self.request)
753 747 return Response(html)
754 748
755 @HasRepoPermissionAnyDecorator(
756 'repository.read', 'repository.write', 'repository.admin')
749 @HasRepoPermissionAnyDecorator("repository.read", "repository.write", "repository.admin")
757 750 def repo_files_annotated_previous(self):
758 751 self.load_default_context()
759 752
760 753 commit_id, bytes_path, bytes_path = self._get_commit_and_path()
761 754 commit = self._get_commit_or_redirect(commit_id)
762 755 prev_commit_id = commit.raw_id
763 line_anchor = self.request.GET.get('line_anchor')
756 line_anchor = self.request.GET.get("line_anchor")
764 757 is_file = False
765 758 try:
766 759 _file = commit.get_node(bytes_path)
@@ -770,18 +763,19 class RepoFilesView(RepoAppView):
770 763
771 764 if is_file:
772 765 history = commit.get_path_history(bytes_path)
773 prev_commit_id = history[1].raw_id \
774 if len(history) > 1 else prev_commit_id
766 prev_commit_id = history[1].raw_id if len(history) > 1 else prev_commit_id
775 767 prev_url = h.route_path(
776 'repo_files:annotated', repo_name=self.db_repo_name,
777 commit_id=prev_commit_id, f_path=bytes_path,
778 _anchor=f'L{line_anchor}')
768 "repo_files:annotated",
769 repo_name=self.db_repo_name,
770 commit_id=prev_commit_id,
771 f_path=bytes_path,
772 _anchor=f"L{line_anchor}",
773 )
779 774
780 775 raise HTTPFound(prev_url)
781 776
782 777 @LoginRequired()
783 @HasRepoPermissionAnyDecorator(
784 'repository.read', 'repository.write', 'repository.admin')
778 @HasRepoPermissionAnyDecorator("repository.read", "repository.write", "repository.admin")
785 779 def repo_nodetree_full(self):
786 780 """
787 781 Returns rendered html of file tree that contains commit date,
@@ -795,17 +789,17 class RepoFilesView(RepoAppView):
795 789 try:
796 790 dir_node = commit.get_node(bytes_path)
797 791 except RepositoryError as e:
798 return Response(f'error: {h.escape(safe_str(e))}')
792 return Response(f"error: {h.escape(safe_str(e))}")
799 793
800 794 if dir_node.is_file():
801 return Response('')
795 return Response("")
802 796
803 797 c.file = dir_node
798 c.file_nodes = dir_node.commit.get_nodes(dir_node.bytes_path, pre_load=dir_node.default_pre_load)
804 799 c.commit = commit
805 at_rev = self.request.GET.get('at')
800 at_rev = self.request.GET.get("at")
806 801
807 html = self._get_tree_at_commit(
808 c, commit.raw_id, dir_node.path, full_load=True, at_rev=at_rev)
802 html = self._get_tree_at_commit(c, commit.raw_id, dir_node.path, full_load=True, at_rev=at_rev)
809 803
810 804 return Response(html)
811 805
@@ -814,15 +808,12 class RepoFilesView(RepoAppView):
814 808 safe_path = f_name.replace('"', '\\"')
815 809 encoded_path = urllib.parse.quote(f_name)
816 810
817 headers = f"attachment; " \
818 f"filename=\"{safe_path}\"; " \
819 f"filename*=UTF-8\'\'{encoded_path}"
811 headers = f"attachment; " f'filename="{safe_path}"; ' f"filename*=UTF-8''{encoded_path}"
820 812
821 813 return header_safe_str(headers)
822 814
823 815 @LoginRequired()
824 @HasRepoPermissionAnyDecorator(
825 'repository.read', 'repository.write', 'repository.admin')
816 @HasRepoPermissionAnyDecorator("repository.read", "repository.write", "repository.admin")
826 817 def repo_file_raw(self):
827 818 """
828 819 Action for show as raw, some mimetypes are "rendered",
@@ -839,16 +830,15 class RepoFilesView(RepoAppView):
839 830 # you can also provide a content-disposition to override the
840 831 # default "attachment" disposition.
841 832 # orig_type: (new_type, new_dispo)
842
843 833 # show images inline:
844 834 # Do not re-add SVG: it is unsafe and permits XSS attacks. One can
845 835 # for example render an SVG with javascript inside or even render
846 836 # HTML.
847 'image/x-icon': ('image/x-icon', 'inline'),
848 'image/png': ('image/png', 'inline'),
849 'image/gif': ('image/gif', 'inline'),
850 'image/jpeg': ('image/jpeg', 'inline'),
851 'application/pdf': ('application/pdf', 'inline'),
837 "image/x-icon": ("image/x-icon", "inline"),
838 "image/png": ("image/png", "inline"),
839 "image/gif": ("image/gif", "inline"),
840 "image/jpeg": ("image/jpeg", "inline"),
841 "application/pdf": ("application/pdf", "inline"),
852 842 }
853 843
854 844 mimetype = file_node.mimetype
@@ -858,7 +848,7 class RepoFilesView(RepoAppView):
858 848 # we don't know anything special about this, handle it safely
859 849 if file_node.is_binary:
860 850 # do same as download raw for binary files
861 mimetype, disposition = 'application/octet-stream', 'attachment'
851 mimetype, disposition = "application/octet-stream", "attachment"
862 852 else:
863 853 # do not just use the original mimetype, but force text/plain,
864 854 # otherwise it would serve text/html and that might be unsafe.
@@ -867,9 +857,9 class RepoFilesView(RepoAppView):
867 857 # binary.This might lead to erroneous text display in some
868 858 # cases, but helps in other cases, like with text files
869 859 # without extension.
870 mimetype, disposition = 'text/plain', 'inline'
860 mimetype, disposition = "text/plain", "inline"
871 861
872 if disposition == 'attachment':
862 if disposition == "attachment":
873 863 disposition = self._get_attachement_headers(f_path)
874 864
875 865 stream_content = file_node.stream_bytes()
@@ -885,8 +875,7 class RepoFilesView(RepoAppView):
885 875 return response
886 876
887 877 @LoginRequired()
888 @HasRepoPermissionAnyDecorator(
889 'repository.read', 'repository.write', 'repository.admin')
878 @HasRepoPermissionAnyDecorator("repository.read", "repository.write", "repository.admin")
890 879 def repo_file_download(self):
891 880 c = self.load_default_context()
892 881
@@ -894,7 +883,7 class RepoFilesView(RepoAppView):
894 883 commit = self._get_commit_or_redirect(commit_id)
895 884 file_node = self._get_filenode_or_redirect(commit, bytes_path)
896 885
897 if self.request.GET.get('lf'):
886 if self.request.GET.get("lf"):
898 887 # only if lf get flag is passed, we download this file
899 888 # as LFS/Largefile
900 889 lf_node = file_node.get_largefile_node()
@@ -917,44 +906,41 class RepoFilesView(RepoAppView):
917 906 return response
918 907
919 908 def _get_nodelist_at_commit(self, repo_name, repo_id, commit_id, f_path):
920
921 cache_seconds = rhodecode.ConfigGet().get_int('rc_cache.cache_repo.expiration_time')
909 cache_seconds = rhodecode.ConfigGet().get_int("rc_cache.cache_repo.expiration_time")
922 910 cache_on = cache_seconds > 0
923 911 log.debug(
924 'Computing FILE SEARCH for repo_id %s commit_id `%s` and path `%s`'
925 'with caching: %s[TTL: %ss]' % (
926 repo_id, commit_id, f_path, cache_on, cache_seconds or 0))
912 "Computing FILE SEARCH for repo_id %s commit_id `%s` and path `%s`"
913 "with caching: %s[TTL: %ss]" % (repo_id, commit_id, f_path, cache_on, cache_seconds or 0)
914 )
927 915
928 cache_namespace_uid = f'repo.{repo_id}'
929 region = rc_cache.get_or_create_region('cache_repo', cache_namespace_uid)
916 cache_namespace_uid = f"repo.{repo_id}"
917 region = rc_cache.get_or_create_region("cache_repo", cache_namespace_uid)
930 918
931 919 @region.conditional_cache_on_arguments(namespace=cache_namespace_uid, condition=cache_on)
932 920 def compute_file_search(_name_hash, _repo_id, _commit_id, _f_path):
933 log.debug('Generating cached nodelist for repo_id:%s, %s, %s', _repo_id, commit_id, f_path)
921 log.debug("Generating cached nodelist for repo_id:%s, %s, %s", _repo_id, commit_id, f_path)
934 922 try:
935 923 _d, _f = ScmModel().get_quick_filter_nodes(repo_name, _commit_id, _f_path)
936 924 except (RepositoryError, CommitDoesNotExistError, Exception) as e:
937 925 log.exception(safe_str(e))
938 h.flash(h.escape(safe_str(e)), category='error')
939 raise HTTPFound(h.route_path('repo_files', repo_name=self.db_repo_name, commit_id='tip', f_path='/'))
926 h.flash(h.escape(safe_str(e)), category="error")
927 raise HTTPFound(h.route_path("repo_files", repo_name=self.db_repo_name, commit_id="tip", f_path="/"))
940 928
941 929 return _d + _f
942 930
943 931 result = compute_file_search(self.db_repo.repo_name_hash, self.db_repo.repo_id, commit_id, f_path)
944 return filter(lambda n: self.path_filter.path_access_allowed(n['name']), result)
932 return filter(lambda n: self.path_filter.path_access_allowed(n["name"]), result)
945 933
946 934 @LoginRequired()
947 @HasRepoPermissionAnyDecorator(
948 'repository.read', 'repository.write', 'repository.admin')
935 @HasRepoPermissionAnyDecorator("repository.read", "repository.write", "repository.admin")
949 936 def repo_nodelist(self):
950 937 self.load_default_context()
951 938
952 939 commit_id, f_path, bytes_path = self._get_commit_and_path()
953 940 commit = self._get_commit_or_redirect(commit_id)
954 941
955 metadata = self._get_nodelist_at_commit(
956 self.db_repo_name, self.db_repo.repo_id, commit.raw_id, f_path)
957 return {'nodes': [x for x in metadata]}
942 metadata = self._get_nodelist_at_commit(self.db_repo_name, self.db_repo.repo_id, commit.raw_id, f_path)
943 return {"nodes": [x for x in metadata]}
958 944
959 945 def _create_references(self, branches_or_tags, symbolic_reference, f_path, ref_type):
960 946 items = []
@@ -971,7 +957,7 class RepoFilesView(RepoAppView):
971 957
972 958 # NOTE(dan): old code we used in "diff" mode compare
973 959 new_f_path = vcspath.join(name, f_path)
974 return f'{new_f_path}@{commit_id}'
960 return f"{new_f_path}@{commit_id}"
975 961
976 962 def _get_node_history(self, commit_obj, f_path, commits=None):
977 963 """
@@ -997,29 +983,26 class RepoFilesView(RepoAppView):
997 983 history = []
998 984 commits_group = ([], _("Changesets"))
999 985 for commit in commits:
1000 branch = ' (%s)' % commit.branch if commit.branch else ''
1001 n_desc = f'r{commit.idx}:{commit.short_id}{branch}'
1002 commits_group[0].append((commit.raw_id, n_desc, 'sha'))
986 branch = " (%s)" % commit.branch if commit.branch else ""
987 n_desc = f"r{commit.idx}:{commit.short_id}{branch}"
988 commits_group[0].append((commit.raw_id, n_desc, "sha"))
1003 989 history.append(commits_group)
1004 990
1005 991 symbolic_reference = self._symbolic_reference
1006 992
1007 if self.rhodecode_vcs_repo.alias == 'svn':
1008 adjusted_f_path = RepoFilesView.adjust_file_path_for_svn(
1009 f_path, self.rhodecode_vcs_repo)
993 if self.rhodecode_vcs_repo.alias == "svn":
994 adjusted_f_path = RepoFilesView.adjust_file_path_for_svn(f_path, self.rhodecode_vcs_repo)
1010 995 if adjusted_f_path != f_path:
1011 996 log.debug(
1012 'Recognized svn tag or branch in file "%s", using svn '
1013 'specific symbolic references', f_path)
997 'Recognized svn tag or branch in file "%s", using svn ' "specific symbolic references", f_path
998 )
1014 999 f_path = adjusted_f_path
1015 1000 symbolic_reference = self._symbolic_reference_svn
1016 1001
1017 branches = self._create_references(
1018 self.rhodecode_vcs_repo.branches, symbolic_reference, f_path, 'branch')
1002 branches = self._create_references(self.rhodecode_vcs_repo.branches, symbolic_reference, f_path, "branch")
1019 1003 branches_group = (branches, _("Branches"))
1020 1004
1021 tags = self._create_references(
1022 self.rhodecode_vcs_repo.tags, symbolic_reference, f_path, 'tag')
1005 tags = self._create_references(self.rhodecode_vcs_repo.tags, symbolic_reference, f_path, "tag")
1023 1006 tags_group = (tags, _("Tags"))
1024 1007
1025 1008 history.append(branches_group)
@@ -1028,8 +1011,7 class RepoFilesView(RepoAppView):
1028 1011 return history, commits
1029 1012
1030 1013 @LoginRequired()
1031 @HasRepoPermissionAnyDecorator(
1032 'repository.read', 'repository.write', 'repository.admin')
1014 @HasRepoPermissionAnyDecorator("repository.read", "repository.write", "repository.admin")
1033 1015 def repo_file_history(self):
1034 1016 self.load_default_context()
1035 1017
@@ -1044,35 +1026,23 class RepoFilesView(RepoAppView):
1044 1026 for section_items, section in file_history:
1045 1027 items = []
1046 1028 for obj_id, obj_text, obj_type in section_items:
1047 at_rev = ''
1048 if obj_type in ['branch', 'bookmark', 'tag']:
1029 at_rev = ""
1030 if obj_type in ["branch", "bookmark", "tag"]:
1049 1031 at_rev = obj_text
1050 entry = {
1051 'id': obj_id,
1052 'text': obj_text,
1053 'type': obj_type,
1054 'at_rev': at_rev
1055 }
1032 entry = {"id": obj_id, "text": obj_text, "type": obj_type, "at_rev": at_rev}
1056 1033
1057 1034 items.append(entry)
1058 1035
1059 res.append({
1060 'text': section,
1061 'children': items
1062 })
1036 res.append({"text": section, "children": items})
1063 1037
1064 data = {
1065 'more': False,
1066 'results': res
1067 }
1038 data = {"more": False, "results": res}
1068 1039 return data
1069 1040
1070 log.warning('Cannot fetch history for directory')
1041 log.warning("Cannot fetch history for directory")
1071 1042 raise HTTPBadRequest()
1072 1043
1073 1044 @LoginRequired()
1074 @HasRepoPermissionAnyDecorator(
1075 'repository.read', 'repository.write', 'repository.admin')
1045 @HasRepoPermissionAnyDecorator("repository.read", "repository.write", "repository.admin")
1076 1046 def repo_file_authors(self):
1077 1047 c = self.load_default_context()
1078 1048
@@ -1084,12 +1054,10 class RepoFilesView(RepoAppView):
1084 1054 raise HTTPBadRequest()
1085 1055
1086 1056 c.file_last_commit = file_node.last_commit
1087 if self.request.GET.get('annotate') == '1':
1057 if self.request.GET.get("annotate") == "1":
1088 1058 # use _hist from annotation if annotation mode is on
1089 1059 commit_ids = {x[1] for x in file_node.annotate}
1090 _hist = (
1091 self.rhodecode_vcs_repo.get_commit(commit_id)
1092 for commit_id in commit_ids)
1060 _hist = (self.rhodecode_vcs_repo.get_commit(commit_id) for commit_id in commit_ids)
1093 1061 else:
1094 1062 _f_history, _hist = self._get_node_history(commit, f_path)
1095 1063 c.file_author = False
@@ -1100,8 +1068,8 class RepoFilesView(RepoAppView):
1100 1068 if author not in unique:
1101 1069 unique[commit.author] = [
1102 1070 h.email(author),
1103 h.person(author, 'username_or_name_or_email'),
1104 1 # counter
1071 h.person(author, "username_or_name_or_email"),
1072 1, # counter
1105 1073 ]
1106 1074
1107 1075 else:
@@ -1113,20 +1081,21 class RepoFilesView(RepoAppView):
1113 1081 return self._get_template_context(c)
1114 1082
1115 1083 @LoginRequired()
1116 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1084 @HasRepoPermissionAnyDecorator("repository.write", "repository.admin")
1117 1085 def repo_files_check_head(self):
1118 1086 self.load_default_context()
1119 1087
1120 1088 commit_id, f_path, bytes_path = self._get_commit_and_path()
1121 _branch_name, _sha_commit_id, is_head = \
1122 self._is_valid_head(commit_id, self.rhodecode_vcs_repo, landing_ref=self.db_repo.landing_ref_name)
1089 _branch_name, _sha_commit_id, is_head = self._is_valid_head(
1090 commit_id, self.rhodecode_vcs_repo, landing_ref=self.db_repo.landing_ref_name
1091 )
1123 1092
1124 new_path = self.request.POST.get('path')
1125 operation = self.request.POST.get('operation')
1126 path_exist = ''
1093 new_path = self.request.POST.get("path")
1094 operation = self.request.POST.get("operation")
1095 path_exist = ""
1127 1096
1128 if new_path and operation in ['create', 'upload']:
1129 new_f_path = os.path.join(f_path.lstrip('/'), new_path)
1097 if new_path and operation in ["create", "upload"]:
1098 new_f_path = os.path.join(f_path.lstrip("/"), new_path)
1130 1099 try:
1131 1100 commit_obj = self.rhodecode_vcs_repo.get_commit(commit_id)
1132 1101 # NOTE(dan): construct whole path without leading /
@@ -1136,24 +1105,19 class RepoFilesView(RepoAppView):
1136 1105 except (EmptyRepositoryError, NodeDoesNotExistError):
1137 1106 pass
1138 1107
1139 return {
1140 'branch': _branch_name,
1141 'sha': _sha_commit_id,
1142 'is_head': is_head,
1143 'path_exists': path_exist
1144 }
1108 return {"branch": _branch_name, "sha": _sha_commit_id, "is_head": is_head, "path_exists": path_exist}
1145 1109
1146 1110 @LoginRequired()
1147 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1111 @HasRepoPermissionAnyDecorator("repository.write", "repository.admin")
1148 1112 def repo_files_remove_file(self):
1149 1113 _ = self.request.translate
1150 1114 c = self.load_default_context()
1151 1115 commit_id, f_path, bytes_path = self._get_commit_and_path()
1152 1116
1153 1117 self._ensure_not_locked()
1154 _branch_name, _sha_commit_id, is_head = \
1155 self._is_valid_head(commit_id, self.rhodecode_vcs_repo,
1156 landing_ref=self.db_repo.landing_ref_name)
1118 _branch_name, _sha_commit_id, is_head = self._is_valid_head(
1119 commit_id, self.rhodecode_vcs_repo, landing_ref=self.db_repo.landing_ref_name
1120 )
1157 1121
1158 1122 self.forbid_non_head(is_head, f_path)
1159 1123 self.check_branch_permission(_branch_name)
@@ -1161,14 +1125,13 class RepoFilesView(RepoAppView):
1161 1125 c.commit = self._get_commit_or_redirect(commit_id)
1162 1126 c.file = self._get_filenode_or_redirect(c.commit, bytes_path)
1163 1127
1164 c.default_message = _(
1165 'Deleted file {} via RhodeCode Enterprise').format(f_path)
1128 c.default_message = _("Deleted file {} via RhodeCode Enterprise").format(f_path)
1166 1129 c.f_path = f_path
1167 1130
1168 1131 return self._get_template_context(c)
1169 1132
1170 1133 @LoginRequired()
1171 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1134 @HasRepoPermissionAnyDecorator("repository.write", "repository.admin")
1172 1135 @CSRFRequired()
1173 1136 def repo_files_delete_file(self):
1174 1137 _ = self.request.translate
@@ -1177,9 +1140,9 class RepoFilesView(RepoAppView):
1177 1140 commit_id, f_path, bytes_path = self._get_commit_and_path()
1178 1141
1179 1142 self._ensure_not_locked()
1180 _branch_name, _sha_commit_id, is_head = \
1181 self._is_valid_head(commit_id, self.rhodecode_vcs_repo,
1182 landing_ref=self.db_repo.landing_ref_name)
1143 _branch_name, _sha_commit_id, is_head = self._is_valid_head(
1144 commit_id, self.rhodecode_vcs_repo, landing_ref=self.db_repo.landing_ref_name
1145 )
1183 1146
1184 1147 self.forbid_non_head(is_head, f_path)
1185 1148 self.check_branch_permission(_branch_name)
@@ -1187,46 +1150,39 class RepoFilesView(RepoAppView):
1187 1150 c.commit = self._get_commit_or_redirect(commit_id)
1188 1151 c.file = self._get_filenode_or_redirect(c.commit, bytes_path)
1189 1152
1190 c.default_message = _('Deleted file {} via RhodeCode Enterprise').format(f_path)
1153 c.default_message = _("Deleted file {} via RhodeCode Enterprise").format(f_path)
1191 1154 c.f_path = f_path
1192 1155 node_path = f_path
1193 1156 author = self._rhodecode_db_user.full_contact
1194 message = self.request.POST.get('message') or c.default_message
1157 message = self.request.POST.get("message") or c.default_message
1195 1158 try:
1196 nodes = {
1197 safe_bytes(node_path): {
1198 'content': b''
1199 }
1200 }
1159 nodes = {safe_bytes(node_path): {"content": b""}}
1201 1160 ScmModel().delete_nodes(
1202 user=self._rhodecode_db_user.user_id, repo=self.db_repo,
1161 user=self._rhodecode_db_user.user_id,
1162 repo=self.db_repo,
1203 1163 message=message,
1204 1164 nodes=nodes,
1205 1165 parent_commit=c.commit,
1206 1166 author=author,
1207 1167 )
1208 1168
1209 h.flash(
1210 _('Successfully deleted file `{}`').format(
1211 h.escape(f_path)), category='success')
1169 h.flash(_("Successfully deleted file `{}`").format(h.escape(f_path)), category="success")
1212 1170 except Exception:
1213 log.exception('Error during commit operation')
1214 h.flash(_('Error occurred during commit'), category='error')
1215 raise HTTPFound(
1216 h.route_path('repo_commit', repo_name=self.db_repo_name,
1217 commit_id='tip'))
1171 log.exception("Error during commit operation")
1172 h.flash(_("Error occurred during commit"), category="error")
1173 raise HTTPFound(h.route_path("repo_commit", repo_name=self.db_repo_name, commit_id="tip"))
1218 1174
1219 1175 @LoginRequired()
1220 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1176 @HasRepoPermissionAnyDecorator("repository.write", "repository.admin")
1221 1177 def repo_files_edit_file(self):
1222 1178 _ = self.request.translate
1223 1179 c = self.load_default_context()
1224 1180 commit_id, f_path, bytes_path = self._get_commit_and_path()
1225 1181
1226 1182 self._ensure_not_locked()
1227 _branch_name, _sha_commit_id, is_head = \
1228 self._is_valid_head(commit_id, self.rhodecode_vcs_repo,
1229 landing_ref=self.db_repo.landing_ref_name)
1183 _branch_name, _sha_commit_id, is_head = self._is_valid_head(
1184 commit_id, self.rhodecode_vcs_repo, landing_ref=self.db_repo.landing_ref_name
1185 )
1230 1186
1231 1187 self.forbid_non_head(is_head, f_path, commit_id=commit_id)
1232 1188 self.check_branch_permission(_branch_name, commit_id=commit_id)
@@ -1236,18 +1192,17 class RepoFilesView(RepoAppView):
1236 1192
1237 1193 if c.file.is_binary:
1238 1194 files_url = h.route_path(
1239 'repo_files',
1240 repo_name=self.db_repo_name,
1241 commit_id=c.commit.raw_id, f_path=f_path)
1195 "repo_files", repo_name=self.db_repo_name, commit_id=c.commit.raw_id, f_path=f_path
1196 )
1242 1197 raise HTTPFound(files_url)
1243 1198
1244 c.default_message = _('Edited file {} via RhodeCode Enterprise').format(f_path)
1199 c.default_message = _("Edited file {} via RhodeCode Enterprise").format(f_path)
1245 1200 c.f_path = f_path
1246 1201
1247 1202 return self._get_template_context(c)
1248 1203
1249 1204 @LoginRequired()
1250 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1205 @HasRepoPermissionAnyDecorator("repository.write", "repository.admin")
1251 1206 @CSRFRequired()
1252 1207 def repo_files_update_file(self):
1253 1208 _ = self.request.translate
@@ -1260,53 +1215,52 class RepoFilesView(RepoAppView):
1260 1215 c.file = self._get_filenode_or_redirect(c.commit, bytes_path)
1261 1216
1262 1217 if c.file.is_binary:
1263 raise HTTPFound(h.route_path('repo_files', repo_name=self.db_repo_name,
1264 commit_id=c.commit.raw_id, f_path=f_path))
1218 raise HTTPFound(
1219 h.route_path("repo_files", repo_name=self.db_repo_name, commit_id=c.commit.raw_id, f_path=f_path)
1220 )
1265 1221
1266 _branch_name, _sha_commit_id, is_head = \
1267 self._is_valid_head(commit_id, self.rhodecode_vcs_repo,
1268 landing_ref=self.db_repo.landing_ref_name)
1222 _branch_name, _sha_commit_id, is_head = self._is_valid_head(
1223 commit_id, self.rhodecode_vcs_repo, landing_ref=self.db_repo.landing_ref_name
1224 )
1269 1225
1270 1226 self.forbid_non_head(is_head, f_path, commit_id=commit_id)
1271 1227 self.check_branch_permission(_branch_name, commit_id=commit_id)
1272 1228
1273 c.default_message = _('Edited file {} via RhodeCode Enterprise').format(f_path)
1229 c.default_message = _("Edited file {} via RhodeCode Enterprise").format(f_path)
1274 1230 c.f_path = f_path
1275 1231
1276 1232 old_content = c.file.str_content
1277 1233 sl = old_content.splitlines(1)
1278 first_line = sl[0] if sl else ''
1234 first_line = sl[0] if sl else ""
1279 1235
1280 1236 r_post = self.request.POST
1281 1237 # line endings: 0 - Unix, 1 - Mac, 2 - DOS
1282 1238 line_ending_mode = detect_mode(first_line, 0)
1283 content = convert_line_endings(r_post.get('content', ''), line_ending_mode)
1239 content = convert_line_endings(r_post.get("content", ""), line_ending_mode)
1284 1240
1285 message = r_post.get('message') or c.default_message
1241 message = r_post.get("message") or c.default_message
1286 1242
1287 1243 org_node_path = c.file.str_path
1288 filename = r_post['filename']
1244 filename = r_post["filename"]
1289 1245
1290 1246 root_path = c.file.dir_path
1291 1247 pure_path = self.create_pure_path(root_path, filename)
1292 1248 node_path = pure_path.as_posix()
1293 1249
1294 default_redirect_url = h.route_path('repo_commit', repo_name=self.db_repo_name,
1295 commit_id=commit_id)
1250 default_redirect_url = h.route_path("repo_commit", repo_name=self.db_repo_name, commit_id=commit_id)
1296 1251 if content == old_content and node_path == org_node_path:
1297 h.flash(_('No changes detected on {}').format(h.escape(org_node_path)),
1298 category='warning')
1252 h.flash(_("No changes detected on {}").format(h.escape(org_node_path)), category="warning")
1299 1253 raise HTTPFound(default_redirect_url)
1300 1254
1301 1255 try:
1302 1256 mapping = {
1303 1257 c.file.bytes_path: {
1304 'org_filename': org_node_path,
1305 'filename': safe_bytes(node_path),
1306 'content': safe_bytes(content),
1307 'lexer': '',
1308 'op': 'mod',
1309 'mode': c.file.mode
1258 "org_filename": org_node_path,
1259 "filename": safe_bytes(node_path),
1260 "content": safe_bytes(content),
1261 "lexer": "",
1262 "op": "mod",
1263 "mode": c.file.mode,
1310 1264 }
1311 1265 }
1312 1266
@@ -1318,19 +1272,17 class RepoFilesView(RepoAppView):
1318 1272 parent_commit=c.commit,
1319 1273 )
1320 1274
1321 h.flash(_('Successfully committed changes to file `{}`').format(
1322 h.escape(f_path)), category='success')
1323 default_redirect_url = h.route_path(
1324 'repo_commit', repo_name=self.db_repo_name, commit_id=commit.raw_id)
1275 h.flash(_("Successfully committed changes to file `{}`").format(h.escape(f_path)), category="success")
1276 default_redirect_url = h.route_path("repo_commit", repo_name=self.db_repo_name, commit_id=commit.raw_id)
1325 1277
1326 1278 except Exception:
1327 log.exception('Error occurred during commit')
1328 h.flash(_('Error occurred during commit'), category='error')
1279 log.exception("Error occurred during commit")
1280 h.flash(_("Error occurred during commit"), category="error")
1329 1281
1330 1282 raise HTTPFound(default_redirect_url)
1331 1283
1332 1284 @LoginRequired()
1333 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1285 @HasRepoPermissionAnyDecorator("repository.write", "repository.admin")
1334 1286 def repo_files_add_file(self):
1335 1287 _ = self.request.translate
1336 1288 c = self.load_default_context()
@@ -1339,7 +1291,7 class RepoFilesView(RepoAppView):
1339 1291 self._ensure_not_locked()
1340 1292
1341 1293 # Check if we need to use this page to upload binary
1342 upload_binary = str2bool(self.request.params.get('upload_binary', False))
1294 upload_binary = str2bool(self.request.params.get("upload_binary", False))
1343 1295
1344 1296 c.commit = self._get_commit_or_redirect(commit_id, redirect_after=False)
1345 1297 if c.commit is None:
@@ -1348,24 +1300,27 class RepoFilesView(RepoAppView):
1348 1300 if self.rhodecode_vcs_repo.is_empty():
1349 1301 # for empty repository we cannot check for current branch, we rely on
1350 1302 # c.commit.branch instead
1351 _branch_name, _sha_commit_id, is_head = c.commit.branch, '', True
1303 _branch_name, _sha_commit_id, is_head = c.commit.branch, "", True
1352 1304 else:
1353 _branch_name, _sha_commit_id, is_head = \
1354 self._is_valid_head(commit_id, self.rhodecode_vcs_repo,
1355 landing_ref=self.db_repo.landing_ref_name)
1305 _branch_name, _sha_commit_id, is_head = self._is_valid_head(
1306 commit_id, self.rhodecode_vcs_repo, landing_ref=self.db_repo.landing_ref_name
1307 )
1356 1308
1357 1309 self.forbid_non_head(is_head, f_path, commit_id=commit_id)
1358 1310 self.check_branch_permission(_branch_name, commit_id=commit_id)
1359 1311
1360 c.default_message = (_('Added file via RhodeCode Enterprise')) \
1361 if not upload_binary else (_('Edited file {} via RhodeCode Enterprise').format(f_path))
1362 c.f_path = f_path.lstrip('/') # ensure not relative path
1312 c.default_message = (
1313 (_("Added file via RhodeCode Enterprise"))
1314 if not upload_binary
1315 else (_("Edited file {} via RhodeCode Enterprise").format(f_path))
1316 )
1317 c.f_path = f_path.lstrip("/") # ensure not relative path
1363 1318 c.replace_binary = upload_binary
1364 1319
1365 1320 return self._get_template_context(c)
1366 1321
1367 1322 @LoginRequired()
1368 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1323 @HasRepoPermissionAnyDecorator("repository.write", "repository.admin")
1369 1324 @CSRFRequired()
1370 1325 def repo_files_create_file(self):
1371 1326 _ = self.request.translate
@@ -1380,56 +1335,48 class RepoFilesView(RepoAppView):
1380 1335
1381 1336 # calculate redirect URL
1382 1337 if self.rhodecode_vcs_repo.is_empty():
1383 default_redirect_url = h.route_path(
1384 'repo_summary', repo_name=self.db_repo_name)
1338 default_redirect_url = h.route_path("repo_summary", repo_name=self.db_repo_name)
1385 1339 else:
1386 default_redirect_url = h.route_path(
1387 'repo_commit', repo_name=self.db_repo_name, commit_id='tip')
1340 default_redirect_url = h.route_path("repo_commit", repo_name=self.db_repo_name, commit_id="tip")
1388 1341
1389 1342 if self.rhodecode_vcs_repo.is_empty():
1390 1343 # for empty repository we cannot check for current branch, we rely on
1391 1344 # c.commit.branch instead
1392 _branch_name, _sha_commit_id, is_head = c.commit.branch, '', True
1345 _branch_name, _sha_commit_id, is_head = c.commit.branch, "", True
1393 1346 else:
1394 _branch_name, _sha_commit_id, is_head = \
1395 self._is_valid_head(commit_id, self.rhodecode_vcs_repo,
1396 landing_ref=self.db_repo.landing_ref_name)
1347 _branch_name, _sha_commit_id, is_head = self._is_valid_head(
1348 commit_id, self.rhodecode_vcs_repo, landing_ref=self.db_repo.landing_ref_name
1349 )
1397 1350
1398 1351 self.forbid_non_head(is_head, f_path, commit_id=commit_id)
1399 1352 self.check_branch_permission(_branch_name, commit_id=commit_id)
1400 1353
1401 c.default_message = (_('Added file via RhodeCode Enterprise'))
1354 c.default_message = _("Added file via RhodeCode Enterprise")
1402 1355 c.f_path = f_path
1403 1356
1404 1357 r_post = self.request.POST
1405 message = r_post.get('message') or c.default_message
1406 filename = r_post.get('filename')
1358 message = r_post.get("message") or c.default_message
1359 filename = r_post.get("filename")
1407 1360 unix_mode = 0
1408 1361
1409 1362 if not filename:
1410 1363 # If there's no commit, redirect to repo summary
1411 1364 if type(c.commit) is EmptyCommit:
1412 redirect_url = h.route_path(
1413 'repo_summary', repo_name=self.db_repo_name)
1365 redirect_url = h.route_path("repo_summary", repo_name=self.db_repo_name)
1414 1366 else:
1415 1367 redirect_url = default_redirect_url
1416 h.flash(_('No filename specified'), category='warning')
1368 h.flash(_("No filename specified"), category="warning")
1417 1369 raise HTTPFound(redirect_url)
1418 1370
1419 1371 root_path = f_path
1420 1372 pure_path = self.create_pure_path(root_path, filename)
1421 node_path = pure_path.as_posix().lstrip('/')
1373 node_path = pure_path.as_posix().lstrip("/")
1422 1374
1423 1375 author = self._rhodecode_db_user.full_contact
1424 content = convert_line_endings(r_post.get('content', ''), unix_mode)
1425 nodes = {
1426 safe_bytes(node_path): {
1427 'content': safe_bytes(content)
1428 }
1429 }
1376 content = convert_line_endings(r_post.get("content", ""), unix_mode)
1377 nodes = {safe_bytes(node_path): {"content": safe_bytes(content)}}
1430 1378
1431 1379 try:
1432
1433 1380 commit = ScmModel().create_nodes(
1434 1381 user=self._rhodecode_db_user.user_id,
1435 1382 repo=self.db_repo,
@@ -1439,26 +1386,27 class RepoFilesView(RepoAppView):
1439 1386 author=author,
1440 1387 )
1441 1388
1442 h.flash(_('Successfully committed new file `{}`').format(h.escape(node_path)), category='success')
1389 h.flash(_("Successfully committed new file `{}`").format(h.escape(node_path)), category="success")
1443 1390
1444 default_redirect_url = h.route_path(
1445 'repo_commit', repo_name=self.db_repo_name, commit_id=commit.raw_id)
1391 default_redirect_url = h.route_path("repo_commit", repo_name=self.db_repo_name, commit_id=commit.raw_id)
1446 1392
1447 1393 except NonRelativePathError:
1448 log.exception('Non Relative path found')
1449 h.flash(_('The location specified must be a relative path and must not '
1450 'contain .. in the path'), category='warning')
1394 log.exception("Non Relative path found")
1395 h.flash(
1396 _("The location specified must be a relative path and must not " "contain .. in the path"),
1397 category="warning",
1398 )
1451 1399 raise HTTPFound(default_redirect_url)
1452 1400 except (NodeError, NodeAlreadyExistsError) as e:
1453 h.flash(h.escape(safe_str(e)), category='error')
1401 h.flash(h.escape(safe_str(e)), category="error")
1454 1402 except Exception:
1455 log.exception('Error occurred during commit')
1456 h.flash(_('Error occurred during commit'), category='error')
1403 log.exception("Error occurred during commit")
1404 h.flash(_("Error occurred during commit"), category="error")
1457 1405
1458 1406 raise HTTPFound(default_redirect_url)
1459 1407
1460 1408 @LoginRequired()
1461 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1409 @HasRepoPermissionAnyDecorator("repository.write", "repository.admin")
1462 1410 @CSRFRequired()
1463 1411 def repo_files_upload_file(self):
1464 1412 _ = self.request.translate
@@ -1473,65 +1421,52 class RepoFilesView(RepoAppView):
1473 1421
1474 1422 # calculate redirect URL
1475 1423 if self.rhodecode_vcs_repo.is_empty():
1476 default_redirect_url = h.route_path(
1477 'repo_summary', repo_name=self.db_repo_name)
1424 default_redirect_url = h.route_path("repo_summary", repo_name=self.db_repo_name)
1478 1425 else:
1479 default_redirect_url = h.route_path(
1480 'repo_commit', repo_name=self.db_repo_name, commit_id='tip')
1426 default_redirect_url = h.route_path("repo_commit", repo_name=self.db_repo_name, commit_id="tip")
1481 1427
1482 1428 if self.rhodecode_vcs_repo.is_empty():
1483 1429 # for empty repository we cannot check for current branch, we rely on
1484 1430 # c.commit.branch instead
1485 _branch_name, _sha_commit_id, is_head = c.commit.branch, '', True
1431 _branch_name, _sha_commit_id, is_head = c.commit.branch, "", True
1486 1432 else:
1487 _branch_name, _sha_commit_id, is_head = \
1488 self._is_valid_head(commit_id, self.rhodecode_vcs_repo,
1489 landing_ref=self.db_repo.landing_ref_name)
1433 _branch_name, _sha_commit_id, is_head = self._is_valid_head(
1434 commit_id, self.rhodecode_vcs_repo, landing_ref=self.db_repo.landing_ref_name
1435 )
1490 1436
1491 1437 error = self.forbid_non_head(is_head, f_path, json_mode=True)
1492 1438 if error:
1493 return {
1494 'error': error,
1495 'redirect_url': default_redirect_url
1496 }
1439 return {"error": error, "redirect_url": default_redirect_url}
1497 1440 error = self.check_branch_permission(_branch_name, json_mode=True)
1498 1441 if error:
1499 return {
1500 'error': error,
1501 'redirect_url': default_redirect_url
1502 }
1442 return {"error": error, "redirect_url": default_redirect_url}
1503 1443
1504 c.default_message = (_('Added file via RhodeCode Enterprise'))
1444 c.default_message = _("Added file via RhodeCode Enterprise")
1505 1445 c.f_path = f_path
1506 1446
1507 1447 r_post = self.request.POST
1508 1448
1509 1449 message = c.default_message
1510 user_message = r_post.getall('message')
1450 user_message = r_post.getall("message")
1511 1451 if isinstance(user_message, list) and user_message:
1512 1452 # we take the first from duplicated results if it's not empty
1513 1453 message = user_message[0] if user_message[0] else message
1514 1454
1515 1455 nodes = {}
1516 1456
1517 for file_obj in r_post.getall('files_upload') or []:
1457 for file_obj in r_post.getall("files_upload") or []:
1518 1458 content = file_obj.file
1519 1459 filename = file_obj.filename
1520 1460
1521 1461 root_path = f_path
1522 1462 pure_path = self.create_pure_path(root_path, filename)
1523 node_path = pure_path.as_posix().lstrip('/')
1463 node_path = pure_path.as_posix().lstrip("/")
1524 1464
1525 nodes[safe_bytes(node_path)] = {
1526 'content': content
1527 }
1465 nodes[safe_bytes(node_path)] = {"content": content}
1528 1466
1529 1467 if not nodes:
1530 error = 'missing files'
1531 return {
1532 'error': error,
1533 'redirect_url': default_redirect_url
1534 }
1468 error = "missing files"
1469 return {"error": error, "redirect_url": default_redirect_url}
1535 1470
1536 1471 author = self._rhodecode_db_user.full_contact
1537 1472
@@ -1545,49 +1480,35 class RepoFilesView(RepoAppView):
1545 1480 author=author,
1546 1481 )
1547 1482 if len(nodes) == 1:
1548 flash_message = _('Successfully committed {} new files').format(len(nodes))
1483 flash_message = _("Successfully committed {} new files").format(len(nodes))
1549 1484 else:
1550 flash_message = _('Successfully committed 1 new file')
1485 flash_message = _("Successfully committed 1 new file")
1551 1486
1552 h.flash(flash_message, category='success')
1487 h.flash(flash_message, category="success")
1553 1488
1554 default_redirect_url = h.route_path(
1555 'repo_commit', repo_name=self.db_repo_name, commit_id=commit.raw_id)
1489 default_redirect_url = h.route_path("repo_commit", repo_name=self.db_repo_name, commit_id=commit.raw_id)
1556 1490
1557 1491 except NonRelativePathError:
1558 log.exception('Non Relative path found')
1559 error = _('The location specified must be a relative path and must not '
1560 'contain .. in the path')
1561 h.flash(error, category='warning')
1492 log.exception("Non Relative path found")
1493 error = _("The location specified must be a relative path and must not " "contain .. in the path")
1494 h.flash(error, category="warning")
1562 1495
1563 return {
1564 'error': error,
1565 'redirect_url': default_redirect_url
1566 }
1496 return {"error": error, "redirect_url": default_redirect_url}
1567 1497 except (NodeError, NodeAlreadyExistsError) as e:
1568 1498 error = h.escape(e)
1569 h.flash(error, category='error')
1499 h.flash(error, category="error")
1570 1500
1571 return {
1572 'error': error,
1573 'redirect_url': default_redirect_url
1574 }
1501 return {"error": error, "redirect_url": default_redirect_url}
1575 1502 except Exception:
1576 log.exception('Error occurred during commit')
1577 error = _('Error occurred during commit')
1578 h.flash(error, category='error')
1579 return {
1580 'error': error,
1581 'redirect_url': default_redirect_url
1582 }
1503 log.exception("Error occurred during commit")
1504 error = _("Error occurred during commit")
1505 h.flash(error, category="error")
1506 return {"error": error, "redirect_url": default_redirect_url}
1583 1507
1584 return {
1585 'error': None,
1586 'redirect_url': default_redirect_url
1587 }
1508 return {"error": None, "redirect_url": default_redirect_url}
1588 1509
1589 1510 @LoginRequired()
1590 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1511 @HasRepoPermissionAnyDecorator("repository.write", "repository.admin")
1591 1512 @CSRFRequired()
1592 1513 def repo_files_replace_file(self):
1593 1514 _ = self.request.translate
@@ -1601,64 +1522,50 class RepoFilesView(RepoAppView):
1601 1522 c.commit = EmptyCommit(alias=self.rhodecode_vcs_repo.alias)
1602 1523
1603 1524 if self.rhodecode_vcs_repo.is_empty():
1604 default_redirect_url = h.route_path(
1605 'repo_summary', repo_name=self.db_repo_name)
1525 default_redirect_url = h.route_path("repo_summary", repo_name=self.db_repo_name)
1606 1526 else:
1607 default_redirect_url = h.route_path(
1608 'repo_commit', repo_name=self.db_repo_name, commit_id='tip')
1527 default_redirect_url = h.route_path("repo_commit", repo_name=self.db_repo_name, commit_id="tip")
1609 1528
1610 1529 if self.rhodecode_vcs_repo.is_empty():
1611 1530 # for empty repository we cannot check for current branch, we rely on
1612 1531 # c.commit.branch instead
1613 _branch_name, _sha_commit_id, is_head = c.commit.branch, '', True
1532 _branch_name, _sha_commit_id, is_head = c.commit.branch, "", True
1614 1533 else:
1615 _branch_name, _sha_commit_id, is_head = \
1616 self._is_valid_head(commit_id, self.rhodecode_vcs_repo,
1617 landing_ref=self.db_repo.landing_ref_name)
1534 _branch_name, _sha_commit_id, is_head = self._is_valid_head(
1535 commit_id, self.rhodecode_vcs_repo, landing_ref=self.db_repo.landing_ref_name
1536 )
1618 1537
1619 1538 error = self.forbid_non_head(is_head, f_path, json_mode=True)
1620 1539 if error:
1621 return {
1622 'error': error,
1623 'redirect_url': default_redirect_url
1624 }
1540 return {"error": error, "redirect_url": default_redirect_url}
1625 1541 error = self.check_branch_permission(_branch_name, json_mode=True)
1626 1542 if error:
1627 return {
1628 'error': error,
1629 'redirect_url': default_redirect_url
1630 }
1543 return {"error": error, "redirect_url": default_redirect_url}
1631 1544
1632 c.default_message = (_('Edited file {} via RhodeCode Enterprise').format(f_path))
1545 c.default_message = _("Edited file {} via RhodeCode Enterprise").format(f_path)
1633 1546 c.f_path = f_path
1634 1547
1635 1548 r_post = self.request.POST
1636 1549
1637 1550 message = c.default_message
1638 user_message = r_post.getall('message')
1551 user_message = r_post.getall("message")
1639 1552 if isinstance(user_message, list) and user_message:
1640 1553 # we take the first from duplicated results if it's not empty
1641 1554 message = user_message[0] if user_message[0] else message
1642 1555
1643 data_for_replacement = r_post.getall('files_upload') or []
1556 data_for_replacement = r_post.getall("files_upload") or []
1644 1557 if (objects_count := len(data_for_replacement)) > 1:
1645 return {
1646 'error': 'too many files for replacement',
1647 'redirect_url': default_redirect_url
1648 }
1558 return {"error": "too many files for replacement", "redirect_url": default_redirect_url}
1649 1559 elif not objects_count:
1650 return {
1651 'error': 'missing files',
1652 'redirect_url': default_redirect_url
1653 }
1560 return {"error": "missing files", "redirect_url": default_redirect_url}
1654 1561
1655 1562 content = data_for_replacement[0].file
1656 1563 retrieved_filename = data_for_replacement[0].filename
1657 1564
1658 if retrieved_filename.split('.')[-1] != f_path.split('.')[-1]:
1565 if retrieved_filename.split(".")[-1] != f_path.split(".")[-1]:
1659 1566 return {
1660 'error': 'file extension of uploaded file doesn\'t match an original file\'s extension',
1661 'redirect_url': default_redirect_url
1567 "error": "file extension of uploaded file doesn't match an original file's extension",
1568 "redirect_url": default_redirect_url,
1662 1569 }
1663 1570
1664 1571 author = self._rhodecode_db_user.full_contact
@@ -1669,36 +1576,26 class RepoFilesView(RepoAppView):
1669 1576 repo=self.db_repo,
1670 1577 message=message,
1671 1578 node={
1672 'content': content,
1673 'file_path': f_path.encode(),
1579 "content": content,
1580 "file_path": f_path.encode(),
1674 1581 },
1675 1582 parent_commit=c.commit,
1676 1583 author=author,
1677 1584 )
1678 1585
1679 h.flash(_('Successfully committed 1 new file'), category='success')
1586 h.flash(_("Successfully committed 1 new file"), category="success")
1680 1587
1681 default_redirect_url = h.route_path(
1682 'repo_commit', repo_name=self.db_repo_name, commit_id=commit.raw_id)
1588 default_redirect_url = h.route_path("repo_commit", repo_name=self.db_repo_name, commit_id=commit.raw_id)
1683 1589
1684 1590 except (NodeError, NodeAlreadyExistsError) as e:
1685 1591 error = h.escape(e)
1686 h.flash(error, category='error')
1592 h.flash(error, category="error")
1687 1593
1688 return {
1689 'error': error,
1690 'redirect_url': default_redirect_url
1691 }
1594 return {"error": error, "redirect_url": default_redirect_url}
1692 1595 except Exception:
1693 log.exception('Error occurred during commit')
1694 error = _('Error occurred during commit')
1695 h.flash(error, category='error')
1696 return {
1697 'error': error,
1698 'redirect_url': default_redirect_url
1699 }
1596 log.exception("Error occurred during commit")
1597 error = _("Error occurred during commit")
1598 h.flash(error, category="error")
1599 return {"error": error, "redirect_url": default_redirect_url}
1700 1600
1701 return {
1702 'error': None,
1703 'redirect_url': default_redirect_url
1704 }
1601 return {"error": None, "redirect_url": default_redirect_url}
@@ -55,7 +55,7 register_backend(
55 55 log = logging.getLogger(__name__)
56 56
57 57
58 FILE_TREE_CACHE_VER = 'v5'
58 FILE_TREE_CACHE_VER = 'v6'
59 59 LICENSE_CACHE_VER = 'v3'
60 60 PERMISSIONS_CACHE_VER = 'v2'
61 61
@@ -153,6 +153,7 def remove_prefix(s, prefix):
153 153
154 154 def find_calling_context(ignore_modules=None, depth=4, output_writer=None, indent=True):
155 155 """
156 How to find calling context:
156 157 Look through the calling stack and return the frame which called
157 158 this function and is part of core module ( ie. rhodecode.* )
158 159
@@ -295,19 +295,16 class GitCommit(base.BaseCommit):
295 295 raise CommitError(f"Directory does not exist for commit {self.raw_id} at '{path}'")
296 296 path = self._fix_path(path)
297 297
298 # call and check tree_id for this path
299 tree_id, _ = self._get_path_tree_id_and_type(path)
300
301 298 path_nodes = []
302 299
303 for bytes_name, stat_, tree_item_id, node_kind in self._remote.tree_items(tree_id):
300 for obj_name, stat_, tree_item_id, node_kind, pre_load_data in self._remote.get_nodes(self.raw_id, path, pre_load):
304 301 if node_kind is None:
305 302 raise CommitError(f"Requested object type={node_kind} cannot be determined")
306 303
307 if path != b"":
308 obj_path = b"/".join((path, bytes_name))
304 if path == b"":
305 obj_path = obj_name
309 306 else:
310 obj_path = bytes_name
307 obj_path = b"/".join((path, obj_name))
311 308
312 309 # cache file mode for git, since we have it already
313 310 if obj_path not in self._path_mode_cache:
@@ -322,12 +319,12 class GitCommit(base.BaseCommit):
322 319 entry = self.nodes[obj_path]
323 320 else:
324 321 if node_kind == NodeKind.SUBMODULE:
325 url = self._get_submodule_url(b"/".join((path, bytes_name)))
326 entry= SubModuleNode(bytes_name, url=url, commit=tree_item_id, alias=self.repository.alias)
322 url = self._get_submodule_url(obj_path)
323 entry= SubModuleNode(obj_name, url=url, commit=tree_item_id, alias=self.repository.alias)
327 324 elif node_kind == NodeKind.DIR:
328 325 entry = DirNode(safe_bytes(obj_path), commit=self)
329 326 elif node_kind == NodeKind.FILE:
330 entry = FileNode(safe_bytes(obj_path), commit=self, mode=stat_, pre_load=pre_load)
327 entry = FileNode(safe_bytes(obj_path), commit=self, mode=stat_, pre_load_data=pre_load_data)
331 328
332 329 if entry:
333 330 self.nodes[obj_path] = entry
@@ -274,7 +274,7 class MercurialCommit(base.BaseCommit):
274 274
275 275 path_nodes = []
276 276
277 for obj_path, (node_kind, flags) in self._remote.dir_items(self.raw_id, path):
277 for obj_path, node_kind, flags, pre_load_data in self._remote.get_nodes(self.raw_id, path, pre_load):
278 278
279 279 if node_kind is None:
280 280 raise CommitError(f"Requested object type={node_kind} cannot be mapped to a proper type")
@@ -295,7 +295,7 class MercurialCommit(base.BaseCommit):
295 295 if node_kind == NodeKind.DIR:
296 296 entry = DirNode(safe_bytes(obj_path), commit=self)
297 297 elif node_kind == NodeKind.FILE:
298 entry = FileNode(safe_bytes(obj_path), commit=self, mode=stat_, pre_load=pre_load)
298 entry = FileNode(safe_bytes(obj_path), commit=self, mode=stat_, pre_load=pre_load, pre_load_data=pre_load_data)
299 299 if entry:
300 300 self.nodes[obj_path] = entry
301 301 path_nodes.append(entry)
@@ -201,8 +201,8 class SubversionCommit(base.BaseCommit):
201 201 path = self._fix_path(path)
202 202
203 203 path_nodes = []
204 for name, node_kind in self._remote.get_nodes(self._svn_rev, path):
205 obj_path = vcspath.join(path, name)
204
205 for obj_path, node_kind, pre_load_data in self._remote.get_nodes(self._svn_rev, path, pre_load):
206 206
207 207 if node_kind is None:
208 208 raise CommitError(f"Requested object type={node_kind} cannot be determined")
@@ -224,7 +224,7 class SubversionCommit(base.BaseCommit):
224 224 if node_kind == NodeKind.DIR:
225 225 entry = nodes.DirNode(safe_bytes(obj_path), commit=self)
226 226 elif node_kind == NodeKind.FILE:
227 entry = nodes.FileNode(safe_bytes(obj_path), commit=self, mode=stat_, pre_load=pre_load)
227 entry = nodes.FileNode(safe_bytes(obj_path), commit=self, mode=stat_, pre_load=pre_load, pre_load_data=pre_load_data)
228 228 if entry:
229 229 self.nodes[obj_path] = entry
230 230 path_nodes.append(entry)
@@ -241,7 +241,7 class FileNode(Node):
241 241
242 242 _filter_pre_load = []
243 243
244 def __init__(self, path: bytes, content: bytes | None = None, commit=None, mode=None, pre_load=None):
244 def __init__(self, path: bytes, content: bytes | None = None, commit=None, mode=None, pre_load=None, pre_load_data=None):
245 245 """
246 246 Only one of ``content`` and ``commit`` may be given. Passing both
247 247 would raise ``NodeError`` exception.
@@ -263,8 +263,10 class FileNode(Node):
263 263 content = safe_bytes(content)
264 264 self._content = content
265 265 self._mode = mode or FILEMODE_DEFAULT
266
267 self._set_bulk_properties(pre_load)
266 if pre_load_data:
267 self._store_pre_load(pre_load_data)
268 else:
269 self._set_bulk_properties(pre_load)
268 270
269 271 def __eq__(self, other):
270 272 eq = super().__eq__(other)
@@ -296,7 +298,10 class FileNode(Node):
296 298 remote = self.commit.get_remote()
297 299 result = remote.bulk_file_request(self.commit.raw_id, self.bytes_path, pre_load)
298 300
299 for attr, value in result.items():
301 self._store_pre_load(result.items())
302
303 def _store_pre_load(self, pre_load_data):
304 for attr, value in pre_load_data:
300 305 if attr == "flags":
301 306 self.__dict__["mode"] = safe_str(value)
302 307 elif attr == "size":
@@ -1141,7 +1141,7 class ReadmeFinder:
1141 1141 self._default_renderer = default_renderer
1142 1142 self._renderer_extensions = self.RENDERER_TO_EXTENSION.get(default_renderer, [])
1143 1143
1144 def search(self, commit, path=b'/'):
1144 def search(self, commit, path=b'/', nodes=None):
1145 1145 """
1146 1146 Find a readme in the given `commit`.
1147 1147 """
@@ -1150,7 +1150,9 class ReadmeFinder:
1150 1150 if commit.get_node(bytes_path).kind != NodeKind.DIR:
1151 1151 return None
1152 1152
1153 nodes = commit.get_nodes(bytes_path)
1153 if not nodes:
1154 nodes = commit.get_nodes(bytes_path)
1155
1154 1156 matches = self._match_readmes(nodes)
1155 1157 matches = self._sort_according_to_priority(matches)
1156 1158 if matches:
@@ -38,7 +38,7 http://docker-dev:10020/ipython/files/ma
38 38
39 39 <% has_files = False %>
40 40 % if not c.file.is_submodule():
41 % for cnt, node in enumerate(c.file):
41 % for cnt, node in enumerate(c.file_nodes):
42 42 <% has_files = True %>
43 43 <tr class="parity${(cnt % 2)}">
44 44 <td class="td-componentname">
@@ -105,6 +105,8 http://docker-dev:10020/ipython/files/ma
105 105 ${h.escape(c.file.name)}
106 106 <pre>${c.file.url}</pre>
107 107 </span>
108 % else:
109 <div>${_('Empty directory')}</div>
108 110 %endif
109 111 </td>
110 112 </tr>
@@ -1218,19 +1218,21 class TestGetSubmoduleUrl(object):
1218 1218 def test_get_nodes_returns_links(self):
1219 1219 repository = mock.MagicMock()
1220 1220 repository.alias = "git"
1221 repository._remote.tree_items.return_value = [(b"subrepo", "stat", 1, NodeKind.SUBMODULE)]
1221 # obj_name, stat_, tree_item_id, node_kind, pre_load_data
1222 repository._remote.get_nodes.return_value = [(b"subrepo", "stat", 1, NodeKind.SUBMODULE, [])]
1222 1223 commit = GitCommit(repository=repository, raw_id="abcdef12", idx=1)
1223 1224 submodule_url = "https://code.rhodecode.com/dulwich"
1225
1224 1226 get_id_patch = mock.patch.object(commit, "_get_path_tree_id_and_type", return_value=(1, NodeKind.DIR))
1225 1227 get_submodule_patch = mock.patch.object(commit, "_get_submodule_url", return_value=submodule_url)
1226 1228
1227 1229 with get_id_patch, get_submodule_patch as submodule_mock:
1228 1230 nodes = commit.get_nodes(b"/abcde")
1229 1231
1230 submodule_mock.assert_called_once_with(b"/abcde/subrepo")
1231 1232 assert len(nodes) == 1
1232 1233 assert type(nodes[0]) == SubModuleNode
1233 1234 assert nodes[0].url == submodule_url
1235 submodule_mock.assert_called_once_with(b"/abcde/subrepo")
1234 1236
1235 1237
1236 1238 class TestGetShadowInstance(object):
General Comments 0
You need to be logged in to leave comments. Login now