##// END OF EJS Templates
added more strict checks for file path in add file controller
marcink -
r3559:328eb707 beta
parent child Browse files
Show More
@@ -1,641 +1,647 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
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
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
60 60
61 61 log = logging.getLogger(__name__)
62 62
63 63
64 64 class FilesController(BaseRepoController):
65 65
66 66 def __before__(self):
67 67 super(FilesController, self).__before__()
68 68 c.cut_off_limit = self.cut_off_limit
69 69
70 70 def __get_cs_or_redirect(self, rev, repo_name, redirect_after=True):
71 71 """
72 72 Safe way to get changeset if error occur it redirects to tip with
73 73 proper message
74 74
75 75 :param rev: revision to fetch
76 76 :param repo_name: repo name to redirect after
77 77 """
78 78
79 79 try:
80 80 return c.rhodecode_repo.get_changeset(rev)
81 81 except EmptyRepositoryError, e:
82 82 if not redirect_after:
83 83 return None
84 84 url_ = url('files_add_home',
85 85 repo_name=c.repo_name,
86 86 revision=0, f_path='')
87 87 add_new = h.link_to(_('click here to add new file'), url_)
88 88 h.flash(h.literal(_('There are no files yet %s') % add_new),
89 89 category='warning')
90 90 redirect(h.url('summary_home', repo_name=repo_name))
91 91
92 92 except RepositoryError, e:
93 93 h.flash(str(e), category='warning')
94 94 redirect(h.url('files_home', repo_name=repo_name, revision='tip'))
95 95
96 96 def __get_filenode_or_redirect(self, repo_name, cs, path):
97 97 """
98 98 Returns file_node, if error occurs or given path is directory,
99 99 it'll redirect to top level path
100 100
101 101 :param repo_name: repo_name
102 102 :param cs: given changeset
103 103 :param path: path to lookup
104 104 """
105 105
106 106 try:
107 107 file_node = cs.get_node(path)
108 108 if file_node.is_dir():
109 109 raise RepositoryError('given path is a directory')
110 110 except RepositoryError, e:
111 111 h.flash(str(e), category='warning')
112 112 redirect(h.url('files_home', repo_name=repo_name,
113 113 revision=cs.raw_id))
114 114
115 115 return file_node
116 116
117 117 @LoginRequired()
118 118 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
119 119 'repository.admin')
120 120 def index(self, repo_name, revision, f_path, annotate=False):
121 121 # redirect to given revision from form if given
122 122 post_revision = request.POST.get('at_rev', None)
123 123 if post_revision:
124 124 cs = self.__get_cs_or_redirect(post_revision, repo_name)
125 125 redirect(url('files_home', repo_name=c.repo_name,
126 126 revision=cs.raw_id, f_path=f_path))
127 127
128 128 c.changeset = self.__get_cs_or_redirect(revision, repo_name)
129 129 c.branch = request.GET.get('branch', None)
130 130 c.f_path = f_path
131 131 c.annotate = annotate
132 132 c.changeset = self.__get_cs_or_redirect(revision, repo_name)
133 133 cur_rev = c.changeset.revision
134 134
135 135 # prev link
136 136 try:
137 137 prev_rev = c.rhodecode_repo.get_changeset(cur_rev).prev(c.branch)
138 138 c.url_prev = url('files_home', repo_name=c.repo_name,
139 139 revision=prev_rev.raw_id, f_path=f_path)
140 140 if c.branch:
141 141 c.url_prev += '?branch=%s' % c.branch
142 142 except (ChangesetDoesNotExistError, VCSError):
143 143 c.url_prev = '#'
144 144
145 145 # next link
146 146 try:
147 147 next_rev = c.rhodecode_repo.get_changeset(cur_rev).next(c.branch)
148 148 c.url_next = url('files_home', repo_name=c.repo_name,
149 149 revision=next_rev.raw_id, f_path=f_path)
150 150 if c.branch:
151 151 c.url_next += '?branch=%s' % c.branch
152 152 except (ChangesetDoesNotExistError, VCSError):
153 153 c.url_next = '#'
154 154
155 155 # files or dirs
156 156 try:
157 157 c.file = c.changeset.get_node(f_path)
158 158
159 159 if c.file.is_file():
160 160 c.load_full_history = False
161 161 file_last_cs = c.file.last_changeset
162 162 c.file_changeset = (c.changeset
163 163 if c.changeset.revision < file_last_cs.revision
164 164 else file_last_cs)
165 165 #determine if we're on branch head
166 166 _branches = c.rhodecode_repo.branches
167 167 c.on_branch_head = revision in _branches.keys() + _branches.values()
168 168 _hist = []
169 169 c.file_history = []
170 170 if c.load_full_history:
171 171 c.file_history, _hist = self._get_node_history(c.changeset, f_path)
172 172
173 173 c.authors = []
174 174 for a in set([x.author for x in _hist]):
175 175 c.authors.append((h.email(a), h.person(a)))
176 176 else:
177 177 c.authors = c.file_history = []
178 178 except RepositoryError, e:
179 179 h.flash(str(e), category='warning')
180 180 redirect(h.url('files_home', repo_name=repo_name,
181 181 revision='tip'))
182 182
183 183 if request.environ.get('HTTP_X_PARTIAL_XHR'):
184 184 return render('files/files_ypjax.html')
185 185
186 186 return render('files/files.html')
187 187
188 188 def history(self, repo_name, revision, f_path, annotate=False):
189 189 if request.environ.get('HTTP_X_PARTIAL_XHR'):
190 190 c.changeset = self.__get_cs_or_redirect(revision, repo_name)
191 191 c.f_path = f_path
192 192 c.annotate = annotate
193 193 c.file = c.changeset.get_node(f_path)
194 194 if c.file.is_file():
195 195 file_last_cs = c.file.last_changeset
196 196 c.file_changeset = (c.changeset
197 197 if c.changeset.revision < file_last_cs.revision
198 198 else file_last_cs)
199 199 c.file_history, _hist = self._get_node_history(c.changeset, f_path)
200 200 c.authors = []
201 201 for a in set([x.author for x in _hist]):
202 202 c.authors.append((h.email(a), h.person(a)))
203 203 return render('files/files_history_box.html')
204 204
205 205 @LoginRequired()
206 206 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
207 207 'repository.admin')
208 208 def rawfile(self, repo_name, revision, f_path):
209 209 cs = self.__get_cs_or_redirect(revision, repo_name)
210 210 file_node = self.__get_filenode_or_redirect(repo_name, cs, f_path)
211 211
212 212 response.content_disposition = 'attachment; filename=%s' % \
213 213 safe_str(f_path.split(Repository.url_sep())[-1])
214 214
215 215 response.content_type = file_node.mimetype
216 216 return file_node.content
217 217
218 218 @LoginRequired()
219 219 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
220 220 'repository.admin')
221 221 def raw(self, repo_name, revision, f_path):
222 222 cs = self.__get_cs_or_redirect(revision, repo_name)
223 223 file_node = self.__get_filenode_or_redirect(repo_name, cs, f_path)
224 224
225 225 raw_mimetype_mapping = {
226 226 # map original mimetype to a mimetype used for "show as raw"
227 227 # you can also provide a content-disposition to override the
228 228 # default "attachment" disposition.
229 229 # orig_type: (new_type, new_dispo)
230 230
231 231 # show images inline:
232 232 'image/x-icon': ('image/x-icon', 'inline'),
233 233 'image/png': ('image/png', 'inline'),
234 234 'image/gif': ('image/gif', 'inline'),
235 235 'image/jpeg': ('image/jpeg', 'inline'),
236 236 'image/svg+xml': ('image/svg+xml', 'inline'),
237 237 }
238 238
239 239 mimetype = file_node.mimetype
240 240 try:
241 241 mimetype, dispo = raw_mimetype_mapping[mimetype]
242 242 except KeyError:
243 243 # we don't know anything special about this, handle it safely
244 244 if file_node.is_binary:
245 245 # do same as download raw for binary files
246 246 mimetype, dispo = 'application/octet-stream', 'attachment'
247 247 else:
248 248 # do not just use the original mimetype, but force text/plain,
249 249 # otherwise it would serve text/html and that might be unsafe.
250 250 # Note: underlying vcs library fakes text/plain mimetype if the
251 251 # mimetype can not be determined and it thinks it is not
252 252 # binary.This might lead to erroneous text display in some
253 253 # cases, but helps in other cases, like with text files
254 254 # without extension.
255 255 mimetype, dispo = 'text/plain', 'inline'
256 256
257 257 if dispo == 'attachment':
258 258 dispo = 'attachment; filename=%s' % \
259 259 safe_str(f_path.split(os.sep)[-1])
260 260
261 261 response.content_disposition = dispo
262 262 response.content_type = mimetype
263 263 return file_node.content
264 264
265 265 @LoginRequired()
266 266 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
267 267 def edit(self, repo_name, revision, f_path):
268 268 repo = c.rhodecode_db_repo
269 269 if repo.enable_locking and repo.locked[0]:
270 270 h.flash(_('This repository is has been locked by %s on %s')
271 271 % (h.person_by_id(repo.locked[0]),
272 272 h.fmt_date(h.time_to_datetime(repo.locked[1]))),
273 273 'warning')
274 274 return redirect(h.url('files_home',
275 275 repo_name=repo_name, revision='tip'))
276 276
277 277 # check if revision is a branch identifier- basically we cannot
278 278 # create multiple heads via file editing
279 279 _branches = repo.scm_instance.branches
280 280 # check if revision is a branch name or branch hash
281 281 if revision not in _branches.keys() + _branches.values():
282 282 h.flash(_('You can only edit files with revision '
283 283 'being a valid branch '), category='warning')
284 284 return redirect(h.url('files_home',
285 285 repo_name=repo_name, revision='tip',
286 286 f_path=f_path))
287 287
288 288 r_post = request.POST
289 289
290 290 c.cs = self.__get_cs_or_redirect(revision, repo_name)
291 291 c.file = self.__get_filenode_or_redirect(repo_name, c.cs, f_path)
292 292
293 293 if c.file.is_binary:
294 294 return redirect(url('files_home', repo_name=c.repo_name,
295 295 revision=c.cs.raw_id, f_path=f_path))
296 296 c.default_message = _('Edited file %s via RhodeCode') % (f_path)
297 297 c.f_path = f_path
298 298
299 299 if r_post:
300 300
301 301 old_content = c.file.content
302 302 sl = old_content.splitlines(1)
303 303 first_line = sl[0] if sl else ''
304 304 # modes: 0 - Unix, 1 - Mac, 2 - DOS
305 305 mode = detect_mode(first_line, 0)
306 306 content = convert_line_endings(r_post.get('content'), mode)
307 307
308 308 message = r_post.get('message') or c.default_message
309 309 author = self.rhodecode_user.full_contact
310 310
311 311 if content == old_content:
312 312 h.flash(_('No changes'),
313 313 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 filename = r_post.get('filename')
359 360 location = r_post.get('location')
360 filename = r_post.get('filename')
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 node_path = os.path.join(location, filename)
368 author = self.rhodecode_user.full_contact
369
370 367 if not content:
371 368 h.flash(_('No content'), category='warning')
372 369 return redirect(url('changeset_home', repo_name=c.repo_name,
373 370 revision='tip'))
374 371 if not filename:
375 372 h.flash(_('No filename'), category='warning')
376 373 return redirect(url('changeset_home', repo_name=c.repo_name,
377 374 revision='tip'))
375 if location.startswith('/') or location.startswith('.') or '../' in location:
376 h.flash(_('location must be relative path and must not '
377 'contain .. in path'), category='warning')
378 return redirect(url('changeset_home', repo_name=c.repo_name,
379 revision='tip'))
380 location = os.path.normpath(location)
381 filename = os.path.basename(filename)
382 node_path = os.path.join(location, filename)
383 author = self.rhodecode_user.full_contact
378 384
379 385 try:
380 386 self.scm_model.create_node(repo=c.rhodecode_repo,
381 387 repo_name=repo_name, cs=c.cs,
382 388 user=self.rhodecode_user.user_id,
383 389 author=author, message=message,
384 390 content=content, f_path=node_path)
385 391 h.flash(_('Successfully committed to %s') % node_path,
386 392 category='success')
387 except NodeAlreadyExistsError, e:
393 except (NodeError, NodeAlreadyExistsError), e:
388 394 h.flash(_(e), category='error')
389 395 except Exception:
390 396 log.error(traceback.format_exc())
391 397 h.flash(_('Error occurred during commit'), category='error')
392 398 return redirect(url('changeset_home',
393 399 repo_name=c.repo_name, revision='tip'))
394 400
395 401 return render('files/files_add.html')
396 402
397 403 @LoginRequired()
398 404 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
399 405 'repository.admin')
400 406 def archivefile(self, repo_name, fname):
401 407
402 408 fileformat = None
403 409 revision = None
404 410 ext = None
405 411 subrepos = request.GET.get('subrepos') == 'true'
406 412
407 413 for a_type, ext_data in settings.ARCHIVE_SPECS.items():
408 414 archive_spec = fname.split(ext_data[1])
409 415 if len(archive_spec) == 2 and archive_spec[1] == '':
410 416 fileformat = a_type or ext_data[1]
411 417 revision = archive_spec[0]
412 418 ext = ext_data[1]
413 419
414 420 try:
415 421 dbrepo = RepoModel().get_by_repo_name(repo_name)
416 422 if dbrepo.enable_downloads is False:
417 423 return _('downloads disabled')
418 424
419 425 if c.rhodecode_repo.alias == 'hg':
420 426 # patch and reset hooks section of UI config to not run any
421 427 # hooks on fetching archives with subrepos
422 428 for k, v in c.rhodecode_repo._repo.ui.configitems('hooks'):
423 429 c.rhodecode_repo._repo.ui.setconfig('hooks', k, None)
424 430
425 431 cs = c.rhodecode_repo.get_changeset(revision)
426 432 content_type = settings.ARCHIVE_SPECS[fileformat][0]
427 433 except ChangesetDoesNotExistError:
428 434 return _('Unknown revision %s') % revision
429 435 except EmptyRepositoryError:
430 436 return _('Empty repository')
431 437 except (ImproperArchiveTypeError, KeyError):
432 438 return _('Unknown archive type')
433 439 # archive cache
434 440 from rhodecode import CONFIG
435 441 rev_name = cs.raw_id[:12]
436 442 archive_name = '%s-%s%s' % (safe_str(repo_name.replace('/', '_')),
437 443 safe_str(rev_name), ext)
438 444
439 445 use_cached_archive = False # defines if we use cached version of archive
440 446 archive_cache_enabled = CONFIG.get('archive_cache_dir')
441 447 if not subrepos and archive_cache_enabled:
442 448 #check if we it's ok to write
443 449 if not os.path.isdir(CONFIG['archive_cache_dir']):
444 450 os.makedirs(CONFIG['archive_cache_dir'])
445 451 cached_archive_path = os.path.join(CONFIG['archive_cache_dir'], archive_name)
446 452 if os.path.isfile(cached_archive_path):
447 453 log.debug('Found cached archive in %s' % cached_archive_path)
448 454 fd, archive = None, cached_archive_path
449 455 use_cached_archive = True
450 456 else:
451 457 log.debug('Archive %s is not yet cached' % (archive_name))
452 458
453 459 if not use_cached_archive:
454 460 #generate new archive
455 461 try:
456 462 fd, archive = tempfile.mkstemp()
457 463 t = open(archive, 'wb')
458 464 log.debug('Creating new temp archive in %s' % archive)
459 465 cs.fill_archive(stream=t, kind=fileformat, subrepos=subrepos)
460 466 if archive_cache_enabled:
461 467 #if we generated the archive and use cache rename that
462 468 log.debug('Storing new archive in %s' % cached_archive_path)
463 469 shutil.move(archive, cached_archive_path)
464 470 archive = cached_archive_path
465 471 finally:
466 472 t.close()
467 473
468 474 def get_chunked_archive(archive):
469 475 stream = open(archive, 'rb')
470 476 while True:
471 477 data = stream.read(16 * 1024)
472 478 if not data:
473 479 stream.close()
474 480 if fd: # fd means we used temporary file
475 481 os.close(fd)
476 482 if not archive_cache_enabled:
477 483 log.debug('Destroing temp archive %s' % archive)
478 484 os.remove(archive)
479 485 break
480 486 yield data
481 487
482 488 response.content_disposition = str('attachment; filename=%s' % (archive_name))
483 489 response.content_type = str(content_type)
484 490 return get_chunked_archive(archive)
485 491
486 492 @LoginRequired()
487 493 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
488 494 'repository.admin')
489 495 def diff(self, repo_name, f_path):
490 496 ignore_whitespace = request.GET.get('ignorews') == '1'
491 497 line_context = request.GET.get('context', 3)
492 498 diff1 = request.GET.get('diff1', '')
493 499 diff2 = request.GET.get('diff2', '')
494 500 c.action = request.GET.get('diff')
495 501 c.no_changes = diff1 == diff2
496 502 c.f_path = f_path
497 503 c.big_diff = False
498 504 c.anchor_url = anchor_url
499 505 c.ignorews_url = _ignorews_url
500 506 c.context_url = _context_url
501 507 c.changes = OrderedDict()
502 508 c.changes[diff2] = []
503 509
504 510 #special case if we want a show rev only, it's impl here
505 511 #to reduce JS and callbacks
506 512
507 513 if request.GET.get('show_rev'):
508 514 if str2bool(request.GET.get('annotate', 'False')):
509 515 _url = url('files_annotate_home', repo_name=c.repo_name,
510 516 revision=diff1, f_path=c.f_path)
511 517 else:
512 518 _url = url('files_home', repo_name=c.repo_name,
513 519 revision=diff1, f_path=c.f_path)
514 520
515 521 return redirect(_url)
516 522 try:
517 523 if diff1 not in ['', None, 'None', '0' * 12, '0' * 40]:
518 524 c.changeset_1 = c.rhodecode_repo.get_changeset(diff1)
519 525 try:
520 526 node1 = c.changeset_1.get_node(f_path)
521 527 if node1.is_dir():
522 528 raise NodeError('%s path is a %s not a file' % (node1, type(node1)))
523 529 except NodeDoesNotExistError:
524 530 c.changeset_1 = EmptyChangeset(cs=diff1,
525 531 revision=c.changeset_1.revision,
526 532 repo=c.rhodecode_repo)
527 533 node1 = FileNode(f_path, '', changeset=c.changeset_1)
528 534 else:
529 535 c.changeset_1 = EmptyChangeset(repo=c.rhodecode_repo)
530 536 node1 = FileNode(f_path, '', changeset=c.changeset_1)
531 537
532 538 if diff2 not in ['', None, 'None', '0' * 12, '0' * 40]:
533 539 c.changeset_2 = c.rhodecode_repo.get_changeset(diff2)
534 540 try:
535 541 node2 = c.changeset_2.get_node(f_path)
536 542 raise NodeError('%s path is a %s not a file' % (node2, type(node2)))
537 543 except NodeDoesNotExistError:
538 544 c.changeset_2 = EmptyChangeset(cs=diff2,
539 545 revision=c.changeset_2.revision,
540 546 repo=c.rhodecode_repo)
541 547 node2 = FileNode(f_path, '', changeset=c.changeset_2)
542 548 else:
543 549 c.changeset_2 = EmptyChangeset(repo=c.rhodecode_repo)
544 550 node2 = FileNode(f_path, '', changeset=c.changeset_2)
545 551 except (RepositoryError, NodeError):
546 552 log.error(traceback.format_exc())
547 553 return redirect(url('files_home', repo_name=c.repo_name,
548 554 f_path=f_path))
549 555
550 556 if c.action == 'download':
551 557 _diff = diffs.get_gitdiff(node1, node2,
552 558 ignore_whitespace=ignore_whitespace,
553 559 context=line_context)
554 560 diff = diffs.DiffProcessor(_diff, format='gitdiff')
555 561
556 562 diff_name = '%s_vs_%s.diff' % (diff1, diff2)
557 563 response.content_type = 'text/plain'
558 564 response.content_disposition = (
559 565 'attachment; filename=%s' % diff_name
560 566 )
561 567 return diff.as_raw()
562 568
563 569 elif c.action == 'raw':
564 570 _diff = diffs.get_gitdiff(node1, node2,
565 571 ignore_whitespace=ignore_whitespace,
566 572 context=line_context)
567 573 diff = diffs.DiffProcessor(_diff, format='gitdiff')
568 574 response.content_type = 'text/plain'
569 575 return diff.as_raw()
570 576
571 577 else:
572 578 fid = h.FID(diff2, node2.path)
573 579 line_context_lcl = get_line_ctx(fid, request.GET)
574 580 ign_whitespace_lcl = get_ignore_ws(fid, request.GET)
575 581
576 582 lim = request.GET.get('fulldiff') or self.cut_off_limit
577 583 _, cs1, cs2, diff, st = diffs.wrapped_diff(filenode_old=node1,
578 584 filenode_new=node2,
579 585 cut_off_limit=lim,
580 586 ignore_whitespace=ign_whitespace_lcl,
581 587 line_context=line_context_lcl,
582 588 enable_comments=False)
583 589 op = ''
584 590 filename = node1.path
585 591 cs_changes = {
586 592 'fid': [cs1, cs2, op, filename, diff, st]
587 593 }
588 594 c.changes = cs_changes
589 595
590 596 return render('files/file_diff.html')
591 597
592 598 def _get_node_history(self, cs, f_path, changesets=None):
593 599 """
594 600 get changesets history for given node
595 601
596 602 :param cs: changeset to calculate history
597 603 :param f_path: path for node to calculate history for
598 604 :param changesets: if passed don't calculate history and take
599 605 changesets defined in this list
600 606 """
601 607 # calculate history based on tip
602 608 tip_cs = c.rhodecode_repo.get_changeset()
603 609 if changesets is None:
604 610 try:
605 611 changesets = tip_cs.get_file_history(f_path)
606 612 except (NodeDoesNotExistError, ChangesetError):
607 613 #this node is not present at tip !
608 614 changesets = cs.get_file_history(f_path)
609 615 hist_l = []
610 616
611 617 changesets_group = ([], _("Changesets"))
612 618 branches_group = ([], _("Branches"))
613 619 tags_group = ([], _("Tags"))
614 620 _hg = cs.repository.alias == 'hg'
615 621 for chs in changesets:
616 622 #_branch = '(%s)' % chs.branch if _hg else ''
617 623 _branch = chs.branch
618 624 n_desc = 'r%s:%s (%s)' % (chs.revision, chs.short_id, _branch)
619 625 changesets_group[0].append((chs.raw_id, n_desc,))
620 626 hist_l.append(changesets_group)
621 627
622 628 for name, chs in c.rhodecode_repo.branches.items():
623 629 branches_group[0].append((chs, name),)
624 630 hist_l.append(branches_group)
625 631
626 632 for name, chs in c.rhodecode_repo.tags.items():
627 633 tags_group[0].append((chs, name),)
628 634 hist_l.append(tags_group)
629 635
630 636 return hist_l, changesets
631 637
632 638 @LoginRequired()
633 639 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
634 640 'repository.admin')
635 641 @jsonify
636 642 def nodelist(self, repo_name, revision, f_path):
637 643 if request.environ.get('HTTP_X_PARTIAL_XHR'):
638 644 cs = self.__get_cs_or_redirect(revision, repo_name)
639 645 _d, _f = ScmModel().get_nodes(repo_name, cs.raw_id, f_path,
640 646 flat=False)
641 647 return {'nodes': _d + _f}
@@ -1,92 +1,92 b''
1 1 <%inherit file="/base/base.html"/>
2 2
3 3 <%def name="title()">
4 4 ${_('%s Edit file') % c.repo_name} - ${c.rhodecode_name}
5 5 </%def>
6 6
7 7 <%def name="js_extra()">
8 8 <script type="text/javascript" src="${h.url('/js/codemirror.js')}"></script>
9 9 </%def>
10 10 <%def name="css_extra()">
11 11 <link rel="stylesheet" type="text/css" href="${h.url('/css/codemirror.css')}"/>
12 12 </%def>
13 13
14 14 <%def name="breadcrumbs_links()">
15 15 ${h.link_to(_(u'Home'),h.url('/'))}
16 16 &raquo;
17 17 ${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
18 18 &raquo;
19 19 ${_('add file')} @ R${c.cs.revision}:${h.short_id(c.cs.raw_id)}
20 20 </%def>
21 21
22 22 <%def name="page_nav()">
23 23 ${self.menu('files')}
24 24 </%def>
25 25 <%def name="main()">
26 26 <div class="box">
27 27 <!-- box / title -->
28 28 <div class="title">
29 29 ${self.breadcrumbs()}
30 30 <ul class="links">
31 31 <li>
32 32 <span style="text-transform: uppercase;">
33 33 <a href="#">${_('branch')}: ${c.cs.branch}</a></span>
34 34 </li>
35 35 </ul>
36 36 </div>
37 37 <div class="table">
38 38 <div id="files_data">
39 39 ${h.form(h.url.current(),method='post',id='eform',enctype="multipart/form-data")}
40 40 <h3>${_('Add new file')}</h3>
41 41 <div class="form">
42 42 <div class="fields">
43 43 <div id="filename_container" class="field file">
44 44 <div class="label">
45 45 <label for="filename">${_('File Name')}:</label>
46 46 </div>
47 47 <div class="input">
48 48 <input type="text" value="" size="30" name="filename" id="filename">
49 49 ${_('or')} <span class="ui-btn" id="upload_file_enable">${_('Upload file')}</span>
50 50 </div>
51 51 </div>
52 52 <div id="upload_file_container" class="field" style="display:none">
53 53 <div class="label">
54 <label for="location">${_('Upload file')}</label>
54 <label for="upload_file_container">${_('Upload file')}</label>
55 55 </div>
56 56 <div class="file">
57 57 <input type="file" size="30" name="upload_file" id="upload_file">
58 58 ${_('or')} <span class="ui-btn" id="file_enable">${_('Create new file')}</span>
59 59 </div>
60 60 </div>
61 61 <div class="field">
62 62 <div class="label">
63 63 <label for="location">${_('Location')}</label>
64 64 </div>
65 65 <div class="input">
66 66 <input type="text" value="${c.f_path}" size="30" name="location" id="location">
67 67 ${_('use / to separate directories')}
68 68 </div>
69 69 </div>
70 70 </div>
71 71 </div>
72 72 <div id="body" class="codeblock">
73 73 <div id="editor_container">
74 74 <pre id="editor_pre"></pre>
75 75 <textarea id="editor" name="content" style="display:none"></textarea>
76 76 </div>
77 77 <div style="padding: 10px;color:#666666">${_('commit message')}</div>
78 78 <textarea id="commit" name="message" style="height: 100px;width: 99%;margin-left:4px" placeholder="${c.default_message}"></textarea>
79 79 </div>
80 80 <div style="text-align: l;padding-top: 5px">
81 81 ${h.submit('commit',_('Commit changes'),class_="ui-btn")}
82 82 ${h.reset('reset',_('Reset'),class_="ui-btn")}
83 83 </div>
84 84 ${h.end_form()}
85 85 <script type="text/javascript">
86 86 var reset_url = "${h.url('files_home',repo_name=c.repo_name,revision=c.cs.raw_id,f_path=c.f_path)}";
87 87 initCodeMirror('editor',reset_url);
88 88 </script>
89 89 </div>
90 90 </div>
91 91 </div>
92 92 </%def>
General Comments 0
You need to be logged in to leave comments. Login now