##// END OF EJS Templates
files: protect against 500 errors on nodelist.
marcink -
r2152:8e19a416 default
parent child Browse files
Show More
@@ -1,1282 +1,1290 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2011-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import itertools
22 22 import logging
23 23 import os
24 24 import shutil
25 25 import tempfile
26 26 import collections
27 27
28 28 from pyramid.httpexceptions import HTTPNotFound, HTTPBadRequest, HTTPFound
29 29 from pyramid.view import view_config
30 30 from pyramid.renderers import render
31 31 from pyramid.response import Response
32 32
33 33 from rhodecode.apps._base import RepoAppView
34 34
35 35 from rhodecode.controllers.utils import parse_path_ref
36 36 from rhodecode.lib import diffs, helpers as h, caches
37 37 from rhodecode.lib import audit_logger
38 38 from rhodecode.lib.exceptions import NonRelativePathError
39 39 from rhodecode.lib.codeblocks import (
40 40 filenode_as_lines_tokens, filenode_as_annotated_lines_tokens)
41 41 from rhodecode.lib.utils2 import (
42 42 convert_line_endings, detect_mode, safe_str, str2bool)
43 43 from rhodecode.lib.auth import (
44 44 LoginRequired, HasRepoPermissionAnyDecorator, CSRFRequired)
45 45 from rhodecode.lib.vcs import path as vcspath
46 46 from rhodecode.lib.vcs.backends.base import EmptyCommit
47 47 from rhodecode.lib.vcs.conf import settings
48 48 from rhodecode.lib.vcs.nodes import FileNode
49 49 from rhodecode.lib.vcs.exceptions import (
50 50 RepositoryError, CommitDoesNotExistError, EmptyRepositoryError,
51 51 ImproperArchiveTypeError, VCSError, NodeAlreadyExistsError,
52 52 NodeDoesNotExistError, CommitError, NodeError)
53 53
54 54 from rhodecode.model.scm import ScmModel
55 55 from rhodecode.model.db import Repository
56 56
57 57 log = logging.getLogger(__name__)
58 58
59 59
60 60 class RepoFilesView(RepoAppView):
61 61
62 62 @staticmethod
63 63 def adjust_file_path_for_svn(f_path, repo):
64 64 """
65 65 Computes the relative path of `f_path`.
66 66
67 67 This is mainly based on prefix matching of the recognized tags and
68 68 branches in the underlying repository.
69 69 """
70 70 tags_and_branches = itertools.chain(
71 71 repo.branches.iterkeys(),
72 72 repo.tags.iterkeys())
73 73 tags_and_branches = sorted(tags_and_branches, key=len, reverse=True)
74 74
75 75 for name in tags_and_branches:
76 76 if f_path.startswith('{}/'.format(name)):
77 77 f_path = vcspath.relpath(f_path, name)
78 78 break
79 79 return f_path
80 80
81 81 def load_default_context(self):
82 82 c = self._get_local_tmpl_context(include_app_defaults=True)
83 83
84 84 c.rhodecode_repo = self.rhodecode_vcs_repo
85 85
86 86 self._register_global_c(c)
87 87 return c
88 88
89 89 def _ensure_not_locked(self):
90 90 _ = self.request.translate
91 91
92 92 repo = self.db_repo
93 93 if repo.enable_locking and repo.locked[0]:
94 94 h.flash(_('This repository has been locked by %s on %s')
95 95 % (h.person_by_id(repo.locked[0]),
96 96 h.format_date(h.time_to_datetime(repo.locked[1]))),
97 97 'warning')
98 98 files_url = h.route_path(
99 99 'repo_files:default_path',
100 100 repo_name=self.db_repo_name, commit_id='tip')
101 101 raise HTTPFound(files_url)
102 102
103 103 def _get_commit_and_path(self):
104 104 default_commit_id = self.db_repo.landing_rev[1]
105 105 default_f_path = '/'
106 106
107 107 commit_id = self.request.matchdict.get(
108 108 'commit_id', default_commit_id)
109 109 f_path = self._get_f_path(self.request.matchdict, default_f_path)
110 110 return commit_id, f_path
111 111
112 112 def _get_default_encoding(self, c):
113 113 enc_list = getattr(c, 'default_encodings', [])
114 114 return enc_list[0] if enc_list else 'UTF-8'
115 115
116 116 def _get_commit_or_redirect(self, commit_id, redirect_after=True):
117 117 """
118 118 This is a safe way to get commit. If an error occurs it redirects to
119 119 tip with proper message
120 120
121 121 :param commit_id: id of commit to fetch
122 122 :param redirect_after: toggle redirection
123 123 """
124 124 _ = self.request.translate
125 125
126 126 try:
127 127 return self.rhodecode_vcs_repo.get_commit(commit_id)
128 128 except EmptyRepositoryError:
129 129 if not redirect_after:
130 130 return None
131 131
132 132 _url = h.route_path(
133 133 'repo_files_add_file',
134 134 repo_name=self.db_repo_name, commit_id=0, f_path='',
135 135 _anchor='edit')
136 136
137 137 if h.HasRepoPermissionAny(
138 138 'repository.write', 'repository.admin')(self.db_repo_name):
139 139 add_new = h.link_to(
140 140 _('Click here to add a new file.'), _url, class_="alert-link")
141 141 else:
142 142 add_new = ""
143 143
144 144 h.flash(h.literal(
145 145 _('There are no files yet. %s') % add_new), category='warning')
146 146 raise HTTPFound(
147 147 h.route_path('repo_summary', repo_name=self.db_repo_name))
148 148
149 149 except (CommitDoesNotExistError, LookupError):
150 150 msg = _('No such commit exists for this repository')
151 151 h.flash(msg, category='error')
152 152 raise HTTPNotFound()
153 153 except RepositoryError as e:
154 154 h.flash(safe_str(h.escape(e)), category='error')
155 155 raise HTTPNotFound()
156 156
157 157 def _get_filenode_or_redirect(self, commit_obj, path):
158 158 """
159 159 Returns file_node, if error occurs or given path is directory,
160 160 it'll redirect to top level path
161 161 """
162 162 _ = self.request.translate
163 163
164 164 try:
165 165 file_node = commit_obj.get_node(path)
166 166 if file_node.is_dir():
167 167 raise RepositoryError('The given path is a directory')
168 168 except CommitDoesNotExistError:
169 169 log.exception('No such commit exists for this repository')
170 170 h.flash(_('No such commit exists for this repository'), category='error')
171 171 raise HTTPNotFound()
172 172 except RepositoryError as e:
173 173 log.warning('Repository error while fetching '
174 174 'filenode `%s`. Err:%s', path, e)
175 175 h.flash(safe_str(h.escape(e)), category='error')
176 176 raise HTTPNotFound()
177 177
178 178 return file_node
179 179
180 180 def _is_valid_head(self, commit_id, repo):
181 181 # check if commit is a branch identifier- basically we cannot
182 182 # create multiple heads via file editing
183 183 valid_heads = repo.branches.keys() + repo.branches.values()
184 184
185 185 if h.is_svn(repo) and not repo.is_empty():
186 186 # Note: Subversion only has one head, we add it here in case there
187 187 # is no branch matched.
188 188 valid_heads.append(repo.get_commit(commit_idx=-1).raw_id)
189 189
190 190 # check if commit is a branch name or branch hash
191 191 return commit_id in valid_heads
192 192
193 193 def _get_tree_cache_manager(self, namespace_type):
194 194 _namespace = caches.get_repo_namespace_key(
195 195 namespace_type, self.db_repo_name)
196 196 return caches.get_cache_manager('repo_cache_long', _namespace)
197 197
198 198 def _get_tree_at_commit(
199 199 self, c, commit_id, f_path, full_load=False, force=False):
200 200 def _cached_tree():
201 201 log.debug('Generating cached file tree for %s, %s, %s',
202 202 self.db_repo_name, commit_id, f_path)
203 203
204 204 c.full_load = full_load
205 205 return render(
206 206 'rhodecode:templates/files/files_browser_tree.mako',
207 207 self._get_template_context(c), self.request)
208 208
209 209 cache_manager = self._get_tree_cache_manager(caches.FILE_TREE)
210 210
211 211 cache_key = caches.compute_key_from_params(
212 212 self.db_repo_name, commit_id, f_path)
213 213
214 214 if force:
215 215 # we want to force recompute of caches
216 216 cache_manager.remove_value(cache_key)
217 217
218 218 return cache_manager.get(cache_key, createfunc=_cached_tree)
219 219
220 220 def _get_archive_spec(self, fname):
221 221 log.debug('Detecting archive spec for: `%s`', fname)
222 222
223 223 fileformat = None
224 224 ext = None
225 225 content_type = None
226 226 for a_type, ext_data in settings.ARCHIVE_SPECS.items():
227 227 content_type, extension = ext_data
228 228
229 229 if fname.endswith(extension):
230 230 fileformat = a_type
231 231 log.debug('archive is of type: %s', fileformat)
232 232 ext = extension
233 233 break
234 234
235 235 if not fileformat:
236 236 raise ValueError()
237 237
238 238 # left over part of whole fname is the commit
239 239 commit_id = fname[:-len(ext)]
240 240
241 241 return commit_id, ext, fileformat, content_type
242 242
243 243 @LoginRequired()
244 244 @HasRepoPermissionAnyDecorator(
245 245 'repository.read', 'repository.write', 'repository.admin')
246 246 @view_config(
247 247 route_name='repo_archivefile', request_method='GET',
248 248 renderer=None)
249 249 def repo_archivefile(self):
250 250 # archive cache config
251 251 from rhodecode import CONFIG
252 252 _ = self.request.translate
253 253 self.load_default_context()
254 254
255 255 fname = self.request.matchdict['fname']
256 256 subrepos = self.request.GET.get('subrepos') == 'true'
257 257
258 258 if not self.db_repo.enable_downloads:
259 259 return Response(_('Downloads disabled'))
260 260
261 261 try:
262 262 commit_id, ext, fileformat, content_type = \
263 263 self._get_archive_spec(fname)
264 264 except ValueError:
265 265 return Response(_('Unknown archive type for: `{}`').format(fname))
266 266
267 267 try:
268 268 commit = self.rhodecode_vcs_repo.get_commit(commit_id)
269 269 except CommitDoesNotExistError:
270 270 return Response(_('Unknown commit_id %s') % commit_id)
271 271 except EmptyRepositoryError:
272 272 return Response(_('Empty repository'))
273 273
274 274 archive_name = '%s-%s%s%s' % (
275 275 safe_str(self.db_repo_name.replace('/', '_')),
276 276 '-sub' if subrepos else '',
277 277 safe_str(commit.short_id), ext)
278 278
279 279 use_cached_archive = False
280 280 archive_cache_enabled = CONFIG.get(
281 281 'archive_cache_dir') and not self.request.GET.get('no_cache')
282 282
283 283 if archive_cache_enabled:
284 284 # check if we it's ok to write
285 285 if not os.path.isdir(CONFIG['archive_cache_dir']):
286 286 os.makedirs(CONFIG['archive_cache_dir'])
287 287 cached_archive_path = os.path.join(
288 288 CONFIG['archive_cache_dir'], archive_name)
289 289 if os.path.isfile(cached_archive_path):
290 290 log.debug('Found cached archive in %s', cached_archive_path)
291 291 fd, archive = None, cached_archive_path
292 292 use_cached_archive = True
293 293 else:
294 294 log.debug('Archive %s is not yet cached', archive_name)
295 295
296 296 if not use_cached_archive:
297 297 # generate new archive
298 298 fd, archive = tempfile.mkstemp()
299 299 log.debug('Creating new temp archive in %s', archive)
300 300 try:
301 301 commit.archive_repo(archive, kind=fileformat, subrepos=subrepos)
302 302 except ImproperArchiveTypeError:
303 303 return _('Unknown archive type')
304 304 if archive_cache_enabled:
305 305 # if we generated the archive and we have cache enabled
306 306 # let's use this for future
307 307 log.debug('Storing new archive in %s', cached_archive_path)
308 308 shutil.move(archive, cached_archive_path)
309 309 archive = cached_archive_path
310 310
311 311 # store download action
312 312 audit_logger.store_web(
313 313 'repo.archive.download', action_data={
314 314 'user_agent': self.request.user_agent,
315 315 'archive_name': archive_name,
316 316 'archive_spec': fname,
317 317 'archive_cached': use_cached_archive},
318 318 user=self._rhodecode_user,
319 319 repo=self.db_repo,
320 320 commit=True
321 321 )
322 322
323 323 def get_chunked_archive(archive):
324 324 with open(archive, 'rb') as stream:
325 325 while True:
326 326 data = stream.read(16 * 1024)
327 327 if not data:
328 328 if fd: # fd means we used temporary file
329 329 os.close(fd)
330 330 if not archive_cache_enabled:
331 331 log.debug('Destroying temp archive %s', archive)
332 332 os.remove(archive)
333 333 break
334 334 yield data
335 335
336 336 response = Response(app_iter=get_chunked_archive(archive))
337 337 response.content_disposition = str(
338 338 'attachment; filename=%s' % archive_name)
339 339 response.content_type = str(content_type)
340 340
341 341 return response
342 342
343 343 def _get_file_node(self, commit_id, f_path):
344 344 if commit_id not in ['', None, 'None', '0' * 12, '0' * 40]:
345 345 commit = self.rhodecode_vcs_repo.get_commit(commit_id=commit_id)
346 346 try:
347 347 node = commit.get_node(f_path)
348 348 if node.is_dir():
349 349 raise NodeError('%s path is a %s not a file'
350 350 % (node, type(node)))
351 351 except NodeDoesNotExistError:
352 352 commit = EmptyCommit(
353 353 commit_id=commit_id,
354 354 idx=commit.idx,
355 355 repo=commit.repository,
356 356 alias=commit.repository.alias,
357 357 message=commit.message,
358 358 author=commit.author,
359 359 date=commit.date)
360 360 node = FileNode(f_path, '', commit=commit)
361 361 else:
362 362 commit = EmptyCommit(
363 363 repo=self.rhodecode_vcs_repo,
364 364 alias=self.rhodecode_vcs_repo.alias)
365 365 node = FileNode(f_path, '', commit=commit)
366 366 return node
367 367
368 368 @LoginRequired()
369 369 @HasRepoPermissionAnyDecorator(
370 370 'repository.read', 'repository.write', 'repository.admin')
371 371 @view_config(
372 372 route_name='repo_files_diff', request_method='GET',
373 373 renderer=None)
374 374 def repo_files_diff(self):
375 375 c = self.load_default_context()
376 376 f_path = self._get_f_path(self.request.matchdict)
377 377 diff1 = self.request.GET.get('diff1', '')
378 378 diff2 = self.request.GET.get('diff2', '')
379 379
380 380 path1, diff1 = parse_path_ref(diff1, default_path=f_path)
381 381
382 382 ignore_whitespace = str2bool(self.request.GET.get('ignorews'))
383 383 line_context = self.request.GET.get('context', 3)
384 384
385 385 if not any((diff1, diff2)):
386 386 h.flash(
387 387 'Need query parameter "diff1" or "diff2" to generate a diff.',
388 388 category='error')
389 389 raise HTTPBadRequest()
390 390
391 391 c.action = self.request.GET.get('diff')
392 392 if c.action not in ['download', 'raw']:
393 393 compare_url = h.route_path(
394 394 'repo_compare',
395 395 repo_name=self.db_repo_name,
396 396 source_ref_type='rev',
397 397 source_ref=diff1,
398 398 target_repo=self.db_repo_name,
399 399 target_ref_type='rev',
400 400 target_ref=diff2,
401 401 _query=dict(f_path=f_path))
402 402 # redirect to new view if we render diff
403 403 raise HTTPFound(compare_url)
404 404
405 405 try:
406 406 node1 = self._get_file_node(diff1, path1)
407 407 node2 = self._get_file_node(diff2, f_path)
408 408 except (RepositoryError, NodeError):
409 409 log.exception("Exception while trying to get node from repository")
410 410 raise HTTPFound(
411 411 h.route_path('repo_files', repo_name=self.db_repo_name,
412 412 commit_id='tip', f_path=f_path))
413 413
414 414 if all(isinstance(node.commit, EmptyCommit)
415 415 for node in (node1, node2)):
416 416 raise HTTPNotFound()
417 417
418 418 c.commit_1 = node1.commit
419 419 c.commit_2 = node2.commit
420 420
421 421 if c.action == 'download':
422 422 _diff = diffs.get_gitdiff(node1, node2,
423 423 ignore_whitespace=ignore_whitespace,
424 424 context=line_context)
425 425 diff = diffs.DiffProcessor(_diff, format='gitdiff')
426 426
427 427 response = Response(diff.as_raw())
428 428 response.content_type = 'text/plain'
429 429 response.content_disposition = (
430 430 'attachment; filename=%s_%s_vs_%s.diff' % (f_path, diff1, diff2)
431 431 )
432 432 charset = self._get_default_encoding(c)
433 433 if charset:
434 434 response.charset = charset
435 435 return response
436 436
437 437 elif c.action == 'raw':
438 438 _diff = diffs.get_gitdiff(node1, node2,
439 439 ignore_whitespace=ignore_whitespace,
440 440 context=line_context)
441 441 diff = diffs.DiffProcessor(_diff, format='gitdiff')
442 442
443 443 response = Response(diff.as_raw())
444 444 response.content_type = 'text/plain'
445 445 charset = self._get_default_encoding(c)
446 446 if charset:
447 447 response.charset = charset
448 448 return response
449 449
450 450 # in case we ever end up here
451 451 raise HTTPNotFound()
452 452
453 453 @LoginRequired()
454 454 @HasRepoPermissionAnyDecorator(
455 455 'repository.read', 'repository.write', 'repository.admin')
456 456 @view_config(
457 457 route_name='repo_files_diff_2way_redirect', request_method='GET',
458 458 renderer=None)
459 459 def repo_files_diff_2way_redirect(self):
460 460 """
461 461 Kept only to make OLD links work
462 462 """
463 463 f_path = self._get_f_path(self.request.matchdict)
464 464 diff1 = self.request.GET.get('diff1', '')
465 465 diff2 = self.request.GET.get('diff2', '')
466 466
467 467 if not any((diff1, diff2)):
468 468 h.flash(
469 469 'Need query parameter "diff1" or "diff2" to generate a diff.',
470 470 category='error')
471 471 raise HTTPBadRequest()
472 472
473 473 compare_url = h.route_path(
474 474 'repo_compare',
475 475 repo_name=self.db_repo_name,
476 476 source_ref_type='rev',
477 477 source_ref=diff1,
478 478 target_ref_type='rev',
479 479 target_ref=diff2,
480 480 _query=dict(f_path=f_path, diffmode='sideside',
481 481 target_repo=self.db_repo_name,))
482 482 raise HTTPFound(compare_url)
483 483
484 484 @LoginRequired()
485 485 @HasRepoPermissionAnyDecorator(
486 486 'repository.read', 'repository.write', 'repository.admin')
487 487 @view_config(
488 488 route_name='repo_files', request_method='GET',
489 489 renderer=None)
490 490 @view_config(
491 491 route_name='repo_files:default_path', request_method='GET',
492 492 renderer=None)
493 493 @view_config(
494 494 route_name='repo_files:default_commit', request_method='GET',
495 495 renderer=None)
496 496 @view_config(
497 497 route_name='repo_files:rendered', request_method='GET',
498 498 renderer=None)
499 499 @view_config(
500 500 route_name='repo_files:annotated', request_method='GET',
501 501 renderer=None)
502 502 def repo_files(self):
503 503 c = self.load_default_context()
504 504
505 505 view_name = getattr(self.request.matched_route, 'name', None)
506 506
507 507 c.annotate = view_name == 'repo_files:annotated'
508 508 # default is false, but .rst/.md files later are auto rendered, we can
509 509 # overwrite auto rendering by setting this GET flag
510 510 c.renderer = view_name == 'repo_files:rendered' or \
511 511 not self.request.GET.get('no-render', False)
512 512
513 513 # redirect to given commit_id from form if given
514 514 get_commit_id = self.request.GET.get('at_rev', None)
515 515 if get_commit_id:
516 516 self._get_commit_or_redirect(get_commit_id)
517 517
518 518 commit_id, f_path = self._get_commit_and_path()
519 519 c.commit = self._get_commit_or_redirect(commit_id)
520 520 c.branch = self.request.GET.get('branch', None)
521 521 c.f_path = f_path
522 522
523 523 # prev link
524 524 try:
525 525 prev_commit = c.commit.prev(c.branch)
526 526 c.prev_commit = prev_commit
527 527 c.url_prev = h.route_path(
528 528 'repo_files', repo_name=self.db_repo_name,
529 529 commit_id=prev_commit.raw_id, f_path=f_path)
530 530 if c.branch:
531 531 c.url_prev += '?branch=%s' % c.branch
532 532 except (CommitDoesNotExistError, VCSError):
533 533 c.url_prev = '#'
534 534 c.prev_commit = EmptyCommit()
535 535
536 536 # next link
537 537 try:
538 538 next_commit = c.commit.next(c.branch)
539 539 c.next_commit = next_commit
540 540 c.url_next = h.route_path(
541 541 'repo_files', repo_name=self.db_repo_name,
542 542 commit_id=next_commit.raw_id, f_path=f_path)
543 543 if c.branch:
544 544 c.url_next += '?branch=%s' % c.branch
545 545 except (CommitDoesNotExistError, VCSError):
546 546 c.url_next = '#'
547 547 c.next_commit = EmptyCommit()
548 548
549 549 # files or dirs
550 550 try:
551 551 c.file = c.commit.get_node(f_path)
552 552 c.file_author = True
553 553 c.file_tree = ''
554 554
555 555 # load file content
556 556 if c.file.is_file():
557 557 c.lf_node = c.file.get_largefile_node()
558 558
559 559 c.file_source_page = 'true'
560 560 c.file_last_commit = c.file.last_commit
561 561 if c.file.size < c.visual.cut_off_limit_diff:
562 562 if c.annotate: # annotation has precedence over renderer
563 563 c.annotated_lines = filenode_as_annotated_lines_tokens(
564 564 c.file
565 565 )
566 566 else:
567 567 c.renderer = (
568 568 c.renderer and h.renderer_from_filename(c.file.path)
569 569 )
570 570 if not c.renderer:
571 571 c.lines = filenode_as_lines_tokens(c.file)
572 572
573 573 c.on_branch_head = self._is_valid_head(
574 574 commit_id, self.rhodecode_vcs_repo)
575 575
576 576 branch = c.commit.branch if (
577 577 c.commit.branch and '/' not in c.commit.branch) else None
578 578 c.branch_or_raw_id = branch or c.commit.raw_id
579 579 c.branch_name = c.commit.branch or h.short_id(c.commit.raw_id)
580 580
581 581 author = c.file_last_commit.author
582 582 c.authors = [[
583 583 h.email(author),
584 584 h.person(author, 'username_or_name_or_email'),
585 585 1
586 586 ]]
587 587
588 588 else: # load tree content at path
589 589 c.file_source_page = 'false'
590 590 c.authors = []
591 591 # this loads a simple tree without metadata to speed things up
592 592 # later via ajax we call repo_nodetree_full and fetch whole
593 593 c.file_tree = self._get_tree_at_commit(
594 594 c, c.commit.raw_id, f_path)
595 595
596 596 except RepositoryError as e:
597 597 h.flash(safe_str(h.escape(e)), category='error')
598 598 raise HTTPNotFound()
599 599
600 600 if self.request.environ.get('HTTP_X_PJAX'):
601 601 html = render('rhodecode:templates/files/files_pjax.mako',
602 602 self._get_template_context(c), self.request)
603 603 else:
604 604 html = render('rhodecode:templates/files/files.mako',
605 605 self._get_template_context(c), self.request)
606 606 return Response(html)
607 607
608 608 @HasRepoPermissionAnyDecorator(
609 609 'repository.read', 'repository.write', 'repository.admin')
610 610 @view_config(
611 611 route_name='repo_files:annotated_previous', request_method='GET',
612 612 renderer=None)
613 613 def repo_files_annotated_previous(self):
614 614 self.load_default_context()
615 615
616 616 commit_id, f_path = self._get_commit_and_path()
617 617 commit = self._get_commit_or_redirect(commit_id)
618 618 prev_commit_id = commit.raw_id
619 619 line_anchor = self.request.GET.get('line_anchor')
620 620 is_file = False
621 621 try:
622 622 _file = commit.get_node(f_path)
623 623 is_file = _file.is_file()
624 624 except (NodeDoesNotExistError, CommitDoesNotExistError, VCSError):
625 625 pass
626 626
627 627 if is_file:
628 628 history = commit.get_file_history(f_path)
629 629 prev_commit_id = history[1].raw_id \
630 630 if len(history) > 1 else prev_commit_id
631 631 prev_url = h.route_path(
632 632 'repo_files:annotated', repo_name=self.db_repo_name,
633 633 commit_id=prev_commit_id, f_path=f_path,
634 634 _anchor='L{}'.format(line_anchor))
635 635
636 636 raise HTTPFound(prev_url)
637 637
638 638 @LoginRequired()
639 639 @HasRepoPermissionAnyDecorator(
640 640 'repository.read', 'repository.write', 'repository.admin')
641 641 @view_config(
642 642 route_name='repo_nodetree_full', request_method='GET',
643 643 renderer=None, xhr=True)
644 644 @view_config(
645 645 route_name='repo_nodetree_full:default_path', request_method='GET',
646 646 renderer=None, xhr=True)
647 647 def repo_nodetree_full(self):
648 648 """
649 649 Returns rendered html of file tree that contains commit date,
650 650 author, commit_id for the specified combination of
651 651 repo, commit_id and file path
652 652 """
653 653 c = self.load_default_context()
654 654
655 655 commit_id, f_path = self._get_commit_and_path()
656 656 commit = self._get_commit_or_redirect(commit_id)
657 657 try:
658 658 dir_node = commit.get_node(f_path)
659 659 except RepositoryError as e:
660 660 return Response('error: {}'.format(safe_str(e)))
661 661
662 662 if dir_node.is_file():
663 663 return Response('')
664 664
665 665 c.file = dir_node
666 666 c.commit = commit
667 667
668 668 # using force=True here, make a little trick. We flush the cache and
669 669 # compute it using the same key as without previous full_load, so now
670 670 # the fully loaded tree is now returned instead of partial,
671 671 # and we store this in caches
672 672 html = self._get_tree_at_commit(
673 673 c, commit.raw_id, dir_node.path, full_load=True, force=True)
674 674
675 675 return Response(html)
676 676
677 677 def _get_attachement_disposition(self, f_path):
678 678 return 'attachment; filename=%s' % \
679 679 safe_str(f_path.split(Repository.NAME_SEP)[-1])
680 680
681 681 @LoginRequired()
682 682 @HasRepoPermissionAnyDecorator(
683 683 'repository.read', 'repository.write', 'repository.admin')
684 684 @view_config(
685 685 route_name='repo_file_raw', request_method='GET',
686 686 renderer=None)
687 687 def repo_file_raw(self):
688 688 """
689 689 Action for show as raw, some mimetypes are "rendered",
690 690 those include images, icons.
691 691 """
692 692 c = self.load_default_context()
693 693
694 694 commit_id, f_path = self._get_commit_and_path()
695 695 commit = self._get_commit_or_redirect(commit_id)
696 696 file_node = self._get_filenode_or_redirect(commit, f_path)
697 697
698 698 raw_mimetype_mapping = {
699 699 # map original mimetype to a mimetype used for "show as raw"
700 700 # you can also provide a content-disposition to override the
701 701 # default "attachment" disposition.
702 702 # orig_type: (new_type, new_dispo)
703 703
704 704 # show images inline:
705 705 # Do not re-add SVG: it is unsafe and permits XSS attacks. One can
706 706 # for example render an SVG with javascript inside or even render
707 707 # HTML.
708 708 'image/x-icon': ('image/x-icon', 'inline'),
709 709 'image/png': ('image/png', 'inline'),
710 710 'image/gif': ('image/gif', 'inline'),
711 711 'image/jpeg': ('image/jpeg', 'inline'),
712 712 'application/pdf': ('application/pdf', 'inline'),
713 713 }
714 714
715 715 mimetype = file_node.mimetype
716 716 try:
717 717 mimetype, disposition = raw_mimetype_mapping[mimetype]
718 718 except KeyError:
719 719 # we don't know anything special about this, handle it safely
720 720 if file_node.is_binary:
721 721 # do same as download raw for binary files
722 722 mimetype, disposition = 'application/octet-stream', 'attachment'
723 723 else:
724 724 # do not just use the original mimetype, but force text/plain,
725 725 # otherwise it would serve text/html and that might be unsafe.
726 726 # Note: underlying vcs library fakes text/plain mimetype if the
727 727 # mimetype can not be determined and it thinks it is not
728 728 # binary.This might lead to erroneous text display in some
729 729 # cases, but helps in other cases, like with text files
730 730 # without extension.
731 731 mimetype, disposition = 'text/plain', 'inline'
732 732
733 733 if disposition == 'attachment':
734 734 disposition = self._get_attachement_disposition(f_path)
735 735
736 736 def stream_node():
737 737 yield file_node.raw_bytes
738 738
739 739 response = Response(app_iter=stream_node())
740 740 response.content_disposition = disposition
741 741 response.content_type = mimetype
742 742
743 743 charset = self._get_default_encoding(c)
744 744 if charset:
745 745 response.charset = charset
746 746
747 747 return response
748 748
749 749 @LoginRequired()
750 750 @HasRepoPermissionAnyDecorator(
751 751 'repository.read', 'repository.write', 'repository.admin')
752 752 @view_config(
753 753 route_name='repo_file_download', request_method='GET',
754 754 renderer=None)
755 755 @view_config(
756 756 route_name='repo_file_download:legacy', request_method='GET',
757 757 renderer=None)
758 758 def repo_file_download(self):
759 759 c = self.load_default_context()
760 760
761 761 commit_id, f_path = self._get_commit_and_path()
762 762 commit = self._get_commit_or_redirect(commit_id)
763 763 file_node = self._get_filenode_or_redirect(commit, f_path)
764 764
765 765 if self.request.GET.get('lf'):
766 766 # only if lf get flag is passed, we download this file
767 767 # as LFS/Largefile
768 768 lf_node = file_node.get_largefile_node()
769 769 if lf_node:
770 770 # overwrite our pointer with the REAL large-file
771 771 file_node = lf_node
772 772
773 773 disposition = self._get_attachement_disposition(f_path)
774 774
775 775 def stream_node():
776 776 yield file_node.raw_bytes
777 777
778 778 response = Response(app_iter=stream_node())
779 779 response.content_disposition = disposition
780 780 response.content_type = file_node.mimetype
781 781
782 782 charset = self._get_default_encoding(c)
783 783 if charset:
784 784 response.charset = charset
785 785
786 786 return response
787 787
788 788 def _get_nodelist_at_commit(self, repo_name, commit_id, f_path):
789 789 def _cached_nodes():
790 790 log.debug('Generating cached nodelist for %s, %s, %s',
791 791 repo_name, commit_id, f_path)
792 try:
792 793 _d, _f = ScmModel().get_nodes(
793 794 repo_name, commit_id, f_path, flat=False)
795 except (RepositoryError, CommitDoesNotExistError, Exception) as e:
796 log.exception(safe_str(e))
797 h.flash(safe_str(h.escape(e)), category='error')
798 raise HTTPFound(h.route_path(
799 'repo_files', repo_name=self.db_repo_name,
800 commit_id='tip', f_path='/'))
794 801 return _d + _f
795 802
796 cache_manager = self._get_tree_cache_manager(caches.FILE_SEARCH_TREE_META)
803 cache_manager = self._get_tree_cache_manager(
804 caches.FILE_SEARCH_TREE_META)
797 805
798 806 cache_key = caches.compute_key_from_params(
799 807 repo_name, commit_id, f_path)
800 808 return cache_manager.get(cache_key, createfunc=_cached_nodes)
801 809
802 810 @LoginRequired()
803 811 @HasRepoPermissionAnyDecorator(
804 812 'repository.read', 'repository.write', 'repository.admin')
805 813 @view_config(
806 814 route_name='repo_files_nodelist', request_method='GET',
807 815 renderer='json_ext', xhr=True)
808 816 def repo_nodelist(self):
809 817 self.load_default_context()
810 818
811 819 commit_id, f_path = self._get_commit_and_path()
812 820 commit = self._get_commit_or_redirect(commit_id)
813 821
814 822 metadata = self._get_nodelist_at_commit(
815 823 self.db_repo_name, commit.raw_id, f_path)
816 824 return {'nodes': metadata}
817 825
818 826 def _create_references(
819 827 self, branches_or_tags, symbolic_reference, f_path):
820 828 items = []
821 829 for name, commit_id in branches_or_tags.items():
822 830 sym_ref = symbolic_reference(commit_id, name, f_path)
823 831 items.append((sym_ref, name))
824 832 return items
825 833
826 834 def _symbolic_reference(self, commit_id, name, f_path):
827 835 return commit_id
828 836
829 837 def _symbolic_reference_svn(self, commit_id, name, f_path):
830 838 new_f_path = vcspath.join(name, f_path)
831 839 return u'%s@%s' % (new_f_path, commit_id)
832 840
833 841 def _get_node_history(self, commit_obj, f_path, commits=None):
834 842 """
835 843 get commit history for given node
836 844
837 845 :param commit_obj: commit to calculate history
838 846 :param f_path: path for node to calculate history for
839 847 :param commits: if passed don't calculate history and take
840 848 commits defined in this list
841 849 """
842 850 _ = self.request.translate
843 851
844 852 # calculate history based on tip
845 853 tip = self.rhodecode_vcs_repo.get_commit()
846 854 if commits is None:
847 855 pre_load = ["author", "branch"]
848 856 try:
849 857 commits = tip.get_file_history(f_path, pre_load=pre_load)
850 858 except (NodeDoesNotExistError, CommitError):
851 859 # this node is not present at tip!
852 860 commits = commit_obj.get_file_history(f_path, pre_load=pre_load)
853 861
854 862 history = []
855 863 commits_group = ([], _("Changesets"))
856 864 for commit in commits:
857 865 branch = ' (%s)' % commit.branch if commit.branch else ''
858 866 n_desc = 'r%s:%s%s' % (commit.idx, commit.short_id, branch)
859 867 commits_group[0].append((commit.raw_id, n_desc,))
860 868 history.append(commits_group)
861 869
862 870 symbolic_reference = self._symbolic_reference
863 871
864 872 if self.rhodecode_vcs_repo.alias == 'svn':
865 873 adjusted_f_path = RepoFilesView.adjust_file_path_for_svn(
866 874 f_path, self.rhodecode_vcs_repo)
867 875 if adjusted_f_path != f_path:
868 876 log.debug(
869 877 'Recognized svn tag or branch in file "%s", using svn '
870 878 'specific symbolic references', f_path)
871 879 f_path = adjusted_f_path
872 880 symbolic_reference = self._symbolic_reference_svn
873 881
874 882 branches = self._create_references(
875 883 self.rhodecode_vcs_repo.branches, symbolic_reference, f_path)
876 884 branches_group = (branches, _("Branches"))
877 885
878 886 tags = self._create_references(
879 887 self.rhodecode_vcs_repo.tags, symbolic_reference, f_path)
880 888 tags_group = (tags, _("Tags"))
881 889
882 890 history.append(branches_group)
883 891 history.append(tags_group)
884 892
885 893 return history, commits
886 894
887 895 @LoginRequired()
888 896 @HasRepoPermissionAnyDecorator(
889 897 'repository.read', 'repository.write', 'repository.admin')
890 898 @view_config(
891 899 route_name='repo_file_history', request_method='GET',
892 900 renderer='json_ext')
893 901 def repo_file_history(self):
894 902 self.load_default_context()
895 903
896 904 commit_id, f_path = self._get_commit_and_path()
897 905 commit = self._get_commit_or_redirect(commit_id)
898 906 file_node = self._get_filenode_or_redirect(commit, f_path)
899 907
900 908 if file_node.is_file():
901 909 file_history, _hist = self._get_node_history(commit, f_path)
902 910
903 911 res = []
904 912 for obj in file_history:
905 913 res.append({
906 914 'text': obj[1],
907 915 'children': [{'id': o[0], 'text': o[1]} for o in obj[0]]
908 916 })
909 917
910 918 data = {
911 919 'more': False,
912 920 'results': res
913 921 }
914 922 return data
915 923
916 924 log.warning('Cannot fetch history for directory')
917 925 raise HTTPBadRequest()
918 926
919 927 @LoginRequired()
920 928 @HasRepoPermissionAnyDecorator(
921 929 'repository.read', 'repository.write', 'repository.admin')
922 930 @view_config(
923 931 route_name='repo_file_authors', request_method='GET',
924 932 renderer='rhodecode:templates/files/file_authors_box.mako')
925 933 def repo_file_authors(self):
926 934 c = self.load_default_context()
927 935
928 936 commit_id, f_path = self._get_commit_and_path()
929 937 commit = self._get_commit_or_redirect(commit_id)
930 938 file_node = self._get_filenode_or_redirect(commit, f_path)
931 939
932 940 if not file_node.is_file():
933 941 raise HTTPBadRequest()
934 942
935 943 c.file_last_commit = file_node.last_commit
936 944 if self.request.GET.get('annotate') == '1':
937 945 # use _hist from annotation if annotation mode is on
938 946 commit_ids = set(x[1] for x in file_node.annotate)
939 947 _hist = (
940 948 self.rhodecode_vcs_repo.get_commit(commit_id)
941 949 for commit_id in commit_ids)
942 950 else:
943 951 _f_history, _hist = self._get_node_history(commit, f_path)
944 952 c.file_author = False
945 953
946 954 unique = collections.OrderedDict()
947 955 for commit in _hist:
948 956 author = commit.author
949 957 if author not in unique:
950 958 unique[commit.author] = [
951 959 h.email(author),
952 960 h.person(author, 'username_or_name_or_email'),
953 961 1 # counter
954 962 ]
955 963
956 964 else:
957 965 # increase counter
958 966 unique[commit.author][2] += 1
959 967
960 968 c.authors = [val for val in unique.values()]
961 969
962 970 return self._get_template_context(c)
963 971
964 972 @LoginRequired()
965 973 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
966 974 @view_config(
967 975 route_name='repo_files_remove_file', request_method='GET',
968 976 renderer='rhodecode:templates/files/files_delete.mako')
969 977 def repo_files_remove_file(self):
970 978 _ = self.request.translate
971 979 c = self.load_default_context()
972 980 commit_id, f_path = self._get_commit_and_path()
973 981
974 982 self._ensure_not_locked()
975 983
976 984 if not self._is_valid_head(commit_id, self.rhodecode_vcs_repo):
977 985 h.flash(_('You can only delete files with commit '
978 986 'being a valid branch '), category='warning')
979 987 raise HTTPFound(
980 988 h.route_path('repo_files',
981 989 repo_name=self.db_repo_name, commit_id='tip',
982 990 f_path=f_path))
983 991
984 992 c.commit = self._get_commit_or_redirect(commit_id)
985 993 c.file = self._get_filenode_or_redirect(c.commit, f_path)
986 994
987 995 c.default_message = _(
988 996 'Deleted file {} via RhodeCode Enterprise').format(f_path)
989 997 c.f_path = f_path
990 998
991 999 return self._get_template_context(c)
992 1000
993 1001 @LoginRequired()
994 1002 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
995 1003 @CSRFRequired()
996 1004 @view_config(
997 1005 route_name='repo_files_delete_file', request_method='POST',
998 1006 renderer=None)
999 1007 def repo_files_delete_file(self):
1000 1008 _ = self.request.translate
1001 1009
1002 1010 c = self.load_default_context()
1003 1011 commit_id, f_path = self._get_commit_and_path()
1004 1012
1005 1013 self._ensure_not_locked()
1006 1014
1007 1015 if not self._is_valid_head(commit_id, self.rhodecode_vcs_repo):
1008 1016 h.flash(_('You can only delete files with commit '
1009 1017 'being a valid branch '), category='warning')
1010 1018 raise HTTPFound(
1011 1019 h.route_path('repo_files',
1012 1020 repo_name=self.db_repo_name, commit_id='tip',
1013 1021 f_path=f_path))
1014 1022
1015 1023 c.commit = self._get_commit_or_redirect(commit_id)
1016 1024 c.file = self._get_filenode_or_redirect(c.commit, f_path)
1017 1025
1018 1026 c.default_message = _(
1019 1027 'Deleted file {} via RhodeCode Enterprise').format(f_path)
1020 1028 c.f_path = f_path
1021 1029 node_path = f_path
1022 1030 author = self._rhodecode_db_user.full_contact
1023 1031 message = self.request.POST.get('message') or c.default_message
1024 1032 try:
1025 1033 nodes = {
1026 1034 node_path: {
1027 1035 'content': ''
1028 1036 }
1029 1037 }
1030 1038 ScmModel().delete_nodes(
1031 1039 user=self._rhodecode_db_user.user_id, repo=self.db_repo,
1032 1040 message=message,
1033 1041 nodes=nodes,
1034 1042 parent_commit=c.commit,
1035 1043 author=author,
1036 1044 )
1037 1045
1038 1046 h.flash(
1039 1047 _('Successfully deleted file `{}`').format(
1040 1048 h.escape(f_path)), category='success')
1041 1049 except Exception:
1042 1050 log.exception('Error during commit operation')
1043 1051 h.flash(_('Error occurred during commit'), category='error')
1044 1052 raise HTTPFound(
1045 1053 h.route_path('repo_commit', repo_name=self.db_repo_name,
1046 1054 commit_id='tip'))
1047 1055
1048 1056 @LoginRequired()
1049 1057 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1050 1058 @view_config(
1051 1059 route_name='repo_files_edit_file', request_method='GET',
1052 1060 renderer='rhodecode:templates/files/files_edit.mako')
1053 1061 def repo_files_edit_file(self):
1054 1062 _ = self.request.translate
1055 1063 c = self.load_default_context()
1056 1064 commit_id, f_path = self._get_commit_and_path()
1057 1065
1058 1066 self._ensure_not_locked()
1059 1067
1060 1068 if not self._is_valid_head(commit_id, self.rhodecode_vcs_repo):
1061 1069 h.flash(_('You can only edit files with commit '
1062 1070 'being a valid branch '), category='warning')
1063 1071 raise HTTPFound(
1064 1072 h.route_path('repo_files',
1065 1073 repo_name=self.db_repo_name, commit_id='tip',
1066 1074 f_path=f_path))
1067 1075
1068 1076 c.commit = self._get_commit_or_redirect(commit_id)
1069 1077 c.file = self._get_filenode_or_redirect(c.commit, f_path)
1070 1078
1071 1079 if c.file.is_binary:
1072 1080 files_url = h.route_path(
1073 1081 'repo_files',
1074 1082 repo_name=self.db_repo_name,
1075 1083 commit_id=c.commit.raw_id, f_path=f_path)
1076 1084 raise HTTPFound(files_url)
1077 1085
1078 1086 c.default_message = _(
1079 1087 'Edited file {} via RhodeCode Enterprise').format(f_path)
1080 1088 c.f_path = f_path
1081 1089
1082 1090 return self._get_template_context(c)
1083 1091
1084 1092 @LoginRequired()
1085 1093 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1086 1094 @CSRFRequired()
1087 1095 @view_config(
1088 1096 route_name='repo_files_update_file', request_method='POST',
1089 1097 renderer=None)
1090 1098 def repo_files_update_file(self):
1091 1099 _ = self.request.translate
1092 1100 c = self.load_default_context()
1093 1101 commit_id, f_path = self._get_commit_and_path()
1094 1102
1095 1103 self._ensure_not_locked()
1096 1104
1097 1105 if not self._is_valid_head(commit_id, self.rhodecode_vcs_repo):
1098 1106 h.flash(_('You can only edit files with commit '
1099 1107 'being a valid branch '), category='warning')
1100 1108 raise HTTPFound(
1101 1109 h.route_path('repo_files',
1102 1110 repo_name=self.db_repo_name, commit_id='tip',
1103 1111 f_path=f_path))
1104 1112
1105 1113 c.commit = self._get_commit_or_redirect(commit_id)
1106 1114 c.file = self._get_filenode_or_redirect(c.commit, f_path)
1107 1115
1108 1116 if c.file.is_binary:
1109 1117 raise HTTPFound(
1110 1118 h.route_path('repo_files',
1111 1119 repo_name=self.db_repo_name,
1112 1120 commit_id=c.commit.raw_id,
1113 1121 f_path=f_path))
1114 1122
1115 1123 c.default_message = _(
1116 1124 'Edited file {} via RhodeCode Enterprise').format(f_path)
1117 1125 c.f_path = f_path
1118 1126 old_content = c.file.content
1119 1127 sl = old_content.splitlines(1)
1120 1128 first_line = sl[0] if sl else ''
1121 1129
1122 1130 r_post = self.request.POST
1123 1131 # modes: 0 - Unix, 1 - Mac, 2 - DOS
1124 1132 mode = detect_mode(first_line, 0)
1125 1133 content = convert_line_endings(r_post.get('content', ''), mode)
1126 1134
1127 1135 message = r_post.get('message') or c.default_message
1128 1136 org_f_path = c.file.unicode_path
1129 1137 filename = r_post['filename']
1130 1138 org_filename = c.file.name
1131 1139
1132 1140 if content == old_content and filename == org_filename:
1133 1141 h.flash(_('No changes'), category='warning')
1134 1142 raise HTTPFound(
1135 1143 h.route_path('repo_commit', repo_name=self.db_repo_name,
1136 1144 commit_id='tip'))
1137 1145 try:
1138 1146 mapping = {
1139 1147 org_f_path: {
1140 1148 'org_filename': org_f_path,
1141 1149 'filename': os.path.join(c.file.dir_path, filename),
1142 1150 'content': content,
1143 1151 'lexer': '',
1144 1152 'op': 'mod',
1145 1153 }
1146 1154 }
1147 1155
1148 1156 ScmModel().update_nodes(
1149 1157 user=self._rhodecode_db_user.user_id,
1150 1158 repo=self.db_repo,
1151 1159 message=message,
1152 1160 nodes=mapping,
1153 1161 parent_commit=c.commit,
1154 1162 )
1155 1163
1156 1164 h.flash(
1157 1165 _('Successfully committed changes to file `{}`').format(
1158 1166 h.escape(f_path)), category='success')
1159 1167 except Exception:
1160 1168 log.exception('Error occurred during commit')
1161 1169 h.flash(_('Error occurred during commit'), category='error')
1162 1170 raise HTTPFound(
1163 1171 h.route_path('repo_commit', repo_name=self.db_repo_name,
1164 1172 commit_id='tip'))
1165 1173
1166 1174 @LoginRequired()
1167 1175 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1168 1176 @view_config(
1169 1177 route_name='repo_files_add_file', request_method='GET',
1170 1178 renderer='rhodecode:templates/files/files_add.mako')
1171 1179 def repo_files_add_file(self):
1172 1180 _ = self.request.translate
1173 1181 c = self.load_default_context()
1174 1182 commit_id, f_path = self._get_commit_and_path()
1175 1183
1176 1184 self._ensure_not_locked()
1177 1185
1178 1186 c.commit = self._get_commit_or_redirect(commit_id, redirect_after=False)
1179 1187 if c.commit is None:
1180 1188 c.commit = EmptyCommit(alias=self.rhodecode_vcs_repo.alias)
1181 1189 c.default_message = (_('Added file via RhodeCode Enterprise'))
1182 1190 c.f_path = f_path.lstrip('/') # ensure not relative path
1183 1191
1184 1192 return self._get_template_context(c)
1185 1193
1186 1194 @LoginRequired()
1187 1195 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1188 1196 @CSRFRequired()
1189 1197 @view_config(
1190 1198 route_name='repo_files_create_file', request_method='POST',
1191 1199 renderer=None)
1192 1200 def repo_files_create_file(self):
1193 1201 _ = self.request.translate
1194 1202 c = self.load_default_context()
1195 1203 commit_id, f_path = self._get_commit_and_path()
1196 1204
1197 1205 self._ensure_not_locked()
1198 1206
1199 1207 r_post = self.request.POST
1200 1208
1201 1209 c.commit = self._get_commit_or_redirect(
1202 1210 commit_id, redirect_after=False)
1203 1211 if c.commit is None:
1204 1212 c.commit = EmptyCommit(alias=self.rhodecode_vcs_repo.alias)
1205 1213 c.default_message = (_('Added file via RhodeCode Enterprise'))
1206 1214 c.f_path = f_path
1207 1215 unix_mode = 0
1208 1216 content = convert_line_endings(r_post.get('content', ''), unix_mode)
1209 1217
1210 1218 message = r_post.get('message') or c.default_message
1211 1219 filename = r_post.get('filename')
1212 1220 location = r_post.get('location', '') # dir location
1213 1221 file_obj = r_post.get('upload_file', None)
1214 1222
1215 1223 if file_obj is not None and hasattr(file_obj, 'filename'):
1216 1224 filename = r_post.get('filename_upload')
1217 1225 content = file_obj.file
1218 1226
1219 1227 if hasattr(content, 'file'):
1220 1228 # non posix systems store real file under file attr
1221 1229 content = content.file
1222 1230
1223 1231 if self.rhodecode_vcs_repo.is_empty:
1224 1232 default_redirect_url = h.route_path(
1225 1233 'repo_summary', repo_name=self.db_repo_name)
1226 1234 else:
1227 1235 default_redirect_url = h.route_path(
1228 1236 'repo_commit', repo_name=self.db_repo_name, commit_id='tip')
1229 1237
1230 1238 # If there's no commit, redirect to repo summary
1231 1239 if type(c.commit) is EmptyCommit:
1232 1240 redirect_url = h.route_path(
1233 1241 'repo_summary', repo_name=self.db_repo_name)
1234 1242 else:
1235 1243 redirect_url = default_redirect_url
1236 1244
1237 1245 if not filename:
1238 1246 h.flash(_('No filename'), category='warning')
1239 1247 raise HTTPFound(redirect_url)
1240 1248
1241 1249 # extract the location from filename,
1242 1250 # allows using foo/bar.txt syntax to create subdirectories
1243 1251 subdir_loc = filename.rsplit('/', 1)
1244 1252 if len(subdir_loc) == 2:
1245 1253 location = os.path.join(location, subdir_loc[0])
1246 1254
1247 1255 # strip all crap out of file, just leave the basename
1248 1256 filename = os.path.basename(filename)
1249 1257 node_path = os.path.join(location, filename)
1250 1258 author = self._rhodecode_db_user.full_contact
1251 1259
1252 1260 try:
1253 1261 nodes = {
1254 1262 node_path: {
1255 1263 'content': content
1256 1264 }
1257 1265 }
1258 1266 ScmModel().create_nodes(
1259 1267 user=self._rhodecode_db_user.user_id,
1260 1268 repo=self.db_repo,
1261 1269 message=message,
1262 1270 nodes=nodes,
1263 1271 parent_commit=c.commit,
1264 1272 author=author,
1265 1273 )
1266 1274
1267 1275 h.flash(
1268 1276 _('Successfully committed new file `{}`').format(
1269 1277 h.escape(node_path)), category='success')
1270 1278 except NonRelativePathError:
1271 1279 log.exception('Non Relative path found')
1272 1280 h.flash(_(
1273 1281 'The location specified must be a relative path and must not '
1274 1282 'contain .. in the path'), category='warning')
1275 1283 raise HTTPFound(default_redirect_url)
1276 1284 except (NodeError, NodeAlreadyExistsError) as e:
1277 1285 h.flash(_(h.escape(e)), category='error')
1278 1286 except Exception:
1279 1287 log.exception('Error occurred during commit')
1280 1288 h.flash(_('Error occurred during commit'), category='error')
1281 1289
1282 1290 raise HTTPFound(default_redirect_url)
General Comments 0
You need to be logged in to leave comments. Login now