##// END OF EJS Templates
files: fix loading of source files events via pjax.
marcink -
r1128:f8436858 default
parent child Browse files
Show More
@@ -1,1125 +1,1127 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2016 RhodeCode GmbH
3 # Copyright (C) 2010-2016 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 """
21 """
22 Files controller for RhodeCode Enterprise
22 Files controller for RhodeCode Enterprise
23 """
23 """
24
24
25 import itertools
25 import itertools
26 import logging
26 import logging
27 import os
27 import os
28 import shutil
28 import shutil
29 import tempfile
29 import tempfile
30
30
31 from pylons import request, response, tmpl_context as c, url
31 from pylons import request, response, tmpl_context as c, url
32 from pylons.i18n.translation import _
32 from pylons.i18n.translation import _
33 from pylons.controllers.util import redirect
33 from pylons.controllers.util import redirect
34 from webob.exc import HTTPNotFound, HTTPBadRequest
34 from webob.exc import HTTPNotFound, HTTPBadRequest
35
35
36 from rhodecode.controllers.utils import parse_path_ref
36 from rhodecode.controllers.utils import parse_path_ref
37 from rhodecode.lib import diffs, helpers as h, caches
37 from rhodecode.lib import diffs, helpers as h, caches
38 from rhodecode.lib.compat import OrderedDict
38 from rhodecode.lib.compat import OrderedDict
39 from rhodecode.lib.codeblocks import (
39 from rhodecode.lib.codeblocks import (
40 filenode_as_lines_tokens, filenode_as_annotated_lines_tokens)
40 filenode_as_lines_tokens, filenode_as_annotated_lines_tokens)
41 from rhodecode.lib.utils import jsonify, action_logger
41 from rhodecode.lib.utils import jsonify, action_logger
42 from rhodecode.lib.utils2 import (
42 from rhodecode.lib.utils2 import (
43 convert_line_endings, detect_mode, safe_str, str2bool)
43 convert_line_endings, detect_mode, safe_str, str2bool)
44 from rhodecode.lib.auth import (
44 from rhodecode.lib.auth import (
45 LoginRequired, HasRepoPermissionAnyDecorator, CSRFRequired, XHRRequired)
45 LoginRequired, HasRepoPermissionAnyDecorator, CSRFRequired, XHRRequired)
46 from rhodecode.lib.base import BaseRepoController, render
46 from rhodecode.lib.base import BaseRepoController, render
47 from rhodecode.lib.vcs import path as vcspath
47 from rhodecode.lib.vcs import path as vcspath
48 from rhodecode.lib.vcs.backends.base import EmptyCommit
48 from rhodecode.lib.vcs.backends.base import EmptyCommit
49 from rhodecode.lib.vcs.conf import settings
49 from rhodecode.lib.vcs.conf import settings
50 from rhodecode.lib.vcs.exceptions import (
50 from rhodecode.lib.vcs.exceptions import (
51 RepositoryError, CommitDoesNotExistError, EmptyRepositoryError,
51 RepositoryError, CommitDoesNotExistError, EmptyRepositoryError,
52 ImproperArchiveTypeError, VCSError, NodeAlreadyExistsError,
52 ImproperArchiveTypeError, VCSError, NodeAlreadyExistsError,
53 NodeDoesNotExistError, CommitError, NodeError)
53 NodeDoesNotExistError, CommitError, NodeError)
54 from rhodecode.lib.vcs.nodes import FileNode
54 from rhodecode.lib.vcs.nodes import FileNode
55
55
56 from rhodecode.model.repo import RepoModel
56 from rhodecode.model.repo import RepoModel
57 from rhodecode.model.scm import ScmModel
57 from rhodecode.model.scm import ScmModel
58 from rhodecode.model.db import Repository
58 from rhodecode.model.db import Repository
59
59
60 from rhodecode.controllers.changeset import (
60 from rhodecode.controllers.changeset import (
61 _ignorews_url, _context_url, get_line_ctx, get_ignore_ws)
61 _ignorews_url, _context_url, get_line_ctx, get_ignore_ws)
62 from rhodecode.lib.exceptions import NonRelativePathError
62 from rhodecode.lib.exceptions import NonRelativePathError
63
63
64 log = logging.getLogger(__name__)
64 log = logging.getLogger(__name__)
65
65
66
66
67 class FilesController(BaseRepoController):
67 class FilesController(BaseRepoController):
68
68
69 def __before__(self):
69 def __before__(self):
70 super(FilesController, self).__before__()
70 super(FilesController, self).__before__()
71 c.cut_off_limit = self.cut_off_limit_file
71 c.cut_off_limit = self.cut_off_limit_file
72
72
73 def _get_default_encoding(self):
73 def _get_default_encoding(self):
74 enc_list = getattr(c, 'default_encodings', [])
74 enc_list = getattr(c, 'default_encodings', [])
75 return enc_list[0] if enc_list else 'UTF-8'
75 return enc_list[0] if enc_list else 'UTF-8'
76
76
77 def __get_commit_or_redirect(self, commit_id, repo_name,
77 def __get_commit_or_redirect(self, commit_id, repo_name,
78 redirect_after=True):
78 redirect_after=True):
79 """
79 """
80 This is a safe way to get commit. If an error occurs it redirects to
80 This is a safe way to get commit. If an error occurs it redirects to
81 tip with proper message
81 tip with proper message
82
82
83 :param commit_id: id of commit to fetch
83 :param commit_id: id of commit to fetch
84 :param repo_name: repo name to redirect after
84 :param repo_name: repo name to redirect after
85 :param redirect_after: toggle redirection
85 :param redirect_after: toggle redirection
86 """
86 """
87 try:
87 try:
88 return c.rhodecode_repo.get_commit(commit_id)
88 return c.rhodecode_repo.get_commit(commit_id)
89 except EmptyRepositoryError:
89 except EmptyRepositoryError:
90 if not redirect_after:
90 if not redirect_after:
91 return None
91 return None
92 url_ = url('files_add_home',
92 url_ = url('files_add_home',
93 repo_name=c.repo_name,
93 repo_name=c.repo_name,
94 revision=0, f_path='', anchor='edit')
94 revision=0, f_path='', anchor='edit')
95 if h.HasRepoPermissionAny(
95 if h.HasRepoPermissionAny(
96 'repository.write', 'repository.admin')(c.repo_name):
96 'repository.write', 'repository.admin')(c.repo_name):
97 add_new = h.link_to(
97 add_new = h.link_to(
98 _('Click here to add a new file.'),
98 _('Click here to add a new file.'),
99 url_, class_="alert-link")
99 url_, class_="alert-link")
100 else:
100 else:
101 add_new = ""
101 add_new = ""
102 h.flash(h.literal(
102 h.flash(h.literal(
103 _('There are no files yet. %s') % add_new), category='warning')
103 _('There are no files yet. %s') % add_new), category='warning')
104 redirect(h.url('summary_home', repo_name=repo_name))
104 redirect(h.url('summary_home', repo_name=repo_name))
105 except (CommitDoesNotExistError, LookupError):
105 except (CommitDoesNotExistError, LookupError):
106 msg = _('No such commit exists for this repository')
106 msg = _('No such commit exists for this repository')
107 h.flash(msg, category='error')
107 h.flash(msg, category='error')
108 raise HTTPNotFound()
108 raise HTTPNotFound()
109 except RepositoryError as e:
109 except RepositoryError as e:
110 h.flash(safe_str(e), category='error')
110 h.flash(safe_str(e), category='error')
111 raise HTTPNotFound()
111 raise HTTPNotFound()
112
112
113 def __get_filenode_or_redirect(self, repo_name, commit, path):
113 def __get_filenode_or_redirect(self, repo_name, commit, path):
114 """
114 """
115 Returns file_node, if error occurs or given path is directory,
115 Returns file_node, if error occurs or given path is directory,
116 it'll redirect to top level path
116 it'll redirect to top level path
117
117
118 :param repo_name: repo_name
118 :param repo_name: repo_name
119 :param commit: given commit
119 :param commit: given commit
120 :param path: path to lookup
120 :param path: path to lookup
121 """
121 """
122 try:
122 try:
123 file_node = commit.get_node(path)
123 file_node = commit.get_node(path)
124 if file_node.is_dir():
124 if file_node.is_dir():
125 raise RepositoryError('The given path is a directory')
125 raise RepositoryError('The given path is a directory')
126 except CommitDoesNotExistError:
126 except CommitDoesNotExistError:
127 msg = _('No such commit exists for this repository')
127 msg = _('No such commit exists for this repository')
128 log.exception(msg)
128 log.exception(msg)
129 h.flash(msg, category='error')
129 h.flash(msg, category='error')
130 raise HTTPNotFound()
130 raise HTTPNotFound()
131 except RepositoryError as e:
131 except RepositoryError as e:
132 h.flash(safe_str(e), category='error')
132 h.flash(safe_str(e), category='error')
133 raise HTTPNotFound()
133 raise HTTPNotFound()
134
134
135 return file_node
135 return file_node
136
136
137 def __get_tree_cache_manager(self, repo_name, namespace_type):
137 def __get_tree_cache_manager(self, repo_name, namespace_type):
138 _namespace = caches.get_repo_namespace_key(namespace_type, repo_name)
138 _namespace = caches.get_repo_namespace_key(namespace_type, repo_name)
139 return caches.get_cache_manager('repo_cache_long', _namespace)
139 return caches.get_cache_manager('repo_cache_long', _namespace)
140
140
141 def _get_tree_at_commit(self, repo_name, commit_id, f_path,
141 def _get_tree_at_commit(self, repo_name, commit_id, f_path,
142 full_load=False, force=False):
142 full_load=False, force=False):
143 def _cached_tree():
143 def _cached_tree():
144 log.debug('Generating cached file tree for %s, %s, %s',
144 log.debug('Generating cached file tree for %s, %s, %s',
145 repo_name, commit_id, f_path)
145 repo_name, commit_id, f_path)
146 c.full_load = full_load
146 c.full_load = full_load
147 return render('files/files_browser_tree.html')
147 return render('files/files_browser_tree.html')
148
148
149 cache_manager = self.__get_tree_cache_manager(
149 cache_manager = self.__get_tree_cache_manager(
150 repo_name, caches.FILE_TREE)
150 repo_name, caches.FILE_TREE)
151
151
152 cache_key = caches.compute_key_from_params(
152 cache_key = caches.compute_key_from_params(
153 repo_name, commit_id, f_path)
153 repo_name, commit_id, f_path)
154
154
155 if force:
155 if force:
156 # we want to force recompute of caches
156 # we want to force recompute of caches
157 cache_manager.remove_value(cache_key)
157 cache_manager.remove_value(cache_key)
158
158
159 return cache_manager.get(cache_key, createfunc=_cached_tree)
159 return cache_manager.get(cache_key, createfunc=_cached_tree)
160
160
161 def _get_nodelist_at_commit(self, repo_name, commit_id, f_path):
161 def _get_nodelist_at_commit(self, repo_name, commit_id, f_path):
162 def _cached_nodes():
162 def _cached_nodes():
163 log.debug('Generating cached nodelist for %s, %s, %s',
163 log.debug('Generating cached nodelist for %s, %s, %s',
164 repo_name, commit_id, f_path)
164 repo_name, commit_id, f_path)
165 _d, _f = ScmModel().get_nodes(
165 _d, _f = ScmModel().get_nodes(
166 repo_name, commit_id, f_path, flat=False)
166 repo_name, commit_id, f_path, flat=False)
167 return _d + _f
167 return _d + _f
168
168
169 cache_manager = self.__get_tree_cache_manager(
169 cache_manager = self.__get_tree_cache_manager(
170 repo_name, caches.FILE_SEARCH_TREE_META)
170 repo_name, caches.FILE_SEARCH_TREE_META)
171
171
172 cache_key = caches.compute_key_from_params(
172 cache_key = caches.compute_key_from_params(
173 repo_name, commit_id, f_path)
173 repo_name, commit_id, f_path)
174 return cache_manager.get(cache_key, createfunc=_cached_nodes)
174 return cache_manager.get(cache_key, createfunc=_cached_nodes)
175
175
176 @LoginRequired()
176 @LoginRequired()
177 @HasRepoPermissionAnyDecorator(
177 @HasRepoPermissionAnyDecorator(
178 'repository.read', 'repository.write', 'repository.admin')
178 'repository.read', 'repository.write', 'repository.admin')
179 def index(
179 def index(
180 self, repo_name, revision, f_path, annotate=False, rendered=False):
180 self, repo_name, revision, f_path, annotate=False, rendered=False):
181 commit_id = revision
181 commit_id = revision
182
182
183 # redirect to given commit_id from form if given
183 # redirect to given commit_id from form if given
184 get_commit_id = request.GET.get('at_rev', None)
184 get_commit_id = request.GET.get('at_rev', None)
185 if get_commit_id:
185 if get_commit_id:
186 self.__get_commit_or_redirect(get_commit_id, repo_name)
186 self.__get_commit_or_redirect(get_commit_id, repo_name)
187
187
188 c.commit = self.__get_commit_or_redirect(commit_id, repo_name)
188 c.commit = self.__get_commit_or_redirect(commit_id, repo_name)
189 c.branch = request.GET.get('branch', None)
189 c.branch = request.GET.get('branch', None)
190 c.f_path = f_path
190 c.f_path = f_path
191 c.annotate = annotate
191 c.annotate = annotate
192 # default is false, but .rst/.md files later are autorendered, we can
192 # default is false, but .rst/.md files later are autorendered, we can
193 # overwrite autorendering by setting this GET flag
193 # overwrite autorendering by setting this GET flag
194 c.renderer = rendered or not request.GET.get('no-render', False)
194 c.renderer = rendered or not request.GET.get('no-render', False)
195
195
196 # prev link
196 # prev link
197 try:
197 try:
198 prev_commit = c.commit.prev(c.branch)
198 prev_commit = c.commit.prev(c.branch)
199 c.prev_commit = prev_commit
199 c.prev_commit = prev_commit
200 c.url_prev = url('files_home', repo_name=c.repo_name,
200 c.url_prev = url('files_home', repo_name=c.repo_name,
201 revision=prev_commit.raw_id, f_path=f_path)
201 revision=prev_commit.raw_id, f_path=f_path)
202 if c.branch:
202 if c.branch:
203 c.url_prev += '?branch=%s' % c.branch
203 c.url_prev += '?branch=%s' % c.branch
204 except (CommitDoesNotExistError, VCSError):
204 except (CommitDoesNotExistError, VCSError):
205 c.url_prev = '#'
205 c.url_prev = '#'
206 c.prev_commit = EmptyCommit()
206 c.prev_commit = EmptyCommit()
207
207
208 # next link
208 # next link
209 try:
209 try:
210 next_commit = c.commit.next(c.branch)
210 next_commit = c.commit.next(c.branch)
211 c.next_commit = next_commit
211 c.next_commit = next_commit
212 c.url_next = url('files_home', repo_name=c.repo_name,
212 c.url_next = url('files_home', repo_name=c.repo_name,
213 revision=next_commit.raw_id, f_path=f_path)
213 revision=next_commit.raw_id, f_path=f_path)
214 if c.branch:
214 if c.branch:
215 c.url_next += '?branch=%s' % c.branch
215 c.url_next += '?branch=%s' % c.branch
216 except (CommitDoesNotExistError, VCSError):
216 except (CommitDoesNotExistError, VCSError):
217 c.url_next = '#'
217 c.url_next = '#'
218 c.next_commit = EmptyCommit()
218 c.next_commit = EmptyCommit()
219
219
220 # files or dirs
220 # files or dirs
221 try:
221 try:
222 c.file = c.commit.get_node(f_path)
222 c.file = c.commit.get_node(f_path)
223 c.file_author = True
223 c.file_author = True
224 c.file_tree = ''
224 c.file_tree = ''
225 if c.file.is_file():
225 if c.file.is_file():
226 c.file_source_page = 'true'
226 c.file_last_commit = c.file.last_commit
227 c.file_last_commit = c.file.last_commit
227 if c.file.size < self.cut_off_limit_file:
228 if c.file.size < self.cut_off_limit_file:
228 if c.annotate: # annotation has precedence over renderer
229 if c.annotate: # annotation has precedence over renderer
229 c.annotated_lines = filenode_as_annotated_lines_tokens(
230 c.annotated_lines = filenode_as_annotated_lines_tokens(
230 c.file
231 c.file
231 )
232 )
232 else:
233 else:
233 c.renderer = (
234 c.renderer = (
234 c.renderer and h.renderer_from_filename(c.file.path)
235 c.renderer and h.renderer_from_filename(c.file.path)
235 )
236 )
236 if not c.renderer:
237 if not c.renderer:
237 c.lines = filenode_as_lines_tokens(c.file)
238 c.lines = filenode_as_lines_tokens(c.file)
238
239
239 c.on_branch_head = self._is_valid_head(
240 c.on_branch_head = self._is_valid_head(
240 commit_id, c.rhodecode_repo)
241 commit_id, c.rhodecode_repo)
241 c.branch_or_raw_id = c.commit.branch or c.commit.raw_id
242 c.branch_or_raw_id = c.commit.branch or c.commit.raw_id
242
243
243 author = c.file_last_commit.author
244 author = c.file_last_commit.author
244 c.authors = [(h.email(author),
245 c.authors = [(h.email(author),
245 h.person(author, 'username_or_name_or_email'))]
246 h.person(author, 'username_or_name_or_email'))]
246 else:
247 else:
248 c.file_source_page = 'false'
247 c.authors = []
249 c.authors = []
248 c.file_tree = self._get_tree_at_commit(
250 c.file_tree = self._get_tree_at_commit(
249 repo_name, c.commit.raw_id, f_path)
251 repo_name, c.commit.raw_id, f_path)
250
252
251 except RepositoryError as e:
253 except RepositoryError as e:
252 h.flash(safe_str(e), category='error')
254 h.flash(safe_str(e), category='error')
253 raise HTTPNotFound()
255 raise HTTPNotFound()
254
256
255 if request.environ.get('HTTP_X_PJAX'):
257 if request.environ.get('HTTP_X_PJAX'):
256 return render('files/files_pjax.html')
258 return render('files/files_pjax.html')
257
259
258 return render('files/files.html')
260 return render('files/files.html')
259
261
260 @LoginRequired()
262 @LoginRequired()
261 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
263 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
262 'repository.admin')
264 'repository.admin')
263 @jsonify
265 @jsonify
264 def history(self, repo_name, revision, f_path):
266 def history(self, repo_name, revision, f_path):
265 commit = self.__get_commit_or_redirect(revision, repo_name)
267 commit = self.__get_commit_or_redirect(revision, repo_name)
266 f_path = f_path
268 f_path = f_path
267 _file = commit.get_node(f_path)
269 _file = commit.get_node(f_path)
268 if _file.is_file():
270 if _file.is_file():
269 file_history, _hist = self._get_node_history(commit, f_path)
271 file_history, _hist = self._get_node_history(commit, f_path)
270
272
271 res = []
273 res = []
272 for obj in file_history:
274 for obj in file_history:
273 res.append({
275 res.append({
274 'text': obj[1],
276 'text': obj[1],
275 'children': [{'id': o[0], 'text': o[1]} for o in obj[0]]
277 'children': [{'id': o[0], 'text': o[1]} for o in obj[0]]
276 })
278 })
277
279
278 data = {
280 data = {
279 'more': False,
281 'more': False,
280 'results': res
282 'results': res
281 }
283 }
282 return data
284 return data
283
285
284 @LoginRequired()
286 @LoginRequired()
285 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
287 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
286 'repository.admin')
288 'repository.admin')
287 def authors(self, repo_name, revision, f_path):
289 def authors(self, repo_name, revision, f_path):
288 commit = self.__get_commit_or_redirect(revision, repo_name)
290 commit = self.__get_commit_or_redirect(revision, repo_name)
289 file_node = commit.get_node(f_path)
291 file_node = commit.get_node(f_path)
290 if file_node.is_file():
292 if file_node.is_file():
291 c.file_last_commit = file_node.last_commit
293 c.file_last_commit = file_node.last_commit
292 if request.GET.get('annotate') == '1':
294 if request.GET.get('annotate') == '1':
293 # use _hist from annotation if annotation mode is on
295 # use _hist from annotation if annotation mode is on
294 commit_ids = set(x[1] for x in file_node.annotate)
296 commit_ids = set(x[1] for x in file_node.annotate)
295 _hist = (
297 _hist = (
296 c.rhodecode_repo.get_commit(commit_id)
298 c.rhodecode_repo.get_commit(commit_id)
297 for commit_id in commit_ids)
299 for commit_id in commit_ids)
298 else:
300 else:
299 _f_history, _hist = self._get_node_history(commit, f_path)
301 _f_history, _hist = self._get_node_history(commit, f_path)
300 c.file_author = False
302 c.file_author = False
301 c.authors = []
303 c.authors = []
302 for author in set(commit.author for commit in _hist):
304 for author in set(commit.author for commit in _hist):
303 c.authors.append((
305 c.authors.append((
304 h.email(author),
306 h.email(author),
305 h.person(author, 'username_or_name_or_email')))
307 h.person(author, 'username_or_name_or_email')))
306 return render('files/file_authors_box.html')
308 return render('files/file_authors_box.html')
307
309
308 @LoginRequired()
310 @LoginRequired()
309 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
311 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
310 'repository.admin')
312 'repository.admin')
311 def rawfile(self, repo_name, revision, f_path):
313 def rawfile(self, repo_name, revision, f_path):
312 """
314 """
313 Action for download as raw
315 Action for download as raw
314 """
316 """
315 commit = self.__get_commit_or_redirect(revision, repo_name)
317 commit = self.__get_commit_or_redirect(revision, repo_name)
316 file_node = self.__get_filenode_or_redirect(repo_name, commit, f_path)
318 file_node = self.__get_filenode_or_redirect(repo_name, commit, f_path)
317
319
318 response.content_disposition = 'attachment; filename=%s' % \
320 response.content_disposition = 'attachment; filename=%s' % \
319 safe_str(f_path.split(Repository.NAME_SEP)[-1])
321 safe_str(f_path.split(Repository.NAME_SEP)[-1])
320
322
321 response.content_type = file_node.mimetype
323 response.content_type = file_node.mimetype
322 charset = self._get_default_encoding()
324 charset = self._get_default_encoding()
323 if charset:
325 if charset:
324 response.charset = charset
326 response.charset = charset
325
327
326 return file_node.content
328 return file_node.content
327
329
328 @LoginRequired()
330 @LoginRequired()
329 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
331 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
330 'repository.admin')
332 'repository.admin')
331 def raw(self, repo_name, revision, f_path):
333 def raw(self, repo_name, revision, f_path):
332 """
334 """
333 Action for show as raw, some mimetypes are "rendered",
335 Action for show as raw, some mimetypes are "rendered",
334 those include images, icons.
336 those include images, icons.
335 """
337 """
336 commit = self.__get_commit_or_redirect(revision, repo_name)
338 commit = self.__get_commit_or_redirect(revision, repo_name)
337 file_node = self.__get_filenode_or_redirect(repo_name, commit, f_path)
339 file_node = self.__get_filenode_or_redirect(repo_name, commit, f_path)
338
340
339 raw_mimetype_mapping = {
341 raw_mimetype_mapping = {
340 # map original mimetype to a mimetype used for "show as raw"
342 # map original mimetype to a mimetype used for "show as raw"
341 # you can also provide a content-disposition to override the
343 # you can also provide a content-disposition to override the
342 # default "attachment" disposition.
344 # default "attachment" disposition.
343 # orig_type: (new_type, new_dispo)
345 # orig_type: (new_type, new_dispo)
344
346
345 # show images inline:
347 # show images inline:
346 # Do not re-add SVG: it is unsafe and permits XSS attacks. One can
348 # Do not re-add SVG: it is unsafe and permits XSS attacks. One can
347 # for example render an SVG with javascript inside or even render
349 # for example render an SVG with javascript inside or even render
348 # HTML.
350 # HTML.
349 'image/x-icon': ('image/x-icon', 'inline'),
351 'image/x-icon': ('image/x-icon', 'inline'),
350 'image/png': ('image/png', 'inline'),
352 'image/png': ('image/png', 'inline'),
351 'image/gif': ('image/gif', 'inline'),
353 'image/gif': ('image/gif', 'inline'),
352 'image/jpeg': ('image/jpeg', 'inline'),
354 'image/jpeg': ('image/jpeg', 'inline'),
353 }
355 }
354
356
355 mimetype = file_node.mimetype
357 mimetype = file_node.mimetype
356 try:
358 try:
357 mimetype, dispo = raw_mimetype_mapping[mimetype]
359 mimetype, dispo = raw_mimetype_mapping[mimetype]
358 except KeyError:
360 except KeyError:
359 # we don't know anything special about this, handle it safely
361 # we don't know anything special about this, handle it safely
360 if file_node.is_binary:
362 if file_node.is_binary:
361 # do same as download raw for binary files
363 # do same as download raw for binary files
362 mimetype, dispo = 'application/octet-stream', 'attachment'
364 mimetype, dispo = 'application/octet-stream', 'attachment'
363 else:
365 else:
364 # do not just use the original mimetype, but force text/plain,
366 # do not just use the original mimetype, but force text/plain,
365 # otherwise it would serve text/html and that might be unsafe.
367 # otherwise it would serve text/html and that might be unsafe.
366 # Note: underlying vcs library fakes text/plain mimetype if the
368 # Note: underlying vcs library fakes text/plain mimetype if the
367 # mimetype can not be determined and it thinks it is not
369 # mimetype can not be determined and it thinks it is not
368 # binary.This might lead to erroneous text display in some
370 # binary.This might lead to erroneous text display in some
369 # cases, but helps in other cases, like with text files
371 # cases, but helps in other cases, like with text files
370 # without extension.
372 # without extension.
371 mimetype, dispo = 'text/plain', 'inline'
373 mimetype, dispo = 'text/plain', 'inline'
372
374
373 if dispo == 'attachment':
375 if dispo == 'attachment':
374 dispo = 'attachment; filename=%s' % safe_str(
376 dispo = 'attachment; filename=%s' % safe_str(
375 f_path.split(os.sep)[-1])
377 f_path.split(os.sep)[-1])
376
378
377 response.content_disposition = dispo
379 response.content_disposition = dispo
378 response.content_type = mimetype
380 response.content_type = mimetype
379 charset = self._get_default_encoding()
381 charset = self._get_default_encoding()
380 if charset:
382 if charset:
381 response.charset = charset
383 response.charset = charset
382 return file_node.content
384 return file_node.content
383
385
384 @CSRFRequired()
386 @CSRFRequired()
385 @LoginRequired()
387 @LoginRequired()
386 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
388 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
387 def delete(self, repo_name, revision, f_path):
389 def delete(self, repo_name, revision, f_path):
388 commit_id = revision
390 commit_id = revision
389
391
390 repo = c.rhodecode_db_repo
392 repo = c.rhodecode_db_repo
391 if repo.enable_locking and repo.locked[0]:
393 if repo.enable_locking and repo.locked[0]:
392 h.flash(_('This repository has been locked by %s on %s')
394 h.flash(_('This repository has been locked by %s on %s')
393 % (h.person_by_id(repo.locked[0]),
395 % (h.person_by_id(repo.locked[0]),
394 h.format_date(h.time_to_datetime(repo.locked[1]))),
396 h.format_date(h.time_to_datetime(repo.locked[1]))),
395 'warning')
397 'warning')
396 return redirect(h.url('files_home',
398 return redirect(h.url('files_home',
397 repo_name=repo_name, revision='tip'))
399 repo_name=repo_name, revision='tip'))
398
400
399 if not self._is_valid_head(commit_id, repo.scm_instance()):
401 if not self._is_valid_head(commit_id, repo.scm_instance()):
400 h.flash(_('You can only delete files with revision '
402 h.flash(_('You can only delete files with revision '
401 'being a valid branch '), category='warning')
403 'being a valid branch '), category='warning')
402 return redirect(h.url('files_home',
404 return redirect(h.url('files_home',
403 repo_name=repo_name, revision='tip',
405 repo_name=repo_name, revision='tip',
404 f_path=f_path))
406 f_path=f_path))
405
407
406 c.commit = self.__get_commit_or_redirect(commit_id, repo_name)
408 c.commit = self.__get_commit_or_redirect(commit_id, repo_name)
407 c.file = self.__get_filenode_or_redirect(repo_name, c.commit, f_path)
409 c.file = self.__get_filenode_or_redirect(repo_name, c.commit, f_path)
408
410
409 c.default_message = _(
411 c.default_message = _(
410 'Deleted file %s via RhodeCode Enterprise') % (f_path)
412 'Deleted file %s via RhodeCode Enterprise') % (f_path)
411 c.f_path = f_path
413 c.f_path = f_path
412 node_path = f_path
414 node_path = f_path
413 author = c.rhodecode_user.full_contact
415 author = c.rhodecode_user.full_contact
414 message = request.POST.get('message') or c.default_message
416 message = request.POST.get('message') or c.default_message
415 try:
417 try:
416 nodes = {
418 nodes = {
417 node_path: {
419 node_path: {
418 'content': ''
420 'content': ''
419 }
421 }
420 }
422 }
421 self.scm_model.delete_nodes(
423 self.scm_model.delete_nodes(
422 user=c.rhodecode_user.user_id, repo=c.rhodecode_db_repo,
424 user=c.rhodecode_user.user_id, repo=c.rhodecode_db_repo,
423 message=message,
425 message=message,
424 nodes=nodes,
426 nodes=nodes,
425 parent_commit=c.commit,
427 parent_commit=c.commit,
426 author=author,
428 author=author,
427 )
429 )
428
430
429 h.flash(_('Successfully deleted file %s') % f_path,
431 h.flash(_('Successfully deleted file %s') % f_path,
430 category='success')
432 category='success')
431 except Exception:
433 except Exception:
432 msg = _('Error occurred during commit')
434 msg = _('Error occurred during commit')
433 log.exception(msg)
435 log.exception(msg)
434 h.flash(msg, category='error')
436 h.flash(msg, category='error')
435 return redirect(url('changeset_home',
437 return redirect(url('changeset_home',
436 repo_name=c.repo_name, revision='tip'))
438 repo_name=c.repo_name, revision='tip'))
437
439
438 @LoginRequired()
440 @LoginRequired()
439 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
441 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
440 def delete_home(self, repo_name, revision, f_path):
442 def delete_home(self, repo_name, revision, f_path):
441 commit_id = revision
443 commit_id = revision
442
444
443 repo = c.rhodecode_db_repo
445 repo = c.rhodecode_db_repo
444 if repo.enable_locking and repo.locked[0]:
446 if repo.enable_locking and repo.locked[0]:
445 h.flash(_('This repository has been locked by %s on %s')
447 h.flash(_('This repository has been locked by %s on %s')
446 % (h.person_by_id(repo.locked[0]),
448 % (h.person_by_id(repo.locked[0]),
447 h.format_date(h.time_to_datetime(repo.locked[1]))),
449 h.format_date(h.time_to_datetime(repo.locked[1]))),
448 'warning')
450 'warning')
449 return redirect(h.url('files_home',
451 return redirect(h.url('files_home',
450 repo_name=repo_name, revision='tip'))
452 repo_name=repo_name, revision='tip'))
451
453
452 if not self._is_valid_head(commit_id, repo.scm_instance()):
454 if not self._is_valid_head(commit_id, repo.scm_instance()):
453 h.flash(_('You can only delete files with revision '
455 h.flash(_('You can only delete files with revision '
454 'being a valid branch '), category='warning')
456 'being a valid branch '), category='warning')
455 return redirect(h.url('files_home',
457 return redirect(h.url('files_home',
456 repo_name=repo_name, revision='tip',
458 repo_name=repo_name, revision='tip',
457 f_path=f_path))
459 f_path=f_path))
458
460
459 c.commit = self.__get_commit_or_redirect(commit_id, repo_name)
461 c.commit = self.__get_commit_or_redirect(commit_id, repo_name)
460 c.file = self.__get_filenode_or_redirect(repo_name, c.commit, f_path)
462 c.file = self.__get_filenode_or_redirect(repo_name, c.commit, f_path)
461
463
462 c.default_message = _(
464 c.default_message = _(
463 'Deleted file %s via RhodeCode Enterprise') % (f_path)
465 'Deleted file %s via RhodeCode Enterprise') % (f_path)
464 c.f_path = f_path
466 c.f_path = f_path
465
467
466 return render('files/files_delete.html')
468 return render('files/files_delete.html')
467
469
468 @CSRFRequired()
470 @CSRFRequired()
469 @LoginRequired()
471 @LoginRequired()
470 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
472 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
471 def edit(self, repo_name, revision, f_path):
473 def edit(self, repo_name, revision, f_path):
472 commit_id = revision
474 commit_id = revision
473
475
474 repo = c.rhodecode_db_repo
476 repo = c.rhodecode_db_repo
475 if repo.enable_locking and repo.locked[0]:
477 if repo.enable_locking and repo.locked[0]:
476 h.flash(_('This repository has been locked by %s on %s')
478 h.flash(_('This repository has been locked by %s on %s')
477 % (h.person_by_id(repo.locked[0]),
479 % (h.person_by_id(repo.locked[0]),
478 h.format_date(h.time_to_datetime(repo.locked[1]))),
480 h.format_date(h.time_to_datetime(repo.locked[1]))),
479 'warning')
481 'warning')
480 return redirect(h.url('files_home',
482 return redirect(h.url('files_home',
481 repo_name=repo_name, revision='tip'))
483 repo_name=repo_name, revision='tip'))
482
484
483 if not self._is_valid_head(commit_id, repo.scm_instance()):
485 if not self._is_valid_head(commit_id, repo.scm_instance()):
484 h.flash(_('You can only edit files with revision '
486 h.flash(_('You can only edit files with revision '
485 'being a valid branch '), category='warning')
487 'being a valid branch '), category='warning')
486 return redirect(h.url('files_home',
488 return redirect(h.url('files_home',
487 repo_name=repo_name, revision='tip',
489 repo_name=repo_name, revision='tip',
488 f_path=f_path))
490 f_path=f_path))
489
491
490 c.commit = self.__get_commit_or_redirect(commit_id, repo_name)
492 c.commit = self.__get_commit_or_redirect(commit_id, repo_name)
491 c.file = self.__get_filenode_or_redirect(repo_name, c.commit, f_path)
493 c.file = self.__get_filenode_or_redirect(repo_name, c.commit, f_path)
492
494
493 if c.file.is_binary:
495 if c.file.is_binary:
494 return redirect(url('files_home', repo_name=c.repo_name,
496 return redirect(url('files_home', repo_name=c.repo_name,
495 revision=c.commit.raw_id, f_path=f_path))
497 revision=c.commit.raw_id, f_path=f_path))
496 c.default_message = _(
498 c.default_message = _(
497 'Edited file %s via RhodeCode Enterprise') % (f_path)
499 'Edited file %s via RhodeCode Enterprise') % (f_path)
498 c.f_path = f_path
500 c.f_path = f_path
499 old_content = c.file.content
501 old_content = c.file.content
500 sl = old_content.splitlines(1)
502 sl = old_content.splitlines(1)
501 first_line = sl[0] if sl else ''
503 first_line = sl[0] if sl else ''
502
504
503 # modes: 0 - Unix, 1 - Mac, 2 - DOS
505 # modes: 0 - Unix, 1 - Mac, 2 - DOS
504 mode = detect_mode(first_line, 0)
506 mode = detect_mode(first_line, 0)
505 content = convert_line_endings(request.POST.get('content', ''), mode)
507 content = convert_line_endings(request.POST.get('content', ''), mode)
506
508
507 message = request.POST.get('message') or c.default_message
509 message = request.POST.get('message') or c.default_message
508 org_f_path = c.file.unicode_path
510 org_f_path = c.file.unicode_path
509 filename = request.POST['filename']
511 filename = request.POST['filename']
510 org_filename = c.file.name
512 org_filename = c.file.name
511
513
512 if content == old_content and filename == org_filename:
514 if content == old_content and filename == org_filename:
513 h.flash(_('No changes'), category='warning')
515 h.flash(_('No changes'), category='warning')
514 return redirect(url('changeset_home', repo_name=c.repo_name,
516 return redirect(url('changeset_home', repo_name=c.repo_name,
515 revision='tip'))
517 revision='tip'))
516 try:
518 try:
517 mapping = {
519 mapping = {
518 org_f_path: {
520 org_f_path: {
519 'org_filename': org_f_path,
521 'org_filename': org_f_path,
520 'filename': os.path.join(c.file.dir_path, filename),
522 'filename': os.path.join(c.file.dir_path, filename),
521 'content': content,
523 'content': content,
522 'lexer': '',
524 'lexer': '',
523 'op': 'mod',
525 'op': 'mod',
524 }
526 }
525 }
527 }
526
528
527 ScmModel().update_nodes(
529 ScmModel().update_nodes(
528 user=c.rhodecode_user.user_id,
530 user=c.rhodecode_user.user_id,
529 repo=c.rhodecode_db_repo,
531 repo=c.rhodecode_db_repo,
530 message=message,
532 message=message,
531 nodes=mapping,
533 nodes=mapping,
532 parent_commit=c.commit,
534 parent_commit=c.commit,
533 )
535 )
534
536
535 h.flash(_('Successfully committed to %s') % f_path,
537 h.flash(_('Successfully committed to %s') % f_path,
536 category='success')
538 category='success')
537 except Exception:
539 except Exception:
538 msg = _('Error occurred during commit')
540 msg = _('Error occurred during commit')
539 log.exception(msg)
541 log.exception(msg)
540 h.flash(msg, category='error')
542 h.flash(msg, category='error')
541 return redirect(url('changeset_home',
543 return redirect(url('changeset_home',
542 repo_name=c.repo_name, revision='tip'))
544 repo_name=c.repo_name, revision='tip'))
543
545
544 @LoginRequired()
546 @LoginRequired()
545 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
547 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
546 def edit_home(self, repo_name, revision, f_path):
548 def edit_home(self, repo_name, revision, f_path):
547 commit_id = revision
549 commit_id = revision
548
550
549 repo = c.rhodecode_db_repo
551 repo = c.rhodecode_db_repo
550 if repo.enable_locking and repo.locked[0]:
552 if repo.enable_locking and repo.locked[0]:
551 h.flash(_('This repository has been locked by %s on %s')
553 h.flash(_('This repository has been locked by %s on %s')
552 % (h.person_by_id(repo.locked[0]),
554 % (h.person_by_id(repo.locked[0]),
553 h.format_date(h.time_to_datetime(repo.locked[1]))),
555 h.format_date(h.time_to_datetime(repo.locked[1]))),
554 'warning')
556 'warning')
555 return redirect(h.url('files_home',
557 return redirect(h.url('files_home',
556 repo_name=repo_name, revision='tip'))
558 repo_name=repo_name, revision='tip'))
557
559
558 if not self._is_valid_head(commit_id, repo.scm_instance()):
560 if not self._is_valid_head(commit_id, repo.scm_instance()):
559 h.flash(_('You can only edit files with revision '
561 h.flash(_('You can only edit files with revision '
560 'being a valid branch '), category='warning')
562 'being a valid branch '), category='warning')
561 return redirect(h.url('files_home',
563 return redirect(h.url('files_home',
562 repo_name=repo_name, revision='tip',
564 repo_name=repo_name, revision='tip',
563 f_path=f_path))
565 f_path=f_path))
564
566
565 c.commit = self.__get_commit_or_redirect(commit_id, repo_name)
567 c.commit = self.__get_commit_or_redirect(commit_id, repo_name)
566 c.file = self.__get_filenode_or_redirect(repo_name, c.commit, f_path)
568 c.file = self.__get_filenode_or_redirect(repo_name, c.commit, f_path)
567
569
568 if c.file.is_binary:
570 if c.file.is_binary:
569 return redirect(url('files_home', repo_name=c.repo_name,
571 return redirect(url('files_home', repo_name=c.repo_name,
570 revision=c.commit.raw_id, f_path=f_path))
572 revision=c.commit.raw_id, f_path=f_path))
571 c.default_message = _(
573 c.default_message = _(
572 'Edited file %s via RhodeCode Enterprise') % (f_path)
574 'Edited file %s via RhodeCode Enterprise') % (f_path)
573 c.f_path = f_path
575 c.f_path = f_path
574
576
575 return render('files/files_edit.html')
577 return render('files/files_edit.html')
576
578
577 def _is_valid_head(self, commit_id, repo):
579 def _is_valid_head(self, commit_id, repo):
578 # check if commit is a branch identifier- basically we cannot
580 # check if commit is a branch identifier- basically we cannot
579 # create multiple heads via file editing
581 # create multiple heads via file editing
580 valid_heads = repo.branches.keys() + repo.branches.values()
582 valid_heads = repo.branches.keys() + repo.branches.values()
581
583
582 if h.is_svn(repo) and not repo.is_empty():
584 if h.is_svn(repo) and not repo.is_empty():
583 # Note: Subversion only has one head, we add it here in case there
585 # Note: Subversion only has one head, we add it here in case there
584 # is no branch matched.
586 # is no branch matched.
585 valid_heads.append(repo.get_commit(commit_idx=-1).raw_id)
587 valid_heads.append(repo.get_commit(commit_idx=-1).raw_id)
586
588
587 # check if commit is a branch name or branch hash
589 # check if commit is a branch name or branch hash
588 return commit_id in valid_heads
590 return commit_id in valid_heads
589
591
590 @CSRFRequired()
592 @CSRFRequired()
591 @LoginRequired()
593 @LoginRequired()
592 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
594 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
593 def add(self, repo_name, revision, f_path):
595 def add(self, repo_name, revision, f_path):
594 repo = Repository.get_by_repo_name(repo_name)
596 repo = Repository.get_by_repo_name(repo_name)
595 if repo.enable_locking and repo.locked[0]:
597 if repo.enable_locking and repo.locked[0]:
596 h.flash(_('This repository has been locked by %s on %s')
598 h.flash(_('This repository has been locked by %s on %s')
597 % (h.person_by_id(repo.locked[0]),
599 % (h.person_by_id(repo.locked[0]),
598 h.format_date(h.time_to_datetime(repo.locked[1]))),
600 h.format_date(h.time_to_datetime(repo.locked[1]))),
599 'warning')
601 'warning')
600 return redirect(h.url('files_home',
602 return redirect(h.url('files_home',
601 repo_name=repo_name, revision='tip'))
603 repo_name=repo_name, revision='tip'))
602
604
603 r_post = request.POST
605 r_post = request.POST
604
606
605 c.commit = self.__get_commit_or_redirect(
607 c.commit = self.__get_commit_or_redirect(
606 revision, repo_name, redirect_after=False)
608 revision, repo_name, redirect_after=False)
607 if c.commit is None:
609 if c.commit is None:
608 c.commit = EmptyCommit(alias=c.rhodecode_repo.alias)
610 c.commit = EmptyCommit(alias=c.rhodecode_repo.alias)
609 c.default_message = (_('Added file via RhodeCode Enterprise'))
611 c.default_message = (_('Added file via RhodeCode Enterprise'))
610 c.f_path = f_path
612 c.f_path = f_path
611 unix_mode = 0
613 unix_mode = 0
612 content = convert_line_endings(r_post.get('content', ''), unix_mode)
614 content = convert_line_endings(r_post.get('content', ''), unix_mode)
613
615
614 message = r_post.get('message') or c.default_message
616 message = r_post.get('message') or c.default_message
615 filename = r_post.get('filename')
617 filename = r_post.get('filename')
616 location = r_post.get('location', '') # dir location
618 location = r_post.get('location', '') # dir location
617 file_obj = r_post.get('upload_file', None)
619 file_obj = r_post.get('upload_file', None)
618
620
619 if file_obj is not None and hasattr(file_obj, 'filename'):
621 if file_obj is not None and hasattr(file_obj, 'filename'):
620 filename = file_obj.filename
622 filename = file_obj.filename
621 content = file_obj.file
623 content = file_obj.file
622
624
623 if hasattr(content, 'file'):
625 if hasattr(content, 'file'):
624 # non posix systems store real file under file attr
626 # non posix systems store real file under file attr
625 content = content.file
627 content = content.file
626
628
627 # If there's no commit, redirect to repo summary
629 # If there's no commit, redirect to repo summary
628 if type(c.commit) is EmptyCommit:
630 if type(c.commit) is EmptyCommit:
629 redirect_url = "summary_home"
631 redirect_url = "summary_home"
630 else:
632 else:
631 redirect_url = "changeset_home"
633 redirect_url = "changeset_home"
632
634
633 if not filename:
635 if not filename:
634 h.flash(_('No filename'), category='warning')
636 h.flash(_('No filename'), category='warning')
635 return redirect(url(redirect_url, repo_name=c.repo_name,
637 return redirect(url(redirect_url, repo_name=c.repo_name,
636 revision='tip'))
638 revision='tip'))
637
639
638 # extract the location from filename,
640 # extract the location from filename,
639 # allows using foo/bar.txt syntax to create subdirectories
641 # allows using foo/bar.txt syntax to create subdirectories
640 subdir_loc = filename.rsplit('/', 1)
642 subdir_loc = filename.rsplit('/', 1)
641 if len(subdir_loc) == 2:
643 if len(subdir_loc) == 2:
642 location = os.path.join(location, subdir_loc[0])
644 location = os.path.join(location, subdir_loc[0])
643
645
644 # strip all crap out of file, just leave the basename
646 # strip all crap out of file, just leave the basename
645 filename = os.path.basename(filename)
647 filename = os.path.basename(filename)
646 node_path = os.path.join(location, filename)
648 node_path = os.path.join(location, filename)
647 author = c.rhodecode_user.full_contact
649 author = c.rhodecode_user.full_contact
648
650
649 try:
651 try:
650 nodes = {
652 nodes = {
651 node_path: {
653 node_path: {
652 'content': content
654 'content': content
653 }
655 }
654 }
656 }
655 self.scm_model.create_nodes(
657 self.scm_model.create_nodes(
656 user=c.rhodecode_user.user_id,
658 user=c.rhodecode_user.user_id,
657 repo=c.rhodecode_db_repo,
659 repo=c.rhodecode_db_repo,
658 message=message,
660 message=message,
659 nodes=nodes,
661 nodes=nodes,
660 parent_commit=c.commit,
662 parent_commit=c.commit,
661 author=author,
663 author=author,
662 )
664 )
663
665
664 h.flash(_('Successfully committed to %s') % node_path,
666 h.flash(_('Successfully committed to %s') % node_path,
665 category='success')
667 category='success')
666 except NonRelativePathError as e:
668 except NonRelativePathError as e:
667 h.flash(_(
669 h.flash(_(
668 'The location specified must be a relative path and must not '
670 'The location specified must be a relative path and must not '
669 'contain .. in the path'), category='warning')
671 'contain .. in the path'), category='warning')
670 return redirect(url('changeset_home', repo_name=c.repo_name,
672 return redirect(url('changeset_home', repo_name=c.repo_name,
671 revision='tip'))
673 revision='tip'))
672 except (NodeError, NodeAlreadyExistsError) as e:
674 except (NodeError, NodeAlreadyExistsError) as e:
673 h.flash(_(e), category='error')
675 h.flash(_(e), category='error')
674 except Exception:
676 except Exception:
675 msg = _('Error occurred during commit')
677 msg = _('Error occurred during commit')
676 log.exception(msg)
678 log.exception(msg)
677 h.flash(msg, category='error')
679 h.flash(msg, category='error')
678 return redirect(url('changeset_home',
680 return redirect(url('changeset_home',
679 repo_name=c.repo_name, revision='tip'))
681 repo_name=c.repo_name, revision='tip'))
680
682
681 @LoginRequired()
683 @LoginRequired()
682 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
684 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
683 def add_home(self, repo_name, revision, f_path):
685 def add_home(self, repo_name, revision, f_path):
684
686
685 repo = Repository.get_by_repo_name(repo_name)
687 repo = Repository.get_by_repo_name(repo_name)
686 if repo.enable_locking and repo.locked[0]:
688 if repo.enable_locking and repo.locked[0]:
687 h.flash(_('This repository has been locked by %s on %s')
689 h.flash(_('This repository has been locked by %s on %s')
688 % (h.person_by_id(repo.locked[0]),
690 % (h.person_by_id(repo.locked[0]),
689 h.format_date(h.time_to_datetime(repo.locked[1]))),
691 h.format_date(h.time_to_datetime(repo.locked[1]))),
690 'warning')
692 'warning')
691 return redirect(h.url('files_home',
693 return redirect(h.url('files_home',
692 repo_name=repo_name, revision='tip'))
694 repo_name=repo_name, revision='tip'))
693
695
694 c.commit = self.__get_commit_or_redirect(
696 c.commit = self.__get_commit_or_redirect(
695 revision, repo_name, redirect_after=False)
697 revision, repo_name, redirect_after=False)
696 if c.commit is None:
698 if c.commit is None:
697 c.commit = EmptyCommit(alias=c.rhodecode_repo.alias)
699 c.commit = EmptyCommit(alias=c.rhodecode_repo.alias)
698 c.default_message = (_('Added file via RhodeCode Enterprise'))
700 c.default_message = (_('Added file via RhodeCode Enterprise'))
699 c.f_path = f_path
701 c.f_path = f_path
700
702
701 return render('files/files_add.html')
703 return render('files/files_add.html')
702
704
703 @LoginRequired()
705 @LoginRequired()
704 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
706 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
705 'repository.admin')
707 'repository.admin')
706 def archivefile(self, repo_name, fname):
708 def archivefile(self, repo_name, fname):
707 fileformat = None
709 fileformat = None
708 commit_id = None
710 commit_id = None
709 ext = None
711 ext = None
710 subrepos = request.GET.get('subrepos') == 'true'
712 subrepos = request.GET.get('subrepos') == 'true'
711
713
712 for a_type, ext_data in settings.ARCHIVE_SPECS.items():
714 for a_type, ext_data in settings.ARCHIVE_SPECS.items():
713 archive_spec = fname.split(ext_data[1])
715 archive_spec = fname.split(ext_data[1])
714 if len(archive_spec) == 2 and archive_spec[1] == '':
716 if len(archive_spec) == 2 and archive_spec[1] == '':
715 fileformat = a_type or ext_data[1]
717 fileformat = a_type or ext_data[1]
716 commit_id = archive_spec[0]
718 commit_id = archive_spec[0]
717 ext = ext_data[1]
719 ext = ext_data[1]
718
720
719 dbrepo = RepoModel().get_by_repo_name(repo_name)
721 dbrepo = RepoModel().get_by_repo_name(repo_name)
720 if not dbrepo.enable_downloads:
722 if not dbrepo.enable_downloads:
721 return _('Downloads disabled')
723 return _('Downloads disabled')
722
724
723 try:
725 try:
724 commit = c.rhodecode_repo.get_commit(commit_id)
726 commit = c.rhodecode_repo.get_commit(commit_id)
725 content_type = settings.ARCHIVE_SPECS[fileformat][0]
727 content_type = settings.ARCHIVE_SPECS[fileformat][0]
726 except CommitDoesNotExistError:
728 except CommitDoesNotExistError:
727 return _('Unknown revision %s') % commit_id
729 return _('Unknown revision %s') % commit_id
728 except EmptyRepositoryError:
730 except EmptyRepositoryError:
729 return _('Empty repository')
731 return _('Empty repository')
730 except KeyError:
732 except KeyError:
731 return _('Unknown archive type')
733 return _('Unknown archive type')
732
734
733 # archive cache
735 # archive cache
734 from rhodecode import CONFIG
736 from rhodecode import CONFIG
735
737
736 archive_name = '%s-%s%s%s' % (
738 archive_name = '%s-%s%s%s' % (
737 safe_str(repo_name.replace('/', '_')),
739 safe_str(repo_name.replace('/', '_')),
738 '-sub' if subrepos else '',
740 '-sub' if subrepos else '',
739 safe_str(commit.short_id), ext)
741 safe_str(commit.short_id), ext)
740
742
741 use_cached_archive = False
743 use_cached_archive = False
742 archive_cache_enabled = CONFIG.get(
744 archive_cache_enabled = CONFIG.get(
743 'archive_cache_dir') and not request.GET.get('no_cache')
745 'archive_cache_dir') and not request.GET.get('no_cache')
744
746
745 if archive_cache_enabled:
747 if archive_cache_enabled:
746 # check if we it's ok to write
748 # check if we it's ok to write
747 if not os.path.isdir(CONFIG['archive_cache_dir']):
749 if not os.path.isdir(CONFIG['archive_cache_dir']):
748 os.makedirs(CONFIG['archive_cache_dir'])
750 os.makedirs(CONFIG['archive_cache_dir'])
749 cached_archive_path = os.path.join(
751 cached_archive_path = os.path.join(
750 CONFIG['archive_cache_dir'], archive_name)
752 CONFIG['archive_cache_dir'], archive_name)
751 if os.path.isfile(cached_archive_path):
753 if os.path.isfile(cached_archive_path):
752 log.debug('Found cached archive in %s', cached_archive_path)
754 log.debug('Found cached archive in %s', cached_archive_path)
753 fd, archive = None, cached_archive_path
755 fd, archive = None, cached_archive_path
754 use_cached_archive = True
756 use_cached_archive = True
755 else:
757 else:
756 log.debug('Archive %s is not yet cached', archive_name)
758 log.debug('Archive %s is not yet cached', archive_name)
757
759
758 if not use_cached_archive:
760 if not use_cached_archive:
759 # generate new archive
761 # generate new archive
760 fd, archive = tempfile.mkstemp()
762 fd, archive = tempfile.mkstemp()
761 log.debug('Creating new temp archive in %s' % (archive,))
763 log.debug('Creating new temp archive in %s' % (archive,))
762 try:
764 try:
763 commit.archive_repo(archive, kind=fileformat, subrepos=subrepos)
765 commit.archive_repo(archive, kind=fileformat, subrepos=subrepos)
764 except ImproperArchiveTypeError:
766 except ImproperArchiveTypeError:
765 return _('Unknown archive type')
767 return _('Unknown archive type')
766 if archive_cache_enabled:
768 if archive_cache_enabled:
767 # if we generated the archive and we have cache enabled
769 # if we generated the archive and we have cache enabled
768 # let's use this for future
770 # let's use this for future
769 log.debug('Storing new archive in %s' % (cached_archive_path,))
771 log.debug('Storing new archive in %s' % (cached_archive_path,))
770 shutil.move(archive, cached_archive_path)
772 shutil.move(archive, cached_archive_path)
771 archive = cached_archive_path
773 archive = cached_archive_path
772
774
773 def get_chunked_archive(archive):
775 def get_chunked_archive(archive):
774 with open(archive, 'rb') as stream:
776 with open(archive, 'rb') as stream:
775 while True:
777 while True:
776 data = stream.read(16 * 1024)
778 data = stream.read(16 * 1024)
777 if not data:
779 if not data:
778 if fd: # fd means we used temporary file
780 if fd: # fd means we used temporary file
779 os.close(fd)
781 os.close(fd)
780 if not archive_cache_enabled:
782 if not archive_cache_enabled:
781 log.debug('Destroying temp archive %s', archive)
783 log.debug('Destroying temp archive %s', archive)
782 os.remove(archive)
784 os.remove(archive)
783 break
785 break
784 yield data
786 yield data
785
787
786 # store download action
788 # store download action
787 action_logger(user=c.rhodecode_user,
789 action_logger(user=c.rhodecode_user,
788 action='user_downloaded_archive:%s' % archive_name,
790 action='user_downloaded_archive:%s' % archive_name,
789 repo=repo_name, ipaddr=self.ip_addr, commit=True)
791 repo=repo_name, ipaddr=self.ip_addr, commit=True)
790 response.content_disposition = str(
792 response.content_disposition = str(
791 'attachment; filename=%s' % archive_name)
793 'attachment; filename=%s' % archive_name)
792 response.content_type = str(content_type)
794 response.content_type = str(content_type)
793
795
794 return get_chunked_archive(archive)
796 return get_chunked_archive(archive)
795
797
796 @LoginRequired()
798 @LoginRequired()
797 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
799 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
798 'repository.admin')
800 'repository.admin')
799 def diff(self, repo_name, f_path):
801 def diff(self, repo_name, f_path):
800 ignore_whitespace = request.GET.get('ignorews') == '1'
802 ignore_whitespace = request.GET.get('ignorews') == '1'
801 line_context = request.GET.get('context', 3)
803 line_context = request.GET.get('context', 3)
802 diff1 = request.GET.get('diff1', '')
804 diff1 = request.GET.get('diff1', '')
803
805
804 path1, diff1 = parse_path_ref(diff1, default_path=f_path)
806 path1, diff1 = parse_path_ref(diff1, default_path=f_path)
805
807
806 diff2 = request.GET.get('diff2', '')
808 diff2 = request.GET.get('diff2', '')
807 c.action = request.GET.get('diff')
809 c.action = request.GET.get('diff')
808 c.no_changes = diff1 == diff2
810 c.no_changes = diff1 == diff2
809 c.f_path = f_path
811 c.f_path = f_path
810 c.big_diff = False
812 c.big_diff = False
811 c.ignorews_url = _ignorews_url
813 c.ignorews_url = _ignorews_url
812 c.context_url = _context_url
814 c.context_url = _context_url
813 c.changes = OrderedDict()
815 c.changes = OrderedDict()
814 c.changes[diff2] = []
816 c.changes[diff2] = []
815
817
816 if not any((diff1, diff2)):
818 if not any((diff1, diff2)):
817 h.flash(
819 h.flash(
818 'Need query parameter "diff1" or "diff2" to generate a diff.',
820 'Need query parameter "diff1" or "diff2" to generate a diff.',
819 category='error')
821 category='error')
820 raise HTTPBadRequest()
822 raise HTTPBadRequest()
821
823
822 # special case if we want a show commit_id only, it's impl here
824 # special case if we want a show commit_id only, it's impl here
823 # to reduce JS and callbacks
825 # to reduce JS and callbacks
824
826
825 if request.GET.get('show_rev') and diff1:
827 if request.GET.get('show_rev') and diff1:
826 if str2bool(request.GET.get('annotate', 'False')):
828 if str2bool(request.GET.get('annotate', 'False')):
827 _url = url('files_annotate_home', repo_name=c.repo_name,
829 _url = url('files_annotate_home', repo_name=c.repo_name,
828 revision=diff1, f_path=path1)
830 revision=diff1, f_path=path1)
829 else:
831 else:
830 _url = url('files_home', repo_name=c.repo_name,
832 _url = url('files_home', repo_name=c.repo_name,
831 revision=diff1, f_path=path1)
833 revision=diff1, f_path=path1)
832
834
833 return redirect(_url)
835 return redirect(_url)
834
836
835 try:
837 try:
836 node1 = self._get_file_node(diff1, path1)
838 node1 = self._get_file_node(diff1, path1)
837 node2 = self._get_file_node(diff2, f_path)
839 node2 = self._get_file_node(diff2, f_path)
838 except (RepositoryError, NodeError):
840 except (RepositoryError, NodeError):
839 log.exception("Exception while trying to get node from repository")
841 log.exception("Exception while trying to get node from repository")
840 return redirect(url(
842 return redirect(url(
841 'files_home', repo_name=c.repo_name, f_path=f_path))
843 'files_home', repo_name=c.repo_name, f_path=f_path))
842
844
843 if all(isinstance(node.commit, EmptyCommit)
845 if all(isinstance(node.commit, EmptyCommit)
844 for node in (node1, node2)):
846 for node in (node1, node2)):
845 raise HTTPNotFound
847 raise HTTPNotFound
846
848
847 c.commit_1 = node1.commit
849 c.commit_1 = node1.commit
848 c.commit_2 = node2.commit
850 c.commit_2 = node2.commit
849
851
850 if c.action == 'download':
852 if c.action == 'download':
851 _diff = diffs.get_gitdiff(node1, node2,
853 _diff = diffs.get_gitdiff(node1, node2,
852 ignore_whitespace=ignore_whitespace,
854 ignore_whitespace=ignore_whitespace,
853 context=line_context)
855 context=line_context)
854 diff = diffs.DiffProcessor(_diff, format='gitdiff')
856 diff = diffs.DiffProcessor(_diff, format='gitdiff')
855
857
856 diff_name = '%s_vs_%s.diff' % (diff1, diff2)
858 diff_name = '%s_vs_%s.diff' % (diff1, diff2)
857 response.content_type = 'text/plain'
859 response.content_type = 'text/plain'
858 response.content_disposition = (
860 response.content_disposition = (
859 'attachment; filename=%s' % (diff_name,)
861 'attachment; filename=%s' % (diff_name,)
860 )
862 )
861 charset = self._get_default_encoding()
863 charset = self._get_default_encoding()
862 if charset:
864 if charset:
863 response.charset = charset
865 response.charset = charset
864 return diff.as_raw()
866 return diff.as_raw()
865
867
866 elif c.action == 'raw':
868 elif c.action == 'raw':
867 _diff = diffs.get_gitdiff(node1, node2,
869 _diff = diffs.get_gitdiff(node1, node2,
868 ignore_whitespace=ignore_whitespace,
870 ignore_whitespace=ignore_whitespace,
869 context=line_context)
871 context=line_context)
870 diff = diffs.DiffProcessor(_diff, format='gitdiff')
872 diff = diffs.DiffProcessor(_diff, format='gitdiff')
871 response.content_type = 'text/plain'
873 response.content_type = 'text/plain'
872 charset = self._get_default_encoding()
874 charset = self._get_default_encoding()
873 if charset:
875 if charset:
874 response.charset = charset
876 response.charset = charset
875 return diff.as_raw()
877 return diff.as_raw()
876
878
877 else:
879 else:
878 fid = h.FID(diff2, node2.path)
880 fid = h.FID(diff2, node2.path)
879 line_context_lcl = get_line_ctx(fid, request.GET)
881 line_context_lcl = get_line_ctx(fid, request.GET)
880 ign_whitespace_lcl = get_ignore_ws(fid, request.GET)
882 ign_whitespace_lcl = get_ignore_ws(fid, request.GET)
881
883
882 __, commit1, commit2, diff, st, data = diffs.wrapped_diff(
884 __, commit1, commit2, diff, st, data = diffs.wrapped_diff(
883 filenode_old=node1,
885 filenode_old=node1,
884 filenode_new=node2,
886 filenode_new=node2,
885 diff_limit=self.cut_off_limit_diff,
887 diff_limit=self.cut_off_limit_diff,
886 file_limit=self.cut_off_limit_file,
888 file_limit=self.cut_off_limit_file,
887 show_full_diff=request.GET.get('fulldiff'),
889 show_full_diff=request.GET.get('fulldiff'),
888 ignore_whitespace=ign_whitespace_lcl,
890 ignore_whitespace=ign_whitespace_lcl,
889 line_context=line_context_lcl,)
891 line_context=line_context_lcl,)
890
892
891 c.lines_added = data['stats']['added'] if data else 0
893 c.lines_added = data['stats']['added'] if data else 0
892 c.lines_deleted = data['stats']['deleted'] if data else 0
894 c.lines_deleted = data['stats']['deleted'] if data else 0
893 c.files = [data]
895 c.files = [data]
894 c.commit_ranges = [c.commit_1, c.commit_2]
896 c.commit_ranges = [c.commit_1, c.commit_2]
895 c.ancestor = None
897 c.ancestor = None
896 c.statuses = []
898 c.statuses = []
897 c.target_repo = c.rhodecode_db_repo
899 c.target_repo = c.rhodecode_db_repo
898 c.filename1 = node1.path
900 c.filename1 = node1.path
899 c.filename = node2.path
901 c.filename = node2.path
900 c.binary_file = node1.is_binary or node2.is_binary
902 c.binary_file = node1.is_binary or node2.is_binary
901 operation = data['operation'] if data else ''
903 operation = data['operation'] if data else ''
902
904
903 commit_changes = {
905 commit_changes = {
904 # TODO: it's passing the old file to the diff to keep the
906 # TODO: it's passing the old file to the diff to keep the
905 # standard but this is not being used for this template,
907 # standard but this is not being used for this template,
906 # but might need both files in the future or a more standard
908 # but might need both files in the future or a more standard
907 # way to work with that
909 # way to work with that
908 'fid': [commit1, commit2, operation,
910 'fid': [commit1, commit2, operation,
909 c.filename, diff, st, data]
911 c.filename, diff, st, data]
910 }
912 }
911
913
912 c.changes = commit_changes
914 c.changes = commit_changes
913
915
914 return render('files/file_diff.html')
916 return render('files/file_diff.html')
915
917
916 @LoginRequired()
918 @LoginRequired()
917 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
919 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
918 'repository.admin')
920 'repository.admin')
919 def diff_2way(self, repo_name, f_path):
921 def diff_2way(self, repo_name, f_path):
920 diff1 = request.GET.get('diff1', '')
922 diff1 = request.GET.get('diff1', '')
921 diff2 = request.GET.get('diff2', '')
923 diff2 = request.GET.get('diff2', '')
922
924
923 nodes = []
925 nodes = []
924 unknown_commits = []
926 unknown_commits = []
925 for commit in [diff1, diff2]:
927 for commit in [diff1, diff2]:
926 try:
928 try:
927 nodes.append(self._get_file_node(commit, f_path))
929 nodes.append(self._get_file_node(commit, f_path))
928 except (RepositoryError, NodeError):
930 except (RepositoryError, NodeError):
929 log.exception('%(commit)s does not exist' % {'commit': commit})
931 log.exception('%(commit)s does not exist' % {'commit': commit})
930 unknown_commits.append(commit)
932 unknown_commits.append(commit)
931 h.flash(h.literal(
933 h.flash(h.literal(
932 _('Commit %(commit)s does not exist.') % {'commit': commit}
934 _('Commit %(commit)s does not exist.') % {'commit': commit}
933 ), category='error')
935 ), category='error')
934
936
935 if unknown_commits:
937 if unknown_commits:
936 return redirect(url('files_home', repo_name=c.repo_name,
938 return redirect(url('files_home', repo_name=c.repo_name,
937 f_path=f_path))
939 f_path=f_path))
938
940
939 if all(isinstance(node.commit, EmptyCommit) for node in nodes):
941 if all(isinstance(node.commit, EmptyCommit) for node in nodes):
940 raise HTTPNotFound
942 raise HTTPNotFound
941
943
942 node1, node2 = nodes
944 node1, node2 = nodes
943
945
944 f_gitdiff = diffs.get_gitdiff(node1, node2, ignore_whitespace=False)
946 f_gitdiff = diffs.get_gitdiff(node1, node2, ignore_whitespace=False)
945 diff_processor = diffs.DiffProcessor(f_gitdiff, format='gitdiff')
947 diff_processor = diffs.DiffProcessor(f_gitdiff, format='gitdiff')
946 diff_data = diff_processor.prepare()
948 diff_data = diff_processor.prepare()
947
949
948 if not diff_data or diff_data[0]['raw_diff'] == '':
950 if not diff_data or diff_data[0]['raw_diff'] == '':
949 h.flash(h.literal(_('%(file_path)s has not changed '
951 h.flash(h.literal(_('%(file_path)s has not changed '
950 'between %(commit_1)s and %(commit_2)s.') % {
952 'between %(commit_1)s and %(commit_2)s.') % {
951 'file_path': f_path,
953 'file_path': f_path,
952 'commit_1': node1.commit.id,
954 'commit_1': node1.commit.id,
953 'commit_2': node2.commit.id
955 'commit_2': node2.commit.id
954 }), category='error')
956 }), category='error')
955 return redirect(url('files_home', repo_name=c.repo_name,
957 return redirect(url('files_home', repo_name=c.repo_name,
956 f_path=f_path))
958 f_path=f_path))
957
959
958 c.diff_data = diff_data[0]
960 c.diff_data = diff_data[0]
959 c.FID = h.FID(diff2, node2.path)
961 c.FID = h.FID(diff2, node2.path)
960 # cleanup some unneeded data
962 # cleanup some unneeded data
961 del c.diff_data['raw_diff']
963 del c.diff_data['raw_diff']
962 del c.diff_data['chunks']
964 del c.diff_data['chunks']
963
965
964 c.node1 = node1
966 c.node1 = node1
965 c.commit_1 = node1.commit
967 c.commit_1 = node1.commit
966 c.node2 = node2
968 c.node2 = node2
967 c.commit_2 = node2.commit
969 c.commit_2 = node2.commit
968
970
969 return render('files/diff_2way.html')
971 return render('files/diff_2way.html')
970
972
971 def _get_file_node(self, commit_id, f_path):
973 def _get_file_node(self, commit_id, f_path):
972 if commit_id not in ['', None, 'None', '0' * 12, '0' * 40]:
974 if commit_id not in ['', None, 'None', '0' * 12, '0' * 40]:
973 commit = c.rhodecode_repo.get_commit(commit_id=commit_id)
975 commit = c.rhodecode_repo.get_commit(commit_id=commit_id)
974 try:
976 try:
975 node = commit.get_node(f_path)
977 node = commit.get_node(f_path)
976 if node.is_dir():
978 if node.is_dir():
977 raise NodeError('%s path is a %s not a file'
979 raise NodeError('%s path is a %s not a file'
978 % (node, type(node)))
980 % (node, type(node)))
979 except NodeDoesNotExistError:
981 except NodeDoesNotExistError:
980 commit = EmptyCommit(
982 commit = EmptyCommit(
981 commit_id=commit_id,
983 commit_id=commit_id,
982 idx=commit.idx,
984 idx=commit.idx,
983 repo=commit.repository,
985 repo=commit.repository,
984 alias=commit.repository.alias,
986 alias=commit.repository.alias,
985 message=commit.message,
987 message=commit.message,
986 author=commit.author,
988 author=commit.author,
987 date=commit.date)
989 date=commit.date)
988 node = FileNode(f_path, '', commit=commit)
990 node = FileNode(f_path, '', commit=commit)
989 else:
991 else:
990 commit = EmptyCommit(
992 commit = EmptyCommit(
991 repo=c.rhodecode_repo,
993 repo=c.rhodecode_repo,
992 alias=c.rhodecode_repo.alias)
994 alias=c.rhodecode_repo.alias)
993 node = FileNode(f_path, '', commit=commit)
995 node = FileNode(f_path, '', commit=commit)
994 return node
996 return node
995
997
996 def _get_node_history(self, commit, f_path, commits=None):
998 def _get_node_history(self, commit, f_path, commits=None):
997 """
999 """
998 get commit history for given node
1000 get commit history for given node
999
1001
1000 :param commit: commit to calculate history
1002 :param commit: commit to calculate history
1001 :param f_path: path for node to calculate history for
1003 :param f_path: path for node to calculate history for
1002 :param commits: if passed don't calculate history and take
1004 :param commits: if passed don't calculate history and take
1003 commits defined in this list
1005 commits defined in this list
1004 """
1006 """
1005 # calculate history based on tip
1007 # calculate history based on tip
1006 tip = c.rhodecode_repo.get_commit()
1008 tip = c.rhodecode_repo.get_commit()
1007 if commits is None:
1009 if commits is None:
1008 pre_load = ["author", "branch"]
1010 pre_load = ["author", "branch"]
1009 try:
1011 try:
1010 commits = tip.get_file_history(f_path, pre_load=pre_load)
1012 commits = tip.get_file_history(f_path, pre_load=pre_load)
1011 except (NodeDoesNotExistError, CommitError):
1013 except (NodeDoesNotExistError, CommitError):
1012 # this node is not present at tip!
1014 # this node is not present at tip!
1013 commits = commit.get_file_history(f_path, pre_load=pre_load)
1015 commits = commit.get_file_history(f_path, pre_load=pre_load)
1014
1016
1015 history = []
1017 history = []
1016 commits_group = ([], _("Changesets"))
1018 commits_group = ([], _("Changesets"))
1017 for commit in commits:
1019 for commit in commits:
1018 branch = ' (%s)' % commit.branch if commit.branch else ''
1020 branch = ' (%s)' % commit.branch if commit.branch else ''
1019 n_desc = 'r%s:%s%s' % (commit.idx, commit.short_id, branch)
1021 n_desc = 'r%s:%s%s' % (commit.idx, commit.short_id, branch)
1020 commits_group[0].append((commit.raw_id, n_desc,))
1022 commits_group[0].append((commit.raw_id, n_desc,))
1021 history.append(commits_group)
1023 history.append(commits_group)
1022
1024
1023 symbolic_reference = self._symbolic_reference
1025 symbolic_reference = self._symbolic_reference
1024
1026
1025 if c.rhodecode_repo.alias == 'svn':
1027 if c.rhodecode_repo.alias == 'svn':
1026 adjusted_f_path = self._adjust_file_path_for_svn(
1028 adjusted_f_path = self._adjust_file_path_for_svn(
1027 f_path, c.rhodecode_repo)
1029 f_path, c.rhodecode_repo)
1028 if adjusted_f_path != f_path:
1030 if adjusted_f_path != f_path:
1029 log.debug(
1031 log.debug(
1030 'Recognized svn tag or branch in file "%s", using svn '
1032 'Recognized svn tag or branch in file "%s", using svn '
1031 'specific symbolic references', f_path)
1033 'specific symbolic references', f_path)
1032 f_path = adjusted_f_path
1034 f_path = adjusted_f_path
1033 symbolic_reference = self._symbolic_reference_svn
1035 symbolic_reference = self._symbolic_reference_svn
1034
1036
1035 branches = self._create_references(
1037 branches = self._create_references(
1036 c.rhodecode_repo.branches, symbolic_reference, f_path)
1038 c.rhodecode_repo.branches, symbolic_reference, f_path)
1037 branches_group = (branches, _("Branches"))
1039 branches_group = (branches, _("Branches"))
1038
1040
1039 tags = self._create_references(
1041 tags = self._create_references(
1040 c.rhodecode_repo.tags, symbolic_reference, f_path)
1042 c.rhodecode_repo.tags, symbolic_reference, f_path)
1041 tags_group = (tags, _("Tags"))
1043 tags_group = (tags, _("Tags"))
1042
1044
1043 history.append(branches_group)
1045 history.append(branches_group)
1044 history.append(tags_group)
1046 history.append(tags_group)
1045
1047
1046 return history, commits
1048 return history, commits
1047
1049
1048 def _adjust_file_path_for_svn(self, f_path, repo):
1050 def _adjust_file_path_for_svn(self, f_path, repo):
1049 """
1051 """
1050 Computes the relative path of `f_path`.
1052 Computes the relative path of `f_path`.
1051
1053
1052 This is mainly based on prefix matching of the recognized tags and
1054 This is mainly based on prefix matching of the recognized tags and
1053 branches in the underlying repository.
1055 branches in the underlying repository.
1054 """
1056 """
1055 tags_and_branches = itertools.chain(
1057 tags_and_branches = itertools.chain(
1056 repo.branches.iterkeys(),
1058 repo.branches.iterkeys(),
1057 repo.tags.iterkeys())
1059 repo.tags.iterkeys())
1058 tags_and_branches = sorted(tags_and_branches, key=len, reverse=True)
1060 tags_and_branches = sorted(tags_and_branches, key=len, reverse=True)
1059
1061
1060 for name in tags_and_branches:
1062 for name in tags_and_branches:
1061 if f_path.startswith(name + '/'):
1063 if f_path.startswith(name + '/'):
1062 f_path = vcspath.relpath(f_path, name)
1064 f_path = vcspath.relpath(f_path, name)
1063 break
1065 break
1064 return f_path
1066 return f_path
1065
1067
1066 def _create_references(
1068 def _create_references(
1067 self, branches_or_tags, symbolic_reference, f_path):
1069 self, branches_or_tags, symbolic_reference, f_path):
1068 items = []
1070 items = []
1069 for name, commit_id in branches_or_tags.items():
1071 for name, commit_id in branches_or_tags.items():
1070 sym_ref = symbolic_reference(commit_id, name, f_path)
1072 sym_ref = symbolic_reference(commit_id, name, f_path)
1071 items.append((sym_ref, name))
1073 items.append((sym_ref, name))
1072 return items
1074 return items
1073
1075
1074 def _symbolic_reference(self, commit_id, name, f_path):
1076 def _symbolic_reference(self, commit_id, name, f_path):
1075 return commit_id
1077 return commit_id
1076
1078
1077 def _symbolic_reference_svn(self, commit_id, name, f_path):
1079 def _symbolic_reference_svn(self, commit_id, name, f_path):
1078 new_f_path = vcspath.join(name, f_path)
1080 new_f_path = vcspath.join(name, f_path)
1079 return u'%s@%s' % (new_f_path, commit_id)
1081 return u'%s@%s' % (new_f_path, commit_id)
1080
1082
1081 @LoginRequired()
1083 @LoginRequired()
1082 @XHRRequired()
1084 @XHRRequired()
1083 @HasRepoPermissionAnyDecorator(
1085 @HasRepoPermissionAnyDecorator(
1084 'repository.read', 'repository.write', 'repository.admin')
1086 'repository.read', 'repository.write', 'repository.admin')
1085 @jsonify
1087 @jsonify
1086 def nodelist(self, repo_name, revision, f_path):
1088 def nodelist(self, repo_name, revision, f_path):
1087 commit = self.__get_commit_or_redirect(revision, repo_name)
1089 commit = self.__get_commit_or_redirect(revision, repo_name)
1088
1090
1089 metadata = self._get_nodelist_at_commit(
1091 metadata = self._get_nodelist_at_commit(
1090 repo_name, commit.raw_id, f_path)
1092 repo_name, commit.raw_id, f_path)
1091 return {'nodes': metadata}
1093 return {'nodes': metadata}
1092
1094
1093 @LoginRequired()
1095 @LoginRequired()
1094 @XHRRequired()
1096 @XHRRequired()
1095 @HasRepoPermissionAnyDecorator(
1097 @HasRepoPermissionAnyDecorator(
1096 'repository.read', 'repository.write', 'repository.admin')
1098 'repository.read', 'repository.write', 'repository.admin')
1097 def nodetree_full(self, repo_name, commit_id, f_path):
1099 def nodetree_full(self, repo_name, commit_id, f_path):
1098 """
1100 """
1099 Returns rendered html of file tree that contains commit date,
1101 Returns rendered html of file tree that contains commit date,
1100 author, revision for the specified combination of
1102 author, revision for the specified combination of
1101 repo, commit_id and file path
1103 repo, commit_id and file path
1102
1104
1103 :param repo_name: name of the repository
1105 :param repo_name: name of the repository
1104 :param commit_id: commit_id of file tree
1106 :param commit_id: commit_id of file tree
1105 :param f_path: file path of the requested directory
1107 :param f_path: file path of the requested directory
1106 """
1108 """
1107
1109
1108 commit = self.__get_commit_or_redirect(commit_id, repo_name)
1110 commit = self.__get_commit_or_redirect(commit_id, repo_name)
1109 try:
1111 try:
1110 dir_node = commit.get_node(f_path)
1112 dir_node = commit.get_node(f_path)
1111 except RepositoryError as e:
1113 except RepositoryError as e:
1112 return 'error {}'.format(safe_str(e))
1114 return 'error {}'.format(safe_str(e))
1113
1115
1114 if dir_node.is_file():
1116 if dir_node.is_file():
1115 return ''
1117 return ''
1116
1118
1117 c.file = dir_node
1119 c.file = dir_node
1118 c.commit = commit
1120 c.commit = commit
1119
1121
1120 # using force=True here, make a little trick. We flush the cache and
1122 # using force=True here, make a little trick. We flush the cache and
1121 # compute it using the same key as without full_load, so the fully
1123 # compute it using the same key as without full_load, so the fully
1122 # loaded cached tree is now returned instead of partial
1124 # loaded cached tree is now returned instead of partial
1123 return self._get_tree_at_commit(
1125 return self._get_tree_at_commit(
1124 repo_name, commit.raw_id, dir_node.path, full_load=True,
1126 repo_name, commit.raw_id, dir_node.path, full_load=True,
1125 force=True)
1127 force=True)
@@ -1,287 +1,286 b''
1 <%inherit file="/base/base.html"/>
1 <%inherit file="/base/base.html"/>
2
2
3 <%def name="title(*args)">
3 <%def name="title(*args)">
4 ${_('%s Files') % c.repo_name}
4 ${_('%s Files') % c.repo_name}
5 %if hasattr(c,'file'):
5 %if hasattr(c,'file'):
6 &middot; ${h.safe_unicode(c.file.path) or '\\'}
6 &middot; ${h.safe_unicode(c.file.path) or '\\'}
7 %endif
7 %endif
8
8
9 %if c.rhodecode_name:
9 %if c.rhodecode_name:
10 &middot; ${h.branding(c.rhodecode_name)}
10 &middot; ${h.branding(c.rhodecode_name)}
11 %endif
11 %endif
12 </%def>
12 </%def>
13
13
14 <%def name="breadcrumbs_links()">
14 <%def name="breadcrumbs_links()">
15 ${_('Files')}
15 ${_('Files')}
16 %if c.file:
16 %if c.file:
17 @ ${h.show_id(c.commit)}
17 @ ${h.show_id(c.commit)}
18 %endif
18 %endif
19 </%def>
19 </%def>
20
20
21 <%def name="menu_bar_nav()">
21 <%def name="menu_bar_nav()">
22 ${self.menu_items(active='repositories')}
22 ${self.menu_items(active='repositories')}
23 </%def>
23 </%def>
24
24
25 <%def name="menu_bar_subnav()">
25 <%def name="menu_bar_subnav()">
26 ${self.repo_menu(active='files')}
26 ${self.repo_menu(active='files')}
27 </%def>
27 </%def>
28
28
29 <%def name="main()">
29 <%def name="main()">
30 <div class="title">
30 <div class="title">
31 ${self.repo_page_title(c.rhodecode_db_repo)}
31 ${self.repo_page_title(c.rhodecode_db_repo)}
32 </div>
32 </div>
33
33
34 <div id="pjax-container" class="summary">
34 <div id="pjax-container" class="summary">
35 <div id="files_data">
35 <div id="files_data">
36 <%include file='files_pjax.html'/>
36 <%include file='files_pjax.html'/>
37 </div>
37 </div>
38 </div>
38 </div>
39 <script>
39 <script>
40 var curState = {
40 var curState = {
41 commit_id: "${c.commit.raw_id}"
41 commit_id: "${c.commit.raw_id}"
42 };
42 };
43
43
44 var getState = function(context) {
44 var getState = function(context) {
45 var url = $(location).attr('href');
45 var url = $(location).attr('href');
46 var _base_url = '${h.url("files_home",repo_name=c.repo_name,revision='',f_path='')}';
46 var _base_url = '${h.url("files_home",repo_name=c.repo_name,revision='',f_path='')}';
47 var _annotate_url = '${h.url("files_annotate_home",repo_name=c.repo_name,revision='',f_path='')}';
47 var _annotate_url = '${h.url("files_annotate_home",repo_name=c.repo_name,revision='',f_path='')}';
48 _base_url = _base_url.replace('//', '/');
48 _base_url = _base_url.replace('//', '/');
49 _annotate_url = _annotate_url.replace('//', '/');
49 _annotate_url = _annotate_url.replace('//', '/');
50
50
51 //extract f_path from url.
51 //extract f_path from url.
52 var parts = url.split(_base_url);
52 var parts = url.split(_base_url);
53 if (parts.length != 2) {
53 if (parts.length != 2) {
54 parts = url.split(_annotate_url);
54 parts = url.split(_annotate_url);
55 if (parts.length != 2) {
55 if (parts.length != 2) {
56 var rev = "tip";
56 var rev = "tip";
57 var f_path = "";
57 var f_path = "";
58 } else {
58 } else {
59 var parts2 = parts[1].split('/');
59 var parts2 = parts[1].split('/');
60 var rev = parts2.shift(); // pop the first element which is the revision
60 var rev = parts2.shift(); // pop the first element which is the revision
61 var f_path = parts2.join('/');
61 var f_path = parts2.join('/');
62 }
62 }
63
63
64 } else {
64 } else {
65 var parts2 = parts[1].split('/');
65 var parts2 = parts[1].split('/');
66 var rev = parts2.shift(); // pop the first element which is the revision
66 var rev = parts2.shift(); // pop the first element which is the revision
67 var f_path = parts2.join('/');
67 var f_path = parts2.join('/');
68 }
68 }
69
69
70 var _node_list_url = pyroutes.url('files_nodelist_home',
70 var _node_list_url = pyroutes.url('files_nodelist_home',
71 {repo_name: templateContext.repo_name,
71 {repo_name: templateContext.repo_name,
72 revision: rev, f_path: f_path});
72 revision: rev, f_path: f_path});
73 var _url_base = pyroutes.url('files_home',
73 var _url_base = pyroutes.url('files_home',
74 {repo_name: templateContext.repo_name,
74 {repo_name: templateContext.repo_name,
75 revision: rev, f_path:'__FPATH__'});
75 revision: rev, f_path:'__FPATH__'});
76 return {
76 return {
77 url: url,
77 url: url,
78 f_path: f_path,
78 f_path: f_path,
79 rev: rev,
79 rev: rev,
80 commit_id: curState.commit_id,
80 commit_id: curState.commit_id,
81 node_list_url: _node_list_url,
81 node_list_url: _node_list_url,
82 url_base: _url_base
82 url_base: _url_base
83 };
83 };
84 };
84 };
85
85
86 var metadataRequest = null;
86 var metadataRequest = null;
87 var getFilesMetadata = function() {
87 var getFilesMetadata = function() {
88 if (metadataRequest && metadataRequest.readyState != 4) {
88 if (metadataRequest && metadataRequest.readyState != 4) {
89 metadataRequest.abort();
89 metadataRequest.abort();
90 }
90 }
91 if (source_page) {
91 if (fileSourcePage) {
92 return false;
92 return false;
93 }
93 }
94
94
95 if ($('#file-tree-wrapper').hasClass('full-load')) {
95 if ($('#file-tree-wrapper').hasClass('full-load')) {
96 // in case our HTML wrapper has full-load class we don't
96 // in case our HTML wrapper has full-load class we don't
97 // trigger the async load of metadata
97 // trigger the async load of metadata
98 return false;
98 return false;
99 }
99 }
100
100
101 var state = getState('metadata');
101 var state = getState('metadata');
102 var url_data = {
102 var url_data = {
103 'repo_name': templateContext.repo_name,
103 'repo_name': templateContext.repo_name,
104 'commit_id': state.commit_id,
104 'commit_id': state.commit_id,
105 'f_path': state.f_path
105 'f_path': state.f_path
106 };
106 };
107
107
108 var url = pyroutes.url('files_nodetree_full', url_data);
108 var url = pyroutes.url('files_nodetree_full', url_data);
109
109
110 metadataRequest = $.ajax({url: url});
110 metadataRequest = $.ajax({url: url});
111
111
112 metadataRequest.done(function(data) {
112 metadataRequest.done(function(data) {
113 $('#file-tree').html(data);
113 $('#file-tree').html(data);
114 timeagoActivate();
114 timeagoActivate();
115 });
115 });
116 metadataRequest.fail(function (data, textStatus, errorThrown) {
116 metadataRequest.fail(function (data, textStatus, errorThrown) {
117 console.log(data);
117 console.log(data);
118 if (data.status != 0) {
118 if (data.status != 0) {
119 alert("Error while fetching metadata.\nError code {0} ({1}).Please consider reloading the page".format(data.status,data.statusText));
119 alert("Error while fetching metadata.\nError code {0} ({1}).Please consider reloading the page".format(data.status,data.statusText));
120 }
120 }
121 });
121 });
122 };
122 };
123
123
124 var callbacks = function() {
124 var callbacks = function() {
125 var state = getState('callbacks');
125 var state = getState('callbacks');
126 timeagoActivate();
126 timeagoActivate();
127
127
128 // used for history, and switch to
128 // used for history, and switch to
129 var initialCommitData = {
129 var initialCommitData = {
130 id: null,
130 id: null,
131 text: '${_("Switch To Commit")}',
131 text: '${_("Switch To Commit")}',
132 type: 'sha',
132 type: 'sha',
133 raw_id: null,
133 raw_id: null,
134 files_url: null
134 files_url: null
135 };
135 };
136
136
137 if ($('#trimmed_message_box').height() < 50) {
137 if ($('#trimmed_message_box').height() < 50) {
138 $('#message_expand').hide();
138 $('#message_expand').hide();
139 }
139 }
140
140
141 $('#message_expand').on('click', function(e) {
141 $('#message_expand').on('click', function(e) {
142 $('#trimmed_message_box').css('max-height', 'none');
142 $('#trimmed_message_box').css('max-height', 'none');
143 $(this).hide();
143 $(this).hide();
144 });
144 });
145
145
146
146 if (fileSourcePage) {
147 if (source_page) {
148 // variants for with source code, not tree view
147 // variants for with source code, not tree view
149
148
150 // select code link event
149 // select code link event
151 $("#hlcode").mouseup(getSelectionLink);
150 $("#hlcode").mouseup(getSelectionLink);
152
151
153 // file history select2
152 // file history select2
154 select2FileHistorySwitcher('#diff1', initialCommitData, state);
153 select2FileHistorySwitcher('#diff1', initialCommitData, state);
155 $('#diff1').on('change', function(e) {
154 $('#diff1').on('change', function(e) {
156 $('#diff').removeClass('disabled').removeAttr("disabled");
155 $('#diff').removeClass('disabled').removeAttr("disabled");
157 $('#show_rev').removeClass('disabled').removeAttr("disabled");
156 $('#show_rev').removeClass('disabled').removeAttr("disabled");
158 });
157 });
159
158
160 // show more authors
159 // show more authors
161 $('#show_authors').on('click', function(e) {
160 $('#show_authors').on('click', function(e) {
162 e.preventDefault();
161 e.preventDefault();
163 var url = pyroutes.url('files_authors_home',
162 var url = pyroutes.url('files_authors_home',
164 {'repo_name': templateContext.repo_name,
163 {'repo_name': templateContext.repo_name,
165 'revision': state.rev, 'f_path': state.f_path});
164 'revision': state.rev, 'f_path': state.f_path});
166
165
167 $.pjax({
166 $.pjax({
168 url: url,
167 url: url,
169 data: 'annotate=${"1" if c.annotate else "0"}',
168 data: 'annotate=${"1" if c.annotate else "0"}',
170 container: '#file_authors',
169 container: '#file_authors',
171 push: false,
170 push: false,
172 timeout: pjaxTimeout
171 timeout: pjaxTimeout
173 }).complete(function(){
172 }).complete(function(){
174 $('#show_authors').hide();
173 $('#show_authors').hide();
175 })
174 })
176 });
175 });
177
176
178 // load file short history
177 // load file short history
179 $('#file_history_overview').on('click', function(e) {
178 $('#file_history_overview').on('click', function(e) {
180 e.preventDefault();
179 e.preventDefault();
181 path = state.f_path;
180 path = state.f_path;
182 if (path.indexOf("#") >= 0) {
181 if (path.indexOf("#") >= 0) {
183 path = path.slice(0, path.indexOf("#"));
182 path = path.slice(0, path.indexOf("#"));
184 }
183 }
185 var url = pyroutes.url('changelog_file_home',
184 var url = pyroutes.url('changelog_file_home',
186 {'repo_name': templateContext.repo_name,
185 {'repo_name': templateContext.repo_name,
187 'revision': state.rev, 'f_path': path, 'limit': 6});
186 'revision': state.rev, 'f_path': path, 'limit': 6});
188 $('#file_history_container').show();
187 $('#file_history_container').show();
189 $('#file_history_container').html('<div class="file-history-inner">{0}</div>'.format(_gettext('Loading ...')));
188 $('#file_history_container').html('<div class="file-history-inner">{0}</div>'.format(_gettext('Loading ...')));
190
189
191 $.pjax({
190 $.pjax({
192 url: url,
191 url: url,
193 container: '#file_history_container',
192 container: '#file_history_container',
194 push: false,
193 push: false,
195 timeout: pjaxTimeout
194 timeout: pjaxTimeout
196 })
195 })
197 });
196 });
198
197
199 }
198 }
200 else {
199 else {
201 getFilesMetadata();
200 getFilesMetadata();
202
201
203 // fuzzy file filter
202 // fuzzy file filter
204 fileBrowserListeners(state.node_list_url, state.url_base);
203 fileBrowserListeners(state.node_list_url, state.url_base);
205
204
206 // switch to widget
205 // switch to widget
207 select2RefSwitcher('#refs_filter', initialCommitData);
206 select2RefSwitcher('#refs_filter', initialCommitData);
208 $('#refs_filter').on('change', function(e) {
207 $('#refs_filter').on('change', function(e) {
209 var data = $('#refs_filter').select2('data');
208 var data = $('#refs_filter').select2('data');
210 curState.commit_id = data.raw_id;
209 curState.commit_id = data.raw_id;
211 $.pjax({url: data.files_url, container: '#pjax-container', timeout: pjaxTimeout});
210 $.pjax({url: data.files_url, container: '#pjax-container', timeout: pjaxTimeout});
212 });
211 });
213
212
214 $("#prev_commit_link").on('click', function(e) {
213 $("#prev_commit_link").on('click', function(e) {
215 var data = $(this).data();
214 var data = $(this).data();
216 curState.commit_id = data.commitId;
215 curState.commit_id = data.commitId;
217 });
216 });
218
217
219 $("#next_commit_link").on('click', function(e) {
218 $("#next_commit_link").on('click', function(e) {
220 var data = $(this).data();
219 var data = $(this).data();
221 curState.commit_id = data.commitId;
220 curState.commit_id = data.commitId;
222 });
221 });
223
222
224 $('#at_rev').on("keypress", function(e) {
223 $('#at_rev').on("keypress", function(e) {
225 /* ENTER PRESSED */
224 /* ENTER PRESSED */
226 if (e.keyCode === 13) {
225 if (e.keyCode === 13) {
227 var rev = $('#at_rev').val();
226 var rev = $('#at_rev').val();
228 // explicit reload page here. with pjax entering bad input
227 // explicit reload page here. with pjax entering bad input
229 // produces not so nice results
228 // produces not so nice results
230 window.location = pyroutes.url('files_home',
229 window.location = pyroutes.url('files_home',
231 {'repo_name': templateContext.repo_name,
230 {'repo_name': templateContext.repo_name,
232 'revision': rev, 'f_path': state.f_path});
231 'revision': rev, 'f_path': state.f_path});
233 }
232 }
234 });
233 });
235 }
234 }
236 };
235 };
237
236
238 var pjaxTimeout = 5000;
237 var pjaxTimeout = 5000;
239
238
240 $(document).pjax(".pjax-link", "#pjax-container", {
239 $(document).pjax(".pjax-link", "#pjax-container", {
241 "fragment": "#pjax-content",
240 "fragment": "#pjax-content",
242 "maxCacheLength": 1000,
241 "maxCacheLength": 1000,
243 "timeout": pjaxTimeout
242 "timeout": pjaxTimeout
244 });
243 });
245
244
246 // define global back/forward states
245 // define global back/forward states
247 var isPjaxPopState = false;
246 var isPjaxPopState = false;
248 $(document).on('pjax:popstate', function() {
247 $(document).on('pjax:popstate', function() {
249 isPjaxPopState = true;
248 isPjaxPopState = true;
250 });
249 });
251
250
252 $(document).on('pjax:end', function(xhr, options) {
251 $(document).on('pjax:end', function(xhr, options) {
253 if (isPjaxPopState) {
252 if (isPjaxPopState) {
254 isPjaxPopState = false;
253 isPjaxPopState = false;
255 callbacks();
254 callbacks();
256 _NODEFILTER.resetFilter();
255 _NODEFILTER.resetFilter();
257 }
256 }
258
257
259 // run callback for tracking if defined for google analytics etc.
258 // run callback for tracking if defined for google analytics etc.
260 // this is used to trigger tracking on pjax
259 // this is used to trigger tracking on pjax
261 if (typeof window.rhodecode_statechange_callback !== 'undefined') {
260 if (typeof window.rhodecode_statechange_callback !== 'undefined') {
262 var state = getState('statechange');
261 var state = getState('statechange');
263 rhodecode_statechange_callback(state.url, null)
262 rhodecode_statechange_callback(state.url, null)
264 }
263 }
265 });
264 });
266
265
267 $(document).on('pjax:success', function(event, xhr, options) {
266 $(document).on('pjax:success', function(event, xhr, options) {
268 if (event.target.id == "file_history_container") {
267 if (event.target.id == "file_history_container") {
269 $('#file_history_overview').hide();
268 $('#file_history_overview').hide();
270 $('#file_history_overview_full').show();
269 $('#file_history_overview_full').show();
271 timeagoActivate();
270 timeagoActivate();
272 } else {
271 } else {
273 callbacks();
272 callbacks();
274 }
273 }
275 });
274 });
276
275
277 $(document).ready(function() {
276 $(document).ready(function() {
278 callbacks();
277 callbacks();
279 var search_GET = "${request.GET.get('search','')}";
278 var search_GET = "${request.GET.get('search','')}";
280 if (search_GET == "1") {
279 if (search_GET == "1") {
281 _NODEFILTER.initFilter();
280 _NODEFILTER.initFilter();
282 }
281 }
283 });
282 });
284
283
285 </script>
284 </script>
286
285
287 </%def>
286 </%def>
@@ -1,53 +1,49 b''
1
1
2 <div id="codeblock" class="browserblock">
2 <div id="codeblock" class="browserblock">
3 <div class="browser-header">
3 <div class="browser-header">
4 <div class="browser-nav">
4 <div class="browser-nav">
5 ${h.form(h.url.current(), method='GET', id='at_rev_form')}
5 ${h.form(h.url.current(), method='GET', id='at_rev_form')}
6 <div class="info_box">
6 <div class="info_box">
7 ${h.hidden('refs_filter')}
7 ${h.hidden('refs_filter')}
8 <div class="info_box_elem previous">
8 <div class="info_box_elem previous">
9 <a id="prev_commit_link" data-commit-id="${c.prev_commit.raw_id}" class="pjax-link ${'disabled' if c.url_prev == '#' else ''}" href="${c.url_prev}" title="${_('Previous commit')}"><i class="icon-chevron-left"></i></a>
9 <a id="prev_commit_link" data-commit-id="${c.prev_commit.raw_id}" class="pjax-link ${'disabled' if c.url_prev == '#' else ''}" href="${c.url_prev}" title="${_('Previous commit')}"><i class="icon-chevron-left"></i></a>
10 </div>
10 </div>
11 <div class="info_box_elem">${h.text('at_rev',value=c.commit.revision)}</div>
11 <div class="info_box_elem">${h.text('at_rev',value=c.commit.revision)}</div>
12 <div class="info_box_elem next">
12 <div class="info_box_elem next">
13 <a id="next_commit_link" data-commit-id="${c.next_commit.raw_id}" class="pjax-link ${'disabled' if c.url_next == '#' else ''}" href="${c.url_next}" title="${_('Next commit')}"><i class="icon-chevron-right"></i></a>
13 <a id="next_commit_link" data-commit-id="${c.next_commit.raw_id}" class="pjax-link ${'disabled' if c.url_next == '#' else ''}" href="${c.url_next}" title="${_('Next commit')}"><i class="icon-chevron-right"></i></a>
14 </div>
14 </div>
15 </div>
15 </div>
16 ${h.end_form()}
16 ${h.end_form()}
17
17
18 <div id="search_activate_id" class="search_activate">
18 <div id="search_activate_id" class="search_activate">
19 <a class="btn btn-default" id="filter_activate" href="javascript:void(0)">${_('Search File List')}</a>
19 <a class="btn btn-default" id="filter_activate" href="javascript:void(0)">${_('Search File List')}</a>
20 </div>
20 </div>
21 <div id="search_deactivate_id" class="search_activate hidden">
21 <div id="search_deactivate_id" class="search_activate hidden">
22 <a class="btn btn-default" id="filter_deactivate" href="javascript:void(0)">${_('Close File List')}</a>
22 <a class="btn btn-default" id="filter_deactivate" href="javascript:void(0)">${_('Close File List')}</a>
23 </div>
23 </div>
24 % if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name):
24 % if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name):
25 <div title="${_('Add New File')}" class="btn btn-primary new-file">
25 <div title="${_('Add New File')}" class="btn btn-primary new-file">
26 <a href="${h.url('files_add_home',repo_name=c.repo_name,revision=c.commit.raw_id,f_path=c.f_path, anchor='edit')}">
26 <a href="${h.url('files_add_home',repo_name=c.repo_name,revision=c.commit.raw_id,f_path=c.f_path, anchor='edit')}">
27 ${_('Add File')}</a>
27 ${_('Add File')}</a>
28 </div>
28 </div>
29 % endif
29 % endif
30 </div>
30 </div>
31
31
32 <div class="browser-search">
32 <div class="browser-search">
33 <div class="node-filter">
33 <div class="node-filter">
34 <div class="node_filter_box hidden" id="node_filter_box_loading" >${_('Loading file list...')}</div>
34 <div class="node_filter_box hidden" id="node_filter_box_loading" >${_('Loading file list...')}</div>
35 <div class="node_filter_box hidden" id="node_filter_box" >
35 <div class="node_filter_box hidden" id="node_filter_box" >
36 <div class="node-filter-path">${h.get_last_path_part(c.file)}/</div>
36 <div class="node-filter-path">${h.get_last_path_part(c.file)}/</div>
37 <div class="node-filter-input">
37 <div class="node-filter-input">
38 <input class="init" type="text" name="filter" size="25" id="node_filter" autocomplete="off">
38 <input class="init" type="text" name="filter" size="25" id="node_filter" autocomplete="off">
39 </div>
39 </div>
40 </div>
40 </div>
41 </div>
41 </div>
42 </div>
42 </div>
43 </div>
43 </div>
44 ## file tree is computed from caches, and filled in
44 ## file tree is computed from caches, and filled in
45 <div id="file-tree">
45 <div id="file-tree">
46 ${c.file_tree}
46 ${c.file_tree}
47 </div>
47 </div>
48
48
49 </div>
49 </div>
50
51 <script>
52 var source_page = false;
53 </script>
@@ -1,52 +1,55 b''
1 <%def name="title(*args)">
1 <%def name="title(*args)">
2 ${_('%s Files') % c.repo_name}
2 ${_('%s Files') % c.repo_name}
3 %if hasattr(c,'file'):
3 %if hasattr(c,'file'):
4 &middot; ${h.safe_unicode(c.file.path) or '\\'}
4 &middot; ${h.safe_unicode(c.file.path) or '\\'}
5 %endif
5 %endif
6
6
7 %if c.rhodecode_name:
7 %if c.rhodecode_name:
8 &middot; ${h.branding(c.rhodecode_name)}
8 &middot; ${h.branding(c.rhodecode_name)}
9 %endif
9 %endif
10 </%def>
10 </%def>
11
11
12 <div id="pjax-content" data-title="${self.title()}">
12 <div id="pjax-content" data-title="${self.title()}">
13 <div class="summary-detail">
13 <div class="summary-detail">
14 <div class="summary-detail-header">
14 <div class="summary-detail-header">
15 <div class="breadcrumbs files_location">
15 <div class="breadcrumbs files_location">
16 <h4>
16 <h4>
17 ${_('Location')}: ${h.files_breadcrumbs(c.repo_name,c.commit.raw_id,c.file.path)}
17 ${_('Location')}: ${h.files_breadcrumbs(c.repo_name,c.commit.raw_id,c.file.path)}
18 %if c.annotate:
18 %if c.annotate:
19 - ${_('annotation')}
19 - ${_('annotation')}
20 %endif
20 %endif
21 </h4>
21 </h4>
22 </div>
22 </div>
23 <div class="btn-collapse" data-toggle="summary-details">
23 <div class="btn-collapse" data-toggle="summary-details">
24 ${_('Show More')}
24 ${_('Show More')}
25 </div>
25 </div>
26 </div><!--end summary-detail-header-->
26 </div><!--end summary-detail-header-->
27
27
28 % if c.file.is_submodule():
28 % if c.file.is_submodule():
29 <span class="submodule-dir">Submodule ${h.escape(c.file.name)}</span>
29 <span class="submodule-dir">Submodule ${h.escape(c.file.name)}</span>
30 % elif c.file.is_dir():
30 % elif c.file.is_dir():
31 <%include file='file_tree_detail.html'/>
31 <%include file='file_tree_detail.html'/>
32 % else:
32 % else:
33 <%include file='files_detail.html'/>
33 <%include file='files_detail.html'/>
34 % endif
34 % endif
35
35
36 </div> <!--end summary-detail-->
36 </div> <!--end summary-detail-->
37
37 <script>
38 // set the pageSource variable
39 var fileSourcePage = ${c.file_source_page};
40 </script>
38 % if c.file.is_dir():
41 % if c.file.is_dir():
39 <div id="commit-stats" class="sidebar-right">
42 <div id="commit-stats" class="sidebar-right">
40 <%include file='file_tree_author_box.html'/>
43 <%include file='file_tree_author_box.html'/>
41 </div>
44 </div>
42
45
43 <%include file='files_browser.html'/>
46 <%include file='files_browser.html'/>
44 % else:
47 % else:
45 <div id="file_authors" class="sidebar-right">
48 <div id="file_authors" class="sidebar-right">
46 <%include file='file_authors_box.html'/>
49 <%include file='file_authors_box.html'/>
47 </div>
50 </div>
48
51
49 <%include file='files_source.html'/>
52 <%include file='files_source.html'/>
50 % endif
53 % endif
51
54
52 </div> No newline at end of file
55 </div>
@@ -1,82 +1,78 b''
1 <%namespace name="sourceblock" file="/codeblocks/source.html"/>
1 <%namespace name="sourceblock" file="/codeblocks/source.html"/>
2
2
3 <div id="codeblock" class="codeblock">
3 <div id="codeblock" class="codeblock">
4 <div class="codeblock-header">
4 <div class="codeblock-header">
5 <div class="stats">
5 <div class="stats">
6 <span> <strong>${c.file}</strong></span>
6 <span> <strong>${c.file}</strong></span>
7 <span> | ${c.file.lines()[0]} ${ungettext('line', 'lines', c.file.lines()[0])}</span>
7 <span> | ${c.file.lines()[0]} ${ungettext('line', 'lines', c.file.lines()[0])}</span>
8 <span> | ${h.format_byte_size_binary(c.file.size)}</span>
8 <span> | ${h.format_byte_size_binary(c.file.size)}</span>
9 <span> | ${c.file.mimetype} </span>
9 <span> | ${c.file.mimetype} </span>
10 <span class="item last"> | ${h.get_lexer_for_filenode(c.file).__class__.__name__}</span>
10 <span class="item last"> | ${h.get_lexer_for_filenode(c.file).__class__.__name__}</span>
11 </div>
11 </div>
12 <div class="buttons">
12 <div class="buttons">
13 <a id="file_history_overview" href="#">
13 <a id="file_history_overview" href="#">
14 ${_('History')}
14 ${_('History')}
15 </a>
15 </a>
16 <a id="file_history_overview_full" style="display: none" href="${h.url('changelog_file_home',repo_name=c.repo_name, revision=c.commit.raw_id, f_path=c.f_path)}">
16 <a id="file_history_overview_full" style="display: none" href="${h.url('changelog_file_home',repo_name=c.repo_name, revision=c.commit.raw_id, f_path=c.f_path)}">
17 ${_('Show Full History')}
17 ${_('Show Full History')}
18 </a> |
18 </a> |
19 %if c.annotate:
19 %if c.annotate:
20 ${h.link_to(_('Source'), h.url('files_home', repo_name=c.repo_name,revision=c.commit.raw_id,f_path=c.f_path))}
20 ${h.link_to(_('Source'), h.url('files_home', repo_name=c.repo_name,revision=c.commit.raw_id,f_path=c.f_path))}
21 %else:
21 %else:
22 ${h.link_to(_('Annotation'), h.url('files_annotate_home',repo_name=c.repo_name,revision=c.commit.raw_id,f_path=c.f_path))}
22 ${h.link_to(_('Annotation'), h.url('files_annotate_home',repo_name=c.repo_name,revision=c.commit.raw_id,f_path=c.f_path))}
23 %endif
23 %endif
24 | ${h.link_to(_('Raw'), h.url('files_raw_home',repo_name=c.repo_name,revision=c.commit.raw_id,f_path=c.f_path))}
24 | ${h.link_to(_('Raw'), h.url('files_raw_home',repo_name=c.repo_name,revision=c.commit.raw_id,f_path=c.f_path))}
25 | <a href="${h.url('files_rawfile_home',repo_name=c.repo_name,revision=c.commit.raw_id,f_path=c.f_path)}">
25 | <a href="${h.url('files_rawfile_home',repo_name=c.repo_name,revision=c.commit.raw_id,f_path=c.f_path)}">
26 ${_('Download')}
26 ${_('Download')}
27 </a>
27 </a>
28
28
29 %if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name):
29 %if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name):
30 |
30 |
31 %if c.on_branch_head and c.branch_or_raw_id and not c.file.is_binary:
31 %if c.on_branch_head and c.branch_or_raw_id and not c.file.is_binary:
32 <a href="${h.url('files_edit_home',repo_name=c.repo_name,revision=c.branch_or_raw_id,f_path=c.f_path, anchor='edit')}">
32 <a href="${h.url('files_edit_home',repo_name=c.repo_name,revision=c.branch_or_raw_id,f_path=c.f_path, anchor='edit')}">
33 ${_('Edit on Branch:%s') % c.branch_or_raw_id}
33 ${_('Edit on Branch:%s') % c.branch_or_raw_id}
34 </a>
34 </a>
35 | <a class="btn-danger btn-link" href="${h.url('files_delete_home',repo_name=c.repo_name,revision=c.branch_or_raw_id,f_path=c.f_path, anchor='edit')}">${_('Delete')}
35 | <a class="btn-danger btn-link" href="${h.url('files_delete_home',repo_name=c.repo_name,revision=c.branch_or_raw_id,f_path=c.f_path, anchor='edit')}">${_('Delete')}
36 </a>
36 </a>
37 %elif c.on_branch_head and c.branch_or_raw_id and c.file.is_binary:
37 %elif c.on_branch_head and c.branch_or_raw_id and c.file.is_binary:
38 ${h.link_to(_('Edit'), '#', class_="btn btn-link disabled tooltip", title=_('Editing binary files not allowed'))}
38 ${h.link_to(_('Edit'), '#', class_="btn btn-link disabled tooltip", title=_('Editing binary files not allowed'))}
39 | ${h.link_to(_('Delete'), h.url('files_delete_home',repo_name=c.repo_name,revision=c.branch_or_raw_id,f_path=c.f_path, anchor='edit'),class_="btn-danger btn-link")}
39 | ${h.link_to(_('Delete'), h.url('files_delete_home',repo_name=c.repo_name,revision=c.branch_or_raw_id,f_path=c.f_path, anchor='edit'),class_="btn-danger btn-link")}
40 %else:
40 %else:
41 ${h.link_to(_('Edit'), '#', class_="btn btn-link disabled tooltip", title=_('Editing files allowed only when on branch head commit'))}
41 ${h.link_to(_('Edit'), '#', class_="btn btn-link disabled tooltip", title=_('Editing files allowed only when on branch head commit'))}
42 | ${h.link_to(_('Delete'), '#', class_="btn btn-danger btn-link disabled tooltip", title=_('Deleting files allowed only when on branch head commit'))}
42 | ${h.link_to(_('Delete'), '#', class_="btn btn-danger btn-link disabled tooltip", title=_('Deleting files allowed only when on branch head commit'))}
43 %endif
43 %endif
44 %endif
44 %endif
45 </div>
45 </div>
46 </div>
46 </div>
47 <div id="file_history_container"></div>
47 <div id="file_history_container"></div>
48 <div class="code-body">
48 <div class="code-body">
49 %if c.file.is_binary:
49 %if c.file.is_binary:
50 <div>
50 <div>
51 ${_('Binary file (%s)') % c.file.mimetype}
51 ${_('Binary file (%s)') % c.file.mimetype}
52 </div>
52 </div>
53 %else:
53 %else:
54 % if c.file.size < c.cut_off_limit:
54 % if c.file.size < c.cut_off_limit:
55 %if c.renderer and not c.annotate:
55 %if c.renderer and not c.annotate:
56 ${h.render(c.file.content, renderer=c.renderer)}
56 ${h.render(c.file.content, renderer=c.renderer)}
57 %else:
57 %else:
58 <table class="cb codehilite">
58 <table class="cb codehilite">
59 %if c.annotate:
59 %if c.annotate:
60 <% color_hasher = h.color_hasher() %>
60 <% color_hasher = h.color_hasher() %>
61 %for annotation, lines in c.annotated_lines:
61 %for annotation, lines in c.annotated_lines:
62 ${sourceblock.render_annotation_lines(annotation, lines, color_hasher)}
62 ${sourceblock.render_annotation_lines(annotation, lines, color_hasher)}
63 %endfor
63 %endfor
64 %else:
64 %else:
65 %for line_num, tokens in enumerate(c.lines, 1):
65 %for line_num, tokens in enumerate(c.lines, 1):
66 ${sourceblock.render_line(line_num, tokens)}
66 ${sourceblock.render_line(line_num, tokens)}
67 %endfor
67 %endfor
68 %endif
68 %endif
69 </table>
69 </table>
70 </div>
70 </div>
71 %endif
71 %endif
72 %else:
72 %else:
73 ${_('File is too big to display')} ${h.link_to(_('Show as raw'),
73 ${_('File is too big to display')} ${h.link_to(_('Show as raw'),
74 h.url('files_raw_home',repo_name=c.repo_name,revision=c.commit.raw_id,f_path=c.f_path))}
74 h.url('files_raw_home',repo_name=c.repo_name,revision=c.commit.raw_id,f_path=c.f_path))}
75 %endif
75 %endif
76 %endif
76 %endif
77 </div>
77 </div>
78 </div>
78 </div> No newline at end of file
79
80 <script>
81 var source_page = true;
82 </script>
General Comments 0
You need to be logged in to leave comments. Login now