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