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