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