##// END OF EJS Templates
disabled edition of binary files
marcink -
r1313:856be614 beta
parent child Browse files
Show More
@@ -1,410 +1,414 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.files
3 rhodecode.controllers.files
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Files controller for RhodeCode
6 Files controller for RhodeCode
7
7
8 :created_on: Apr 21, 2010
8 :created_on: Apr 21, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import os
26 import os
27 import logging
27 import logging
28 import mimetypes
28 import mimetypes
29 import traceback
29 import traceback
30
30
31 from pylons import request, response, session, tmpl_context as c, url
31 from pylons import request, response, session, 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
34
35 from vcs.backends import ARCHIVE_SPECS
35 from vcs.backends import ARCHIVE_SPECS
36 from vcs.exceptions import RepositoryError, ChangesetDoesNotExistError, \
36 from vcs.exceptions import RepositoryError, ChangesetDoesNotExistError, \
37 EmptyRepositoryError, ImproperArchiveTypeError, VCSError
37 EmptyRepositoryError, ImproperArchiveTypeError, VCSError
38 from vcs.nodes import FileNode, NodeKind
38 from vcs.nodes import FileNode, NodeKind
39 from vcs.utils import diffs as differ
39 from vcs.utils import diffs as differ
40
40
41 from rhodecode.lib import convert_line_endings, detect_mode
41 from rhodecode.lib import convert_line_endings, detect_mode
42 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
42 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
43 from rhodecode.lib.base import BaseRepoController, render
43 from rhodecode.lib.base import BaseRepoController, render
44 from rhodecode.lib.utils import EmptyChangeset
44 from rhodecode.lib.utils import EmptyChangeset
45 import rhodecode.lib.helpers as h
45 import rhodecode.lib.helpers as h
46 from rhodecode.model.repo import RepoModel
46 from rhodecode.model.repo import RepoModel
47
47
48 log = logging.getLogger(__name__)
48 log = logging.getLogger(__name__)
49
49
50
50
51 class FilesController(BaseRepoController):
51 class FilesController(BaseRepoController):
52
52
53 @LoginRequired()
53 @LoginRequired()
54 def __before__(self):
54 def __before__(self):
55 super(FilesController, self).__before__()
55 super(FilesController, self).__before__()
56 c.cut_off_limit = self.cut_off_limit
56 c.cut_off_limit = self.cut_off_limit
57
57
58 def __get_cs_or_redirect(self, rev, repo_name):
58 def __get_cs_or_redirect(self, rev, repo_name):
59 """
59 """
60 Safe way to get changeset if error occur it redirects to tip with
60 Safe way to get changeset if error occur it redirects to tip with
61 proper message
61 proper message
62
62
63 :param rev: revision to fetch
63 :param rev: revision to fetch
64 :param repo_name: repo name to redirect after
64 :param repo_name: repo name to redirect after
65 """
65 """
66
66
67 try:
67 try:
68 return c.rhodecode_repo.get_changeset(rev)
68 return c.rhodecode_repo.get_changeset(rev)
69 except EmptyRepositoryError, e:
69 except EmptyRepositoryError, e:
70 h.flash(_('There are no files yet'), category='warning')
70 h.flash(_('There are no files yet'), category='warning')
71 redirect(h.url('summary_home', repo_name=repo_name))
71 redirect(h.url('summary_home', repo_name=repo_name))
72
72
73 except RepositoryError, e:
73 except RepositoryError, e:
74 h.flash(str(e), category='warning')
74 h.flash(str(e), category='warning')
75 redirect(h.url('files_home', repo_name=repo_name, revision='tip'))
75 redirect(h.url('files_home', repo_name=repo_name, revision='tip'))
76
76
77 def __get_filenode_or_redirect(self, repo_name, cs, path):
77 def __get_filenode_or_redirect(self, repo_name, cs, path):
78 """
78 """
79 Returns file_node, if error occurs or given path is directory,
79 Returns file_node, if error occurs or given path is directory,
80 it'll redirect to top level path
80 it'll redirect to top level path
81
81
82 :param repo_name: repo_name
82 :param repo_name: repo_name
83 :param cs: given changeset
83 :param cs: given changeset
84 :param path: path to lookup
84 :param path: path to lookup
85 """
85 """
86
86
87 try:
87 try:
88 file_node = cs.get_node(path)
88 file_node = cs.get_node(path)
89 if file_node.is_dir():
89 if file_node.is_dir():
90 raise RepositoryError('given path is a directory')
90 raise RepositoryError('given path is a directory')
91 except RepositoryError, e:
91 except RepositoryError, e:
92 h.flash(str(e), category='warning')
92 h.flash(str(e), category='warning')
93 redirect(h.url('files_home', repo_name=repo_name,
93 redirect(h.url('files_home', repo_name=repo_name,
94 revision=cs.raw_id))
94 revision=cs.raw_id))
95
95
96 return file_node
96 return file_node
97
97
98 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
98 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
99 'repository.admin')
99 'repository.admin')
100 def index(self, repo_name, revision, f_path):
100 def index(self, repo_name, revision, f_path):
101 #reditect to given revision from form if given
101 #reditect to given revision from form if given
102 post_revision = request.POST.get('at_rev', None)
102 post_revision = request.POST.get('at_rev', None)
103 if post_revision:
103 if post_revision:
104 cs = self.__get_cs_or_redirect(post_revision, repo_name)
104 cs = self.__get_cs_or_redirect(post_revision, repo_name)
105 redirect(url('files_home', repo_name=c.repo_name,
105 redirect(url('files_home', repo_name=c.repo_name,
106 revision=cs.raw_id, f_path=f_path))
106 revision=cs.raw_id, f_path=f_path))
107
107
108 c.changeset = self.__get_cs_or_redirect(revision, repo_name)
108 c.changeset = self.__get_cs_or_redirect(revision, repo_name)
109 c.branch = request.GET.get('branch', None)
109 c.branch = request.GET.get('branch', None)
110 c.f_path = f_path
110 c.f_path = f_path
111
111
112 cur_rev = c.changeset.revision
112 cur_rev = c.changeset.revision
113
113
114 #prev link
114 #prev link
115 try:
115 try:
116 prev_rev = c.rhodecode_repo.get_changeset(cur_rev).prev(c.branch)
116 prev_rev = c.rhodecode_repo.get_changeset(cur_rev).prev(c.branch)
117 c.url_prev = url('files_home', repo_name=c.repo_name,
117 c.url_prev = url('files_home', repo_name=c.repo_name,
118 revision=prev_rev.raw_id, f_path=f_path)
118 revision=prev_rev.raw_id, f_path=f_path)
119 if c.branch:
119 if c.branch:
120 c.url_prev += '?branch=%s' % c.branch
120 c.url_prev += '?branch=%s' % c.branch
121 except (ChangesetDoesNotExistError, VCSError):
121 except (ChangesetDoesNotExistError, VCSError):
122 c.url_prev = '#'
122 c.url_prev = '#'
123
123
124 #next link
124 #next link
125 try:
125 try:
126 next_rev = c.rhodecode_repo.get_changeset(cur_rev).next(c.branch)
126 next_rev = c.rhodecode_repo.get_changeset(cur_rev).next(c.branch)
127 c.url_next = url('files_home', repo_name=c.repo_name,
127 c.url_next = url('files_home', repo_name=c.repo_name,
128 revision=next_rev.raw_id, f_path=f_path)
128 revision=next_rev.raw_id, f_path=f_path)
129 if c.branch:
129 if c.branch:
130 c.url_next += '?branch=%s' % c.branch
130 c.url_next += '?branch=%s' % c.branch
131 except (ChangesetDoesNotExistError, VCSError):
131 except (ChangesetDoesNotExistError, VCSError):
132 c.url_next = '#'
132 c.url_next = '#'
133
133
134 #files or dirs
134 #files or dirs
135 try:
135 try:
136 c.files_list = c.changeset.get_node(f_path)
136 c.files_list = c.changeset.get_node(f_path)
137
137
138 if c.files_list.is_file():
138 if c.files_list.is_file():
139 c.file_history = self._get_node_history(c.changeset, f_path)
139 c.file_history = self._get_node_history(c.changeset, f_path)
140 else:
140 else:
141 c.file_history = []
141 c.file_history = []
142 except RepositoryError, e:
142 except RepositoryError, e:
143 h.flash(str(e), category='warning')
143 h.flash(str(e), category='warning')
144 redirect(h.url('files_home', repo_name=repo_name,
144 redirect(h.url('files_home', repo_name=repo_name,
145 revision=revision))
145 revision=revision))
146
146
147 return render('files/files.html')
147 return render('files/files.html')
148
148
149 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
149 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
150 'repository.admin')
150 'repository.admin')
151 def rawfile(self, repo_name, revision, f_path):
151 def rawfile(self, repo_name, revision, f_path):
152 cs = self.__get_cs_or_redirect(revision, repo_name)
152 cs = self.__get_cs_or_redirect(revision, repo_name)
153 file_node = self.__get_filenode_or_redirect(repo_name, cs, f_path)
153 file_node = self.__get_filenode_or_redirect(repo_name, cs, f_path)
154
154
155 response.content_disposition = 'attachment; filename=%s' % \
155 response.content_disposition = 'attachment; filename=%s' % \
156 f_path.split(os.sep)[-1].encode('utf8', 'replace')
156 f_path.split(os.sep)[-1].encode('utf8', 'replace')
157
157
158 response.content_type = file_node.mimetype
158 response.content_type = file_node.mimetype
159 return file_node.content
159 return file_node.content
160
160
161 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
161 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
162 'repository.admin')
162 'repository.admin')
163 def raw(self, repo_name, revision, f_path):
163 def raw(self, repo_name, revision, f_path):
164 cs = self.__get_cs_or_redirect(revision, repo_name)
164 cs = self.__get_cs_or_redirect(revision, repo_name)
165 file_node = self.__get_filenode_or_redirect(repo_name, cs, f_path)
165 file_node = self.__get_filenode_or_redirect(repo_name, cs, f_path)
166
166
167 raw_mimetype_mapping = {
167 raw_mimetype_mapping = {
168 # map original mimetype to a mimetype used for "show as raw"
168 # map original mimetype to a mimetype used for "show as raw"
169 # you can also provide a content-disposition to override the
169 # you can also provide a content-disposition to override the
170 # default "attachment" disposition.
170 # default "attachment" disposition.
171 # orig_type: (new_type, new_dispo)
171 # orig_type: (new_type, new_dispo)
172
172
173 # show images inline:
173 # show images inline:
174 'image/x-icon': ('image/x-icon', 'inline'),
174 'image/x-icon': ('image/x-icon', 'inline'),
175 'image/png': ('image/png', 'inline'),
175 'image/png': ('image/png', 'inline'),
176 'image/gif': ('image/gif', 'inline'),
176 'image/gif': ('image/gif', 'inline'),
177 'image/jpeg': ('image/jpeg', 'inline'),
177 'image/jpeg': ('image/jpeg', 'inline'),
178 'image/svg+xml': ('image/svg+xml', 'inline'),
178 'image/svg+xml': ('image/svg+xml', 'inline'),
179 }
179 }
180
180
181 mimetype = file_node.mimetype
181 mimetype = file_node.mimetype
182 try:
182 try:
183 mimetype, dispo = raw_mimetype_mapping[mimetype]
183 mimetype, dispo = raw_mimetype_mapping[mimetype]
184 except KeyError:
184 except KeyError:
185 # we don't know anything special about this, handle it safely
185 # we don't know anything special about this, handle it safely
186 if file_node.is_binary:
186 if file_node.is_binary:
187 # do same as download raw for binary files
187 # do same as download raw for binary files
188 mimetype, dispo = 'application/octet-stream', 'attachment'
188 mimetype, dispo = 'application/octet-stream', 'attachment'
189 else:
189 else:
190 # do not just use the original mimetype, but force text/plain,
190 # do not just use the original mimetype, but force text/plain,
191 # otherwise it would serve text/html and that might be unsafe.
191 # otherwise it would serve text/html and that might be unsafe.
192 # Note: underlying vcs library fakes text/plain mimetype if the
192 # Note: underlying vcs library fakes text/plain mimetype if the
193 # mimetype can not be determined and it thinks it is not
193 # mimetype can not be determined and it thinks it is not
194 # binary.This might lead to erroneous text display in some
194 # binary.This might lead to erroneous text display in some
195 # cases, but helps in other cases, like with text files
195 # cases, but helps in other cases, like with text files
196 # without extension.
196 # without extension.
197 mimetype, dispo = 'text/plain', 'inline'
197 mimetype, dispo = 'text/plain', 'inline'
198
198
199 if dispo == 'attachment':
199 if dispo == 'attachment':
200 dispo = 'attachment; filename=%s' % \
200 dispo = 'attachment; filename=%s' % \
201 f_path.split(os.sep)[-1].encode('utf8', 'replace')
201 f_path.split(os.sep)[-1].encode('utf8', 'replace')
202
202
203 response.content_disposition = dispo
203 response.content_disposition = dispo
204 response.content_type = mimetype
204 response.content_type = mimetype
205 return file_node.content
205 return file_node.content
206
206
207 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
207 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
208 'repository.admin')
208 'repository.admin')
209 def annotate(self, repo_name, revision, f_path):
209 def annotate(self, repo_name, revision, f_path):
210 c.cs = self.__get_cs_or_redirect(revision, repo_name)
210 c.cs = self.__get_cs_or_redirect(revision, repo_name)
211 c.file = self.__get_filenode_or_redirect(repo_name, c.cs, f_path)
211 c.file = self.__get_filenode_or_redirect(repo_name, c.cs, f_path)
212
212
213 c.file_history = self._get_node_history(c.cs, f_path)
213 c.file_history = self._get_node_history(c.cs, f_path)
214 c.f_path = f_path
214 c.f_path = f_path
215 return render('files/files_annotate.html')
215 return render('files/files_annotate.html')
216
216
217 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
217 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
218 def edit(self, repo_name, revision, f_path):
218 def edit(self, repo_name, revision, f_path):
219 r_post = request.POST
219 r_post = request.POST
220
220
221 c.cs = self.__get_cs_or_redirect(revision, repo_name)
221 c.cs = self.__get_cs_or_redirect(revision, repo_name)
222 c.file = self.__get_filenode_or_redirect(repo_name, c.cs, f_path)
222 c.file = self.__get_filenode_or_redirect(repo_name, c.cs, f_path)
223
223
224 if c.file.is_binary:
225 return redirect(url('files_home', repo_name=c.repo_name,
226 revision=c.cs.raw_id, f_path=f_path))
227
224 c.file_history = self._get_node_history(c.cs, f_path)
228 c.file_history = self._get_node_history(c.cs, f_path)
225 c.f_path = f_path
229 c.f_path = f_path
226
230
227 if r_post:
231 if r_post:
228
232
229 old_content = c.file.content
233 old_content = c.file.content
230 sl = old_content.splitlines(1)
234 sl = old_content.splitlines(1)
231 first_line = sl[0] if sl else ''
235 first_line = sl[0] if sl else ''
232 # modes: 0 - Unix, 1 - Mac, 2 - DOS
236 # modes: 0 - Unix, 1 - Mac, 2 - DOS
233 mode = detect_mode(first_line, 0)
237 mode = detect_mode(first_line, 0)
234 content = convert_line_endings(r_post.get('content'), mode)
238 content = convert_line_endings(r_post.get('content'), mode)
235
239
236 message = r_post.get('message') or (_('Edited %s via RhodeCode')
240 message = r_post.get('message') or (_('Edited %s via RhodeCode')
237 % (f_path))
241 % (f_path))
238 author = self.rhodecode_user.full_contact
242 author = self.rhodecode_user.full_contact
239
243
240 if content == old_content:
244 if content == old_content:
241 h.flash(_('No changes'),
245 h.flash(_('No changes'),
242 category='warning')
246 category='warning')
243 return redirect(url('changeset_home', repo_name=c.repo_name,
247 return redirect(url('changeset_home', repo_name=c.repo_name,
244 revision='tip'))
248 revision='tip'))
245
249
246 try:
250 try:
247 self.scm_model.commit_change(repo=c.rhodecode_repo,
251 self.scm_model.commit_change(repo=c.rhodecode_repo,
248 repo_name=repo_name, cs=c.cs,
252 repo_name=repo_name, cs=c.cs,
249 user=self.rhodecode_user,
253 user=self.rhodecode_user,
250 author=author, message=message,
254 author=author, message=message,
251 content=content, f_path=f_path)
255 content=content, f_path=f_path)
252 h.flash(_('Successfully committed to %s' % f_path),
256 h.flash(_('Successfully committed to %s' % f_path),
253 category='success')
257 category='success')
254
258
255 except Exception:
259 except Exception:
256 log.error(traceback.format_exc())
260 log.error(traceback.format_exc())
257 h.flash(_('Error occurred during commit'), category='error')
261 h.flash(_('Error occurred during commit'), category='error')
258 return redirect(url('changeset_home',
262 return redirect(url('changeset_home',
259 repo_name=c.repo_name, revision='tip'))
263 repo_name=c.repo_name, revision='tip'))
260
264
261 return render('files/files_edit.html')
265 return render('files/files_edit.html')
262
266
263 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
267 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
264 'repository.admin')
268 'repository.admin')
265 def archivefile(self, repo_name, fname):
269 def archivefile(self, repo_name, fname):
266
270
267 fileformat = None
271 fileformat = None
268 revision = None
272 revision = None
269 ext = None
273 ext = None
270
274
271 for a_type, ext_data in ARCHIVE_SPECS.items():
275 for a_type, ext_data in ARCHIVE_SPECS.items():
272 archive_spec = fname.split(ext_data[1])
276 archive_spec = fname.split(ext_data[1])
273 if len(archive_spec) == 2 and archive_spec[1] == '':
277 if len(archive_spec) == 2 and archive_spec[1] == '':
274 fileformat = a_type or ext_data[1]
278 fileformat = a_type or ext_data[1]
275 revision = archive_spec[0]
279 revision = archive_spec[0]
276 ext = ext_data[1]
280 ext = ext_data[1]
277
281
278 try:
282 try:
279 dbrepo = RepoModel().get_by_repo_name(repo_name)
283 dbrepo = RepoModel().get_by_repo_name(repo_name)
280 if dbrepo.enable_downloads is False:
284 if dbrepo.enable_downloads is False:
281 return _('downloads disabled')
285 return _('downloads disabled')
282
286
283 cs = c.rhodecode_repo.get_changeset(revision)
287 cs = c.rhodecode_repo.get_changeset(revision)
284 content_type = ARCHIVE_SPECS[fileformat][0]
288 content_type = ARCHIVE_SPECS[fileformat][0]
285 except ChangesetDoesNotExistError:
289 except ChangesetDoesNotExistError:
286 return _('Unknown revision %s') % revision
290 return _('Unknown revision %s') % revision
287 except EmptyRepositoryError:
291 except EmptyRepositoryError:
288 return _('Empty repository')
292 return _('Empty repository')
289 except (ImproperArchiveTypeError, KeyError):
293 except (ImproperArchiveTypeError, KeyError):
290 return _('Unknown archive type')
294 return _('Unknown archive type')
291
295
292 response.content_type = content_type
296 response.content_type = content_type
293 response.content_disposition = 'attachment; filename=%s-%s%s' \
297 response.content_disposition = 'attachment; filename=%s-%s%s' \
294 % (repo_name, revision, ext)
298 % (repo_name, revision, ext)
295
299
296 import tempfile
300 import tempfile
297 archive = tempfile.mkstemp()[1]
301 archive = tempfile.mkstemp()[1]
298 t = open(archive, 'wb')
302 t = open(archive, 'wb')
299 cs.fill_archive(stream=t, kind=fileformat)
303 cs.fill_archive(stream=t, kind=fileformat)
300
304
301 def get_chunked_archive(archive):
305 def get_chunked_archive(archive):
302 stream = open(archive, 'rb')
306 stream = open(archive, 'rb')
303 while True:
307 while True:
304 data = stream.read(4096)
308 data = stream.read(4096)
305 if not data:
309 if not data:
306 os.remove(archive)
310 os.remove(archive)
307 break
311 break
308 yield data
312 yield data
309
313
310 return get_chunked_archive(archive)
314 return get_chunked_archive(archive)
311
315
312 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
316 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
313 'repository.admin')
317 'repository.admin')
314 def diff(self, repo_name, f_path):
318 def diff(self, repo_name, f_path):
315 diff1 = request.GET.get('diff1')
319 diff1 = request.GET.get('diff1')
316 diff2 = request.GET.get('diff2')
320 diff2 = request.GET.get('diff2')
317 c.action = request.GET.get('diff')
321 c.action = request.GET.get('diff')
318 c.no_changes = diff1 == diff2
322 c.no_changes = diff1 == diff2
319 c.f_path = f_path
323 c.f_path = f_path
320 c.big_diff = False
324 c.big_diff = False
321
325
322 try:
326 try:
323 if diff1 not in ['', None, 'None', '0' * 12, '0' * 40]:
327 if diff1 not in ['', None, 'None', '0' * 12, '0' * 40]:
324 c.changeset_1 = c.rhodecode_repo.get_changeset(diff1)
328 c.changeset_1 = c.rhodecode_repo.get_changeset(diff1)
325 node1 = c.changeset_1.get_node(f_path)
329 node1 = c.changeset_1.get_node(f_path)
326 else:
330 else:
327 c.changeset_1 = EmptyChangeset(repo=c.rhodecode_repo)
331 c.changeset_1 = EmptyChangeset(repo=c.rhodecode_repo)
328 node1 = FileNode('.', '', changeset=c.changeset_1)
332 node1 = FileNode('.', '', changeset=c.changeset_1)
329
333
330 if diff2 not in ['', None, 'None', '0' * 12, '0' * 40]:
334 if diff2 not in ['', None, 'None', '0' * 12, '0' * 40]:
331 c.changeset_2 = c.rhodecode_repo.get_changeset(diff2)
335 c.changeset_2 = c.rhodecode_repo.get_changeset(diff2)
332 node2 = c.changeset_2.get_node(f_path)
336 node2 = c.changeset_2.get_node(f_path)
333 else:
337 else:
334 c.changeset_2 = EmptyChangeset(repo=c.rhodecode_repo)
338 c.changeset_2 = EmptyChangeset(repo=c.rhodecode_repo)
335 node2 = FileNode('.', '', changeset=c.changeset_2)
339 node2 = FileNode('.', '', changeset=c.changeset_2)
336 except RepositoryError:
340 except RepositoryError:
337 return redirect(url('files_home',
341 return redirect(url('files_home',
338 repo_name=c.repo_name, f_path=f_path))
342 repo_name=c.repo_name, f_path=f_path))
339
343
340 if c.action == 'download':
344 if c.action == 'download':
341 diff = differ.DiffProcessor(differ.get_gitdiff(node1, node2),
345 diff = differ.DiffProcessor(differ.get_gitdiff(node1, node2),
342 format='gitdiff')
346 format='gitdiff')
343
347
344 diff_name = '%s_vs_%s.diff' % (diff1, diff2)
348 diff_name = '%s_vs_%s.diff' % (diff1, diff2)
345 response.content_type = 'text/plain'
349 response.content_type = 'text/plain'
346 response.content_disposition = 'attachment; filename=%s' \
350 response.content_disposition = 'attachment; filename=%s' \
347 % diff_name
351 % diff_name
348 return diff.raw_diff()
352 return diff.raw_diff()
349
353
350 elif c.action == 'raw':
354 elif c.action == 'raw':
351 diff = differ.DiffProcessor(differ.get_gitdiff(node1, node2),
355 diff = differ.DiffProcessor(differ.get_gitdiff(node1, node2),
352 format='gitdiff')
356 format='gitdiff')
353 response.content_type = 'text/plain'
357 response.content_type = 'text/plain'
354 return diff.raw_diff()
358 return diff.raw_diff()
355
359
356 elif c.action == 'diff':
360 elif c.action == 'diff':
357 if node1.is_binary or node2.is_binary:
361 if node1.is_binary or node2.is_binary:
358 c.cur_diff = _('Binary file')
362 c.cur_diff = _('Binary file')
359 elif node1.size > self.cut_off_limit or \
363 elif node1.size > self.cut_off_limit or \
360 node2.size > self.cut_off_limit:
364 node2.size > self.cut_off_limit:
361 c.cur_diff = ''
365 c.cur_diff = ''
362 c.big_diff = True
366 c.big_diff = True
363 else:
367 else:
364 diff = differ.DiffProcessor(differ.get_gitdiff(node1, node2),
368 diff = differ.DiffProcessor(differ.get_gitdiff(node1, node2),
365 format='gitdiff')
369 format='gitdiff')
366 c.cur_diff = diff.as_html()
370 c.cur_diff = diff.as_html()
367 else:
371 else:
368
372
369 #default option
373 #default option
370 if node1.is_binary or node2.is_binary:
374 if node1.is_binary or node2.is_binary:
371 c.cur_diff = _('Binary file')
375 c.cur_diff = _('Binary file')
372 elif node1.size > self.cut_off_limit or \
376 elif node1.size > self.cut_off_limit or \
373 node2.size > self.cut_off_limit:
377 node2.size > self.cut_off_limit:
374 c.cur_diff = ''
378 c.cur_diff = ''
375 c.big_diff = True
379 c.big_diff = True
376
380
377 else:
381 else:
378 diff = differ.DiffProcessor(differ.get_gitdiff(node1, node2),
382 diff = differ.DiffProcessor(differ.get_gitdiff(node1, node2),
379 format='gitdiff')
383 format='gitdiff')
380 c.cur_diff = diff.as_html()
384 c.cur_diff = diff.as_html()
381
385
382 if not c.cur_diff and not c.big_diff:
386 if not c.cur_diff and not c.big_diff:
383 c.no_changes = True
387 c.no_changes = True
384 return render('files/file_diff.html')
388 return render('files/file_diff.html')
385
389
386 def _get_node_history(self, cs, f_path):
390 def _get_node_history(self, cs, f_path):
387 changesets = cs.get_file_history(f_path)
391 changesets = cs.get_file_history(f_path)
388 hist_l = []
392 hist_l = []
389
393
390 changesets_group = ([], _("Changesets"))
394 changesets_group = ([], _("Changesets"))
391 branches_group = ([], _("Branches"))
395 branches_group = ([], _("Branches"))
392 tags_group = ([], _("Tags"))
396 tags_group = ([], _("Tags"))
393
397
394 for chs in changesets:
398 for chs in changesets:
395 n_desc = 'r%s:%s' % (chs.revision, chs.short_id)
399 n_desc = 'r%s:%s' % (chs.revision, chs.short_id)
396 changesets_group[0].append((chs.raw_id, n_desc,))
400 changesets_group[0].append((chs.raw_id, n_desc,))
397
401
398 hist_l.append(changesets_group)
402 hist_l.append(changesets_group)
399
403
400 for name, chs in c.rhodecode_repo.branches.items():
404 for name, chs in c.rhodecode_repo.branches.items():
401 #chs = chs.split(':')[-1]
405 #chs = chs.split(':')[-1]
402 branches_group[0].append((chs, name),)
406 branches_group[0].append((chs, name),)
403 hist_l.append(branches_group)
407 hist_l.append(branches_group)
404
408
405 for name, chs in c.rhodecode_repo.tags.items():
409 for name, chs in c.rhodecode_repo.tags.items():
406 #chs = chs.split(':')[-1]
410 #chs = chs.split(':')[-1]
407 tags_group[0].append((chs, name),)
411 tags_group[0].append((chs, name),)
408 hist_l.append(tags_group)
412 hist_l.append(tags_group)
409
413
410 return hist_l
414 return hist_l
@@ -1,95 +1,97 b''
1 <%inherit file="/base/base.html"/>
1 <%inherit file="/base/base.html"/>
2
2
3 <%def name="title()">
3 <%def name="title()">
4 ${c.repo_name} ${_('File annotate')} - ${c.rhodecode_name}
4 ${c.repo_name} ${_('File annotate')} - ${c.rhodecode_name}
5 </%def>
5 </%def>
6
6
7 <%def name="breadcrumbs_links()">
7 <%def name="breadcrumbs_links()">
8 ${h.link_to(u'Home',h.url('/'))}
8 ${h.link_to(u'Home',h.url('/'))}
9 &raquo;
9 &raquo;
10 ${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
10 ${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
11 &raquo;
11 &raquo;
12 ${_('annotate')} @ R${c.cs.revision}:${h.short_id(c.cs.raw_id)}
12 ${_('annotate')} @ R${c.cs.revision}:${h.short_id(c.cs.raw_id)}
13 </%def>
13 </%def>
14
14
15 <%def name="page_nav()">
15 <%def name="page_nav()">
16 ${self.menu('files')}
16 ${self.menu('files')}
17 </%def>
17 </%def>
18 <%def name="main()">
18 <%def name="main()">
19 <div class="box">
19 <div class="box">
20 <!-- box / title -->
20 <!-- box / title -->
21 <div class="title">
21 <div class="title">
22 ${self.breadcrumbs()}
22 ${self.breadcrumbs()}
23 <ul class="links">
23 <ul class="links">
24 <li>
24 <li>
25 <span style="text-transform: uppercase;"><a href="#">${_('branch')}: ${c.cs.branch}</a></span>
25 <span style="text-transform: uppercase;"><a href="#">${_('branch')}: ${c.cs.branch}</a></span>
26 </li>
26 </li>
27 </ul>
27 </ul>
28 </div>
28 </div>
29 <div class="table">
29 <div class="table">
30 <div id="files_data">
30 <div id="files_data">
31 <h3 class="files_location">${_('Location')}: ${h.files_breadcrumbs(c.repo_name,c.cs.revision,c.file.path)}</h3>
31 <h3 class="files_location">${_('Location')}: ${h.files_breadcrumbs(c.repo_name,c.cs.revision,c.file.path)}</h3>
32 <dl class="overview">
32 <dl class="overview">
33 <dt>${_('Revision')}</dt>
33 <dt>${_('Revision')}</dt>
34 <dd>${h.link_to("r%s:%s" % (c.file.last_changeset.revision,h.short_id(c.file.last_changeset.raw_id)),
34 <dd>${h.link_to("r%s:%s" % (c.file.last_changeset.revision,h.short_id(c.file.last_changeset.raw_id)),
35 h.url('changeset_home',repo_name=c.repo_name,revision=c.file.last_changeset.raw_id))} </dd>
35 h.url('changeset_home',repo_name=c.repo_name,revision=c.file.last_changeset.raw_id))} </dd>
36 <dt>${_('Size')}</dt>
36 <dt>${_('Size')}</dt>
37 <dd>${h.format_byte_size(c.file.size,binary=True)}</dd>
37 <dd>${h.format_byte_size(c.file.size,binary=True)}</dd>
38 <dt>${_('Mimetype')}</dt>
38 <dt>${_('Mimetype')}</dt>
39 <dd>${c.file.mimetype}</dd>
39 <dd>${c.file.mimetype}</dd>
40 <dt>${_('Options')}</dt>
40 <dt>${_('Options')}</dt>
41 <dd>${h.link_to(_('show source'),
41 <dd>${h.link_to(_('show source'),
42 h.url('files_home',repo_name=c.repo_name,revision=c.cs.raw_id,f_path=c.f_path))}
42 h.url('files_home',repo_name=c.repo_name,revision=c.cs.raw_id,f_path=c.f_path))}
43 / ${h.link_to(_('show as raw'),
43 / ${h.link_to(_('show as raw'),
44 h.url('files_raw_home',repo_name=c.repo_name,revision=c.cs.raw_id,f_path=c.f_path))}
44 h.url('files_raw_home',repo_name=c.repo_name,revision=c.cs.raw_id,f_path=c.f_path))}
45 / ${h.link_to(_('download as raw'),
45 / ${h.link_to(_('download as raw'),
46 h.url('files_rawfile_home',repo_name=c.repo_name,revision=c.cs.raw_id,f_path=c.f_path))}
46 h.url('files_rawfile_home',repo_name=c.repo_name,revision=c.cs.raw_id,f_path=c.f_path))}
47 % if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name):
47 % if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name):
48 % if not c.file.is_binary:
48 / ${h.link_to(_('edit'),
49 / ${h.link_to(_('edit'),
49 h.url('files_edit_home',repo_name=c.repo_name,revision=c.cs.raw_id,f_path=c.f_path))}
50 h.url('files_edit_home',repo_name=c.repo_name,revision=c.cs.raw_id,f_path=c.f_path))}
50 % endif
51 % endif
52 % endif
51 </dd>
53 </dd>
52 <dt>${_('History')}</dt>
54 <dt>${_('History')}</dt>
53 <dd>
55 <dd>
54 <div>
56 <div>
55 ${h.form(h.url('files_diff_home',repo_name=c.repo_name,f_path=c.f_path),method='get')}
57 ${h.form(h.url('files_diff_home',repo_name=c.repo_name,f_path=c.f_path),method='get')}
56 ${h.hidden('diff2',c.file.last_changeset.raw_id)}
58 ${h.hidden('diff2',c.file.last_changeset.raw_id)}
57 ${h.select('diff1',c.file.last_changeset.raw_id,c.file_history)}
59 ${h.select('diff1',c.file.last_changeset.raw_id,c.file_history)}
58 ${h.submit('diff','diff to revision',class_="ui-button-small")}
60 ${h.submit('diff','diff to revision',class_="ui-button-small")}
59 ${h.submit('show_rev','show at revision',class_="ui-button-small")}
61 ${h.submit('show_rev','show at revision',class_="ui-button-small")}
60 ${h.end_form()}
62 ${h.end_form()}
61 </div>
63 </div>
62 </dd>
64 </dd>
63 </dl>
65 </dl>
64 <div id="body" class="codeblock">
66 <div id="body" class="codeblock">
65 <div class="code-header">
67 <div class="code-header">
66 <div class="revision">${c.file.name}@r${c.file.last_changeset.revision}:${h.short_id(c.file.last_changeset.raw_id)}</div>
68 <div class="revision">${c.file.name}@r${c.file.last_changeset.revision}:${h.short_id(c.file.last_changeset.raw_id)}</div>
67 <div class="commit">"${c.file.message}"</div>
69 <div class="commit">"${c.file.message}"</div>
68 </div>
70 </div>
69 <div class="code-body">
71 <div class="code-body">
70 %if c.file.is_binary:
72 %if c.file.is_binary:
71 ${_('Binary file (%s)') % c.file.mimetype}
73 ${_('Binary file (%s)') % c.file.mimetype}
72 %else:
74 %else:
73 % if c.file.size < c.cut_off_limit:
75 % if c.file.size < c.cut_off_limit:
74 ${h.pygmentize_annotation(c.repo_name,c.file,linenos=True,anchorlinenos=True,lineanchors='S',cssclass="code-highlight")}
76 ${h.pygmentize_annotation(c.repo_name,c.file,linenos=True,anchorlinenos=True,lineanchors='S',cssclass="code-highlight")}
75 %else:
77 %else:
76 ${_('File is to big to display')} ${h.link_to(_('show as raw'),
78 ${_('File is to big to display')} ${h.link_to(_('show as raw'),
77 h.url('files_raw_home',repo_name=c.repo_name,revision=c.cs.revision,f_path=c.f_path))}
79 h.url('files_raw_home',repo_name=c.repo_name,revision=c.cs.revision,f_path=c.f_path))}
78 %endif
80 %endif
79 <script type="text/javascript">
81 <script type="text/javascript">
80 YAHOO.util.Event.onDOMReady(function(){
82 YAHOO.util.Event.onDOMReady(function(){
81 YAHOO.util.Event.addListener('show_rev','click',function(e){
83 YAHOO.util.Event.addListener('show_rev','click',function(e){
82 YAHOO.util.Event.preventDefault(e);
84 YAHOO.util.Event.preventDefault(e);
83 var cs = YAHOO.util.Dom.get('diff1').value;
85 var cs = YAHOO.util.Dom.get('diff1').value;
84 var url = "${h.url('files_annotate_home',repo_name=c.repo_name,revision='__CS__',f_path=c.f_path)}".replace('__CS__',cs);
86 var url = "${h.url('files_annotate_home',repo_name=c.repo_name,revision='__CS__',f_path=c.f_path)}".replace('__CS__',cs);
85 window.location = url;
87 window.location = url;
86 });
88 });
87 });
89 });
88 </script>
90 </script>
89 %endif
91 %endif
90 </div>
92 </div>
91 </div>
93 </div>
92 </div>
94 </div>
93 </div>
95 </div>
94 </div>
96 </div>
95 </%def> No newline at end of file
97 </%def>
@@ -1,95 +1,97 b''
1 <dl>
1 <dl>
2 <dt>${_('Revision')}</dt>
2 <dt>${_('Revision')}</dt>
3 <dd>
3 <dd>
4 ${h.link_to("r%s:%s" % (c.files_list.last_changeset.revision,h.short_id(c.files_list.last_changeset.raw_id)),
4 ${h.link_to("r%s:%s" % (c.files_list.last_changeset.revision,h.short_id(c.files_list.last_changeset.raw_id)),
5 h.url('changeset_home',repo_name=c.repo_name,revision=c.files_list.last_changeset.raw_id))}
5 h.url('changeset_home',repo_name=c.repo_name,revision=c.files_list.last_changeset.raw_id))}
6 </dd>
6 </dd>
7 <dt>${_('Size')}</dt>
7 <dt>${_('Size')}</dt>
8 <dd>${h.format_byte_size(c.files_list.size,binary=True)}</dd>
8 <dd>${h.format_byte_size(c.files_list.size,binary=True)}</dd>
9 <dt>${_('Mimetype')}</dt>
9 <dt>${_('Mimetype')}</dt>
10 <dd>${c.files_list.mimetype}</dd>
10 <dd>${c.files_list.mimetype}</dd>
11 <dt>${_('Options')}</dt>
11 <dt>${_('Options')}</dt>
12 <dd>${h.link_to(_('show annotation'),
12 <dd>${h.link_to(_('show annotation'),
13 h.url('files_annotate_home',repo_name=c.repo_name,revision=c.changeset.raw_id,f_path=c.f_path))}
13 h.url('files_annotate_home',repo_name=c.repo_name,revision=c.changeset.raw_id,f_path=c.f_path))}
14 / ${h.link_to(_('show as raw'),
14 / ${h.link_to(_('show as raw'),
15 h.url('files_raw_home',repo_name=c.repo_name,revision=c.changeset.raw_id,f_path=c.f_path))}
15 h.url('files_raw_home',repo_name=c.repo_name,revision=c.changeset.raw_id,f_path=c.f_path))}
16 / ${h.link_to(_('download as raw'),
16 / ${h.link_to(_('download as raw'),
17 h.url('files_rawfile_home',repo_name=c.repo_name,revision=c.changeset.raw_id,f_path=c.f_path))}
17 h.url('files_rawfile_home',repo_name=c.repo_name,revision=c.changeset.raw_id,f_path=c.f_path))}
18 % if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name):
18 % if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name):
19 % if not c.files_list.is_binary:
19 / ${h.link_to(_('edit'),
20 / ${h.link_to(_('edit'),
20 h.url('files_edit_home',repo_name=c.repo_name,revision=c.changeset.raw_id,f_path=c.f_path))}
21 h.url('files_edit_home',repo_name=c.repo_name,revision=c.changeset.raw_id,f_path=c.f_path))}
21 % endif
22 % endif
23 % endif
22 </dd>
24 </dd>
23 <dt>${_('History')}</dt>
25 <dt>${_('History')}</dt>
24 <dd>
26 <dd>
25 <div>
27 <div>
26 ${h.form(h.url('files_diff_home',repo_name=c.repo_name,f_path=c.f_path),method='get')}
28 ${h.form(h.url('files_diff_home',repo_name=c.repo_name,f_path=c.f_path),method='get')}
27 ${h.hidden('diff2',c.files_list.last_changeset.raw_id)}
29 ${h.hidden('diff2',c.files_list.last_changeset.raw_id)}
28 ${h.select('diff1',c.files_list.last_changeset.raw_id,c.file_history)}
30 ${h.select('diff1',c.files_list.last_changeset.raw_id,c.file_history)}
29 ${h.submit('diff','diff to revision',class_="ui-button-small")}
31 ${h.submit('diff','diff to revision',class_="ui-button-small")}
30 ${h.submit('show_rev','show at revision',class_="ui-button-small")}
32 ${h.submit('show_rev','show at revision',class_="ui-button-small")}
31 ${h.end_form()}
33 ${h.end_form()}
32 </div>
34 </div>
33 </dd>
35 </dd>
34 </dl>
36 </dl>
35
37
36
38
37 <div id="body" class="codeblock">
39 <div id="body" class="codeblock">
38 <div class="code-header">
40 <div class="code-header">
39 <div class="revision">${c.files_list.name}@r${c.files_list.last_changeset.revision}:${h.short_id(c.files_list.last_changeset.raw_id)}</div>
41 <div class="revision">${c.files_list.name}@r${c.files_list.last_changeset.revision}:${h.short_id(c.files_list.last_changeset.raw_id)}</div>
40 <div class="commit">"${c.files_list.last_changeset.message}"</div>
42 <div class="commit">"${c.files_list.last_changeset.message}"</div>
41 </div>
43 </div>
42 <div class="code-body">
44 <div class="code-body">
43 %if c.files_list.is_binary:
45 %if c.files_list.is_binary:
44 ${_('Binary file (%s)') % c.files_list.mimetype}
46 ${_('Binary file (%s)') % c.files_list.mimetype}
45 %else:
47 %else:
46 % if c.files_list.size < c.cut_off_limit:
48 % if c.files_list.size < c.cut_off_limit:
47 ${h.pygmentize(c.files_list,linenos=True,anchorlinenos=True,lineanchors='L',cssclass="code-highlight")}
49 ${h.pygmentize(c.files_list,linenos=True,anchorlinenos=True,lineanchors='L',cssclass="code-highlight")}
48 %else:
50 %else:
49 ${_('File is to big to display')} ${h.link_to(_('show as raw'),
51 ${_('File is to big to display')} ${h.link_to(_('show as raw'),
50 h.url('files_raw_home',repo_name=c.repo_name,revision=c.changeset.raw_id,f_path=c.f_path))}
52 h.url('files_raw_home',repo_name=c.repo_name,revision=c.changeset.raw_id,f_path=c.f_path))}
51 %endif
53 %endif
52
54
53 <script type="text/javascript">
55 <script type="text/javascript">
54 function highlight_lines(lines){
56 function highlight_lines(lines){
55 for(pos in lines){
57 for(pos in lines){
56 YUD.setStyle('L'+lines[pos],'background-color','#FFFFBE');
58 YUD.setStyle('L'+lines[pos],'background-color','#FFFFBE');
57 }
59 }
58 }
60 }
59 page_highlights = location.href.substring(location.href.indexOf('#')+1).split('L');
61 page_highlights = location.href.substring(location.href.indexOf('#')+1).split('L');
60 if (page_highlights.length == 2){
62 if (page_highlights.length == 2){
61 highlight_ranges = page_highlights[1].split(",");
63 highlight_ranges = page_highlights[1].split(",");
62
64
63 var h_lines = [];
65 var h_lines = [];
64 for (pos in highlight_ranges){
66 for (pos in highlight_ranges){
65 var _range = highlight_ranges[pos].split('-');
67 var _range = highlight_ranges[pos].split('-');
66 if(_range.length == 2){
68 if(_range.length == 2){
67 var start = parseInt(_range[0]);
69 var start = parseInt(_range[0]);
68 var end = parseInt(_range[1]);
70 var end = parseInt(_range[1]);
69 if (start < end){
71 if (start < end){
70 for(var i=start;i<=end;i++){
72 for(var i=start;i<=end;i++){
71 h_lines.push(i);
73 h_lines.push(i);
72 }
74 }
73 }
75 }
74 }
76 }
75 else{
77 else{
76 h_lines.push(parseInt(highlight_ranges[pos]));
78 h_lines.push(parseInt(highlight_ranges[pos]));
77 }
79 }
78 }
80 }
79 highlight_lines(h_lines);
81 highlight_lines(h_lines);
80 }
82 }
81 </script>
83 </script>
82 %endif
84 %endif
83 </div>
85 </div>
84 </div>
86 </div>
85
87
86 <script type="text/javascript">
88 <script type="text/javascript">
87 YAHOO.util.Event.onDOMReady(function(){
89 YAHOO.util.Event.onDOMReady(function(){
88 YAHOO.util.Event.addListener('show_rev','click',function(e){
90 YAHOO.util.Event.addListener('show_rev','click',function(e){
89 YAHOO.util.Event.preventDefault(e);
91 YAHOO.util.Event.preventDefault(e);
90 var cs = YAHOO.util.Dom.get('diff1').value;
92 var cs = YAHOO.util.Dom.get('diff1').value;
91 var url = "${h.url('files_home',repo_name=c.repo_name,revision='__CS__',f_path=c.f_path)}".replace('__CS__',cs);
93 var url = "${h.url('files_home',repo_name=c.repo_name,revision='__CS__',f_path=c.f_path)}".replace('__CS__',cs);
92 window.location = url;
94 window.location = url;
93 });
95 });
94 });
96 });
95 </script> No newline at end of file
97 </script>
General Comments 0
You need to be logged in to leave comments. Login now