##// END OF EJS Templates
2way compare: fixed missing "<" escape code
marcink -
r4060:ba7b8dfa default
parent child Browse files
Show More
@@ -1,732 +1,732 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 import shutil
31 31
32 32 from pylons import request, response, tmpl_context as c, url
33 33 from pylons.i18n.translation import _
34 34 from pylons.controllers.util import redirect
35 35 from rhodecode.lib.utils import jsonify, action_logger
36 36
37 37 from rhodecode.lib import diffs
38 38 from rhodecode.lib import helpers as h
39 39
40 40 from rhodecode.lib.compat import OrderedDict, json
41 41 from rhodecode.lib.utils2 import convert_line_endings, detect_mode, safe_str,\
42 42 str2bool
43 43 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
44 44 from rhodecode.lib.base import BaseRepoController, render
45 45 from rhodecode.lib.vcs.backends.base import EmptyChangeset
46 46 from rhodecode.lib.vcs.conf import settings
47 47 from rhodecode.lib.vcs.exceptions import RepositoryError, \
48 48 ChangesetDoesNotExistError, EmptyRepositoryError, \
49 49 ImproperArchiveTypeError, VCSError, NodeAlreadyExistsError,\
50 50 NodeDoesNotExistError, ChangesetError, NodeError
51 51 from rhodecode.lib.vcs.nodes import FileNode
52 52
53 53 from rhodecode.model.repo import RepoModel
54 54 from rhodecode.model.scm import ScmModel
55 55 from rhodecode.model.db import Repository
56 56
57 57 from rhodecode.controllers.changeset import anchor_url, _ignorews_url,\
58 58 _context_url, get_line_ctx, get_ignore_ws
59 59 from webob.exc import HTTPNotFound
60 60 from rhodecode.lib.exceptions import NonRelativePathError
61 61
62 62
63 63 log = logging.getLogger(__name__)
64 64
65 65
66 66 class FilesController(BaseRepoController):
67 67
68 68 def __before__(self):
69 69 super(FilesController, self).__before__()
70 70 c.cut_off_limit = self.cut_off_limit
71 71
72 72 def __get_cs_or_redirect(self, rev, repo_name, redirect_after=True):
73 73 """
74 74 Safe way to get changeset if error occur it redirects to tip with
75 75 proper message
76 76
77 77 :param rev: revision to fetch
78 78 :param repo_name: repo name to redirect after
79 79 """
80 80
81 81 try:
82 82 return c.rhodecode_repo.get_changeset(rev)
83 83 except EmptyRepositoryError, e:
84 84 if not redirect_after:
85 85 return None
86 86 url_ = url('files_add_home',
87 87 repo_name=c.repo_name,
88 88 revision=0, f_path='')
89 89 add_new = h.link_to(_('Click here to add new file'), url_)
90 90 h.flash(h.literal(_('There are no files yet %s') % add_new),
91 91 category='warning')
92 92 redirect(h.url('summary_home', repo_name=repo_name))
93 93
94 94 except RepositoryError, e: # including ChangesetDoesNotExistError
95 95 h.flash(str(e), category='error')
96 96 raise HTTPNotFound()
97 97
98 98 def __get_filenode_or_redirect(self, repo_name, cs, path):
99 99 """
100 100 Returns file_node, if error occurs or given path is directory,
101 101 it'll redirect to top level path
102 102
103 103 :param repo_name: repo_name
104 104 :param cs: given changeset
105 105 :param path: path to lookup
106 106 """
107 107
108 108 try:
109 109 file_node = cs.get_node(path)
110 110 if file_node.is_dir():
111 111 raise RepositoryError('given path is a directory')
112 112 except RepositoryError, e:
113 113 h.flash(str(e), category='error')
114 114 raise HTTPNotFound()
115 115
116 116 return file_node
117 117
118 118 @LoginRequired()
119 119 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
120 120 'repository.admin')
121 121 def index(self, repo_name, revision, f_path, annotate=False):
122 122 # redirect to given revision from form if given
123 123 post_revision = request.POST.get('at_rev', None)
124 124 if post_revision:
125 125 cs = self.__get_cs_or_redirect(post_revision, repo_name)
126 126
127 127 c.changeset = self.__get_cs_or_redirect(revision, repo_name)
128 128 c.branch = request.GET.get('branch', None)
129 129 c.f_path = f_path
130 130 c.annotate = annotate
131 131 c.changeset = self.__get_cs_or_redirect(revision, repo_name)
132 132 cur_rev = c.changeset.revision
133 133
134 134 # prev link
135 135 try:
136 136 prev_rev = c.rhodecode_repo.get_changeset(cur_rev).prev(c.branch)
137 137 c.url_prev = url('files_home', repo_name=c.repo_name,
138 138 revision=prev_rev.raw_id, f_path=f_path)
139 139 if c.branch:
140 140 c.url_prev += '?branch=%s' % c.branch
141 141 except (ChangesetDoesNotExistError, VCSError):
142 142 c.url_prev = '#'
143 143
144 144 # next link
145 145 try:
146 146 next_rev = c.rhodecode_repo.get_changeset(cur_rev).next(c.branch)
147 147 c.url_next = url('files_home', repo_name=c.repo_name,
148 148 revision=next_rev.raw_id, f_path=f_path)
149 149 if c.branch:
150 150 c.url_next += '?branch=%s' % c.branch
151 151 except (ChangesetDoesNotExistError, VCSError):
152 152 c.url_next = '#'
153 153
154 154 # files or dirs
155 155 try:
156 156 c.file = c.changeset.get_node(f_path)
157 157
158 158 if c.file.is_file():
159 159 c.load_full_history = False
160 160 file_last_cs = c.file.last_changeset
161 161 c.file_changeset = (c.changeset
162 162 if c.changeset.revision < file_last_cs.revision
163 163 else file_last_cs)
164 164 #determine if we're on branch head
165 165 _branches = c.rhodecode_repo.branches
166 166 c.on_branch_head = revision in _branches.keys() + _branches.values()
167 167 _hist = []
168 168 c.file_history = []
169 169 if c.load_full_history:
170 170 c.file_history, _hist = self._get_node_history(c.changeset, f_path)
171 171
172 172 c.authors = []
173 173 for a in set([x.author for x in _hist]):
174 174 c.authors.append((h.email(a), h.person(a)))
175 175 else:
176 176 c.authors = c.file_history = []
177 177 except RepositoryError, e:
178 178 h.flash(str(e), category='error')
179 179 raise HTTPNotFound()
180 180
181 181 if request.environ.get('HTTP_X_PARTIAL_XHR'):
182 182 return render('files/files_ypjax.html')
183 183
184 184 return render('files/files.html')
185 185
186 186 @LoginRequired()
187 187 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
188 188 'repository.admin')
189 189 def history(self, repo_name, revision, f_path, annotate=False):
190 190 if request.environ.get('HTTP_X_PARTIAL_XHR'):
191 191 c.changeset = self.__get_cs_or_redirect(revision, repo_name)
192 192 c.f_path = f_path
193 193 c.annotate = annotate
194 194 c.file = c.changeset.get_node(f_path)
195 195 if c.file.is_file():
196 196 file_last_cs = c.file.last_changeset
197 197 c.file_changeset = (c.changeset
198 198 if c.changeset.revision < file_last_cs.revision
199 199 else file_last_cs)
200 200 c.file_history, _hist = self._get_node_history(c.changeset, f_path)
201 201 c.authors = []
202 202 for a in set([x.author for x in _hist]):
203 203 c.authors.append((h.email(a), h.person(a)))
204 204 return render('files/files_history_box.html')
205 205
206 206 @LoginRequired()
207 207 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
208 208 'repository.admin')
209 209 def rawfile(self, repo_name, revision, f_path):
210 210 cs = self.__get_cs_or_redirect(revision, repo_name)
211 211 file_node = self.__get_filenode_or_redirect(repo_name, cs, f_path)
212 212
213 213 response.content_disposition = 'attachment; filename=%s' % \
214 214 safe_str(f_path.split(Repository.url_sep())[-1])
215 215
216 216 response.content_type = file_node.mimetype
217 217 return file_node.content
218 218
219 219 @LoginRequired()
220 220 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
221 221 'repository.admin')
222 222 def raw(self, repo_name, revision, f_path):
223 223 cs = self.__get_cs_or_redirect(revision, repo_name)
224 224 file_node = self.__get_filenode_or_redirect(repo_name, cs, f_path)
225 225
226 226 raw_mimetype_mapping = {
227 227 # map original mimetype to a mimetype used for "show as raw"
228 228 # you can also provide a content-disposition to override the
229 229 # default "attachment" disposition.
230 230 # orig_type: (new_type, new_dispo)
231 231
232 232 # show images inline:
233 233 'image/x-icon': ('image/x-icon', 'inline'),
234 234 'image/png': ('image/png', 'inline'),
235 235 'image/gif': ('image/gif', 'inline'),
236 236 'image/jpeg': ('image/jpeg', 'inline'),
237 237 'image/svg+xml': ('image/svg+xml', 'inline'),
238 238 }
239 239
240 240 mimetype = file_node.mimetype
241 241 try:
242 242 mimetype, dispo = raw_mimetype_mapping[mimetype]
243 243 except KeyError:
244 244 # we don't know anything special about this, handle it safely
245 245 if file_node.is_binary:
246 246 # do same as download raw for binary files
247 247 mimetype, dispo = 'application/octet-stream', 'attachment'
248 248 else:
249 249 # do not just use the original mimetype, but force text/plain,
250 250 # otherwise it would serve text/html and that might be unsafe.
251 251 # Note: underlying vcs library fakes text/plain mimetype if the
252 252 # mimetype can not be determined and it thinks it is not
253 253 # binary.This might lead to erroneous text display in some
254 254 # cases, but helps in other cases, like with text files
255 255 # without extension.
256 256 mimetype, dispo = 'text/plain', 'inline'
257 257
258 258 if dispo == 'attachment':
259 259 dispo = 'attachment; filename=%s' % \
260 260 safe_str(f_path.split(os.sep)[-1])
261 261
262 262 response.content_disposition = dispo
263 263 response.content_type = mimetype
264 264 return file_node.content
265 265
266 266 @LoginRequired()
267 267 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
268 268 def edit(self, repo_name, revision, f_path):
269 269 repo = c.rhodecode_db_repo
270 270 if repo.enable_locking and repo.locked[0]:
271 271 h.flash(_('This repository is has been locked by %s on %s')
272 272 % (h.person_by_id(repo.locked[0]),
273 273 h.fmt_date(h.time_to_datetime(repo.locked[1]))),
274 274 'warning')
275 275 return redirect(h.url('files_home',
276 276 repo_name=repo_name, revision='tip'))
277 277
278 278 # check if revision is a branch identifier- basically we cannot
279 279 # create multiple heads via file editing
280 280 _branches = repo.scm_instance.branches
281 281 # check if revision is a branch name or branch hash
282 282 if revision not in _branches.keys() + _branches.values():
283 283 h.flash(_('You can only edit files with revision '
284 284 'being a valid branch '), category='warning')
285 285 return redirect(h.url('files_home',
286 286 repo_name=repo_name, revision='tip',
287 287 f_path=f_path))
288 288
289 289 r_post = request.POST
290 290
291 291 c.cs = self.__get_cs_or_redirect(revision, repo_name)
292 292 c.file = self.__get_filenode_or_redirect(repo_name, c.cs, f_path)
293 293
294 294 if c.file.is_binary:
295 295 return redirect(url('files_home', repo_name=c.repo_name,
296 296 revision=c.cs.raw_id, f_path=f_path))
297 297 c.default_message = _('Edited file %s via RhodeCode') % (f_path)
298 298 c.f_path = f_path
299 299
300 300 if r_post:
301 301
302 302 old_content = c.file.content
303 303 sl = old_content.splitlines(1)
304 304 first_line = sl[0] if sl else ''
305 305 # modes: 0 - Unix, 1 - Mac, 2 - DOS
306 306 mode = detect_mode(first_line, 0)
307 307 content = convert_line_endings(r_post.get('content', ''), mode)
308 308
309 309 message = r_post.get('message') or c.default_message
310 310 author = self.rhodecode_user.full_contact
311 311
312 312 if content == old_content:
313 313 h.flash(_('No changes'), category='warning')
314 314 return redirect(url('changeset_home', repo_name=c.repo_name,
315 315 revision='tip'))
316 316 try:
317 317 self.scm_model.commit_change(repo=c.rhodecode_repo,
318 318 repo_name=repo_name, cs=c.cs,
319 319 user=self.rhodecode_user.user_id,
320 320 author=author, message=message,
321 321 content=content, f_path=f_path)
322 322 h.flash(_('Successfully committed to %s') % f_path,
323 323 category='success')
324 324
325 325 except Exception:
326 326 log.error(traceback.format_exc())
327 327 h.flash(_('Error occurred during commit'), category='error')
328 328 return redirect(url('changeset_home',
329 329 repo_name=c.repo_name, revision='tip'))
330 330
331 331 return render('files/files_edit.html')
332 332
333 333 @LoginRequired()
334 334 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
335 335 def add(self, repo_name, revision, f_path):
336 336
337 337 repo = Repository.get_by_repo_name(repo_name)
338 338 if repo.enable_locking and repo.locked[0]:
339 339 h.flash(_('This repository is has been locked by %s on %s')
340 340 % (h.person_by_id(repo.locked[0]),
341 341 h.fmt_date(h.time_to_datetime(repo.locked[1]))),
342 342 'warning')
343 343 return redirect(h.url('files_home',
344 344 repo_name=repo_name, revision='tip'))
345 345
346 346 r_post = request.POST
347 347 c.cs = self.__get_cs_or_redirect(revision, repo_name,
348 348 redirect_after=False)
349 349 if c.cs is None:
350 350 c.cs = EmptyChangeset(alias=c.rhodecode_repo.alias)
351 351 c.default_message = (_('Added file via RhodeCode'))
352 352 c.f_path = f_path
353 353
354 354 if r_post:
355 355 unix_mode = 0
356 356 content = convert_line_endings(r_post.get('content', ''), unix_mode)
357 357
358 358 message = r_post.get('message') or c.default_message
359 359 filename = r_post.get('filename')
360 360 location = r_post.get('location', '')
361 361 file_obj = r_post.get('upload_file', None)
362 362
363 363 if file_obj is not None and hasattr(file_obj, 'filename'):
364 364 filename = file_obj.filename
365 365 content = file_obj.file
366 366
367 367 if not content:
368 368 h.flash(_('No content'), category='warning')
369 369 return redirect(url('changeset_home', repo_name=c.repo_name,
370 370 revision='tip'))
371 371 if not filename:
372 372 h.flash(_('No filename'), category='warning')
373 373 return redirect(url('changeset_home', repo_name=c.repo_name,
374 374 revision='tip'))
375 375 #strip all crap out of file, just leave the basename
376 376 filename = os.path.basename(filename)
377 377 node_path = os.path.join(location, filename)
378 378 author = self.rhodecode_user.full_contact
379 379
380 380 try:
381 381 nodes = {
382 382 node_path: {
383 383 'content': content
384 384 }
385 385 }
386 386 self.scm_model.create_nodes(
387 387 user=c.rhodecode_user.user_id, repo=c.rhodecode_db_repo,
388 388 message=message,
389 389 nodes=nodes,
390 390 parent_cs=c.cs,
391 391 author=author,
392 392 )
393 393
394 394 h.flash(_('Successfully committed to %s') % node_path,
395 395 category='success')
396 396 except NonRelativePathError, e:
397 397 h.flash(_('Location must be relative path and must not '
398 398 'contain .. in path'), category='warning')
399 399 return redirect(url('changeset_home', repo_name=c.repo_name,
400 400 revision='tip'))
401 401 except (NodeError, NodeAlreadyExistsError), e:
402 402 h.flash(_(e), category='error')
403 403 except Exception:
404 404 log.error(traceback.format_exc())
405 405 h.flash(_('Error occurred during commit'), category='error')
406 406 return redirect(url('changeset_home',
407 407 repo_name=c.repo_name, revision='tip'))
408 408
409 409 return render('files/files_add.html')
410 410
411 411 @LoginRequired()
412 412 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
413 413 'repository.admin')
414 414 def archivefile(self, repo_name, fname):
415 415
416 416 fileformat = None
417 417 revision = None
418 418 ext = None
419 419 subrepos = request.GET.get('subrepos') == 'true'
420 420
421 421 for a_type, ext_data in settings.ARCHIVE_SPECS.items():
422 422 archive_spec = fname.split(ext_data[1])
423 423 if len(archive_spec) == 2 and archive_spec[1] == '':
424 424 fileformat = a_type or ext_data[1]
425 425 revision = archive_spec[0]
426 426 ext = ext_data[1]
427 427
428 428 try:
429 429 dbrepo = RepoModel().get_by_repo_name(repo_name)
430 430 if not dbrepo.enable_downloads:
431 431 return _('Downloads disabled')
432 432
433 433 if c.rhodecode_repo.alias == 'hg':
434 434 # patch and reset hooks section of UI config to not run any
435 435 # hooks on fetching archives with subrepos
436 436 for k, v in c.rhodecode_repo._repo.ui.configitems('hooks'):
437 437 c.rhodecode_repo._repo.ui.setconfig('hooks', k, None)
438 438
439 439 cs = c.rhodecode_repo.get_changeset(revision)
440 440 content_type = settings.ARCHIVE_SPECS[fileformat][0]
441 441 except ChangesetDoesNotExistError:
442 442 return _('Unknown revision %s') % revision
443 443 except EmptyRepositoryError:
444 444 return _('Empty repository')
445 445 except (ImproperArchiveTypeError, KeyError):
446 446 return _('Unknown archive type')
447 447 # archive cache
448 448 from rhodecode import CONFIG
449 449 rev_name = cs.raw_id[:12]
450 450 archive_name = '%s-%s%s' % (safe_str(repo_name.replace('/', '_')),
451 451 safe_str(rev_name), ext)
452 452
453 453 use_cached_archive = False # defines if we use cached version of archive
454 454 archive_cache_enabled = CONFIG.get('archive_cache_dir')
455 455 if not subrepos and archive_cache_enabled:
456 456 #check if we it's ok to write
457 457 if not os.path.isdir(CONFIG['archive_cache_dir']):
458 458 os.makedirs(CONFIG['archive_cache_dir'])
459 459 cached_archive_path = os.path.join(CONFIG['archive_cache_dir'], archive_name)
460 460 if os.path.isfile(cached_archive_path):
461 461 log.debug('Found cached archive in %s' % cached_archive_path)
462 462 fd, archive = None, cached_archive_path
463 463 use_cached_archive = True
464 464 else:
465 465 log.debug('Archive %s is not yet cached' % (archive_name))
466 466
467 467 if not use_cached_archive:
468 468 #generate new archive
469 469 try:
470 470 fd, archive = tempfile.mkstemp()
471 471 t = open(archive, 'wb')
472 472 log.debug('Creating new temp archive in %s' % archive)
473 473 cs.fill_archive(stream=t, kind=fileformat, subrepos=subrepos)
474 474 if archive_cache_enabled:
475 475 #if we generated the archive and use cache rename that
476 476 log.debug('Storing new archive in %s' % cached_archive_path)
477 477 shutil.move(archive, cached_archive_path)
478 478 archive = cached_archive_path
479 479 finally:
480 480 t.close()
481 481
482 482 def get_chunked_archive(archive):
483 483 stream = open(archive, 'rb')
484 484 while True:
485 485 data = stream.read(16 * 1024)
486 486 if not data:
487 487 stream.close()
488 488 if fd: # fd means we used temporary file
489 489 os.close(fd)
490 490 if not archive_cache_enabled:
491 491 log.debug('Destroing temp archive %s' % archive)
492 492 os.remove(archive)
493 493 break
494 494 yield data
495 495 # store download action
496 496 action_logger(user=c.rhodecode_user,
497 497 action='user_downloaded_archive:%s' % (archive_name),
498 498 repo=repo_name, ipaddr=self.ip_addr, commit=True)
499 499 response.content_disposition = str('attachment; filename=%s' % (archive_name))
500 500 response.content_type = str(content_type)
501 501 return get_chunked_archive(archive)
502 502
503 503 @LoginRequired()
504 504 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
505 505 'repository.admin')
506 506 def diff(self, repo_name, f_path):
507 507 ignore_whitespace = request.GET.get('ignorews') == '1'
508 508 line_context = request.GET.get('context', 3)
509 509 diff1 = request.GET.get('diff1', '')
510 510 diff2 = request.GET.get('diff2', '')
511 511 c.action = request.GET.get('diff')
512 512 c.no_changes = diff1 == diff2
513 513 c.f_path = f_path
514 514 c.big_diff = False
515 515 c.anchor_url = anchor_url
516 516 c.ignorews_url = _ignorews_url
517 517 c.context_url = _context_url
518 518 c.changes = OrderedDict()
519 519 c.changes[diff2] = []
520 520
521 521 #special case if we want a show rev only, it's impl here
522 522 #to reduce JS and callbacks
523 523
524 524 if request.GET.get('show_rev'):
525 525 if str2bool(request.GET.get('annotate', 'False')):
526 526 _url = url('files_annotate_home', repo_name=c.repo_name,
527 527 revision=diff1, f_path=c.f_path)
528 528 else:
529 529 _url = url('files_home', repo_name=c.repo_name,
530 530 revision=diff1, f_path=c.f_path)
531 531
532 532 return redirect(_url)
533 533 try:
534 534 if diff1 not in ['', None, 'None', '0' * 12, '0' * 40]:
535 535 c.changeset_1 = c.rhodecode_repo.get_changeset(diff1)
536 536 try:
537 537 node1 = c.changeset_1.get_node(f_path)
538 538 if node1.is_dir():
539 539 raise NodeError('%s path is a %s not a file'
540 540 % (node1, type(node1)))
541 541 except NodeDoesNotExistError:
542 542 c.changeset_1 = EmptyChangeset(cs=diff1,
543 543 revision=c.changeset_1.revision,
544 544 repo=c.rhodecode_repo)
545 545 node1 = FileNode(f_path, '', changeset=c.changeset_1)
546 546 else:
547 547 c.changeset_1 = EmptyChangeset(repo=c.rhodecode_repo)
548 548 node1 = FileNode(f_path, '', changeset=c.changeset_1)
549 549
550 550 if diff2 not in ['', None, 'None', '0' * 12, '0' * 40]:
551 551 c.changeset_2 = c.rhodecode_repo.get_changeset(diff2)
552 552 try:
553 553 node2 = c.changeset_2.get_node(f_path)
554 554 if node2.is_dir():
555 555 raise NodeError('%s path is a %s not a file'
556 556 % (node2, type(node2)))
557 557 except NodeDoesNotExistError:
558 558 c.changeset_2 = EmptyChangeset(cs=diff2,
559 559 revision=c.changeset_2.revision,
560 560 repo=c.rhodecode_repo)
561 561 node2 = FileNode(f_path, '', changeset=c.changeset_2)
562 562 else:
563 563 c.changeset_2 = EmptyChangeset(repo=c.rhodecode_repo)
564 564 node2 = FileNode(f_path, '', changeset=c.changeset_2)
565 565 except (RepositoryError, NodeError):
566 566 log.error(traceback.format_exc())
567 567 return redirect(url('files_home', repo_name=c.repo_name,
568 568 f_path=f_path))
569 569
570 570 if c.action == 'download':
571 571 _diff = diffs.get_gitdiff(node1, node2,
572 572 ignore_whitespace=ignore_whitespace,
573 573 context=line_context)
574 574 diff = diffs.DiffProcessor(_diff, format='gitdiff')
575 575
576 576 diff_name = '%s_vs_%s.diff' % (diff1, diff2)
577 577 response.content_type = 'text/plain'
578 578 response.content_disposition = (
579 579 'attachment; filename=%s' % diff_name
580 580 )
581 581 return diff.as_raw()
582 582
583 583 elif c.action == 'raw':
584 584 _diff = diffs.get_gitdiff(node1, node2,
585 585 ignore_whitespace=ignore_whitespace,
586 586 context=line_context)
587 587 diff = diffs.DiffProcessor(_diff, format='gitdiff')
588 588 response.content_type = 'text/plain'
589 589 return diff.as_raw()
590 590
591 591 else:
592 592 fid = h.FID(diff2, node2.path)
593 593 line_context_lcl = get_line_ctx(fid, request.GET)
594 594 ign_whitespace_lcl = get_ignore_ws(fid, request.GET)
595 595
596 596 lim = request.GET.get('fulldiff') or self.cut_off_limit
597 597 _, cs1, cs2, diff, st = diffs.wrapped_diff(filenode_old=node1,
598 598 filenode_new=node2,
599 599 cut_off_limit=lim,
600 600 ignore_whitespace=ign_whitespace_lcl,
601 601 line_context=line_context_lcl,
602 602 enable_comments=False)
603 603 op = ''
604 604 filename = node1.path
605 605 cs_changes = {
606 606 'fid': [cs1, cs2, op, filename, diff, st]
607 607 }
608 608 c.changes = cs_changes
609 609
610 610 return render('files/file_diff.html')
611 611
612 612 @LoginRequired()
613 613 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
614 614 'repository.admin')
615 615 def diff_2way(self, repo_name, f_path):
616 616 diff1 = request.GET.get('diff1', '')
617 617 diff2 = request.GET.get('diff2', '')
618 618 try:
619 619 if diff1 not in ['', None, 'None', '0' * 12, '0' * 40]:
620 620 c.changeset_1 = c.rhodecode_repo.get_changeset(diff1)
621 621 try:
622 622 node1 = c.changeset_1.get_node(f_path)
623 623 if node1.is_dir():
624 624 raise NodeError('%s path is a %s not a file'
625 625 % (node1, type(node1)))
626 626 except NodeDoesNotExistError:
627 627 c.changeset_1 = EmptyChangeset(cs=diff1,
628 628 revision=c.changeset_1.revision,
629 629 repo=c.rhodecode_repo)
630 630 node1 = FileNode(f_path, '', changeset=c.changeset_1)
631 631 else:
632 632 c.changeset_1 = EmptyChangeset(repo=c.rhodecode_repo)
633 633 node1 = FileNode(f_path, '', changeset=c.changeset_1)
634 634
635 635 if diff2 not in ['', None, 'None', '0' * 12, '0' * 40]:
636 636 c.changeset_2 = c.rhodecode_repo.get_changeset(diff2)
637 637 try:
638 638 node2 = c.changeset_2.get_node(f_path)
639 639 if node2.is_dir():
640 640 raise NodeError('%s path is a %s not a file'
641 641 % (node2, type(node2)))
642 642 except NodeDoesNotExistError:
643 643 c.changeset_2 = EmptyChangeset(cs=diff2,
644 644 revision=c.changeset_2.revision,
645 645 repo=c.rhodecode_repo)
646 646 node2 = FileNode(f_path, '', changeset=c.changeset_2)
647 647 else:
648 648 c.changeset_2 = EmptyChangeset(repo=c.rhodecode_repo)
649 649 node2 = FileNode(f_path, '', changeset=c.changeset_2)
650 650 except (RepositoryError, NodeError):
651 651 log.error(traceback.format_exc())
652 652 return redirect(url('files_home', repo_name=c.repo_name,
653 653 f_path=f_path))
654 654 if node2.is_binary:
655 655 node2_content = 'binary file'
656 656 else:
657 657 node2_content = node2.content
658 658
659 659 if node1.is_binary:
660 660 node1_content = 'binary file'
661 661 else:
662 662 node1_content = node1.content
663 663
664 664 html_escape_table = {
665 665 "&": "\u0026",
666 666 '"': "\u0022",
667 667 "'": "\u0027",
668 668 ">": "\u003e",
669 "<": "\<",
669 "<": "\u003c",
670 670 '\\': "\u005c",
671 671 '\n': '\\n'
672 672 }
673 673
674 674 c.orig1 = h.html_escape((node1_content), html_escape_table)
675 675 c.orig2 = h.html_escape((node2_content), html_escape_table)
676 676 c.node1 = node1
677 677 c.node2 = node2
678 678 c.cs1 = c.changeset_1
679 679 c.cs2 = c.changeset_2
680 680
681 681 return render('files/diff_2way.html')
682 682
683 683 def _get_node_history(self, cs, f_path, changesets=None):
684 684 """
685 685 get changesets history for given node
686 686
687 687 :param cs: changeset to calculate history
688 688 :param f_path: path for node to calculate history for
689 689 :param changesets: if passed don't calculate history and take
690 690 changesets defined in this list
691 691 """
692 692 # calculate history based on tip
693 693 tip_cs = c.rhodecode_repo.get_changeset()
694 694 if changesets is None:
695 695 try:
696 696 changesets = tip_cs.get_file_history(f_path)
697 697 except (NodeDoesNotExistError, ChangesetError):
698 698 #this node is not present at tip !
699 699 changesets = cs.get_file_history(f_path)
700 700 hist_l = []
701 701
702 702 changesets_group = ([], _("Changesets"))
703 703 branches_group = ([], _("Branches"))
704 704 tags_group = ([], _("Tags"))
705 705 _hg = cs.repository.alias == 'hg'
706 706 for chs in changesets:
707 707 #_branch = '(%s)' % chs.branch if _hg else ''
708 708 _branch = chs.branch
709 709 n_desc = 'r%s:%s (%s)' % (chs.revision, chs.short_id, _branch)
710 710 changesets_group[0].append((chs.raw_id, n_desc,))
711 711 hist_l.append(changesets_group)
712 712
713 713 for name, chs in c.rhodecode_repo.branches.items():
714 714 branches_group[0].append((chs, name),)
715 715 hist_l.append(branches_group)
716 716
717 717 for name, chs in c.rhodecode_repo.tags.items():
718 718 tags_group[0].append((chs, name),)
719 719 hist_l.append(tags_group)
720 720
721 721 return hist_l, changesets
722 722
723 723 @LoginRequired()
724 724 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
725 725 'repository.admin')
726 726 @jsonify
727 727 def nodelist(self, repo_name, revision, f_path):
728 728 if request.environ.get('HTTP_X_PARTIAL_XHR'):
729 729 cs = self.__get_cs_or_redirect(revision, repo_name)
730 730 _d, _f = ScmModel().get_nodes(repo_name, cs.raw_id, f_path,
731 731 flat=False)
732 732 return {'nodes': _d + _f}
General Comments 0
You need to be logged in to leave comments. Login now