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