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