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