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