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