##// END OF EJS Templates
archives: if implicit ref name was used to obtain archive, redirect to explicit commit sha so we can...
milka -
r4648:e8391c99 default
parent child Browse files
Show More
@@ -1,1575 +1,1581 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
27 import urllib
28 import pathlib2
28 import pathlib2
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, safe_unicode)
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(safe_str(h.escape(e)), category='error')
189 h.flash(safe_str(h.escape(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(safe_str(h.escape(e)), category='error')
209 h.flash(safe_str(h.escape(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 pathlib2.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 = pathlib2.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
377 if commit_id != commit.raw_id:
378 fname='{}{}'.format(commit.raw_id, ext)
379 raise HTTPFound(self.request.current_route_path(fname=fname))
380
376 try:
381 try:
377 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
378 except Exception:
383 except Exception:
379 return Response(_('No node at path {} for this repository').format(at_path))
384 return Response(_('No node at path {} for this repository').format(at_path))
380
385
381 # path sha is part of subdir
386 # path sha is part of subdir
382 path_sha = ''
387 path_sha = ''
383 if at_path != default_at_path:
388 if at_path != default_at_path:
384 path_sha = sha1(at_path)[:8]
389 path_sha = sha1(at_path)[:8]
385 short_sha = '-{}'.format(safe_str(commit.short_id))
390 short_sha = '-{}'.format(safe_str(commit.short_id))
386 # used for cache etc
391 # used for cache etc
387 archive_name = self._get_archive_name(
392 archive_name = self._get_archive_name(
388 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,
389 path_sha=path_sha, with_hash=with_hash)
394 path_sha=path_sha, with_hash=with_hash)
390
395
391 if not with_hash:
396 if not with_hash:
392 short_sha = ''
397 short_sha = ''
393 path_sha = ''
398 path_sha = ''
394
399
395 # what end client gets served
400 # what end client gets served
396 response_archive_name = self._get_archive_name(
401 response_archive_name = self._get_archive_name(
397 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,
398 path_sha=path_sha, with_hash=with_hash)
403 path_sha=path_sha, with_hash=with_hash)
399 # remove extension from our archive directory name
404 # remove extension from our archive directory name
400 archive_dir_name = response_archive_name[:-len(ext)]
405 archive_dir_name = response_archive_name[:-len(ext)]
401
406
402 use_cached_archive = False
407 use_cached_archive = False
403 archive_cache_dir = CONFIG.get('archive_cache_dir')
408 archive_cache_dir = CONFIG.get('archive_cache_dir')
404 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')
405 cached_archive_path = None
410 cached_archive_path = None
406
411
407 if archive_cache_enabled:
412 if archive_cache_enabled:
408 # check if we it's ok to write
413 # check if we it's ok to write, and re-create the archive cache
409 if not os.path.isdir(CONFIG['archive_cache_dir']):
414 if not os.path.isdir(CONFIG['archive_cache_dir']):
410 os.makedirs(CONFIG['archive_cache_dir'])
415 os.makedirs(CONFIG['archive_cache_dir'])
416
411 cached_archive_path = os.path.join(
417 cached_archive_path = os.path.join(
412 CONFIG['archive_cache_dir'], archive_name)
418 CONFIG['archive_cache_dir'], archive_name)
413 if os.path.isfile(cached_archive_path):
419 if os.path.isfile(cached_archive_path):
414 log.debug('Found cached archive in %s', cached_archive_path)
420 log.debug('Found cached archive in %s', cached_archive_path)
415 fd, archive = None, cached_archive_path
421 fd, archive = None, cached_archive_path
416 use_cached_archive = True
422 use_cached_archive = True
417 else:
423 else:
418 log.debug('Archive %s is not yet cached', archive_name)
424 log.debug('Archive %s is not yet cached', archive_name)
419
425
420 # generate new archive, as previous was not found in the cache
426 # generate new archive, as previous was not found in the cache
421 if not use_cached_archive:
427 if not use_cached_archive:
422 _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
423 fd, archive = tempfile.mkstemp(dir=_dir)
429 fd, archive = tempfile.mkstemp(dir=_dir)
424 log.debug('Creating new temp archive in %s', archive)
430 log.debug('Creating new temp archive in %s', archive)
425 try:
431 try:
426 commit.archive_repo(archive, archive_dir_name=archive_dir_name,
432 commit.archive_repo(archive, archive_dir_name=archive_dir_name,
427 kind=fileformat, subrepos=subrepos,
433 kind=fileformat, subrepos=subrepos,
428 archive_at_path=at_path)
434 archive_at_path=at_path)
429 except ImproperArchiveTypeError:
435 except ImproperArchiveTypeError:
430 return _('Unknown archive type')
436 return _('Unknown archive type')
431 if archive_cache_enabled:
437 if archive_cache_enabled:
432 # if we generated the archive and we have cache enabled
438 # if we generated the archive and we have cache enabled
433 # let's use this for future
439 # let's use this for future
434 log.debug('Storing new archive in %s', cached_archive_path)
440 log.debug('Storing new archive in %s', cached_archive_path)
435 shutil.move(archive, cached_archive_path)
441 shutil.move(archive, cached_archive_path)
436 archive = cached_archive_path
442 archive = cached_archive_path
437
443
438 # store download action
444 # store download action
439 audit_logger.store_web(
445 audit_logger.store_web(
440 'repo.archive.download', action_data={
446 'repo.archive.download', action_data={
441 'user_agent': self.request.user_agent,
447 'user_agent': self.request.user_agent,
442 'archive_name': archive_name,
448 'archive_name': archive_name,
443 'archive_spec': fname,
449 'archive_spec': fname,
444 'archive_cached': use_cached_archive},
450 'archive_cached': use_cached_archive},
445 user=self._rhodecode_user,
451 user=self._rhodecode_user,
446 repo=self.db_repo,
452 repo=self.db_repo,
447 commit=True
453 commit=True
448 )
454 )
449
455
450 def get_chunked_archive(archive_path):
456 def get_chunked_archive(archive_path):
451 with open(archive_path, 'rb') as stream:
457 with open(archive_path, 'rb') as stream:
452 while True:
458 while True:
453 data = stream.read(16 * 1024)
459 data = stream.read(16 * 1024)
454 if not data:
460 if not data:
455 if fd: # fd means we used temporary file
461 if fd: # fd means we used temporary file
456 os.close(fd)
462 os.close(fd)
457 if not archive_cache_enabled:
463 if not archive_cache_enabled:
458 log.debug('Destroying temp archive %s', archive_path)
464 log.debug('Destroying temp archive %s', archive_path)
459 os.remove(archive_path)
465 os.remove(archive_path)
460 break
466 break
461 yield data
467 yield data
462
468
463 response = Response(app_iter=get_chunked_archive(archive))
469 response = Response(app_iter=get_chunked_archive(archive))
464 response.content_disposition = str('attachment; filename=%s' % response_archive_name)
470 response.content_disposition = str('attachment; filename=%s' % response_archive_name)
465 response.content_type = str(content_type)
471 response.content_type = str(content_type)
466
472
467 return response
473 return response
468
474
469 def _get_file_node(self, commit_id, f_path):
475 def _get_file_node(self, commit_id, f_path):
470 if commit_id not in ['', None, 'None', '0' * 12, '0' * 40]:
476 if commit_id not in ['', None, 'None', '0' * 12, '0' * 40]:
471 commit = self.rhodecode_vcs_repo.get_commit(commit_id=commit_id)
477 commit = self.rhodecode_vcs_repo.get_commit(commit_id=commit_id)
472 try:
478 try:
473 node = commit.get_node(f_path)
479 node = commit.get_node(f_path)
474 if node.is_dir():
480 if node.is_dir():
475 raise NodeError('%s path is a %s not a file'
481 raise NodeError('%s path is a %s not a file'
476 % (node, type(node)))
482 % (node, type(node)))
477 except NodeDoesNotExistError:
483 except NodeDoesNotExistError:
478 commit = EmptyCommit(
484 commit = EmptyCommit(
479 commit_id=commit_id,
485 commit_id=commit_id,
480 idx=commit.idx,
486 idx=commit.idx,
481 repo=commit.repository,
487 repo=commit.repository,
482 alias=commit.repository.alias,
488 alias=commit.repository.alias,
483 message=commit.message,
489 message=commit.message,
484 author=commit.author,
490 author=commit.author,
485 date=commit.date)
491 date=commit.date)
486 node = FileNode(f_path, '', commit=commit)
492 node = FileNode(f_path, '', commit=commit)
487 else:
493 else:
488 commit = EmptyCommit(
494 commit = EmptyCommit(
489 repo=self.rhodecode_vcs_repo,
495 repo=self.rhodecode_vcs_repo,
490 alias=self.rhodecode_vcs_repo.alias)
496 alias=self.rhodecode_vcs_repo.alias)
491 node = FileNode(f_path, '', commit=commit)
497 node = FileNode(f_path, '', commit=commit)
492 return node
498 return node
493
499
494 @LoginRequired()
500 @LoginRequired()
495 @HasRepoPermissionAnyDecorator(
501 @HasRepoPermissionAnyDecorator(
496 'repository.read', 'repository.write', 'repository.admin')
502 'repository.read', 'repository.write', 'repository.admin')
497 def repo_files_diff(self):
503 def repo_files_diff(self):
498 c = self.load_default_context()
504 c = self.load_default_context()
499 f_path = self._get_f_path(self.request.matchdict)
505 f_path = self._get_f_path(self.request.matchdict)
500 diff1 = self.request.GET.get('diff1', '')
506 diff1 = self.request.GET.get('diff1', '')
501 diff2 = self.request.GET.get('diff2', '')
507 diff2 = self.request.GET.get('diff2', '')
502
508
503 path1, diff1 = parse_path_ref(diff1, default_path=f_path)
509 path1, diff1 = parse_path_ref(diff1, default_path=f_path)
504
510
505 ignore_whitespace = str2bool(self.request.GET.get('ignorews'))
511 ignore_whitespace = str2bool(self.request.GET.get('ignorews'))
506 line_context = self.request.GET.get('context', 3)
512 line_context = self.request.GET.get('context', 3)
507
513
508 if not any((diff1, diff2)):
514 if not any((diff1, diff2)):
509 h.flash(
515 h.flash(
510 'Need query parameter "diff1" or "diff2" to generate a diff.',
516 'Need query parameter "diff1" or "diff2" to generate a diff.',
511 category='error')
517 category='error')
512 raise HTTPBadRequest()
518 raise HTTPBadRequest()
513
519
514 c.action = self.request.GET.get('diff')
520 c.action = self.request.GET.get('diff')
515 if c.action not in ['download', 'raw']:
521 if c.action not in ['download', 'raw']:
516 compare_url = h.route_path(
522 compare_url = h.route_path(
517 'repo_compare',
523 'repo_compare',
518 repo_name=self.db_repo_name,
524 repo_name=self.db_repo_name,
519 source_ref_type='rev',
525 source_ref_type='rev',
520 source_ref=diff1,
526 source_ref=diff1,
521 target_repo=self.db_repo_name,
527 target_repo=self.db_repo_name,
522 target_ref_type='rev',
528 target_ref_type='rev',
523 target_ref=diff2,
529 target_ref=diff2,
524 _query=dict(f_path=f_path))
530 _query=dict(f_path=f_path))
525 # redirect to new view if we render diff
531 # redirect to new view if we render diff
526 raise HTTPFound(compare_url)
532 raise HTTPFound(compare_url)
527
533
528 try:
534 try:
529 node1 = self._get_file_node(diff1, path1)
535 node1 = self._get_file_node(diff1, path1)
530 node2 = self._get_file_node(diff2, f_path)
536 node2 = self._get_file_node(diff2, f_path)
531 except (RepositoryError, NodeError):
537 except (RepositoryError, NodeError):
532 log.exception("Exception while trying to get node from repository")
538 log.exception("Exception while trying to get node from repository")
533 raise HTTPFound(
539 raise HTTPFound(
534 h.route_path('repo_files', repo_name=self.db_repo_name,
540 h.route_path('repo_files', repo_name=self.db_repo_name,
535 commit_id='tip', f_path=f_path))
541 commit_id='tip', f_path=f_path))
536
542
537 if all(isinstance(node.commit, EmptyCommit)
543 if all(isinstance(node.commit, EmptyCommit)
538 for node in (node1, node2)):
544 for node in (node1, node2)):
539 raise HTTPNotFound()
545 raise HTTPNotFound()
540
546
541 c.commit_1 = node1.commit
547 c.commit_1 = node1.commit
542 c.commit_2 = node2.commit
548 c.commit_2 = node2.commit
543
549
544 if c.action == 'download':
550 if c.action == 'download':
545 _diff = diffs.get_gitdiff(node1, node2,
551 _diff = diffs.get_gitdiff(node1, node2,
546 ignore_whitespace=ignore_whitespace,
552 ignore_whitespace=ignore_whitespace,
547 context=line_context)
553 context=line_context)
548 diff = diffs.DiffProcessor(_diff, format='gitdiff')
554 diff = diffs.DiffProcessor(_diff, format='gitdiff')
549
555
550 response = Response(self.path_filter.get_raw_patch(diff))
556 response = Response(self.path_filter.get_raw_patch(diff))
551 response.content_type = 'text/plain'
557 response.content_type = 'text/plain'
552 response.content_disposition = (
558 response.content_disposition = (
553 'attachment; filename=%s_%s_vs_%s.diff' % (f_path, diff1, diff2)
559 'attachment; filename=%s_%s_vs_%s.diff' % (f_path, diff1, diff2)
554 )
560 )
555 charset = self._get_default_encoding(c)
561 charset = self._get_default_encoding(c)
556 if charset:
562 if charset:
557 response.charset = charset
563 response.charset = charset
558 return response
564 return response
559
565
560 elif c.action == 'raw':
566 elif c.action == 'raw':
561 _diff = diffs.get_gitdiff(node1, node2,
567 _diff = diffs.get_gitdiff(node1, node2,
562 ignore_whitespace=ignore_whitespace,
568 ignore_whitespace=ignore_whitespace,
563 context=line_context)
569 context=line_context)
564 diff = diffs.DiffProcessor(_diff, format='gitdiff')
570 diff = diffs.DiffProcessor(_diff, format='gitdiff')
565
571
566 response = Response(self.path_filter.get_raw_patch(diff))
572 response = Response(self.path_filter.get_raw_patch(diff))
567 response.content_type = 'text/plain'
573 response.content_type = 'text/plain'
568 charset = self._get_default_encoding(c)
574 charset = self._get_default_encoding(c)
569 if charset:
575 if charset:
570 response.charset = charset
576 response.charset = charset
571 return response
577 return response
572
578
573 # in case we ever end up here
579 # in case we ever end up here
574 raise HTTPNotFound()
580 raise HTTPNotFound()
575
581
576 @LoginRequired()
582 @LoginRequired()
577 @HasRepoPermissionAnyDecorator(
583 @HasRepoPermissionAnyDecorator(
578 'repository.read', 'repository.write', 'repository.admin')
584 'repository.read', 'repository.write', 'repository.admin')
579 def repo_files_diff_2way_redirect(self):
585 def repo_files_diff_2way_redirect(self):
580 """
586 """
581 Kept only to make OLD links work
587 Kept only to make OLD links work
582 """
588 """
583 f_path = self._get_f_path_unchecked(self.request.matchdict)
589 f_path = self._get_f_path_unchecked(self.request.matchdict)
584 diff1 = self.request.GET.get('diff1', '')
590 diff1 = self.request.GET.get('diff1', '')
585 diff2 = self.request.GET.get('diff2', '')
591 diff2 = self.request.GET.get('diff2', '')
586
592
587 if not any((diff1, diff2)):
593 if not any((diff1, diff2)):
588 h.flash(
594 h.flash(
589 'Need query parameter "diff1" or "diff2" to generate a diff.',
595 'Need query parameter "diff1" or "diff2" to generate a diff.',
590 category='error')
596 category='error')
591 raise HTTPBadRequest()
597 raise HTTPBadRequest()
592
598
593 compare_url = h.route_path(
599 compare_url = h.route_path(
594 'repo_compare',
600 'repo_compare',
595 repo_name=self.db_repo_name,
601 repo_name=self.db_repo_name,
596 source_ref_type='rev',
602 source_ref_type='rev',
597 source_ref=diff1,
603 source_ref=diff1,
598 target_ref_type='rev',
604 target_ref_type='rev',
599 target_ref=diff2,
605 target_ref=diff2,
600 _query=dict(f_path=f_path, diffmode='sideside',
606 _query=dict(f_path=f_path, diffmode='sideside',
601 target_repo=self.db_repo_name,))
607 target_repo=self.db_repo_name,))
602 raise HTTPFound(compare_url)
608 raise HTTPFound(compare_url)
603
609
604 @LoginRequired()
610 @LoginRequired()
605 def repo_files_default_commit_redirect(self):
611 def repo_files_default_commit_redirect(self):
606 """
612 """
607 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
608 commit for repository
614 commit for repository
609 """
615 """
610 c = self.load_default_context()
616 c = self.load_default_context()
611 ref_name = c.rhodecode_db_repo.landing_ref_name
617 ref_name = c.rhodecode_db_repo.landing_ref_name
612 landing_url = h.repo_files_by_ref_url(
618 landing_url = h.repo_files_by_ref_url(
613 c.rhodecode_db_repo.repo_name,
619 c.rhodecode_db_repo.repo_name,
614 c.rhodecode_db_repo.repo_type,
620 c.rhodecode_db_repo.repo_type,
615 f_path='',
621 f_path='',
616 ref_name=ref_name,
622 ref_name=ref_name,
617 commit_id='tip',
623 commit_id='tip',
618 query=dict(at=ref_name)
624 query=dict(at=ref_name)
619 )
625 )
620
626
621 raise HTTPFound(landing_url)
627 raise HTTPFound(landing_url)
622
628
623 @LoginRequired()
629 @LoginRequired()
624 @HasRepoPermissionAnyDecorator(
630 @HasRepoPermissionAnyDecorator(
625 'repository.read', 'repository.write', 'repository.admin')
631 'repository.read', 'repository.write', 'repository.admin')
626 def repo_files(self):
632 def repo_files(self):
627 c = self.load_default_context()
633 c = self.load_default_context()
628
634
629 view_name = getattr(self.request.matched_route, 'name', None)
635 view_name = getattr(self.request.matched_route, 'name', None)
630
636
631 c.annotate = view_name == 'repo_files:annotated'
637 c.annotate = view_name == 'repo_files:annotated'
632 # 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
633 # overwrite auto rendering by setting this GET flag
639 # overwrite auto rendering by setting this GET flag
634 c.renderer = view_name == 'repo_files:rendered' or \
640 c.renderer = view_name == 'repo_files:rendered' or \
635 not self.request.GET.get('no-render', False)
641 not self.request.GET.get('no-render', False)
636
642
637 commit_id, f_path = self._get_commit_and_path()
643 commit_id, f_path = self._get_commit_and_path()
638
644
639 c.commit = self._get_commit_or_redirect(commit_id)
645 c.commit = self._get_commit_or_redirect(commit_id)
640 c.branch = self.request.GET.get('branch', None)
646 c.branch = self.request.GET.get('branch', None)
641 c.f_path = f_path
647 c.f_path = f_path
642 at_rev = self.request.GET.get('at')
648 at_rev = self.request.GET.get('at')
643
649
644 # prev link
650 # prev link
645 try:
651 try:
646 prev_commit = c.commit.prev(c.branch)
652 prev_commit = c.commit.prev(c.branch)
647 c.prev_commit = prev_commit
653 c.prev_commit = prev_commit
648 c.url_prev = h.route_path(
654 c.url_prev = h.route_path(
649 'repo_files', repo_name=self.db_repo_name,
655 'repo_files', repo_name=self.db_repo_name,
650 commit_id=prev_commit.raw_id, f_path=f_path)
656 commit_id=prev_commit.raw_id, f_path=f_path)
651 if c.branch:
657 if c.branch:
652 c.url_prev += '?branch=%s' % c.branch
658 c.url_prev += '?branch=%s' % c.branch
653 except (CommitDoesNotExistError, VCSError):
659 except (CommitDoesNotExistError, VCSError):
654 c.url_prev = '#'
660 c.url_prev = '#'
655 c.prev_commit = EmptyCommit()
661 c.prev_commit = EmptyCommit()
656
662
657 # next link
663 # next link
658 try:
664 try:
659 next_commit = c.commit.next(c.branch)
665 next_commit = c.commit.next(c.branch)
660 c.next_commit = next_commit
666 c.next_commit = next_commit
661 c.url_next = h.route_path(
667 c.url_next = h.route_path(
662 'repo_files', repo_name=self.db_repo_name,
668 'repo_files', repo_name=self.db_repo_name,
663 commit_id=next_commit.raw_id, f_path=f_path)
669 commit_id=next_commit.raw_id, f_path=f_path)
664 if c.branch:
670 if c.branch:
665 c.url_next += '?branch=%s' % c.branch
671 c.url_next += '?branch=%s' % c.branch
666 except (CommitDoesNotExistError, VCSError):
672 except (CommitDoesNotExistError, VCSError):
667 c.url_next = '#'
673 c.url_next = '#'
668 c.next_commit = EmptyCommit()
674 c.next_commit = EmptyCommit()
669
675
670 # files or dirs
676 # files or dirs
671 try:
677 try:
672 c.file = c.commit.get_node(f_path)
678 c.file = c.commit.get_node(f_path)
673 c.file_author = True
679 c.file_author = True
674 c.file_tree = ''
680 c.file_tree = ''
675
681
676 # load file content
682 # load file content
677 if c.file.is_file():
683 if c.file.is_file():
678 c.lf_node = {}
684 c.lf_node = {}
679
685
680 has_lf_enabled = self._is_lf_enabled(self.db_repo)
686 has_lf_enabled = self._is_lf_enabled(self.db_repo)
681 if has_lf_enabled:
687 if has_lf_enabled:
682 c.lf_node = c.file.get_largefile_node()
688 c.lf_node = c.file.get_largefile_node()
683
689
684 c.file_source_page = 'true'
690 c.file_source_page = 'true'
685 c.file_last_commit = c.file.last_commit
691 c.file_last_commit = c.file.last_commit
686
692
687 c.file_size_too_big = c.file.size > c.visual.cut_off_limit_file
693 c.file_size_too_big = c.file.size > c.visual.cut_off_limit_file
688
694
689 if not (c.file_size_too_big or c.file.is_binary):
695 if not (c.file_size_too_big or c.file.is_binary):
690 if c.annotate: # annotation has precedence over renderer
696 if c.annotate: # annotation has precedence over renderer
691 c.annotated_lines = filenode_as_annotated_lines_tokens(
697 c.annotated_lines = filenode_as_annotated_lines_tokens(
692 c.file
698 c.file
693 )
699 )
694 else:
700 else:
695 c.renderer = (
701 c.renderer = (
696 c.renderer and h.renderer_from_filename(c.file.path)
702 c.renderer and h.renderer_from_filename(c.file.path)
697 )
703 )
698 if not c.renderer:
704 if not c.renderer:
699 c.lines = filenode_as_lines_tokens(c.file)
705 c.lines = filenode_as_lines_tokens(c.file)
700
706
701 _branch_name, _sha_commit_id, is_head = \
707 _branch_name, _sha_commit_id, is_head = \
702 self._is_valid_head(commit_id, self.rhodecode_vcs_repo,
708 self._is_valid_head(commit_id, self.rhodecode_vcs_repo,
703 landing_ref=self.db_repo.landing_ref_name)
709 landing_ref=self.db_repo.landing_ref_name)
704 c.on_branch_head = is_head
710 c.on_branch_head = is_head
705
711
706 branch = c.commit.branch if (
712 branch = c.commit.branch if (
707 c.commit.branch and '/' not in c.commit.branch) else None
713 c.commit.branch and '/' not in c.commit.branch) else None
708 c.branch_or_raw_id = branch or c.commit.raw_id
714 c.branch_or_raw_id = branch or c.commit.raw_id
709 c.branch_name = c.commit.branch or h.short_id(c.commit.raw_id)
715 c.branch_name = c.commit.branch or h.short_id(c.commit.raw_id)
710
716
711 author = c.file_last_commit.author
717 author = c.file_last_commit.author
712 c.authors = [[
718 c.authors = [[
713 h.email(author),
719 h.email(author),
714 h.person(author, 'username_or_name_or_email'),
720 h.person(author, 'username_or_name_or_email'),
715 1
721 1
716 ]]
722 ]]
717
723
718 else: # load tree content at path
724 else: # load tree content at path
719 c.file_source_page = 'false'
725 c.file_source_page = 'false'
720 c.authors = []
726 c.authors = []
721 # this loads a simple tree without metadata to speed things up
727 # this loads a simple tree without metadata to speed things up
722 # later via ajax we call repo_nodetree_full and fetch whole
728 # later via ajax we call repo_nodetree_full and fetch whole
723 c.file_tree = self._get_tree_at_commit(c, c.commit.raw_id, f_path, at_rev=at_rev)
729 c.file_tree = self._get_tree_at_commit(c, c.commit.raw_id, f_path, at_rev=at_rev)
724
730
725 c.readme_data, c.readme_file = \
731 c.readme_data, c.readme_file = \
726 self._get_readme_data(self.db_repo, c.visual.default_renderer,
732 self._get_readme_data(self.db_repo, c.visual.default_renderer,
727 c.commit.raw_id, f_path)
733 c.commit.raw_id, f_path)
728
734
729 except RepositoryError as e:
735 except RepositoryError as e:
730 h.flash(safe_str(h.escape(e)), category='error')
736 h.flash(safe_str(h.escape(e)), category='error')
731 raise HTTPNotFound()
737 raise HTTPNotFound()
732
738
733 if self.request.environ.get('HTTP_X_PJAX'):
739 if self.request.environ.get('HTTP_X_PJAX'):
734 html = render('rhodecode:templates/files/files_pjax.mako',
740 html = render('rhodecode:templates/files/files_pjax.mako',
735 self._get_template_context(c), self.request)
741 self._get_template_context(c), self.request)
736 else:
742 else:
737 html = render('rhodecode:templates/files/files.mako',
743 html = render('rhodecode:templates/files/files.mako',
738 self._get_template_context(c), self.request)
744 self._get_template_context(c), self.request)
739 return Response(html)
745 return Response(html)
740
746
741 @HasRepoPermissionAnyDecorator(
747 @HasRepoPermissionAnyDecorator(
742 'repository.read', 'repository.write', 'repository.admin')
748 'repository.read', 'repository.write', 'repository.admin')
743 def repo_files_annotated_previous(self):
749 def repo_files_annotated_previous(self):
744 self.load_default_context()
750 self.load_default_context()
745
751
746 commit_id, f_path = self._get_commit_and_path()
752 commit_id, f_path = self._get_commit_and_path()
747 commit = self._get_commit_or_redirect(commit_id)
753 commit = self._get_commit_or_redirect(commit_id)
748 prev_commit_id = commit.raw_id
754 prev_commit_id = commit.raw_id
749 line_anchor = self.request.GET.get('line_anchor')
755 line_anchor = self.request.GET.get('line_anchor')
750 is_file = False
756 is_file = False
751 try:
757 try:
752 _file = commit.get_node(f_path)
758 _file = commit.get_node(f_path)
753 is_file = _file.is_file()
759 is_file = _file.is_file()
754 except (NodeDoesNotExistError, CommitDoesNotExistError, VCSError):
760 except (NodeDoesNotExistError, CommitDoesNotExistError, VCSError):
755 pass
761 pass
756
762
757 if is_file:
763 if is_file:
758 history = commit.get_path_history(f_path)
764 history = commit.get_path_history(f_path)
759 prev_commit_id = history[1].raw_id \
765 prev_commit_id = history[1].raw_id \
760 if len(history) > 1 else prev_commit_id
766 if len(history) > 1 else prev_commit_id
761 prev_url = h.route_path(
767 prev_url = h.route_path(
762 'repo_files:annotated', repo_name=self.db_repo_name,
768 'repo_files:annotated', repo_name=self.db_repo_name,
763 commit_id=prev_commit_id, f_path=f_path,
769 commit_id=prev_commit_id, f_path=f_path,
764 _anchor='L{}'.format(line_anchor))
770 _anchor='L{}'.format(line_anchor))
765
771
766 raise HTTPFound(prev_url)
772 raise HTTPFound(prev_url)
767
773
768 @LoginRequired()
774 @LoginRequired()
769 @HasRepoPermissionAnyDecorator(
775 @HasRepoPermissionAnyDecorator(
770 'repository.read', 'repository.write', 'repository.admin')
776 'repository.read', 'repository.write', 'repository.admin')
771 def repo_nodetree_full(self):
777 def repo_nodetree_full(self):
772 """
778 """
773 Returns rendered html of file tree that contains commit date,
779 Returns rendered html of file tree that contains commit date,
774 author, commit_id for the specified combination of
780 author, commit_id for the specified combination of
775 repo, commit_id and file path
781 repo, commit_id and file path
776 """
782 """
777 c = self.load_default_context()
783 c = self.load_default_context()
778
784
779 commit_id, f_path = self._get_commit_and_path()
785 commit_id, f_path = self._get_commit_and_path()
780 commit = self._get_commit_or_redirect(commit_id)
786 commit = self._get_commit_or_redirect(commit_id)
781 try:
787 try:
782 dir_node = commit.get_node(f_path)
788 dir_node = commit.get_node(f_path)
783 except RepositoryError as e:
789 except RepositoryError as e:
784 return Response('error: {}'.format(h.escape(safe_str(e))))
790 return Response('error: {}'.format(h.escape(safe_str(e))))
785
791
786 if dir_node.is_file():
792 if dir_node.is_file():
787 return Response('')
793 return Response('')
788
794
789 c.file = dir_node
795 c.file = dir_node
790 c.commit = commit
796 c.commit = commit
791 at_rev = self.request.GET.get('at')
797 at_rev = self.request.GET.get('at')
792
798
793 html = self._get_tree_at_commit(
799 html = self._get_tree_at_commit(
794 c, commit.raw_id, dir_node.path, full_load=True, at_rev=at_rev)
800 c, commit.raw_id, dir_node.path, full_load=True, at_rev=at_rev)
795
801
796 return Response(html)
802 return Response(html)
797
803
798 def _get_attachement_headers(self, f_path):
804 def _get_attachement_headers(self, f_path):
799 f_name = safe_str(f_path.split(Repository.NAME_SEP)[-1])
805 f_name = safe_str(f_path.split(Repository.NAME_SEP)[-1])
800 safe_path = f_name.replace('"', '\\"')
806 safe_path = f_name.replace('"', '\\"')
801 encoded_path = urllib.quote(f_name)
807 encoded_path = urllib.quote(f_name)
802
808
803 return "attachment; " \
809 return "attachment; " \
804 "filename=\"{}\"; " \
810 "filename=\"{}\"; " \
805 "filename*=UTF-8\'\'{}".format(safe_path, encoded_path)
811 "filename*=UTF-8\'\'{}".format(safe_path, encoded_path)
806
812
807 @LoginRequired()
813 @LoginRequired()
808 @HasRepoPermissionAnyDecorator(
814 @HasRepoPermissionAnyDecorator(
809 'repository.read', 'repository.write', 'repository.admin')
815 'repository.read', 'repository.write', 'repository.admin')
810 def repo_file_raw(self):
816 def repo_file_raw(self):
811 """
817 """
812 Action for show as raw, some mimetypes are "rendered",
818 Action for show as raw, some mimetypes are "rendered",
813 those include images, icons.
819 those include images, icons.
814 """
820 """
815 c = self.load_default_context()
821 c = self.load_default_context()
816
822
817 commit_id, f_path = self._get_commit_and_path()
823 commit_id, f_path = self._get_commit_and_path()
818 commit = self._get_commit_or_redirect(commit_id)
824 commit = self._get_commit_or_redirect(commit_id)
819 file_node = self._get_filenode_or_redirect(commit, f_path)
825 file_node = self._get_filenode_or_redirect(commit, f_path)
820
826
821 raw_mimetype_mapping = {
827 raw_mimetype_mapping = {
822 # map original mimetype to a mimetype used for "show as raw"
828 # map original mimetype to a mimetype used for "show as raw"
823 # you can also provide a content-disposition to override the
829 # you can also provide a content-disposition to override the
824 # default "attachment" disposition.
830 # default "attachment" disposition.
825 # orig_type: (new_type, new_dispo)
831 # orig_type: (new_type, new_dispo)
826
832
827 # show images inline:
833 # show images inline:
828 # Do not re-add SVG: it is unsafe and permits XSS attacks. One can
834 # Do not re-add SVG: it is unsafe and permits XSS attacks. One can
829 # for example render an SVG with javascript inside or even render
835 # for example render an SVG with javascript inside or even render
830 # HTML.
836 # HTML.
831 'image/x-icon': ('image/x-icon', 'inline'),
837 'image/x-icon': ('image/x-icon', 'inline'),
832 'image/png': ('image/png', 'inline'),
838 'image/png': ('image/png', 'inline'),
833 'image/gif': ('image/gif', 'inline'),
839 'image/gif': ('image/gif', 'inline'),
834 'image/jpeg': ('image/jpeg', 'inline'),
840 'image/jpeg': ('image/jpeg', 'inline'),
835 'application/pdf': ('application/pdf', 'inline'),
841 'application/pdf': ('application/pdf', 'inline'),
836 }
842 }
837
843
838 mimetype = file_node.mimetype
844 mimetype = file_node.mimetype
839 try:
845 try:
840 mimetype, disposition = raw_mimetype_mapping[mimetype]
846 mimetype, disposition = raw_mimetype_mapping[mimetype]
841 except KeyError:
847 except KeyError:
842 # we don't know anything special about this, handle it safely
848 # we don't know anything special about this, handle it safely
843 if file_node.is_binary:
849 if file_node.is_binary:
844 # do same as download raw for binary files
850 # do same as download raw for binary files
845 mimetype, disposition = 'application/octet-stream', 'attachment'
851 mimetype, disposition = 'application/octet-stream', 'attachment'
846 else:
852 else:
847 # do not just use the original mimetype, but force text/plain,
853 # do not just use the original mimetype, but force text/plain,
848 # otherwise it would serve text/html and that might be unsafe.
854 # otherwise it would serve text/html and that might be unsafe.
849 # Note: underlying vcs library fakes text/plain mimetype if the
855 # Note: underlying vcs library fakes text/plain mimetype if the
850 # mimetype can not be determined and it thinks it is not
856 # mimetype can not be determined and it thinks it is not
851 # binary.This might lead to erroneous text display in some
857 # binary.This might lead to erroneous text display in some
852 # cases, but helps in other cases, like with text files
858 # cases, but helps in other cases, like with text files
853 # without extension.
859 # without extension.
854 mimetype, disposition = 'text/plain', 'inline'
860 mimetype, disposition = 'text/plain', 'inline'
855
861
856 if disposition == 'attachment':
862 if disposition == 'attachment':
857 disposition = self._get_attachement_headers(f_path)
863 disposition = self._get_attachement_headers(f_path)
858
864
859 stream_content = file_node.stream_bytes()
865 stream_content = file_node.stream_bytes()
860
866
861 response = Response(app_iter=stream_content)
867 response = Response(app_iter=stream_content)
862 response.content_disposition = disposition
868 response.content_disposition = disposition
863 response.content_type = mimetype
869 response.content_type = mimetype
864
870
865 charset = self._get_default_encoding(c)
871 charset = self._get_default_encoding(c)
866 if charset:
872 if charset:
867 response.charset = charset
873 response.charset = charset
868
874
869 return response
875 return response
870
876
871 @LoginRequired()
877 @LoginRequired()
872 @HasRepoPermissionAnyDecorator(
878 @HasRepoPermissionAnyDecorator(
873 'repository.read', 'repository.write', 'repository.admin')
879 'repository.read', 'repository.write', 'repository.admin')
874 def repo_file_download(self):
880 def repo_file_download(self):
875 c = self.load_default_context()
881 c = self.load_default_context()
876
882
877 commit_id, f_path = self._get_commit_and_path()
883 commit_id, f_path = self._get_commit_and_path()
878 commit = self._get_commit_or_redirect(commit_id)
884 commit = self._get_commit_or_redirect(commit_id)
879 file_node = self._get_filenode_or_redirect(commit, f_path)
885 file_node = self._get_filenode_or_redirect(commit, f_path)
880
886
881 if self.request.GET.get('lf'):
887 if self.request.GET.get('lf'):
882 # only if lf get flag is passed, we download this file
888 # only if lf get flag is passed, we download this file
883 # as LFS/Largefile
889 # as LFS/Largefile
884 lf_node = file_node.get_largefile_node()
890 lf_node = file_node.get_largefile_node()
885 if lf_node:
891 if lf_node:
886 # overwrite our pointer with the REAL large-file
892 # overwrite our pointer with the REAL large-file
887 file_node = lf_node
893 file_node = lf_node
888
894
889 disposition = self._get_attachement_headers(f_path)
895 disposition = self._get_attachement_headers(f_path)
890
896
891 stream_content = file_node.stream_bytes()
897 stream_content = file_node.stream_bytes()
892
898
893 response = Response(app_iter=stream_content)
899 response = Response(app_iter=stream_content)
894 response.content_disposition = disposition
900 response.content_disposition = disposition
895 response.content_type = file_node.mimetype
901 response.content_type = file_node.mimetype
896
902
897 charset = self._get_default_encoding(c)
903 charset = self._get_default_encoding(c)
898 if charset:
904 if charset:
899 response.charset = charset
905 response.charset = charset
900
906
901 return response
907 return response
902
908
903 def _get_nodelist_at_commit(self, repo_name, repo_id, commit_id, f_path):
909 def _get_nodelist_at_commit(self, repo_name, repo_id, commit_id, f_path):
904
910
905 cache_seconds = safe_int(
911 cache_seconds = safe_int(
906 rhodecode.CONFIG.get('rc_cache.cache_repo.expiration_time'))
912 rhodecode.CONFIG.get('rc_cache.cache_repo.expiration_time'))
907 cache_on = cache_seconds > 0
913 cache_on = cache_seconds > 0
908 log.debug(
914 log.debug(
909 'Computing FILE SEARCH for repo_id %s commit_id `%s` and path `%s`'
915 'Computing FILE SEARCH for repo_id %s commit_id `%s` and path `%s`'
910 'with caching: %s[TTL: %ss]' % (
916 'with caching: %s[TTL: %ss]' % (
911 repo_id, commit_id, f_path, cache_on, cache_seconds or 0))
917 repo_id, commit_id, f_path, cache_on, cache_seconds or 0))
912
918
913 cache_namespace_uid = 'cache_repo.{}'.format(repo_id)
919 cache_namespace_uid = 'cache_repo.{}'.format(repo_id)
914 region = rc_cache.get_or_create_region('cache_repo', cache_namespace_uid)
920 region = rc_cache.get_or_create_region('cache_repo', cache_namespace_uid)
915
921
916 @region.conditional_cache_on_arguments(namespace=cache_namespace_uid, condition=cache_on)
922 @region.conditional_cache_on_arguments(namespace=cache_namespace_uid, condition=cache_on)
917 def compute_file_search(_name_hash, _repo_id, _commit_id, _f_path):
923 def compute_file_search(_name_hash, _repo_id, _commit_id, _f_path):
918 log.debug('Generating cached nodelist for repo_id:%s, %s, %s',
924 log.debug('Generating cached nodelist for repo_id:%s, %s, %s',
919 _repo_id, commit_id, f_path)
925 _repo_id, commit_id, f_path)
920 try:
926 try:
921 _d, _f = ScmModel().get_quick_filter_nodes(repo_name, _commit_id, _f_path)
927 _d, _f = ScmModel().get_quick_filter_nodes(repo_name, _commit_id, _f_path)
922 except (RepositoryError, CommitDoesNotExistError, Exception) as e:
928 except (RepositoryError, CommitDoesNotExistError, Exception) as e:
923 log.exception(safe_str(e))
929 log.exception(safe_str(e))
924 h.flash(safe_str(h.escape(e)), category='error')
930 h.flash(safe_str(h.escape(e)), category='error')
925 raise HTTPFound(h.route_path(
931 raise HTTPFound(h.route_path(
926 'repo_files', repo_name=self.db_repo_name,
932 'repo_files', repo_name=self.db_repo_name,
927 commit_id='tip', f_path='/'))
933 commit_id='tip', f_path='/'))
928
934
929 return _d + _f
935 return _d + _f
930
936
931 result = compute_file_search(self.db_repo.repo_name_hash, self.db_repo.repo_id,
937 result = compute_file_search(self.db_repo.repo_name_hash, self.db_repo.repo_id,
932 commit_id, f_path)
938 commit_id, f_path)
933 return filter(lambda n: self.path_filter.path_access_allowed(n['name']), result)
939 return filter(lambda n: self.path_filter.path_access_allowed(n['name']), result)
934
940
935 @LoginRequired()
941 @LoginRequired()
936 @HasRepoPermissionAnyDecorator(
942 @HasRepoPermissionAnyDecorator(
937 'repository.read', 'repository.write', 'repository.admin')
943 'repository.read', 'repository.write', 'repository.admin')
938 def repo_nodelist(self):
944 def repo_nodelist(self):
939 self.load_default_context()
945 self.load_default_context()
940
946
941 commit_id, f_path = self._get_commit_and_path()
947 commit_id, f_path = self._get_commit_and_path()
942 commit = self._get_commit_or_redirect(commit_id)
948 commit = self._get_commit_or_redirect(commit_id)
943
949
944 metadata = self._get_nodelist_at_commit(
950 metadata = self._get_nodelist_at_commit(
945 self.db_repo_name, self.db_repo.repo_id, commit.raw_id, f_path)
951 self.db_repo_name, self.db_repo.repo_id, commit.raw_id, f_path)
946 return {'nodes': metadata}
952 return {'nodes': metadata}
947
953
948 def _create_references(self, branches_or_tags, symbolic_reference, f_path, ref_type):
954 def _create_references(self, branches_or_tags, symbolic_reference, f_path, ref_type):
949 items = []
955 items = []
950 for name, commit_id in branches_or_tags.items():
956 for name, commit_id in branches_or_tags.items():
951 sym_ref = symbolic_reference(commit_id, name, f_path, ref_type)
957 sym_ref = symbolic_reference(commit_id, name, f_path, ref_type)
952 items.append((sym_ref, name, ref_type))
958 items.append((sym_ref, name, ref_type))
953 return items
959 return items
954
960
955 def _symbolic_reference(self, commit_id, name, f_path, ref_type):
961 def _symbolic_reference(self, commit_id, name, f_path, ref_type):
956 return commit_id
962 return commit_id
957
963
958 def _symbolic_reference_svn(self, commit_id, name, f_path, ref_type):
964 def _symbolic_reference_svn(self, commit_id, name, f_path, ref_type):
959 return commit_id
965 return commit_id
960
966
961 # NOTE(dan): old code we used in "diff" mode compare
967 # NOTE(dan): old code we used in "diff" mode compare
962 new_f_path = vcspath.join(name, f_path)
968 new_f_path = vcspath.join(name, f_path)
963 return u'%s@%s' % (new_f_path, commit_id)
969 return u'%s@%s' % (new_f_path, commit_id)
964
970
965 def _get_node_history(self, commit_obj, f_path, commits=None):
971 def _get_node_history(self, commit_obj, f_path, commits=None):
966 """
972 """
967 get commit history for given node
973 get commit history for given node
968
974
969 :param commit_obj: commit to calculate history
975 :param commit_obj: commit to calculate history
970 :param f_path: path for node to calculate history for
976 :param f_path: path for node to calculate history for
971 :param commits: if passed don't calculate history and take
977 :param commits: if passed don't calculate history and take
972 commits defined in this list
978 commits defined in this list
973 """
979 """
974 _ = self.request.translate
980 _ = self.request.translate
975
981
976 # calculate history based on tip
982 # calculate history based on tip
977 tip = self.rhodecode_vcs_repo.get_commit()
983 tip = self.rhodecode_vcs_repo.get_commit()
978 if commits is None:
984 if commits is None:
979 pre_load = ["author", "branch"]
985 pre_load = ["author", "branch"]
980 try:
986 try:
981 commits = tip.get_path_history(f_path, pre_load=pre_load)
987 commits = tip.get_path_history(f_path, pre_load=pre_load)
982 except (NodeDoesNotExistError, CommitError):
988 except (NodeDoesNotExistError, CommitError):
983 # this node is not present at tip!
989 # this node is not present at tip!
984 commits = commit_obj.get_path_history(f_path, pre_load=pre_load)
990 commits = commit_obj.get_path_history(f_path, pre_load=pre_load)
985
991
986 history = []
992 history = []
987 commits_group = ([], _("Changesets"))
993 commits_group = ([], _("Changesets"))
988 for commit in commits:
994 for commit in commits:
989 branch = ' (%s)' % commit.branch if commit.branch else ''
995 branch = ' (%s)' % commit.branch if commit.branch else ''
990 n_desc = 'r%s:%s%s' % (commit.idx, commit.short_id, branch)
996 n_desc = 'r%s:%s%s' % (commit.idx, commit.short_id, branch)
991 commits_group[0].append((commit.raw_id, n_desc, 'sha'))
997 commits_group[0].append((commit.raw_id, n_desc, 'sha'))
992 history.append(commits_group)
998 history.append(commits_group)
993
999
994 symbolic_reference = self._symbolic_reference
1000 symbolic_reference = self._symbolic_reference
995
1001
996 if self.rhodecode_vcs_repo.alias == 'svn':
1002 if self.rhodecode_vcs_repo.alias == 'svn':
997 adjusted_f_path = RepoFilesView.adjust_file_path_for_svn(
1003 adjusted_f_path = RepoFilesView.adjust_file_path_for_svn(
998 f_path, self.rhodecode_vcs_repo)
1004 f_path, self.rhodecode_vcs_repo)
999 if adjusted_f_path != f_path:
1005 if adjusted_f_path != f_path:
1000 log.debug(
1006 log.debug(
1001 'Recognized svn tag or branch in file "%s", using svn '
1007 'Recognized svn tag or branch in file "%s", using svn '
1002 'specific symbolic references', f_path)
1008 'specific symbolic references', f_path)
1003 f_path = adjusted_f_path
1009 f_path = adjusted_f_path
1004 symbolic_reference = self._symbolic_reference_svn
1010 symbolic_reference = self._symbolic_reference_svn
1005
1011
1006 branches = self._create_references(
1012 branches = self._create_references(
1007 self.rhodecode_vcs_repo.branches, symbolic_reference, f_path, 'branch')
1013 self.rhodecode_vcs_repo.branches, symbolic_reference, f_path, 'branch')
1008 branches_group = (branches, _("Branches"))
1014 branches_group = (branches, _("Branches"))
1009
1015
1010 tags = self._create_references(
1016 tags = self._create_references(
1011 self.rhodecode_vcs_repo.tags, symbolic_reference, f_path, 'tag')
1017 self.rhodecode_vcs_repo.tags, symbolic_reference, f_path, 'tag')
1012 tags_group = (tags, _("Tags"))
1018 tags_group = (tags, _("Tags"))
1013
1019
1014 history.append(branches_group)
1020 history.append(branches_group)
1015 history.append(tags_group)
1021 history.append(tags_group)
1016
1022
1017 return history, commits
1023 return history, commits
1018
1024
1019 @LoginRequired()
1025 @LoginRequired()
1020 @HasRepoPermissionAnyDecorator(
1026 @HasRepoPermissionAnyDecorator(
1021 'repository.read', 'repository.write', 'repository.admin')
1027 'repository.read', 'repository.write', 'repository.admin')
1022 def repo_file_history(self):
1028 def repo_file_history(self):
1023 self.load_default_context()
1029 self.load_default_context()
1024
1030
1025 commit_id, f_path = self._get_commit_and_path()
1031 commit_id, f_path = self._get_commit_and_path()
1026 commit = self._get_commit_or_redirect(commit_id)
1032 commit = self._get_commit_or_redirect(commit_id)
1027 file_node = self._get_filenode_or_redirect(commit, f_path)
1033 file_node = self._get_filenode_or_redirect(commit, f_path)
1028
1034
1029 if file_node.is_file():
1035 if file_node.is_file():
1030 file_history, _hist = self._get_node_history(commit, f_path)
1036 file_history, _hist = self._get_node_history(commit, f_path)
1031
1037
1032 res = []
1038 res = []
1033 for section_items, section in file_history:
1039 for section_items, section in file_history:
1034 items = []
1040 items = []
1035 for obj_id, obj_text, obj_type in section_items:
1041 for obj_id, obj_text, obj_type in section_items:
1036 at_rev = ''
1042 at_rev = ''
1037 if obj_type in ['branch', 'bookmark', 'tag']:
1043 if obj_type in ['branch', 'bookmark', 'tag']:
1038 at_rev = obj_text
1044 at_rev = obj_text
1039 entry = {
1045 entry = {
1040 'id': obj_id,
1046 'id': obj_id,
1041 'text': obj_text,
1047 'text': obj_text,
1042 'type': obj_type,
1048 'type': obj_type,
1043 'at_rev': at_rev
1049 'at_rev': at_rev
1044 }
1050 }
1045
1051
1046 items.append(entry)
1052 items.append(entry)
1047
1053
1048 res.append({
1054 res.append({
1049 'text': section,
1055 'text': section,
1050 'children': items
1056 'children': items
1051 })
1057 })
1052
1058
1053 data = {
1059 data = {
1054 'more': False,
1060 'more': False,
1055 'results': res
1061 'results': res
1056 }
1062 }
1057 return data
1063 return data
1058
1064
1059 log.warning('Cannot fetch history for directory')
1065 log.warning('Cannot fetch history for directory')
1060 raise HTTPBadRequest()
1066 raise HTTPBadRequest()
1061
1067
1062 @LoginRequired()
1068 @LoginRequired()
1063 @HasRepoPermissionAnyDecorator(
1069 @HasRepoPermissionAnyDecorator(
1064 'repository.read', 'repository.write', 'repository.admin')
1070 'repository.read', 'repository.write', 'repository.admin')
1065 def repo_file_authors(self):
1071 def repo_file_authors(self):
1066 c = self.load_default_context()
1072 c = self.load_default_context()
1067
1073
1068 commit_id, f_path = self._get_commit_and_path()
1074 commit_id, f_path = self._get_commit_and_path()
1069 commit = self._get_commit_or_redirect(commit_id)
1075 commit = self._get_commit_or_redirect(commit_id)
1070 file_node = self._get_filenode_or_redirect(commit, f_path)
1076 file_node = self._get_filenode_or_redirect(commit, f_path)
1071
1077
1072 if not file_node.is_file():
1078 if not file_node.is_file():
1073 raise HTTPBadRequest()
1079 raise HTTPBadRequest()
1074
1080
1075 c.file_last_commit = file_node.last_commit
1081 c.file_last_commit = file_node.last_commit
1076 if self.request.GET.get('annotate') == '1':
1082 if self.request.GET.get('annotate') == '1':
1077 # use _hist from annotation if annotation mode is on
1083 # use _hist from annotation if annotation mode is on
1078 commit_ids = set(x[1] for x in file_node.annotate)
1084 commit_ids = set(x[1] for x in file_node.annotate)
1079 _hist = (
1085 _hist = (
1080 self.rhodecode_vcs_repo.get_commit(commit_id)
1086 self.rhodecode_vcs_repo.get_commit(commit_id)
1081 for commit_id in commit_ids)
1087 for commit_id in commit_ids)
1082 else:
1088 else:
1083 _f_history, _hist = self._get_node_history(commit, f_path)
1089 _f_history, _hist = self._get_node_history(commit, f_path)
1084 c.file_author = False
1090 c.file_author = False
1085
1091
1086 unique = collections.OrderedDict()
1092 unique = collections.OrderedDict()
1087 for commit in _hist:
1093 for commit in _hist:
1088 author = commit.author
1094 author = commit.author
1089 if author not in unique:
1095 if author not in unique:
1090 unique[commit.author] = [
1096 unique[commit.author] = [
1091 h.email(author),
1097 h.email(author),
1092 h.person(author, 'username_or_name_or_email'),
1098 h.person(author, 'username_or_name_or_email'),
1093 1 # counter
1099 1 # counter
1094 ]
1100 ]
1095
1101
1096 else:
1102 else:
1097 # increase counter
1103 # increase counter
1098 unique[commit.author][2] += 1
1104 unique[commit.author][2] += 1
1099
1105
1100 c.authors = [val for val in unique.values()]
1106 c.authors = [val for val in unique.values()]
1101
1107
1102 return self._get_template_context(c)
1108 return self._get_template_context(c)
1103
1109
1104 @LoginRequired()
1110 @LoginRequired()
1105 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1111 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1106 def repo_files_check_head(self):
1112 def repo_files_check_head(self):
1107 self.load_default_context()
1113 self.load_default_context()
1108
1114
1109 commit_id, f_path = self._get_commit_and_path()
1115 commit_id, f_path = self._get_commit_and_path()
1110 _branch_name, _sha_commit_id, is_head = \
1116 _branch_name, _sha_commit_id, is_head = \
1111 self._is_valid_head(commit_id, self.rhodecode_vcs_repo,
1117 self._is_valid_head(commit_id, self.rhodecode_vcs_repo,
1112 landing_ref=self.db_repo.landing_ref_name)
1118 landing_ref=self.db_repo.landing_ref_name)
1113
1119
1114 new_path = self.request.POST.get('path')
1120 new_path = self.request.POST.get('path')
1115 operation = self.request.POST.get('operation')
1121 operation = self.request.POST.get('operation')
1116 path_exist = ''
1122 path_exist = ''
1117
1123
1118 if new_path and operation in ['create', 'upload']:
1124 if new_path and operation in ['create', 'upload']:
1119 new_f_path = os.path.join(f_path.lstrip('/'), new_path)
1125 new_f_path = os.path.join(f_path.lstrip('/'), new_path)
1120 try:
1126 try:
1121 commit_obj = self.rhodecode_vcs_repo.get_commit(commit_id)
1127 commit_obj = self.rhodecode_vcs_repo.get_commit(commit_id)
1122 # NOTE(dan): construct whole path without leading /
1128 # NOTE(dan): construct whole path without leading /
1123 file_node = commit_obj.get_node(new_f_path)
1129 file_node = commit_obj.get_node(new_f_path)
1124 if file_node is not None:
1130 if file_node is not None:
1125 path_exist = new_f_path
1131 path_exist = new_f_path
1126 except EmptyRepositoryError:
1132 except EmptyRepositoryError:
1127 pass
1133 pass
1128 except Exception:
1134 except Exception:
1129 pass
1135 pass
1130
1136
1131 return {
1137 return {
1132 'branch': _branch_name,
1138 'branch': _branch_name,
1133 'sha': _sha_commit_id,
1139 'sha': _sha_commit_id,
1134 'is_head': is_head,
1140 'is_head': is_head,
1135 'path_exists': path_exist
1141 'path_exists': path_exist
1136 }
1142 }
1137
1143
1138 @LoginRequired()
1144 @LoginRequired()
1139 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1145 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1140 def repo_files_remove_file(self):
1146 def repo_files_remove_file(self):
1141 _ = self.request.translate
1147 _ = self.request.translate
1142 c = self.load_default_context()
1148 c = self.load_default_context()
1143 commit_id, f_path = self._get_commit_and_path()
1149 commit_id, f_path = self._get_commit_and_path()
1144
1150
1145 self._ensure_not_locked()
1151 self._ensure_not_locked()
1146 _branch_name, _sha_commit_id, is_head = \
1152 _branch_name, _sha_commit_id, is_head = \
1147 self._is_valid_head(commit_id, self.rhodecode_vcs_repo,
1153 self._is_valid_head(commit_id, self.rhodecode_vcs_repo,
1148 landing_ref=self.db_repo.landing_ref_name)
1154 landing_ref=self.db_repo.landing_ref_name)
1149
1155
1150 self.forbid_non_head(is_head, f_path)
1156 self.forbid_non_head(is_head, f_path)
1151 self.check_branch_permission(_branch_name)
1157 self.check_branch_permission(_branch_name)
1152
1158
1153 c.commit = self._get_commit_or_redirect(commit_id)
1159 c.commit = self._get_commit_or_redirect(commit_id)
1154 c.file = self._get_filenode_or_redirect(c.commit, f_path)
1160 c.file = self._get_filenode_or_redirect(c.commit, f_path)
1155
1161
1156 c.default_message = _(
1162 c.default_message = _(
1157 'Deleted file {} via RhodeCode Enterprise').format(f_path)
1163 'Deleted file {} via RhodeCode Enterprise').format(f_path)
1158 c.f_path = f_path
1164 c.f_path = f_path
1159
1165
1160 return self._get_template_context(c)
1166 return self._get_template_context(c)
1161
1167
1162 @LoginRequired()
1168 @LoginRequired()
1163 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1169 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1164 @CSRFRequired()
1170 @CSRFRequired()
1165 def repo_files_delete_file(self):
1171 def repo_files_delete_file(self):
1166 _ = self.request.translate
1172 _ = self.request.translate
1167
1173
1168 c = self.load_default_context()
1174 c = self.load_default_context()
1169 commit_id, f_path = self._get_commit_and_path()
1175 commit_id, f_path = self._get_commit_and_path()
1170
1176
1171 self._ensure_not_locked()
1177 self._ensure_not_locked()
1172 _branch_name, _sha_commit_id, is_head = \
1178 _branch_name, _sha_commit_id, is_head = \
1173 self._is_valid_head(commit_id, self.rhodecode_vcs_repo,
1179 self._is_valid_head(commit_id, self.rhodecode_vcs_repo,
1174 landing_ref=self.db_repo.landing_ref_name)
1180 landing_ref=self.db_repo.landing_ref_name)
1175
1181
1176 self.forbid_non_head(is_head, f_path)
1182 self.forbid_non_head(is_head, f_path)
1177 self.check_branch_permission(_branch_name)
1183 self.check_branch_permission(_branch_name)
1178
1184
1179 c.commit = self._get_commit_or_redirect(commit_id)
1185 c.commit = self._get_commit_or_redirect(commit_id)
1180 c.file = self._get_filenode_or_redirect(c.commit, f_path)
1186 c.file = self._get_filenode_or_redirect(c.commit, f_path)
1181
1187
1182 c.default_message = _(
1188 c.default_message = _(
1183 'Deleted file {} via RhodeCode Enterprise').format(f_path)
1189 'Deleted file {} via RhodeCode Enterprise').format(f_path)
1184 c.f_path = f_path
1190 c.f_path = f_path
1185 node_path = f_path
1191 node_path = f_path
1186 author = self._rhodecode_db_user.full_contact
1192 author = self._rhodecode_db_user.full_contact
1187 message = self.request.POST.get('message') or c.default_message
1193 message = self.request.POST.get('message') or c.default_message
1188 try:
1194 try:
1189 nodes = {
1195 nodes = {
1190 node_path: {
1196 node_path: {
1191 'content': ''
1197 'content': ''
1192 }
1198 }
1193 }
1199 }
1194 ScmModel().delete_nodes(
1200 ScmModel().delete_nodes(
1195 user=self._rhodecode_db_user.user_id, repo=self.db_repo,
1201 user=self._rhodecode_db_user.user_id, repo=self.db_repo,
1196 message=message,
1202 message=message,
1197 nodes=nodes,
1203 nodes=nodes,
1198 parent_commit=c.commit,
1204 parent_commit=c.commit,
1199 author=author,
1205 author=author,
1200 )
1206 )
1201
1207
1202 h.flash(
1208 h.flash(
1203 _('Successfully deleted file `{}`').format(
1209 _('Successfully deleted file `{}`').format(
1204 h.escape(f_path)), category='success')
1210 h.escape(f_path)), category='success')
1205 except Exception:
1211 except Exception:
1206 log.exception('Error during commit operation')
1212 log.exception('Error during commit operation')
1207 h.flash(_('Error occurred during commit'), category='error')
1213 h.flash(_('Error occurred during commit'), category='error')
1208 raise HTTPFound(
1214 raise HTTPFound(
1209 h.route_path('repo_commit', repo_name=self.db_repo_name,
1215 h.route_path('repo_commit', repo_name=self.db_repo_name,
1210 commit_id='tip'))
1216 commit_id='tip'))
1211
1217
1212 @LoginRequired()
1218 @LoginRequired()
1213 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1219 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1214 def repo_files_edit_file(self):
1220 def repo_files_edit_file(self):
1215 _ = self.request.translate
1221 _ = self.request.translate
1216 c = self.load_default_context()
1222 c = self.load_default_context()
1217 commit_id, f_path = self._get_commit_and_path()
1223 commit_id, f_path = self._get_commit_and_path()
1218
1224
1219 self._ensure_not_locked()
1225 self._ensure_not_locked()
1220 _branch_name, _sha_commit_id, is_head = \
1226 _branch_name, _sha_commit_id, is_head = \
1221 self._is_valid_head(commit_id, self.rhodecode_vcs_repo,
1227 self._is_valid_head(commit_id, self.rhodecode_vcs_repo,
1222 landing_ref=self.db_repo.landing_ref_name)
1228 landing_ref=self.db_repo.landing_ref_name)
1223
1229
1224 self.forbid_non_head(is_head, f_path, commit_id=commit_id)
1230 self.forbid_non_head(is_head, f_path, commit_id=commit_id)
1225 self.check_branch_permission(_branch_name, commit_id=commit_id)
1231 self.check_branch_permission(_branch_name, commit_id=commit_id)
1226
1232
1227 c.commit = self._get_commit_or_redirect(commit_id)
1233 c.commit = self._get_commit_or_redirect(commit_id)
1228 c.file = self._get_filenode_or_redirect(c.commit, f_path)
1234 c.file = self._get_filenode_or_redirect(c.commit, f_path)
1229
1235
1230 if c.file.is_binary:
1236 if c.file.is_binary:
1231 files_url = h.route_path(
1237 files_url = h.route_path(
1232 'repo_files',
1238 'repo_files',
1233 repo_name=self.db_repo_name,
1239 repo_name=self.db_repo_name,
1234 commit_id=c.commit.raw_id, f_path=f_path)
1240 commit_id=c.commit.raw_id, f_path=f_path)
1235 raise HTTPFound(files_url)
1241 raise HTTPFound(files_url)
1236
1242
1237 c.default_message = _('Edited file {} via RhodeCode Enterprise').format(f_path)
1243 c.default_message = _('Edited file {} via RhodeCode Enterprise').format(f_path)
1238 c.f_path = f_path
1244 c.f_path = f_path
1239
1245
1240 return self._get_template_context(c)
1246 return self._get_template_context(c)
1241
1247
1242 @LoginRequired()
1248 @LoginRequired()
1243 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1249 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1244 @CSRFRequired()
1250 @CSRFRequired()
1245 def repo_files_update_file(self):
1251 def repo_files_update_file(self):
1246 _ = self.request.translate
1252 _ = self.request.translate
1247 c = self.load_default_context()
1253 c = self.load_default_context()
1248 commit_id, f_path = self._get_commit_and_path()
1254 commit_id, f_path = self._get_commit_and_path()
1249
1255
1250 self._ensure_not_locked()
1256 self._ensure_not_locked()
1251
1257
1252 c.commit = self._get_commit_or_redirect(commit_id)
1258 c.commit = self._get_commit_or_redirect(commit_id)
1253 c.file = self._get_filenode_or_redirect(c.commit, f_path)
1259 c.file = self._get_filenode_or_redirect(c.commit, f_path)
1254
1260
1255 if c.file.is_binary:
1261 if c.file.is_binary:
1256 raise HTTPFound(h.route_path('repo_files', repo_name=self.db_repo_name,
1262 raise HTTPFound(h.route_path('repo_files', repo_name=self.db_repo_name,
1257 commit_id=c.commit.raw_id, f_path=f_path))
1263 commit_id=c.commit.raw_id, f_path=f_path))
1258
1264
1259 _branch_name, _sha_commit_id, is_head = \
1265 _branch_name, _sha_commit_id, is_head = \
1260 self._is_valid_head(commit_id, self.rhodecode_vcs_repo,
1266 self._is_valid_head(commit_id, self.rhodecode_vcs_repo,
1261 landing_ref=self.db_repo.landing_ref_name)
1267 landing_ref=self.db_repo.landing_ref_name)
1262
1268
1263 self.forbid_non_head(is_head, f_path, commit_id=commit_id)
1269 self.forbid_non_head(is_head, f_path, commit_id=commit_id)
1264 self.check_branch_permission(_branch_name, commit_id=commit_id)
1270 self.check_branch_permission(_branch_name, commit_id=commit_id)
1265
1271
1266 c.default_message = _('Edited file {} via RhodeCode Enterprise').format(f_path)
1272 c.default_message = _('Edited file {} via RhodeCode Enterprise').format(f_path)
1267 c.f_path = f_path
1273 c.f_path = f_path
1268
1274
1269 old_content = c.file.content
1275 old_content = c.file.content
1270 sl = old_content.splitlines(1)
1276 sl = old_content.splitlines(1)
1271 first_line = sl[0] if sl else ''
1277 first_line = sl[0] if sl else ''
1272
1278
1273 r_post = self.request.POST
1279 r_post = self.request.POST
1274 # line endings: 0 - Unix, 1 - Mac, 2 - DOS
1280 # line endings: 0 - Unix, 1 - Mac, 2 - DOS
1275 line_ending_mode = detect_mode(first_line, 0)
1281 line_ending_mode = detect_mode(first_line, 0)
1276 content = convert_line_endings(r_post.get('content', ''), line_ending_mode)
1282 content = convert_line_endings(r_post.get('content', ''), line_ending_mode)
1277
1283
1278 message = r_post.get('message') or c.default_message
1284 message = r_post.get('message') or c.default_message
1279 org_node_path = c.file.unicode_path
1285 org_node_path = c.file.unicode_path
1280 filename = r_post['filename']
1286 filename = r_post['filename']
1281
1287
1282 root_path = c.file.dir_path
1288 root_path = c.file.dir_path
1283 pure_path = self.create_pure_path(root_path, filename)
1289 pure_path = self.create_pure_path(root_path, filename)
1284 node_path = safe_unicode(bytes(pure_path))
1290 node_path = safe_unicode(bytes(pure_path))
1285
1291
1286 default_redirect_url = h.route_path('repo_commit', repo_name=self.db_repo_name,
1292 default_redirect_url = h.route_path('repo_commit', repo_name=self.db_repo_name,
1287 commit_id=commit_id)
1293 commit_id=commit_id)
1288 if content == old_content and node_path == org_node_path:
1294 if content == old_content and node_path == org_node_path:
1289 h.flash(_('No changes detected on {}').format(h.escape(org_node_path)),
1295 h.flash(_('No changes detected on {}').format(h.escape(org_node_path)),
1290 category='warning')
1296 category='warning')
1291 raise HTTPFound(default_redirect_url)
1297 raise HTTPFound(default_redirect_url)
1292
1298
1293 try:
1299 try:
1294 mapping = {
1300 mapping = {
1295 org_node_path: {
1301 org_node_path: {
1296 'org_filename': org_node_path,
1302 'org_filename': org_node_path,
1297 'filename': node_path,
1303 'filename': node_path,
1298 'content': content,
1304 'content': content,
1299 'lexer': '',
1305 'lexer': '',
1300 'op': 'mod',
1306 'op': 'mod',
1301 'mode': c.file.mode
1307 'mode': c.file.mode
1302 }
1308 }
1303 }
1309 }
1304
1310
1305 commit = ScmModel().update_nodes(
1311 commit = ScmModel().update_nodes(
1306 user=self._rhodecode_db_user.user_id,
1312 user=self._rhodecode_db_user.user_id,
1307 repo=self.db_repo,
1313 repo=self.db_repo,
1308 message=message,
1314 message=message,
1309 nodes=mapping,
1315 nodes=mapping,
1310 parent_commit=c.commit,
1316 parent_commit=c.commit,
1311 )
1317 )
1312
1318
1313 h.flash(_('Successfully committed changes to file `{}`').format(
1319 h.flash(_('Successfully committed changes to file `{}`').format(
1314 h.escape(f_path)), category='success')
1320 h.escape(f_path)), category='success')
1315 default_redirect_url = h.route_path(
1321 default_redirect_url = h.route_path(
1316 'repo_commit', repo_name=self.db_repo_name, commit_id=commit.raw_id)
1322 'repo_commit', repo_name=self.db_repo_name, commit_id=commit.raw_id)
1317
1323
1318 except Exception:
1324 except Exception:
1319 log.exception('Error occurred during commit')
1325 log.exception('Error occurred during commit')
1320 h.flash(_('Error occurred during commit'), category='error')
1326 h.flash(_('Error occurred during commit'), category='error')
1321
1327
1322 raise HTTPFound(default_redirect_url)
1328 raise HTTPFound(default_redirect_url)
1323
1329
1324 @LoginRequired()
1330 @LoginRequired()
1325 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1331 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1326 def repo_files_add_file(self):
1332 def repo_files_add_file(self):
1327 _ = self.request.translate
1333 _ = self.request.translate
1328 c = self.load_default_context()
1334 c = self.load_default_context()
1329 commit_id, f_path = self._get_commit_and_path()
1335 commit_id, f_path = self._get_commit_and_path()
1330
1336
1331 self._ensure_not_locked()
1337 self._ensure_not_locked()
1332
1338
1333 c.commit = self._get_commit_or_redirect(commit_id, redirect_after=False)
1339 c.commit = self._get_commit_or_redirect(commit_id, redirect_after=False)
1334 if c.commit is None:
1340 if c.commit is None:
1335 c.commit = EmptyCommit(alias=self.rhodecode_vcs_repo.alias)
1341 c.commit = EmptyCommit(alias=self.rhodecode_vcs_repo.alias)
1336
1342
1337 if self.rhodecode_vcs_repo.is_empty():
1343 if self.rhodecode_vcs_repo.is_empty():
1338 # for empty repository we cannot check for current branch, we rely on
1344 # for empty repository we cannot check for current branch, we rely on
1339 # c.commit.branch instead
1345 # c.commit.branch instead
1340 _branch_name, _sha_commit_id, is_head = c.commit.branch, '', True
1346 _branch_name, _sha_commit_id, is_head = c.commit.branch, '', True
1341 else:
1347 else:
1342 _branch_name, _sha_commit_id, is_head = \
1348 _branch_name, _sha_commit_id, is_head = \
1343 self._is_valid_head(commit_id, self.rhodecode_vcs_repo,
1349 self._is_valid_head(commit_id, self.rhodecode_vcs_repo,
1344 landing_ref=self.db_repo.landing_ref_name)
1350 landing_ref=self.db_repo.landing_ref_name)
1345
1351
1346 self.forbid_non_head(is_head, f_path, commit_id=commit_id)
1352 self.forbid_non_head(is_head, f_path, commit_id=commit_id)
1347 self.check_branch_permission(_branch_name, commit_id=commit_id)
1353 self.check_branch_permission(_branch_name, commit_id=commit_id)
1348
1354
1349 c.default_message = (_('Added file via RhodeCode Enterprise'))
1355 c.default_message = (_('Added file via RhodeCode Enterprise'))
1350 c.f_path = f_path.lstrip('/') # ensure not relative path
1356 c.f_path = f_path.lstrip('/') # ensure not relative path
1351
1357
1352 return self._get_template_context(c)
1358 return self._get_template_context(c)
1353
1359
1354 @LoginRequired()
1360 @LoginRequired()
1355 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1361 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1356 @CSRFRequired()
1362 @CSRFRequired()
1357 def repo_files_create_file(self):
1363 def repo_files_create_file(self):
1358 _ = self.request.translate
1364 _ = self.request.translate
1359 c = self.load_default_context()
1365 c = self.load_default_context()
1360 commit_id, f_path = self._get_commit_and_path()
1366 commit_id, f_path = self._get_commit_and_path()
1361
1367
1362 self._ensure_not_locked()
1368 self._ensure_not_locked()
1363
1369
1364 c.commit = self._get_commit_or_redirect(commit_id, redirect_after=False)
1370 c.commit = self._get_commit_or_redirect(commit_id, redirect_after=False)
1365 if c.commit is None:
1371 if c.commit is None:
1366 c.commit = EmptyCommit(alias=self.rhodecode_vcs_repo.alias)
1372 c.commit = EmptyCommit(alias=self.rhodecode_vcs_repo.alias)
1367
1373
1368 # calculate redirect URL
1374 # calculate redirect URL
1369 if self.rhodecode_vcs_repo.is_empty():
1375 if self.rhodecode_vcs_repo.is_empty():
1370 default_redirect_url = h.route_path(
1376 default_redirect_url = h.route_path(
1371 'repo_summary', repo_name=self.db_repo_name)
1377 'repo_summary', repo_name=self.db_repo_name)
1372 else:
1378 else:
1373 default_redirect_url = h.route_path(
1379 default_redirect_url = h.route_path(
1374 'repo_commit', repo_name=self.db_repo_name, commit_id='tip')
1380 'repo_commit', repo_name=self.db_repo_name, commit_id='tip')
1375
1381
1376 if self.rhodecode_vcs_repo.is_empty():
1382 if self.rhodecode_vcs_repo.is_empty():
1377 # for empty repository we cannot check for current branch, we rely on
1383 # for empty repository we cannot check for current branch, we rely on
1378 # c.commit.branch instead
1384 # c.commit.branch instead
1379 _branch_name, _sha_commit_id, is_head = c.commit.branch, '', True
1385 _branch_name, _sha_commit_id, is_head = c.commit.branch, '', True
1380 else:
1386 else:
1381 _branch_name, _sha_commit_id, is_head = \
1387 _branch_name, _sha_commit_id, is_head = \
1382 self._is_valid_head(commit_id, self.rhodecode_vcs_repo,
1388 self._is_valid_head(commit_id, self.rhodecode_vcs_repo,
1383 landing_ref=self.db_repo.landing_ref_name)
1389 landing_ref=self.db_repo.landing_ref_name)
1384
1390
1385 self.forbid_non_head(is_head, f_path, commit_id=commit_id)
1391 self.forbid_non_head(is_head, f_path, commit_id=commit_id)
1386 self.check_branch_permission(_branch_name, commit_id=commit_id)
1392 self.check_branch_permission(_branch_name, commit_id=commit_id)
1387
1393
1388 c.default_message = (_('Added file via RhodeCode Enterprise'))
1394 c.default_message = (_('Added file via RhodeCode Enterprise'))
1389 c.f_path = f_path
1395 c.f_path = f_path
1390
1396
1391 r_post = self.request.POST
1397 r_post = self.request.POST
1392 message = r_post.get('message') or c.default_message
1398 message = r_post.get('message') or c.default_message
1393 filename = r_post.get('filename')
1399 filename = r_post.get('filename')
1394 unix_mode = 0
1400 unix_mode = 0
1395 content = convert_line_endings(r_post.get('content', ''), unix_mode)
1401 content = convert_line_endings(r_post.get('content', ''), unix_mode)
1396
1402
1397 if not filename:
1403 if not filename:
1398 # If there's no commit, redirect to repo summary
1404 # If there's no commit, redirect to repo summary
1399 if type(c.commit) is EmptyCommit:
1405 if type(c.commit) is EmptyCommit:
1400 redirect_url = h.route_path(
1406 redirect_url = h.route_path(
1401 'repo_summary', repo_name=self.db_repo_name)
1407 'repo_summary', repo_name=self.db_repo_name)
1402 else:
1408 else:
1403 redirect_url = default_redirect_url
1409 redirect_url = default_redirect_url
1404 h.flash(_('No filename specified'), category='warning')
1410 h.flash(_('No filename specified'), category='warning')
1405 raise HTTPFound(redirect_url)
1411 raise HTTPFound(redirect_url)
1406
1412
1407 root_path = f_path
1413 root_path = f_path
1408 pure_path = self.create_pure_path(root_path, filename)
1414 pure_path = self.create_pure_path(root_path, filename)
1409 node_path = safe_unicode(bytes(pure_path).lstrip('/'))
1415 node_path = safe_unicode(bytes(pure_path).lstrip('/'))
1410
1416
1411 author = self._rhodecode_db_user.full_contact
1417 author = self._rhodecode_db_user.full_contact
1412 nodes = {
1418 nodes = {
1413 node_path: {
1419 node_path: {
1414 'content': content
1420 'content': content
1415 }
1421 }
1416 }
1422 }
1417
1423
1418 try:
1424 try:
1419
1425
1420 commit = ScmModel().create_nodes(
1426 commit = ScmModel().create_nodes(
1421 user=self._rhodecode_db_user.user_id,
1427 user=self._rhodecode_db_user.user_id,
1422 repo=self.db_repo,
1428 repo=self.db_repo,
1423 message=message,
1429 message=message,
1424 nodes=nodes,
1430 nodes=nodes,
1425 parent_commit=c.commit,
1431 parent_commit=c.commit,
1426 author=author,
1432 author=author,
1427 )
1433 )
1428
1434
1429 h.flash(_('Successfully committed new file `{}`').format(
1435 h.flash(_('Successfully committed new file `{}`').format(
1430 h.escape(node_path)), category='success')
1436 h.escape(node_path)), category='success')
1431
1437
1432 default_redirect_url = h.route_path(
1438 default_redirect_url = h.route_path(
1433 'repo_commit', repo_name=self.db_repo_name, commit_id=commit.raw_id)
1439 'repo_commit', repo_name=self.db_repo_name, commit_id=commit.raw_id)
1434
1440
1435 except NonRelativePathError:
1441 except NonRelativePathError:
1436 log.exception('Non Relative path found')
1442 log.exception('Non Relative path found')
1437 h.flash(_('The location specified must be a relative path and must not '
1443 h.flash(_('The location specified must be a relative path and must not '
1438 'contain .. in the path'), category='warning')
1444 'contain .. in the path'), category='warning')
1439 raise HTTPFound(default_redirect_url)
1445 raise HTTPFound(default_redirect_url)
1440 except (NodeError, NodeAlreadyExistsError) as e:
1446 except (NodeError, NodeAlreadyExistsError) as e:
1441 h.flash(_(h.escape(e)), category='error')
1447 h.flash(_(h.escape(e)), category='error')
1442 except Exception:
1448 except Exception:
1443 log.exception('Error occurred during commit')
1449 log.exception('Error occurred during commit')
1444 h.flash(_('Error occurred during commit'), category='error')
1450 h.flash(_('Error occurred during commit'), category='error')
1445
1451
1446 raise HTTPFound(default_redirect_url)
1452 raise HTTPFound(default_redirect_url)
1447
1453
1448 @LoginRequired()
1454 @LoginRequired()
1449 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1455 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1450 @CSRFRequired()
1456 @CSRFRequired()
1451 def repo_files_upload_file(self):
1457 def repo_files_upload_file(self):
1452 _ = self.request.translate
1458 _ = self.request.translate
1453 c = self.load_default_context()
1459 c = self.load_default_context()
1454 commit_id, f_path = self._get_commit_and_path()
1460 commit_id, f_path = self._get_commit_and_path()
1455
1461
1456 self._ensure_not_locked()
1462 self._ensure_not_locked()
1457
1463
1458 c.commit = self._get_commit_or_redirect(commit_id, redirect_after=False)
1464 c.commit = self._get_commit_or_redirect(commit_id, redirect_after=False)
1459 if c.commit is None:
1465 if c.commit is None:
1460 c.commit = EmptyCommit(alias=self.rhodecode_vcs_repo.alias)
1466 c.commit = EmptyCommit(alias=self.rhodecode_vcs_repo.alias)
1461
1467
1462 # calculate redirect URL
1468 # calculate redirect URL
1463 if self.rhodecode_vcs_repo.is_empty():
1469 if self.rhodecode_vcs_repo.is_empty():
1464 default_redirect_url = h.route_path(
1470 default_redirect_url = h.route_path(
1465 'repo_summary', repo_name=self.db_repo_name)
1471 'repo_summary', repo_name=self.db_repo_name)
1466 else:
1472 else:
1467 default_redirect_url = h.route_path(
1473 default_redirect_url = h.route_path(
1468 'repo_commit', repo_name=self.db_repo_name, commit_id='tip')
1474 'repo_commit', repo_name=self.db_repo_name, commit_id='tip')
1469
1475
1470 if self.rhodecode_vcs_repo.is_empty():
1476 if self.rhodecode_vcs_repo.is_empty():
1471 # for empty repository we cannot check for current branch, we rely on
1477 # for empty repository we cannot check for current branch, we rely on
1472 # c.commit.branch instead
1478 # c.commit.branch instead
1473 _branch_name, _sha_commit_id, is_head = c.commit.branch, '', True
1479 _branch_name, _sha_commit_id, is_head = c.commit.branch, '', True
1474 else:
1480 else:
1475 _branch_name, _sha_commit_id, is_head = \
1481 _branch_name, _sha_commit_id, is_head = \
1476 self._is_valid_head(commit_id, self.rhodecode_vcs_repo,
1482 self._is_valid_head(commit_id, self.rhodecode_vcs_repo,
1477 landing_ref=self.db_repo.landing_ref_name)
1483 landing_ref=self.db_repo.landing_ref_name)
1478
1484
1479 error = self.forbid_non_head(is_head, f_path, json_mode=True)
1485 error = self.forbid_non_head(is_head, f_path, json_mode=True)
1480 if error:
1486 if error:
1481 return {
1487 return {
1482 'error': error,
1488 'error': error,
1483 'redirect_url': default_redirect_url
1489 'redirect_url': default_redirect_url
1484 }
1490 }
1485 error = self.check_branch_permission(_branch_name, json_mode=True)
1491 error = self.check_branch_permission(_branch_name, json_mode=True)
1486 if error:
1492 if error:
1487 return {
1493 return {
1488 'error': error,
1494 'error': error,
1489 'redirect_url': default_redirect_url
1495 'redirect_url': default_redirect_url
1490 }
1496 }
1491
1497
1492 c.default_message = (_('Uploaded file via RhodeCode Enterprise'))
1498 c.default_message = (_('Uploaded file via RhodeCode Enterprise'))
1493 c.f_path = f_path
1499 c.f_path = f_path
1494
1500
1495 r_post = self.request.POST
1501 r_post = self.request.POST
1496
1502
1497 message = c.default_message
1503 message = c.default_message
1498 user_message = r_post.getall('message')
1504 user_message = r_post.getall('message')
1499 if isinstance(user_message, list) and user_message:
1505 if isinstance(user_message, list) and user_message:
1500 # we take the first from duplicated results if it's not empty
1506 # we take the first from duplicated results if it's not empty
1501 message = user_message[0] if user_message[0] else message
1507 message = user_message[0] if user_message[0] else message
1502
1508
1503 nodes = {}
1509 nodes = {}
1504
1510
1505 for file_obj in r_post.getall('files_upload') or []:
1511 for file_obj in r_post.getall('files_upload') or []:
1506 content = file_obj.file
1512 content = file_obj.file
1507 filename = file_obj.filename
1513 filename = file_obj.filename
1508
1514
1509 root_path = f_path
1515 root_path = f_path
1510 pure_path = self.create_pure_path(root_path, filename)
1516 pure_path = self.create_pure_path(root_path, filename)
1511 node_path = safe_unicode(bytes(pure_path).lstrip('/'))
1517 node_path = safe_unicode(bytes(pure_path).lstrip('/'))
1512
1518
1513 nodes[node_path] = {
1519 nodes[node_path] = {
1514 'content': content
1520 'content': content
1515 }
1521 }
1516
1522
1517 if not nodes:
1523 if not nodes:
1518 error = 'missing files'
1524 error = 'missing files'
1519 return {
1525 return {
1520 'error': error,
1526 'error': error,
1521 'redirect_url': default_redirect_url
1527 'redirect_url': default_redirect_url
1522 }
1528 }
1523
1529
1524 author = self._rhodecode_db_user.full_contact
1530 author = self._rhodecode_db_user.full_contact
1525
1531
1526 try:
1532 try:
1527 commit = ScmModel().create_nodes(
1533 commit = ScmModel().create_nodes(
1528 user=self._rhodecode_db_user.user_id,
1534 user=self._rhodecode_db_user.user_id,
1529 repo=self.db_repo,
1535 repo=self.db_repo,
1530 message=message,
1536 message=message,
1531 nodes=nodes,
1537 nodes=nodes,
1532 parent_commit=c.commit,
1538 parent_commit=c.commit,
1533 author=author,
1539 author=author,
1534 )
1540 )
1535 if len(nodes) == 1:
1541 if len(nodes) == 1:
1536 flash_message = _('Successfully committed {} new files').format(len(nodes))
1542 flash_message = _('Successfully committed {} new files').format(len(nodes))
1537 else:
1543 else:
1538 flash_message = _('Successfully committed 1 new file')
1544 flash_message = _('Successfully committed 1 new file')
1539
1545
1540 h.flash(flash_message, category='success')
1546 h.flash(flash_message, category='success')
1541
1547
1542 default_redirect_url = h.route_path(
1548 default_redirect_url = h.route_path(
1543 'repo_commit', repo_name=self.db_repo_name, commit_id=commit.raw_id)
1549 'repo_commit', repo_name=self.db_repo_name, commit_id=commit.raw_id)
1544
1550
1545 except NonRelativePathError:
1551 except NonRelativePathError:
1546 log.exception('Non Relative path found')
1552 log.exception('Non Relative path found')
1547 error = _('The location specified must be a relative path and must not '
1553 error = _('The location specified must be a relative path and must not '
1548 'contain .. in the path')
1554 'contain .. in the path')
1549 h.flash(error, category='warning')
1555 h.flash(error, category='warning')
1550
1556
1551 return {
1557 return {
1552 'error': error,
1558 'error': error,
1553 'redirect_url': default_redirect_url
1559 'redirect_url': default_redirect_url
1554 }
1560 }
1555 except (NodeError, NodeAlreadyExistsError) as e:
1561 except (NodeError, NodeAlreadyExistsError) as e:
1556 error = h.escape(e)
1562 error = h.escape(e)
1557 h.flash(error, category='error')
1563 h.flash(error, category='error')
1558
1564
1559 return {
1565 return {
1560 'error': error,
1566 'error': error,
1561 'redirect_url': default_redirect_url
1567 'redirect_url': default_redirect_url
1562 }
1568 }
1563 except Exception:
1569 except Exception:
1564 log.exception('Error occurred during commit')
1570 log.exception('Error occurred during commit')
1565 error = _('Error occurred during commit')
1571 error = _('Error occurred during commit')
1566 h.flash(error, category='error')
1572 h.flash(error, category='error')
1567 return {
1573 return {
1568 'error': error,
1574 'error': error,
1569 'redirect_url': default_redirect_url
1575 'redirect_url': default_redirect_url
1570 }
1576 }
1571
1577
1572 return {
1578 return {
1573 'error': None,
1579 'error': None,
1574 'redirect_url': default_redirect_url
1580 'redirect_url': default_redirect_url
1575 }
1581 }
General Comments 0
You need to be logged in to leave comments. Login now