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