##// END OF EJS Templates
added tags, and branches to file history select box, fixed annotation changeset errors crash on wrongly given revions
marcink -
r774:c47152c5 beta
parent child Browse files
Show More
@@ -1,218 +1,247 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # files controller for pylons
3 # files controller for pylons
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5
5
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
9 # of the License or (at your opinion) any later version of the license.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
19 # MA 02110-1301, USA.
20 """
20 """
21 Created on April 21, 2010
21 Created on April 21, 2010
22 files controller for pylons
22 files controller for pylons
23 @author: marcink
23 @author: marcink
24 """
24 """
25 from mercurial import archival
25 from mercurial import archival
26 from pylons import request, response, session, tmpl_context as c, url
26 from pylons import request, response, session, tmpl_context as c, url
27 from pylons.i18n.translation import _
27 from pylons.i18n.translation import _
28 from pylons.controllers.util import redirect
28 from pylons.controllers.util import redirect
29 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
29 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
30 from rhodecode.lib.base import BaseController, render
30 from rhodecode.lib.base import BaseController, render
31 from rhodecode.lib.utils import EmptyChangeset
31 from rhodecode.lib.utils import EmptyChangeset
32 from rhodecode.model.scm import ScmModel
32 from rhodecode.model.scm import ScmModel
33 from vcs.exceptions import RepositoryError, ChangesetError
33 from vcs.exceptions import RepositoryError, ChangesetError
34 from vcs.nodes import FileNode
34 from vcs.nodes import FileNode
35 from vcs.utils import diffs as differ
35 from vcs.utils import diffs as differ
36 import logging
36 import logging
37 import rhodecode.lib.helpers as h
37 import rhodecode.lib.helpers as h
38 import tempfile
38 import tempfile
39
39
40 log = logging.getLogger(__name__)
40 log = logging.getLogger(__name__)
41
41
42 class FilesController(BaseController):
42 class FilesController(BaseController):
43
43
44 @LoginRequired()
44 @LoginRequired()
45 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
45 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
46 'repository.admin')
46 'repository.admin')
47 def __before__(self):
47 def __before__(self):
48 super(FilesController, self).__before__()
48 super(FilesController, self).__before__()
49 c.file_size_limit = 250 * 1024 #limit of file size to display
49 c.file_size_limit = 250 * 1024 #limit of file size to display
50
50
51 def index(self, repo_name, revision, f_path):
51 def index(self, repo_name, revision, f_path):
52 hg_model = ScmModel()
52 hg_model = ScmModel()
53 c.repo = hg_model.get_repo(c.repo_name)
53 c.repo = hg_model.get_repo(c.repo_name)
54 revision = request.POST.get('at_rev', None) or revision
54 revision = request.POST.get('at_rev', None) or revision
55
55
56 def get_next_rev(cur):
56 def get_next_rev(cur):
57 max_rev = len(c.repo.revisions) - 1
57 max_rev = len(c.repo.revisions) - 1
58 r = cur + 1
58 r = cur + 1
59 if r > max_rev:
59 if r > max_rev:
60 r = max_rev
60 r = max_rev
61 return r
61 return r
62
62
63 def get_prev_rev(cur):
63 def get_prev_rev(cur):
64 r = cur - 1
64 r = cur - 1
65 return r
65 return r
66
66
67 c.f_path = f_path
67 c.f_path = f_path
68
68
69
69
70 try:
70 try:
71 c.changeset = c.repo.get_changeset(revision)
71 c.changeset = c.repo.get_changeset(revision)
72 cur_rev = c.changeset.revision
72 cur_rev = c.changeset.revision
73 prev_rev = c.repo.get_changeset(get_prev_rev(cur_rev)).raw_id
73 prev_rev = c.repo.get_changeset(get_prev_rev(cur_rev)).raw_id
74 next_rev = c.repo.get_changeset(get_next_rev(cur_rev)).raw_id
74 next_rev = c.repo.get_changeset(get_next_rev(cur_rev)).raw_id
75
75
76 c.url_prev = url('files_home', repo_name=c.repo_name,
76 c.url_prev = url('files_home', repo_name=c.repo_name,
77 revision=prev_rev, f_path=f_path)
77 revision=prev_rev, f_path=f_path)
78 c.url_next = url('files_home', repo_name=c.repo_name,
78 c.url_next = url('files_home', repo_name=c.repo_name,
79 revision=next_rev, f_path=f_path)
79 revision=next_rev, f_path=f_path)
80
80
81 try:
81 try:
82 c.files_list = c.changeset.get_node(f_path)
82 c.files_list = c.changeset.get_node(f_path)
83 c.file_history = self._get_history(c.repo, c.files_list, f_path)
83 c.file_history = self._get_history(c.repo, c.files_list, f_path)
84
85 except RepositoryError, e:
84 except RepositoryError, e:
86 h.flash(str(e), category='warning')
85 h.flash(str(e), category='warning')
87 redirect(h.url('files_home', repo_name=repo_name, revision=revision))
86 redirect(h.url('files_home', repo_name=repo_name, revision=revision))
88
87
89 except RepositoryError, e:
88 except RepositoryError, e:
90 h.flash(str(e), category='warning')
89 h.flash(str(e), category='warning')
91 redirect(h.url('files_home', repo_name=repo_name, revision='tip'))
90 redirect(h.url('files_home', repo_name=repo_name, revision='tip'))
92
91
93
92
94
93
95 return render('files/files.html')
94 return render('files/files.html')
96
95
97 def rawfile(self, repo_name, revision, f_path):
96 def rawfile(self, repo_name, revision, f_path):
98 hg_model = ScmModel()
97 hg_model = ScmModel()
99 c.repo = hg_model.get_repo(c.repo_name)
98 c.repo = hg_model.get_repo(c.repo_name)
100 file_node = c.repo.get_changeset(revision).get_node(f_path)
99 file_node = c.repo.get_changeset(revision).get_node(f_path)
101 response.content_type = file_node.mimetype
100 response.content_type = file_node.mimetype
102 response.content_disposition = 'attachment; filename=%s' \
101 response.content_disposition = 'attachment; filename=%s' \
103 % f_path.split('/')[-1]
102 % f_path.split('/')[-1]
104 return file_node.content
103 return file_node.content
105
104
106 def raw(self, repo_name, revision, f_path):
105 def raw(self, repo_name, revision, f_path):
107 hg_model = ScmModel()
106 hg_model = ScmModel()
108 c.repo = hg_model.get_repo(c.repo_name)
107 c.repo = hg_model.get_repo(c.repo_name)
109 file_node = c.repo.get_changeset(revision).get_node(f_path)
108 file_node = c.repo.get_changeset(revision).get_node(f_path)
110 response.content_type = 'text/plain'
109 response.content_type = 'text/plain'
111
110
112 return file_node.content
111 return file_node.content
113
112
114 def annotate(self, repo_name, revision, f_path):
113 def annotate(self, repo_name, revision, f_path):
115 hg_model = ScmModel()
114 hg_model = ScmModel()
116 c.repo = hg_model.get_repo(c.repo_name)
115 c.repo = hg_model.get_repo(c.repo_name)
117 c.cs = c.repo.get_changeset(revision)
116
118 c.file = c.cs.get_node(f_path)
117 try:
118 c.cs = c.repo.get_changeset(revision)
119 c.file = c.cs.get_node(f_path)
120 except RepositoryError, e:
121 h.flash(str(e), category='warning')
122 redirect(h.url('files_home', repo_name=repo_name, revision=revision))
123
119 c.file_history = self._get_history(c.repo, c.file, f_path)
124 c.file_history = self._get_history(c.repo, c.file, f_path)
120
125
121 c.f_path = f_path
126 c.f_path = f_path
122
127
123 return render('files/files_annotate.html')
128 return render('files/files_annotate.html')
124
129
125 def archivefile(self, repo_name, revision, fileformat):
130 def archivefile(self, repo_name, revision, fileformat):
126 archive_specs = {
131 archive_specs = {
127 '.tar.bz2': ('application/x-tar', 'tbz2'),
132 '.tar.bz2': ('application/x-tar', 'tbz2'),
128 '.tar.gz': ('application/x-tar', 'tgz'),
133 '.tar.gz': ('application/x-tar', 'tgz'),
129 '.zip': ('application/zip', 'zip'),
134 '.zip': ('application/zip', 'zip'),
130 }
135 }
131 if not archive_specs.has_key(fileformat):
136 if not archive_specs.has_key(fileformat):
132 return 'Unknown archive type %s' % fileformat
137 return 'Unknown archive type %s' % fileformat
133
138
134 def read_in_chunks(file_object, chunk_size=1024 * 40):
139 def read_in_chunks(file_object, chunk_size=1024 * 40):
135 """Lazy function (generator) to read a file piece by piece.
140 """Lazy function (generator) to read a file piece by piece.
136 Default chunk size: 40k."""
141 Default chunk size: 40k."""
137 while True:
142 while True:
138 data = file_object.read(chunk_size)
143 data = file_object.read(chunk_size)
139 if not data:
144 if not data:
140 break
145 break
141 yield data
146 yield data
142
147
143 archive = tempfile.TemporaryFile()
148 archive = tempfile.TemporaryFile()
144 repo = ScmModel().get_repo(repo_name).repo
149 repo = ScmModel().get_repo(repo_name).repo
145 fname = '%s-%s%s' % (repo_name, revision, fileformat)
150 fname = '%s-%s%s' % (repo_name, revision, fileformat)
146 archival.archive(repo, archive, revision, archive_specs[fileformat][1],
151 archival.archive(repo, archive, revision, archive_specs[fileformat][1],
147 prefix='%s-%s' % (repo_name, revision))
152 prefix='%s-%s' % (repo_name, revision))
148 response.content_type = archive_specs[fileformat][0]
153 response.content_type = archive_specs[fileformat][0]
149 response.content_disposition = 'attachment; filename=%s' % fname
154 response.content_disposition = 'attachment; filename=%s' % fname
150 archive.seek(0)
155 archive.seek(0)
151 return read_in_chunks(archive)
156 return read_in_chunks(archive)
152
157
153 def diff(self, repo_name, f_path):
158 def diff(self, repo_name, f_path):
154 hg_model = ScmModel()
159 hg_model = ScmModel()
155 diff1 = request.GET.get('diff1')
160 diff1 = request.GET.get('diff1')
156 diff2 = request.GET.get('diff2')
161 diff2 = request.GET.get('diff2')
157 c.action = request.GET.get('diff')
162 c.action = request.GET.get('diff')
158 c.no_changes = diff1 == diff2
163 c.no_changes = diff1 == diff2
159 c.f_path = f_path
164 c.f_path = f_path
160 c.repo = hg_model.get_repo(c.repo_name)
165 c.repo = hg_model.get_repo(c.repo_name)
161
166
162 try:
167 try:
163 if diff1 not in ['', None, 'None', '0' * 12, '0' * 40]:
168 if diff1 not in ['', None, 'None', '0' * 12, '0' * 40]:
164 c.changeset_1 = c.repo.get_changeset(diff1)
169 c.changeset_1 = c.repo.get_changeset(diff1)
165 node1 = c.changeset_1.get_node(f_path)
170 node1 = c.changeset_1.get_node(f_path)
166 else:
171 else:
167 c.changeset_1 = EmptyChangeset()
172 c.changeset_1 = EmptyChangeset()
168 node1 = FileNode('.', '', changeset=c.changeset_1)
173 node1 = FileNode('.', '', changeset=c.changeset_1)
169
174
170 if diff2 not in ['', None, 'None', '0' * 12, '0' * 40]:
175 if diff2 not in ['', None, 'None', '0' * 12, '0' * 40]:
171 c.changeset_2 = c.repo.get_changeset(diff2)
176 c.changeset_2 = c.repo.get_changeset(diff2)
172 node2 = c.changeset_2.get_node(f_path)
177 node2 = c.changeset_2.get_node(f_path)
173 else:
178 else:
174 c.changeset_2 = EmptyChangeset()
179 c.changeset_2 = EmptyChangeset()
175 node2 = FileNode('.', '', changeset=c.changeset_2)
180 node2 = FileNode('.', '', changeset=c.changeset_2)
176 except RepositoryError:
181 except RepositoryError:
177 return redirect(url('files_home',
182 return redirect(url('files_home',
178 repo_name=c.repo_name, f_path=f_path))
183 repo_name=c.repo_name, f_path=f_path))
179
184
180 f_udiff = differ.get_udiff(node1, node2)
185 f_udiff = differ.get_udiff(node1, node2)
181 diff = differ.DiffProcessor(f_udiff)
186 diff = differ.DiffProcessor(f_udiff)
182
187
183 if c.action == 'download':
188 if c.action == 'download':
184 diff_name = '%s_vs_%s.diff' % (diff1, diff2)
189 diff_name = '%s_vs_%s.diff' % (diff1, diff2)
185 response.content_type = 'text/plain'
190 response.content_type = 'text/plain'
186 response.content_disposition = 'attachment; filename=%s' \
191 response.content_disposition = 'attachment; filename=%s' \
187 % diff_name
192 % diff_name
188 return diff.raw_diff()
193 return diff.raw_diff()
189
194
190 elif c.action == 'raw':
195 elif c.action == 'raw':
191 response.content_type = 'text/plain'
196 response.content_type = 'text/plain'
192 return diff.raw_diff()
197 return diff.raw_diff()
193
198
194 elif c.action == 'diff':
199 elif c.action == 'diff':
195 if node1.size > c.file_size_limit or node2.size > c.file_size_limit:
200 if node1.size > c.file_size_limit or node2.size > c.file_size_limit:
196 c.cur_diff = _('Diff is to big to display')
201 c.cur_diff = _('Diff is to big to display')
197 else:
202 else:
198 c.cur_diff = diff.as_html()
203 c.cur_diff = diff.as_html()
199 else:
204 else:
200 #default option
205 #default option
201 if node1.size > c.file_size_limit or node2.size > c.file_size_limit:
206 if node1.size > c.file_size_limit or node2.size > c.file_size_limit:
202 c.cur_diff = _('Diff is to big to display')
207 c.cur_diff = _('Diff is to big to display')
203 else:
208 else:
204 c.cur_diff = diff.as_html()
209 c.cur_diff = diff.as_html()
205
210
206 if not c.cur_diff: c.no_changes = True
211 if not c.cur_diff: c.no_changes = True
207 return render('files/file_diff.html')
212 return render('files/file_diff.html')
208
213
209 def _get_history(self, repo, node, f_path):
214 def _get_history(self, repo, node, f_path):
210 from vcs.nodes import NodeKind
215 from vcs.nodes import NodeKind
211 if not node.kind is NodeKind.FILE:
216 if not node.kind is NodeKind.FILE:
212 return []
217 return []
213 changesets = node.history
218 changesets = node.history
214 hist_l = []
219 hist_l = []
220
221 changesets_group = ([], _("Changesets"))
222 branches_group = ([], _("Branches"))
223 tags_group = ([], _("Tags"))
224
215 for chs in changesets:
225 for chs in changesets:
216 n_desc = 'r%s:%s' % (chs.revision, chs.short_id)
226 n_desc = 'r%s:%s' % (chs.revision, chs.short_id)
217 hist_l.append((chs.raw_id, n_desc,))
227 changesets_group[0].append((chs.raw_id, n_desc,))
228
229 hist_l.append(changesets_group)
230
231 for name, chs in c.repository_branches.items():
232 #chs = chs.split(':')[-1]
233 branches_group[0].append((chs, name),)
234 hist_l.append(branches_group)
235
236 for name, chs in c.repository_tags.items():
237 #chs = chs.split(':')[-1]
238 tags_group[0].append((chs, name),)
239 hist_l.append(tags_group)
240
218 return hist_l
241 return hist_l
242
243 # [
244 # ([("u1", "User1"), ("u2", "User2")], "Users"),
245 # ([("g1", "Group1"), ("g2", "Group2")], "Groups")
246 # ]
247
General Comments 0
You need to be logged in to leave comments. Login now