##// END OF EJS Templates
archive file fixes for python 2.5
marcink -
r2291:982d8a80 beta
parent child Browse files
Show More
@@ -1,486 +1,487 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) 2010-2012 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 from __future__ import with_statement
26 26 import os
27 27 import logging
28 28 import traceback
29 29 import tempfile
30 30
31 31 from pylons import request, response, tmpl_context as c, url
32 32 from pylons.i18n.translation import _
33 33 from pylons.controllers.util import redirect
34 34 from pylons.decorators import jsonify
35 35
36 36 from rhodecode.lib import diffs
37 37 from rhodecode.lib import helpers as h
38 38
39 39 from rhodecode.lib.compat import OrderedDict
40 40 from rhodecode.lib.utils2 import convert_line_endings, detect_mode, safe_str
41 41 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
42 42 from rhodecode.lib.base import BaseRepoController, render
43 43 from rhodecode.lib.utils import EmptyChangeset
44 44 from rhodecode.lib.vcs.conf import settings
45 45 from rhodecode.lib.vcs.exceptions import RepositoryError, \
46 46 ChangesetDoesNotExistError, EmptyRepositoryError, \
47 47 ImproperArchiveTypeError, VCSError, NodeAlreadyExistsError
48 48 from rhodecode.lib.vcs.nodes import FileNode
49 49
50 50 from rhodecode.model.repo import RepoModel
51 51 from rhodecode.model.scm import ScmModel
52 52 from rhodecode.model.db import Repository
53 53
54 54 from rhodecode.controllers.changeset import anchor_url, _ignorews_url,\
55 55 _context_url, get_line_ctx, get_ignore_ws
56 56
57 57
58 58 log = logging.getLogger(__name__)
59 59
60 60
61 61 class FilesController(BaseRepoController):
62 62
63 63 @LoginRequired()
64 64 def __before__(self):
65 65 super(FilesController, self).__before__()
66 66 c.cut_off_limit = self.cut_off_limit
67 67
68 68 def __get_cs_or_redirect(self, rev, repo_name, redirect_after=True):
69 69 """
70 70 Safe way to get changeset if error occur it redirects to tip with
71 71 proper message
72 72
73 73 :param rev: revision to fetch
74 74 :param repo_name: repo name to redirect after
75 75 """
76 76
77 77 try:
78 78 return c.rhodecode_repo.get_changeset(rev)
79 79 except EmptyRepositoryError, e:
80 80 if not redirect_after:
81 81 return None
82 82 url_ = url('files_add_home',
83 83 repo_name=c.repo_name,
84 84 revision=0, f_path='')
85 85 add_new = '<a href="%s">[%s]</a>' % (url_, _('add new'))
86 86 h.flash(h.literal(_('There are no files yet %s' % add_new)),
87 87 category='warning')
88 88 redirect(h.url('summary_home', repo_name=repo_name))
89 89
90 90 except RepositoryError, e:
91 91 h.flash(str(e), category='warning')
92 92 redirect(h.url('files_home', repo_name=repo_name, revision='tip'))
93 93
94 94 def __get_filenode_or_redirect(self, repo_name, cs, path):
95 95 """
96 96 Returns file_node, if error occurs or given path is directory,
97 97 it'll redirect to top level path
98 98
99 99 :param repo_name: repo_name
100 100 :param cs: given changeset
101 101 :param path: path to lookup
102 102 """
103 103
104 104 try:
105 105 file_node = cs.get_node(path)
106 106 if file_node.is_dir():
107 107 raise RepositoryError('given path is a directory')
108 108 except RepositoryError, e:
109 109 h.flash(str(e), category='warning')
110 110 redirect(h.url('files_home', repo_name=repo_name,
111 111 revision=cs.raw_id))
112 112
113 113 return file_node
114 114
115 115 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
116 116 'repository.admin')
117 117 def index(self, repo_name, revision, f_path, annotate=False):
118 118 # redirect to given revision from form if given
119 119 post_revision = request.POST.get('at_rev', None)
120 120 if post_revision:
121 121 cs = self.__get_cs_or_redirect(post_revision, repo_name)
122 122 redirect(url('files_home', repo_name=c.repo_name,
123 123 revision=cs.raw_id, f_path=f_path))
124 124
125 125 c.changeset = self.__get_cs_or_redirect(revision, repo_name)
126 126 c.branch = request.GET.get('branch', None)
127 127 c.f_path = f_path
128 128 c.annotate = annotate
129 129 cur_rev = c.changeset.revision
130 130
131 131 # prev link
132 132 try:
133 133 prev_rev = c.rhodecode_repo.get_changeset(cur_rev).prev(c.branch)
134 134 c.url_prev = url('files_home', repo_name=c.repo_name,
135 135 revision=prev_rev.raw_id, f_path=f_path)
136 136 if c.branch:
137 137 c.url_prev += '?branch=%s' % c.branch
138 138 except (ChangesetDoesNotExistError, VCSError):
139 139 c.url_prev = '#'
140 140
141 141 # next link
142 142 try:
143 143 next_rev = c.rhodecode_repo.get_changeset(cur_rev).next(c.branch)
144 144 c.url_next = url('files_home', repo_name=c.repo_name,
145 145 revision=next_rev.raw_id, f_path=f_path)
146 146 if c.branch:
147 147 c.url_next += '?branch=%s' % c.branch
148 148 except (ChangesetDoesNotExistError, VCSError):
149 149 c.url_next = '#'
150 150
151 151 # files or dirs
152 152 try:
153 153 c.file = c.changeset.get_node(f_path)
154 154
155 155 if c.file.is_file():
156 156 c.file_history = self._get_node_history(c.changeset, f_path)
157 157 else:
158 158 c.file_history = []
159 159 except RepositoryError, e:
160 160 h.flash(str(e), category='warning')
161 161 redirect(h.url('files_home', repo_name=repo_name,
162 162 revision=revision))
163 163
164 164 return render('files/files.html')
165 165
166 166 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
167 167 'repository.admin')
168 168 def rawfile(self, repo_name, revision, f_path):
169 169 cs = self.__get_cs_or_redirect(revision, repo_name)
170 170 file_node = self.__get_filenode_or_redirect(repo_name, cs, f_path)
171 171
172 172 response.content_disposition = 'attachment; filename=%s' % \
173 173 safe_str(f_path.split(Repository.url_sep())[-1])
174 174
175 175 response.content_type = file_node.mimetype
176 176 return file_node.content
177 177
178 178 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
179 179 'repository.admin')
180 180 def raw(self, repo_name, revision, f_path):
181 181 cs = self.__get_cs_or_redirect(revision, repo_name)
182 182 file_node = self.__get_filenode_or_redirect(repo_name, cs, f_path)
183 183
184 184 raw_mimetype_mapping = {
185 185 # map original mimetype to a mimetype used for "show as raw"
186 186 # you can also provide a content-disposition to override the
187 187 # default "attachment" disposition.
188 188 # orig_type: (new_type, new_dispo)
189 189
190 190 # show images inline:
191 191 'image/x-icon': ('image/x-icon', 'inline'),
192 192 'image/png': ('image/png', 'inline'),
193 193 'image/gif': ('image/gif', 'inline'),
194 194 'image/jpeg': ('image/jpeg', 'inline'),
195 195 'image/svg+xml': ('image/svg+xml', 'inline'),
196 196 }
197 197
198 198 mimetype = file_node.mimetype
199 199 try:
200 200 mimetype, dispo = raw_mimetype_mapping[mimetype]
201 201 except KeyError:
202 202 # we don't know anything special about this, handle it safely
203 203 if file_node.is_binary:
204 204 # do same as download raw for binary files
205 205 mimetype, dispo = 'application/octet-stream', 'attachment'
206 206 else:
207 207 # do not just use the original mimetype, but force text/plain,
208 208 # otherwise it would serve text/html and that might be unsafe.
209 209 # Note: underlying vcs library fakes text/plain mimetype if the
210 210 # mimetype can not be determined and it thinks it is not
211 211 # binary.This might lead to erroneous text display in some
212 212 # cases, but helps in other cases, like with text files
213 213 # without extension.
214 214 mimetype, dispo = 'text/plain', 'inline'
215 215
216 216 if dispo == 'attachment':
217 217 dispo = 'attachment; filename=%s' % \
218 218 safe_str(f_path.split(os.sep)[-1])
219 219
220 220 response.content_disposition = dispo
221 221 response.content_type = mimetype
222 222 return file_node.content
223 223
224 224 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
225 225 def edit(self, repo_name, revision, f_path):
226 226 r_post = request.POST
227 227
228 228 c.cs = self.__get_cs_or_redirect(revision, repo_name)
229 229 c.file = self.__get_filenode_or_redirect(repo_name, c.cs, f_path)
230 230
231 231 if c.file.is_binary:
232 232 return redirect(url('files_home', repo_name=c.repo_name,
233 233 revision=c.cs.raw_id, f_path=f_path))
234 234
235 235 c.f_path = f_path
236 236
237 237 if r_post:
238 238
239 239 old_content = c.file.content
240 240 sl = old_content.splitlines(1)
241 241 first_line = sl[0] if sl else ''
242 242 # modes: 0 - Unix, 1 - Mac, 2 - DOS
243 243 mode = detect_mode(first_line, 0)
244 244 content = convert_line_endings(r_post.get('content'), mode)
245 245
246 246 message = r_post.get('message') or (_('Edited %s via RhodeCode')
247 247 % (f_path))
248 248 author = self.rhodecode_user.full_contact
249 249
250 250 if content == old_content:
251 251 h.flash(_('No changes'),
252 252 category='warning')
253 253 return redirect(url('changeset_home', repo_name=c.repo_name,
254 254 revision='tip'))
255 255
256 256 try:
257 257 self.scm_model.commit_change(repo=c.rhodecode_repo,
258 258 repo_name=repo_name, cs=c.cs,
259 259 user=self.rhodecode_user,
260 260 author=author, message=message,
261 261 content=content, f_path=f_path)
262 262 h.flash(_('Successfully committed to %s' % f_path),
263 263 category='success')
264 264
265 265 except Exception:
266 266 log.error(traceback.format_exc())
267 267 h.flash(_('Error occurred during commit'), category='error')
268 268 return redirect(url('changeset_home',
269 269 repo_name=c.repo_name, revision='tip'))
270 270
271 271 return render('files/files_edit.html')
272 272
273 273 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
274 274 def add(self, repo_name, revision, f_path):
275 275 r_post = request.POST
276 276 c.cs = self.__get_cs_or_redirect(revision, repo_name,
277 277 redirect_after=False)
278 278 if c.cs is None:
279 279 c.cs = EmptyChangeset(alias=c.rhodecode_repo.alias)
280 280
281 281 c.f_path = f_path
282 282
283 283 if r_post:
284 284 unix_mode = 0
285 285 content = convert_line_endings(r_post.get('content'), unix_mode)
286 286
287 287 message = r_post.get('message') or (_('Added %s via RhodeCode')
288 288 % (f_path))
289 289 location = r_post.get('location')
290 290 filename = r_post.get('filename')
291 291 file_obj = r_post.get('upload_file', None)
292 292
293 293 if file_obj is not None and hasattr(file_obj, 'filename'):
294 294 filename = file_obj.filename
295 295 content = file_obj.file
296 296
297 297 node_path = os.path.join(location, filename)
298 298 author = self.rhodecode_user.full_contact
299 299
300 300 if not content:
301 301 h.flash(_('No content'), category='warning')
302 302 return redirect(url('changeset_home', repo_name=c.repo_name,
303 303 revision='tip'))
304 304 if not filename:
305 305 h.flash(_('No filename'), category='warning')
306 306 return redirect(url('changeset_home', repo_name=c.repo_name,
307 307 revision='tip'))
308 308
309 309 try:
310 310 self.scm_model.create_node(repo=c.rhodecode_repo,
311 311 repo_name=repo_name, cs=c.cs,
312 312 user=self.rhodecode_user,
313 313 author=author, message=message,
314 314 content=content, f_path=node_path)
315 315 h.flash(_('Successfully committed to %s' % node_path),
316 316 category='success')
317 317 except NodeAlreadyExistsError, e:
318 318 h.flash(_(e), category='error')
319 319 except Exception:
320 320 log.error(traceback.format_exc())
321 321 h.flash(_('Error occurred during commit'), category='error')
322 322 return redirect(url('changeset_home',
323 323 repo_name=c.repo_name, revision='tip'))
324 324
325 325 return render('files/files_add.html')
326 326
327 327 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
328 328 'repository.admin')
329 329 def archivefile(self, repo_name, fname):
330 330
331 331 fileformat = None
332 332 revision = None
333 333 ext = None
334 334 subrepos = request.GET.get('subrepos') == 'true'
335 335
336 336 for a_type, ext_data in settings.ARCHIVE_SPECS.items():
337 337 archive_spec = fname.split(ext_data[1])
338 338 if len(archive_spec) == 2 and archive_spec[1] == '':
339 339 fileformat = a_type or ext_data[1]
340 340 revision = archive_spec[0]
341 341 ext = ext_data[1]
342 342
343 343 try:
344 344 dbrepo = RepoModel().get_by_repo_name(repo_name)
345 345 if dbrepo.enable_downloads is False:
346 346 return _('downloads disabled')
347 347
348 348 if c.rhodecode_repo.alias == 'hg':
349 349 # patch and reset hooks section of UI config to not run any
350 350 # hooks on fetching archives with subrepos
351 351 for k, v in c.rhodecode_repo._repo.ui.configitems('hooks'):
352 352 c.rhodecode_repo._repo.ui.setconfig('hooks', k, None)
353 353
354 354 cs = c.rhodecode_repo.get_changeset(revision)
355 355 content_type = settings.ARCHIVE_SPECS[fileformat][0]
356 356 except ChangesetDoesNotExistError:
357 357 return _('Unknown revision %s') % revision
358 358 except EmptyRepositoryError:
359 359 return _('Empty repository')
360 360 except (ImproperArchiveTypeError, KeyError):
361 361 return _('Unknown archive type')
362 362
363 archive = tempfile.NamedTemporaryFile(mode='w+r+b', delete=False)
364 cs.fill_archive(stream=archive, kind=fileformat, subrepos=subrepos)
365 archive.close()
363 fd, _archive_name = tempfile.mkstemp(suffix='rcarchive')
364 with open(_archive_name, 'wb') as f:
365 cs.fill_archive(stream=f, kind=fileformat, subrepos=subrepos)
366
366 367 response.content_type = content_type
367 368 response.content_disposition = 'attachment; filename=%s-%s%s' \
368 369 % (repo_name, revision[:12], ext)
369 response.content_length = str(os.path.getsize(archive.name))
370 response.content_length = str(os.path.getsize(_archive_name))
370 371
371 372 def get_chunked_archive(tmpfile):
372 373 while True:
373 374 data = tmpfile.read(16 * 1024)
374 375 if not data:
375 376 tmpfile.close()
376 377 os.unlink(tmpfile.name)
377 378 break
378 379 yield data
379 return get_chunked_archive(tmpfile=open(archive.name,'rb'))
380 return get_chunked_archive(tmpfile=open(_archive_name, 'rb'))
380 381
381 382 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
382 383 'repository.admin')
383 384 def diff(self, repo_name, f_path):
384 385 ignore_whitespace = request.GET.get('ignorews') == '1'
385 386 line_context = request.GET.get('context', 3)
386 387 diff1 = request.GET.get('diff1', '')
387 388 diff2 = request.GET.get('diff2', '')
388 389 c.action = request.GET.get('diff')
389 390 c.no_changes = diff1 == diff2
390 391 c.f_path = f_path
391 392 c.big_diff = False
392 393 c.anchor_url = anchor_url
393 394 c.ignorews_url = _ignorews_url
394 395 c.context_url = _context_url
395 396 c.changes = OrderedDict()
396 397 c.changes[diff2] = []
397 398 try:
398 399 if diff1 not in ['', None, 'None', '0' * 12, '0' * 40]:
399 400 c.changeset_1 = c.rhodecode_repo.get_changeset(diff1)
400 401 node1 = c.changeset_1.get_node(f_path)
401 402 else:
402 403 c.changeset_1 = EmptyChangeset(repo=c.rhodecode_repo)
403 404 node1 = FileNode('.', '', changeset=c.changeset_1)
404 405
405 406 if diff2 not in ['', None, 'None', '0' * 12, '0' * 40]:
406 407 c.changeset_2 = c.rhodecode_repo.get_changeset(diff2)
407 408 node2 = c.changeset_2.get_node(f_path)
408 409 else:
409 410 c.changeset_2 = EmptyChangeset(repo=c.rhodecode_repo)
410 411 node2 = FileNode('.', '', changeset=c.changeset_2)
411 412 except RepositoryError:
412 413 return redirect(url('files_home', repo_name=c.repo_name,
413 414 f_path=f_path))
414 415
415 416 if c.action == 'download':
416 417 _diff = diffs.get_gitdiff(node1, node2,
417 418 ignore_whitespace=ignore_whitespace,
418 419 context=line_context)
419 420 diff = diffs.DiffProcessor(_diff, format='gitdiff')
420 421
421 422 diff_name = '%s_vs_%s.diff' % (diff1, diff2)
422 423 response.content_type = 'text/plain'
423 424 response.content_disposition = (
424 425 'attachment; filename=%s' % diff_name
425 426 )
426 427 return diff.raw_diff()
427 428
428 429 elif c.action == 'raw':
429 430 _diff = diffs.get_gitdiff(node1, node2,
430 431 ignore_whitespace=ignore_whitespace,
431 432 context=line_context)
432 433 diff = diffs.DiffProcessor(_diff, format='gitdiff')
433 434 response.content_type = 'text/plain'
434 435 return diff.raw_diff()
435 436
436 437 else:
437 438 fid = h.FID(diff2, node2.path)
438 439 line_context_lcl = get_line_ctx(fid, request.GET)
439 440 ign_whitespace_lcl = get_ignore_ws(fid, request.GET)
440 441
441 442 lim = request.GET.get('fulldiff') or self.cut_off_limit
442 443 _, cs1, cs2, diff, st = diffs.wrapped_diff(filenode_old=node1,
443 444 filenode_new=node2,
444 445 cut_off_limit=lim,
445 446 ignore_whitespace=ign_whitespace_lcl,
446 447 line_context=line_context_lcl,
447 448 enable_comments=False)
448 449
449 450 c.changes = [('', node2, diff, cs1, cs2, st,)]
450 451
451 452 return render('files/file_diff.html')
452 453
453 454 def _get_node_history(self, cs, f_path):
454 455 changesets = cs.get_file_history(f_path)
455 456 hist_l = []
456 457
457 458 changesets_group = ([], _("Changesets"))
458 459 branches_group = ([], _("Branches"))
459 460 tags_group = ([], _("Tags"))
460 461 _hg = cs.repository.alias == 'hg'
461 462 for chs in changesets:
462 463 _branch = '(%s)' % chs.branch if _hg else ''
463 464 n_desc = 'r%s:%s %s' % (chs.revision, chs.short_id, _branch)
464 465 changesets_group[0].append((chs.raw_id, n_desc,))
465 466
466 467 hist_l.append(changesets_group)
467 468
468 469 for name, chs in c.rhodecode_repo.branches.items():
469 470 branches_group[0].append((chs, name),)
470 471 hist_l.append(branches_group)
471 472
472 473 for name, chs in c.rhodecode_repo.tags.items():
473 474 tags_group[0].append((chs, name),)
474 475 hist_l.append(tags_group)
475 476
476 477 return hist_l
477 478
478 479 @jsonify
479 480 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
480 481 'repository.admin')
481 482 def nodelist(self, repo_name, revision, f_path):
482 483 if request.environ.get('HTTP_X_PARTIAL_XHR'):
483 484 cs = self.__get_cs_or_redirect(revision, repo_name)
484 485 _d, _f = ScmModel().get_nodes(repo_name, cs.raw_id, f_path,
485 486 flat=False)
486 487 return _d + _f
General Comments 0
You need to be logged in to leave comments. Login now