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