##// END OF EJS Templates
Added support for ascendent characters for inMemoryCommit
marcink -
r1306:6e1d2450 beta
parent child Browse files
Show More
@@ -1,396 +1,405 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 28 import mimetypes
29 29 import traceback
30 30
31 31 from pylons import request, response, session, tmpl_context as c, url
32 32 from pylons.i18n.translation import _
33 33 from pylons.controllers.util import redirect
34 34
35 35 from vcs.backends import ARCHIVE_SPECS
36 36 from vcs.exceptions import RepositoryError, ChangesetDoesNotExistError, \
37 37 EmptyRepositoryError, ImproperArchiveTypeError, VCSError
38 38 from vcs.nodes import FileNode, NodeKind
39 39 from vcs.utils import diffs as differ
40 40
41 41 from rhodecode.lib import convert_line_endings, detect_mode
42 42 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
43 43 from rhodecode.lib.base import BaseRepoController, render
44 44 from rhodecode.lib.utils import EmptyChangeset
45 45 import rhodecode.lib.helpers as h
46 46 from rhodecode.model.repo import RepoModel
47 47
48 48 log = logging.getLogger(__name__)
49 49
50 50
51 51 class FilesController(BaseRepoController):
52 52
53 53 @LoginRequired()
54 54 def __before__(self):
55 55 super(FilesController, self).__before__()
56 56 c.cut_off_limit = self.cut_off_limit
57 57
58 58 def __get_cs_or_redirect(self, rev, repo_name):
59 59 """
60 60 Safe way to get changeset if error occur it redirects to tip with
61 61 proper message
62 62
63 63 :param rev: revision to fetch
64 64 :param repo_name: repo name to redirect after
65 65 """
66 66
67 67 try:
68 68 return c.rhodecode_repo.get_changeset(rev)
69 69 except EmptyRepositoryError, e:
70 70 h.flash(_('There are no files yet'), category='warning')
71 71 redirect(h.url('summary_home', repo_name=repo_name))
72 72
73 73 except RepositoryError, e:
74 74 h.flash(str(e), category='warning')
75 75 redirect(h.url('files_home', repo_name=repo_name, revision='tip'))
76 76
77 77 def __get_filenode_or_redirect(self, repo_name, cs, path):
78 78 """
79 79 Returns file_node, if error occurs or given path is directory,
80 80 it'll redirect to top level path
81 81
82 82 :param repo_name: repo_name
83 83 :param cs: given changeset
84 84 :param path: path to lookup
85 85 """
86 86
87 87 try:
88 88 file_node = cs.get_node(path)
89 89 if file_node.is_dir():
90 90 raise RepositoryError('given path is a directory')
91 91 except RepositoryError, e:
92 92 h.flash(str(e), category='warning')
93 93 redirect(h.url('files_home', repo_name=repo_name,
94 94 revision=cs.raw_id))
95 95
96 96 return file_node
97 97
98 98 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
99 99 'repository.admin')
100 100 def index(self, repo_name, revision, f_path):
101 101 #reditect to given revision from form if given
102 102 post_revision = request.POST.get('at_rev', None)
103 103 if post_revision:
104 104 cs = self.__get_cs_or_redirect(post_revision, repo_name)
105 105 redirect(url('files_home', repo_name=c.repo_name,
106 106 revision=cs.raw_id, f_path=f_path))
107 107
108 108 c.changeset = self.__get_cs_or_redirect(revision, repo_name)
109 109 c.branch = request.GET.get('branch', None)
110 110 c.f_path = f_path
111 111
112 112 cur_rev = c.changeset.revision
113 113
114 114 #prev link
115 115 try:
116 116 prev_rev = c.rhodecode_repo.get_changeset(cur_rev).prev(c.branch)
117 117 c.url_prev = url('files_home', repo_name=c.repo_name,
118 118 revision=prev_rev.raw_id, f_path=f_path)
119 119 if c.branch:
120 120 c.url_prev += '?branch=%s' % c.branch
121 121 except (ChangesetDoesNotExistError, VCSError):
122 122 c.url_prev = '#'
123 123
124 124 #next link
125 125 try:
126 126 next_rev = c.rhodecode_repo.get_changeset(cur_rev).next(c.branch)
127 127 c.url_next = url('files_home', repo_name=c.repo_name,
128 128 revision=next_rev.raw_id, f_path=f_path)
129 129 if c.branch:
130 130 c.url_next += '?branch=%s' % c.branch
131 131 except (ChangesetDoesNotExistError, VCSError):
132 132 c.url_next = '#'
133 133
134 134 #files or dirs
135 135 try:
136 136 c.files_list = c.changeset.get_node(f_path)
137 137
138 138 if c.files_list.is_file():
139 139 c.file_history = self._get_node_history(c.changeset, f_path)
140 140 else:
141 141 c.file_history = []
142 142 except RepositoryError, e:
143 143 h.flash(str(e), category='warning')
144 144 redirect(h.url('files_home', repo_name=repo_name,
145 145 revision=revision))
146 146
147 147 return render('files/files.html')
148 148
149 149 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
150 150 'repository.admin')
151 151 def rawfile(self, repo_name, revision, f_path):
152 152 cs = self.__get_cs_or_redirect(revision, repo_name)
153 153 file_node = self.__get_filenode_or_redirect(repo_name, cs, f_path)
154 154
155 155 response.content_disposition = 'attachment; filename=%s' % \
156 156 f_path.split(os.sep)[-1].encode('utf8', 'replace')
157 157
158 158 response.content_type = file_node.mimetype
159 159 return file_node.content
160 160
161 161 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
162 162 'repository.admin')
163 163 def raw(self, repo_name, revision, f_path):
164 164 cs = self.__get_cs_or_redirect(revision, repo_name)
165 165 file_node = self.__get_filenode_or_redirect(repo_name, cs, f_path)
166 166
167 167 raw_mimetype_mapping = {
168 168 # map original mimetype to a mimetype used for "show as raw"
169 169 # you can also provide a content-disposition to override the
170 170 # default "attachment" disposition.
171 171 # orig_type: (new_type, new_dispo)
172 172
173 173 # show images inline:
174 174 'image/x-icon': ('image/x-icon', 'inline'),
175 175 'image/png': ('image/png', 'inline'),
176 176 'image/gif': ('image/gif', 'inline'),
177 177 'image/jpeg': ('image/jpeg', 'inline'),
178 178 'image/svg+xml': ('image/svg+xml', 'inline'),
179 179 }
180 180
181 181 mimetype = file_node.mimetype
182 182 try:
183 183 mimetype, dispo = raw_mimetype_mapping[mimetype]
184 184 except KeyError:
185 185 # we don't know anything special about this, handle it safely
186 186 if file_node.is_binary:
187 187 # do same as download raw for binary files
188 188 mimetype, dispo = 'application/octet-stream', 'attachment'
189 189 else:
190 190 # do not just use the original mimetype, but force text/plain,
191 191 # otherwise it would serve text/html and that might be unsafe.
192 192 # Note: underlying vcs library fakes text/plain mimetype if the
193 193 # mimetype can not be determined and it thinks it is not
194 194 # binary.This might lead to erroneous text display in some
195 195 # cases, but helps in other cases, like with text files
196 196 # without extension.
197 197 mimetype, dispo = 'text/plain', 'inline'
198 198
199 199 if dispo == 'attachment':
200 200 dispo = 'attachment; filename=%s' % \
201 201 f_path.split(os.sep)[-1].encode('utf8', 'replace')
202 202
203 203 response.content_disposition = dispo
204 204 response.content_type = mimetype
205 205 return file_node.content
206 206
207 207 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
208 208 'repository.admin')
209 209 def annotate(self, repo_name, revision, f_path):
210 210 c.cs = self.__get_cs_or_redirect(revision, repo_name)
211 211 c.file = self.__get_filenode_or_redirect(repo_name, c.cs, f_path)
212 212
213 213 c.file_history = self._get_node_history(c.cs, f_path)
214 214 c.f_path = f_path
215 215 return render('files/files_annotate.html')
216 216
217 217 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
218 218 def edit(self, repo_name, revision, f_path):
219 219 r_post = request.POST
220 220
221 221 if c.rhodecode_repo.alias == 'hg':
222 222 from vcs.backends.hg import MercurialInMemoryChangeset as IMC
223 223 elif c.rhodecode_repo.alias == 'git':
224 224 from vcs.backends.git import GitInMemoryChangeset as IMC
225 225
226 226 c.cs = self.__get_cs_or_redirect(revision, repo_name)
227 227 c.file = self.__get_filenode_or_redirect(repo_name, c.cs, f_path)
228 228
229 229 c.file_history = self._get_node_history(c.cs, f_path)
230 230 c.f_path = f_path
231 231
232 232 if r_post:
233 233
234 234 old_content = c.file.content
235 sl = old_content.splitlines(1)
236 first_line = sl[0] if sl else ''
235 237 # modes: 0 - Unix, 1 - Mac, 2 - DOS
236 mode = detect_mode(old_content.splitlines(1)[0], 0)
238 mode = detect_mode(first_line, 0)
237 239 content = convert_line_endings(r_post.get('content'), mode)
240
238 241 message = r_post.get('message') or (_('Edited %s via RhodeCode')
239 242 % (f_path))
240 243
241 244 if content == old_content:
242 245 h.flash(_('No changes'),
243 246 category='warning')
244 return redirect(url('changeset_home',
245 repo_name=c.repo_name, revision='tip'))
247 return redirect(url('changeset_home', repo_name=c.repo_name,
248 revision='tip'))
249
246 250 try:
247 new_node = FileNode(f_path, content)
251 content = content.encode('utf8')
252 message = message.encode('utf8')
253 path = f_path.encode('utf8')
254 author = self.rhodecode_user.full_contact.encode('utf8')
248 255 m = IMC(c.rhodecode_repo)
249 m.change(new_node)
256 m.change(FileNode(path, content))
250 257 m.commit(message=message,
251 author=self.rhodecode_user.full_contact,
258 author=author,
252 259 parents=[c.cs], branch=c.cs.branch)
253 260 h.flash(_('Successfully committed to %s' % f_path),
254 261 category='success')
262
255 263 except Exception, e:
256 264 log.error(traceback.format_exc())
257 265 h.flash(_('Error occurred during commit'), category='error')
266 raise
258 267 return redirect(url('changeset_home',
259 268 repo_name=c.repo_name, revision='tip'))
260 269
261 270 return render('files/files_edit.html')
262 271
263 272 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
264 273 'repository.admin')
265 274 def archivefile(self, repo_name, fname):
266 275
267 276 fileformat = None
268 277 revision = None
269 278 ext = None
270 279
271 280 for a_type, ext_data in ARCHIVE_SPECS.items():
272 281 archive_spec = fname.split(ext_data[1])
273 282 if len(archive_spec) == 2 and archive_spec[1] == '':
274 283 fileformat = a_type or ext_data[1]
275 284 revision = archive_spec[0]
276 285 ext = ext_data[1]
277 286
278 287 try:
279 288 dbrepo = RepoModel().get_by_repo_name(repo_name)
280 289 if dbrepo.enable_downloads is False:
281 290 return _('downloads disabled')
282 291
283 292 cs = c.rhodecode_repo.get_changeset(revision)
284 293 content_type = ARCHIVE_SPECS[fileformat][0]
285 294 except ChangesetDoesNotExistError:
286 295 return _('Unknown revision %s') % revision
287 296 except EmptyRepositoryError:
288 297 return _('Empty repository')
289 298 except (ImproperArchiveTypeError, KeyError):
290 299 return _('Unknown archive type')
291 300
292 301 response.content_type = content_type
293 302 response.content_disposition = 'attachment; filename=%s-%s%s' \
294 303 % (repo_name, revision, ext)
295 304
296 305 return cs.get_chunked_archive(stream=None, kind=fileformat)
297 306
298 307 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
299 308 'repository.admin')
300 309 def diff(self, repo_name, f_path):
301 310 diff1 = request.GET.get('diff1')
302 311 diff2 = request.GET.get('diff2')
303 312 c.action = request.GET.get('diff')
304 313 c.no_changes = diff1 == diff2
305 314 c.f_path = f_path
306 315 c.big_diff = False
307 316
308 317 try:
309 318 if diff1 not in ['', None, 'None', '0' * 12, '0' * 40]:
310 319 c.changeset_1 = c.rhodecode_repo.get_changeset(diff1)
311 320 node1 = c.changeset_1.get_node(f_path)
312 321 else:
313 322 c.changeset_1 = EmptyChangeset(repo=c.rhodecode_repo)
314 323 node1 = FileNode('.', '', changeset=c.changeset_1)
315 324
316 325 if diff2 not in ['', None, 'None', '0' * 12, '0' * 40]:
317 326 c.changeset_2 = c.rhodecode_repo.get_changeset(diff2)
318 327 node2 = c.changeset_2.get_node(f_path)
319 328 else:
320 329 c.changeset_2 = EmptyChangeset(repo=c.rhodecode_repo)
321 330 node2 = FileNode('.', '', changeset=c.changeset_2)
322 331 except RepositoryError:
323 332 return redirect(url('files_home',
324 333 repo_name=c.repo_name, f_path=f_path))
325 334
326 335 if c.action == 'download':
327 336 diff = differ.DiffProcessor(differ.get_gitdiff(node1, node2),
328 337 format='gitdiff')
329 338
330 339 diff_name = '%s_vs_%s.diff' % (diff1, diff2)
331 340 response.content_type = 'text/plain'
332 341 response.content_disposition = 'attachment; filename=%s' \
333 342 % diff_name
334 343 return diff.raw_diff()
335 344
336 345 elif c.action == 'raw':
337 346 diff = differ.DiffProcessor(differ.get_gitdiff(node1, node2),
338 347 format='gitdiff')
339 348 response.content_type = 'text/plain'
340 349 return diff.raw_diff()
341 350
342 351 elif c.action == 'diff':
343 352 if node1.is_binary or node2.is_binary:
344 353 c.cur_diff = _('Binary file')
345 354 elif node1.size > self.cut_off_limit or \
346 355 node2.size > self.cut_off_limit:
347 356 c.cur_diff = ''
348 357 c.big_diff = True
349 358 else:
350 359 diff = differ.DiffProcessor(differ.get_gitdiff(node1, node2),
351 360 format='gitdiff')
352 361 c.cur_diff = diff.as_html()
353 362 else:
354 363
355 364 #default option
356 365 if node1.is_binary or node2.is_binary:
357 366 c.cur_diff = _('Binary file')
358 367 elif node1.size > self.cut_off_limit or \
359 368 node2.size > self.cut_off_limit:
360 369 c.cur_diff = ''
361 370 c.big_diff = True
362 371
363 372 else:
364 373 diff = differ.DiffProcessor(differ.get_gitdiff(node1, node2),
365 374 format='gitdiff')
366 375 c.cur_diff = diff.as_html()
367 376
368 377 if not c.cur_diff and not c.big_diff:
369 378 c.no_changes = True
370 379 return render('files/file_diff.html')
371 380
372 381 def _get_node_history(self, cs, f_path):
373 382 changesets = cs.get_file_history(f_path)
374 383 hist_l = []
375 384
376 385 changesets_group = ([], _("Changesets"))
377 386 branches_group = ([], _("Branches"))
378 387 tags_group = ([], _("Tags"))
379 388
380 389 for chs in changesets:
381 390 n_desc = 'r%s:%s' % (chs.revision, chs.short_id)
382 391 changesets_group[0].append((chs.raw_id, n_desc,))
383 392
384 393 hist_l.append(changesets_group)
385 394
386 395 for name, chs in c.rhodecode_repo.branches.items():
387 396 #chs = chs.split(':')[-1]
388 397 branches_group[0].append((chs, name),)
389 398 hist_l.append(branches_group)
390 399
391 400 for name, chs in c.rhodecode_repo.tags.items():
392 401 #chs = chs.split(':')[-1]
393 402 tags_group[0].append((chs, name),)
394 403 hist_l.append(tags_group)
395 404
396 405 return hist_l
General Comments 0
You need to be logged in to leave comments. Login now