##// END OF EJS Templates
fixed archives headers, Thanks to Thomas Waldmann
marcink -
r1238:438524e8 default
parent child Browse files
Show More
@@ -1,279 +1,279
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 import os
25 import os
26 import tempfile
26 import tempfile
27 import logging
27 import logging
28 import rhodecode.lib.helpers as h
28 import rhodecode.lib.helpers as h
29
29
30 from mercurial import archival
30 from mercurial import archival
31
31
32 from pylons import request, response, session, tmpl_context as c, url
32 from pylons import request, response, session, tmpl_context as c, url
33 from pylons.i18n.translation import _
33 from pylons.i18n.translation import _
34 from pylons.controllers.util import redirect
34 from pylons.controllers.util import redirect
35
35
36 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
36 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
37 from rhodecode.lib.base import BaseController, render
37 from rhodecode.lib.base import BaseController, render
38 from rhodecode.lib.utils import EmptyChangeset
38 from rhodecode.lib.utils import EmptyChangeset
39 from rhodecode.model.scm import ScmModel
39 from rhodecode.model.scm import ScmModel
40
40
41 from vcs.exceptions import RepositoryError, ChangesetError, \
41 from vcs.exceptions import RepositoryError, ChangesetError, \
42 ChangesetDoesNotExistError, EmptyRepositoryError
42 ChangesetDoesNotExistError, EmptyRepositoryError
43 from vcs.nodes import FileNode
43 from vcs.nodes import FileNode
44 from vcs.utils import diffs as differ
44 from vcs.utils import diffs as differ
45
45
46 log = logging.getLogger(__name__)
46 log = logging.getLogger(__name__)
47
47
48
48
49 class FilesController(BaseController):
49 class FilesController(BaseController):
50
50
51 @LoginRequired()
51 @LoginRequired()
52 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
52 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
53 'repository.admin')
53 'repository.admin')
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 _repo = ScmModel().get_repo(c.repo_name)
67 _repo = ScmModel().get_repo(c.repo_name)
68 try:
68 try:
69 return _repo.get_changeset(rev)
69 return _repo.get_changeset(rev)
70 except EmptyRepositoryError, e:
70 except EmptyRepositoryError, e:
71 h.flash(_('There are no files yet'), category='warning')
71 h.flash(_('There are no files yet'), category='warning')
72 redirect(h.url('summary_home', repo_name=repo_name))
72 redirect(h.url('summary_home', repo_name=repo_name))
73
73
74 except RepositoryError, e:
74 except RepositoryError, e:
75 h.flash(str(e), category='warning')
75 h.flash(str(e), category='warning')
76 redirect(h.url('files_home', repo_name=repo_name, revision='tip'))
76 redirect(h.url('files_home', repo_name=repo_name, revision='tip'))
77
77
78 def index(self, repo_name, revision, f_path):
78 def index(self, repo_name, revision, f_path):
79 cs = self.__get_cs_or_redirect(revision, repo_name)
79 cs = self.__get_cs_or_redirect(revision, repo_name)
80 c.repo = ScmModel().get_repo(c.repo_name)
80 c.repo = ScmModel().get_repo(c.repo_name)
81
81
82 revision = request.POST.get('at_rev', None) or revision
82 revision = request.POST.get('at_rev', None) or revision
83
83
84 def get_next_rev(cur):
84 def get_next_rev(cur):
85 max_rev = len(c.repo.revisions) - 1
85 max_rev = len(c.repo.revisions) - 1
86 r = cur + 1
86 r = cur + 1
87 if r > max_rev:
87 if r > max_rev:
88 r = max_rev
88 r = max_rev
89 return r
89 return r
90
90
91 def get_prev_rev(cur):
91 def get_prev_rev(cur):
92 r = cur - 1
92 r = cur - 1
93 return r
93 return r
94
94
95 c.f_path = f_path
95 c.f_path = f_path
96 c.changeset = cs
96 c.changeset = cs
97 cur_rev = c.changeset.revision
97 cur_rev = c.changeset.revision
98 prev_rev = c.repo.get_changeset(get_prev_rev(cur_rev)).raw_id
98 prev_rev = c.repo.get_changeset(get_prev_rev(cur_rev)).raw_id
99 next_rev = c.repo.get_changeset(get_next_rev(cur_rev)).raw_id
99 next_rev = c.repo.get_changeset(get_next_rev(cur_rev)).raw_id
100
100
101 c.url_prev = url('files_home', repo_name=c.repo_name,
101 c.url_prev = url('files_home', repo_name=c.repo_name,
102 revision=prev_rev, f_path=f_path)
102 revision=prev_rev, f_path=f_path)
103 c.url_next = url('files_home', repo_name=c.repo_name,
103 c.url_next = url('files_home', repo_name=c.repo_name,
104 revision=next_rev, f_path=f_path)
104 revision=next_rev, f_path=f_path)
105
105
106 try:
106 try:
107 c.files_list = c.changeset.get_node(f_path)
107 c.files_list = c.changeset.get_node(f_path)
108 c.file_history = self._get_history(c.repo, c.files_list, f_path)
108 c.file_history = self._get_history(c.repo, c.files_list, f_path)
109 except RepositoryError, e:
109 except RepositoryError, e:
110 h.flash(str(e), category='warning')
110 h.flash(str(e), category='warning')
111 redirect(h.url('files_home', repo_name=repo_name,
111 redirect(h.url('files_home', repo_name=repo_name,
112 revision=revision))
112 revision=revision))
113
113
114
114
115 return render('files/files.html')
115 return render('files/files.html')
116
116
117 def rawfile(self, repo_name, revision, f_path):
117 def rawfile(self, repo_name, revision, f_path):
118 cs = self.__get_cs_or_redirect(revision, repo_name)
118 cs = self.__get_cs_or_redirect(revision, repo_name)
119 try:
119 try:
120 file_node = cs.get_node(f_path)
120 file_node = cs.get_node(f_path)
121 except RepositoryError, e:
121 except RepositoryError, e:
122 h.flash(str(e), category='warning')
122 h.flash(str(e), category='warning')
123 redirect(h.url('files_home', repo_name=repo_name,
123 redirect(h.url('files_home', repo_name=repo_name,
124 revision=cs.raw_id))
124 revision=cs.raw_id))
125
125
126 fname = f_path.split(os.sep)[-1].encode('utf8', 'replace')
126 fname = f_path.split(os.sep)[-1].encode('utf8', 'replace')
127
127
128 response.content_disposition = 'attachment; filename=%s' % fname
128 response.content_disposition = 'attachment; filename=%s' % fname
129 response.content_type = file_node.mimetype
129 response.content_type = file_node.mimetype
130 return file_node.content
130 return file_node.content
131
131
132 def raw(self, repo_name, revision, f_path):
132 def raw(self, repo_name, revision, f_path):
133 cs = self.__get_cs_or_redirect(revision, repo_name)
133 cs = self.__get_cs_or_redirect(revision, repo_name)
134 try:
134 try:
135 file_node = cs.get_node(f_path)
135 file_node = cs.get_node(f_path)
136 except RepositoryError, e:
136 except RepositoryError, e:
137 h.flash(str(e), category='warning')
137 h.flash(str(e), category='warning')
138 redirect(h.url('files_home', repo_name=repo_name,
138 redirect(h.url('files_home', repo_name=repo_name,
139 revision=cs.raw_id))
139 revision=cs.raw_id))
140
140
141 response.content_type = 'text/plain'
141 response.content_type = 'text/plain'
142 return file_node.content
142 return file_node.content
143
143
144 def annotate(self, repo_name, revision, f_path):
144 def annotate(self, repo_name, revision, f_path):
145 cs = self.__get_cs_or_redirect(revision, repo_name)
145 cs = self.__get_cs_or_redirect(revision, repo_name)
146 try:
146 try:
147 c.file = cs.get_node(f_path)
147 c.file = cs.get_node(f_path)
148 except RepositoryError, e:
148 except RepositoryError, e:
149 h.flash(str(e), category='warning')
149 h.flash(str(e), category='warning')
150 redirect(h.url('files_home', repo_name=repo_name, revision=cs.raw_id))
150 redirect(h.url('files_home', repo_name=repo_name, revision=cs.raw_id))
151
151
152 c.file_history = self._get_history(ScmModel().get_repo(c.repo_name), c.file, f_path)
152 c.file_history = self._get_history(ScmModel().get_repo(c.repo_name), c.file, f_path)
153 c.cs = cs
153 c.cs = cs
154 c.f_path = f_path
154 c.f_path = f_path
155
155
156 return render('files/files_annotate.html')
156 return render('files/files_annotate.html')
157
157
158 def archivefile(self, repo_name, revision, fileformat):
158 def archivefile(self, repo_name, revision, fileformat):
159 archive_specs = {
159 archive_specs = {
160 '.tar.bz2': ('application/x-tar', 'tbz2'),
160 '.tar.bz2': ('application/x-bzip2', 'tbz2'),
161 '.tar.gz': ('application/x-tar', 'tgz'),
161 '.tar.gz': ('application/x-gzip', 'tgz'),
162 '.zip': ('application/zip', 'zip'),
162 '.zip': ('application/zip', 'zip'),
163 }
163 }
164 if not archive_specs.has_key(fileformat):
164 if not archive_specs.has_key(fileformat):
165 return 'Unknown archive type %s' % fileformat
165 return 'Unknown archive type %s' % fileformat
166
166
167 def read_in_chunks(file_object, chunk_size=1024 * 40):
167 def read_in_chunks(file_object, chunk_size=1024 * 40):
168 """Lazy function (generator) to read a file piece by piece.
168 """Lazy function (generator) to read a file piece by piece.
169 Default chunk size: 40k."""
169 Default chunk size: 40k."""
170 while True:
170 while True:
171 data = file_object.read(chunk_size)
171 data = file_object.read(chunk_size)
172 if not data:
172 if not data:
173 break
173 break
174 yield data
174 yield data
175
175
176 archive = tempfile.TemporaryFile()
176 archive = tempfile.TemporaryFile()
177 repo = ScmModel().get_repo(repo_name).repo
177 repo = ScmModel().get_repo(repo_name).repo
178 fname = '%s-%s%s' % (repo_name, revision, fileformat)
178 fname = '%s-%s%s' % (repo_name, revision, fileformat)
179 archival.archive(repo, archive, revision, archive_specs[fileformat][1],
179 archival.archive(repo, archive, revision, archive_specs[fileformat][1],
180 prefix='%s-%s' % (repo_name, revision))
180 prefix='%s-%s' % (repo_name, revision))
181 response.content_type = archive_specs[fileformat][0]
181 response.content_type = archive_specs[fileformat][0]
182 response.content_disposition = 'attachment; filename=%s' % fname
182 response.content_disposition = 'attachment; filename=%s' % fname
183 archive.seek(0)
183 archive.seek(0)
184 return read_in_chunks(archive)
184 return read_in_chunks(archive)
185
185
186 def diff(self, repo_name, f_path):
186 def diff(self, repo_name, f_path):
187 hg_model = ScmModel()
187 hg_model = ScmModel()
188 diff1 = request.GET.get('diff1')
188 diff1 = request.GET.get('diff1')
189 diff2 = request.GET.get('diff2')
189 diff2 = request.GET.get('diff2')
190 c.action = request.GET.get('diff')
190 c.action = request.GET.get('diff')
191 c.no_changes = diff1 == diff2
191 c.no_changes = diff1 == diff2
192 c.f_path = f_path
192 c.f_path = f_path
193 c.repo = hg_model.get_repo(c.repo_name)
193 c.repo = hg_model.get_repo(c.repo_name)
194
194
195 try:
195 try:
196 if diff1 not in ['', None, 'None', '0' * 12, '0' * 40]:
196 if diff1 not in ['', None, 'None', '0' * 12, '0' * 40]:
197 c.changeset_1 = c.repo.get_changeset(diff1)
197 c.changeset_1 = c.repo.get_changeset(diff1)
198 node1 = c.changeset_1.get_node(f_path)
198 node1 = c.changeset_1.get_node(f_path)
199 else:
199 else:
200 c.changeset_1 = EmptyChangeset()
200 c.changeset_1 = EmptyChangeset()
201 node1 = FileNode('.', '', changeset=c.changeset_1)
201 node1 = FileNode('.', '', changeset=c.changeset_1)
202
202
203 if diff2 not in ['', None, 'None', '0' * 12, '0' * 40]:
203 if diff2 not in ['', None, 'None', '0' * 12, '0' * 40]:
204 c.changeset_2 = c.repo.get_changeset(diff2)
204 c.changeset_2 = c.repo.get_changeset(diff2)
205 node2 = c.changeset_2.get_node(f_path)
205 node2 = c.changeset_2.get_node(f_path)
206 else:
206 else:
207 c.changeset_2 = EmptyChangeset()
207 c.changeset_2 = EmptyChangeset()
208 node2 = FileNode('.', '', changeset=c.changeset_2)
208 node2 = FileNode('.', '', changeset=c.changeset_2)
209 except RepositoryError:
209 except RepositoryError:
210 return redirect(url('files_home',
210 return redirect(url('files_home',
211 repo_name=c.repo_name, f_path=f_path))
211 repo_name=c.repo_name, f_path=f_path))
212
212
213 f_udiff = differ.get_udiff(node1, node2)
213 f_udiff = differ.get_udiff(node1, node2)
214 diff = differ.DiffProcessor(f_udiff)
214 diff = differ.DiffProcessor(f_udiff)
215
215
216 if c.action == 'download':
216 if c.action == 'download':
217 diff_name = '%s_vs_%s.diff' % (diff1, diff2)
217 diff_name = '%s_vs_%s.diff' % (diff1, diff2)
218 response.content_type = 'text/plain'
218 response.content_type = 'text/plain'
219 response.content_disposition = 'attachment; filename=%s' \
219 response.content_disposition = 'attachment; filename=%s' \
220 % diff_name
220 % diff_name
221 if node1.is_binary or node2.is_binary:
221 if node1.is_binary or node2.is_binary:
222 return _('binary file changed')
222 return _('binary file changed')
223 return diff.raw_diff()
223 return diff.raw_diff()
224
224
225 elif c.action == 'raw':
225 elif c.action == 'raw':
226 response.content_type = 'text/plain'
226 response.content_type = 'text/plain'
227 if node1.is_binary or node2.is_binary:
227 if node1.is_binary or node2.is_binary:
228 return _('binary file changed')
228 return _('binary file changed')
229 return diff.raw_diff()
229 return diff.raw_diff()
230
230
231 elif c.action == 'diff':
231 elif c.action == 'diff':
232 if node1.is_binary or node2.is_binary:
232 if node1.is_binary or node2.is_binary:
233 c.cur_diff = _('Binary file')
233 c.cur_diff = _('Binary file')
234 elif node1.size > self.cut_off_limit or \
234 elif node1.size > self.cut_off_limit or \
235 node2.size > self.cut_off_limit:
235 node2.size > self.cut_off_limit:
236 c.cur_diff = _('Diff is too big to display')
236 c.cur_diff = _('Diff is too big to display')
237 else:
237 else:
238 c.cur_diff = diff.as_html()
238 c.cur_diff = diff.as_html()
239 else:
239 else:
240 #default option
240 #default option
241 if node1.size > self.cut_off_limit or node2.size > self.cut_off_limit:
241 if node1.size > self.cut_off_limit or node2.size > self.cut_off_limit:
242 c.cur_diff = _('Diff is to big to display')
242 c.cur_diff = _('Diff is to big to display')
243 elif node1.is_binary or node2.is_binary:
243 elif node1.is_binary or node2.is_binary:
244 c.cur_diff = _('Binary file')
244 c.cur_diff = _('Binary file')
245 else:
245 else:
246 c.cur_diff = diff.as_html()
246 c.cur_diff = diff.as_html()
247
247
248 if not c.cur_diff:
248 if not c.cur_diff:
249 c.no_changes = True
249 c.no_changes = True
250 return render('files/file_diff.html')
250 return render('files/file_diff.html')
251
251
252 def _get_history(self, repo, node, f_path):
252 def _get_history(self, repo, node, f_path):
253 from vcs.nodes import NodeKind
253 from vcs.nodes import NodeKind
254 if not node.kind is NodeKind.FILE:
254 if not node.kind is NodeKind.FILE:
255 return []
255 return []
256 changesets = node.history
256 changesets = node.history
257 hist_l = []
257 hist_l = []
258
258
259 changesets_group = ([], _("Changesets"))
259 changesets_group = ([], _("Changesets"))
260 branches_group = ([], _("Branches"))
260 branches_group = ([], _("Branches"))
261 tags_group = ([], _("Tags"))
261 tags_group = ([], _("Tags"))
262
262
263 for chs in changesets:
263 for chs in changesets:
264 n_desc = 'r%s:%s' % (chs.revision, chs.short_id)
264 n_desc = 'r%s:%s' % (chs.revision, chs.short_id)
265 changesets_group[0].append((chs.raw_id, n_desc,))
265 changesets_group[0].append((chs.raw_id, n_desc,))
266
266
267 hist_l.append(changesets_group)
267 hist_l.append(changesets_group)
268
268
269 for name, chs in c.repository_branches.items():
269 for name, chs in c.repository_branches.items():
270 #chs = chs.split(':')[-1]
270 #chs = chs.split(':')[-1]
271 branches_group[0].append((chs, name),)
271 branches_group[0].append((chs, name),)
272 hist_l.append(branches_group)
272 hist_l.append(branches_group)
273
273
274 for name, chs in c.repository_tags.items():
274 for name, chs in c.repository_tags.items():
275 #chs = chs.split(':')[-1]
275 #chs = chs.split(':')[-1]
276 tags_group[0].append((chs, name),)
276 tags_group[0].append((chs, name),)
277 hist_l.append(tags_group)
277 hist_l.append(tags_group)
278
278
279 return hist_l
279 return hist_l
General Comments 0
You need to be logged in to leave comments. Login now