files.py
267 lines
| 9.6 KiB
| text/x-python
|
PythonLexer
r812 | # -*- coding: utf-8 -*- | |||
""" | ||||
rhodecode.controllers.files | ||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||
r547 | ||||
r812 | Files controller for RhodeCode | |||
:created_on: Apr 21, 2010 | ||||
:author: marcink | ||||
r902 | :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com> | |||
r812 | :license: GPLv3, see COPYING for more details. | |||
""" | ||||
r547 | # This program is free software; you can redistribute it and/or | |||
# modify it under the terms of the GNU General Public License | ||||
# as published by the Free Software Foundation; version 2 | ||||
# of the License or (at your opinion) any later version of the license. | ||||
# | ||||
# This program is distributed in the hope that it will be useful, | ||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
# GNU General Public License for more details. | ||||
# | ||||
# You should have received a copy of the GNU General Public License | ||||
# along with this program; if not, write to the Free Software | ||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | ||||
# MA 02110-1301, USA. | ||||
r812 | import tempfile | |||
import logging | ||||
import rhodecode.lib.helpers as h | ||||
r547 | from mercurial import archival | |||
r812 | ||||
r547 | from pylons import request, response, session, tmpl_context as c, url | |||
from pylons.i18n.translation import _ | ||||
from pylons.controllers.util import redirect | ||||
r812 | ||||
r547 | from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator | |||
from rhodecode.lib.base import BaseController, render | ||||
from rhodecode.lib.utils import EmptyChangeset | ||||
r691 | from rhodecode.model.scm import ScmModel | |||
r812 | ||||
r948 | from vcs.backends import ARCHIVE_SPECS | |||
r915 | from vcs.exceptions import RepositoryError, ChangesetError, \ | |||
r953 | ChangesetDoesNotExistError, EmptyRepositoryError, ImproperArchiveTypeError | |||
r547 | from vcs.nodes import FileNode | |||
from vcs.utils import diffs as differ | ||||
r636 | ||||
r547 | log = logging.getLogger(__name__) | |||
class FilesController(BaseController): | ||||
r636 | ||||
r547 | @LoginRequired() | |||
@HasRepoPermissionAnyDecorator('repository.read', 'repository.write', | ||||
r636 | 'repository.admin') | |||
r547 | def __before__(self): | |||
super(FilesController, self).__before__() | ||||
r813 | c.cut_off_limit = self.cut_off_limit | |||
r547 | ||||
def index(self, repo_name, revision, f_path): | ||||
r1038 | c.repo, dbrepo = ScmModel().get(c.repo_name, retval='repo') | |||
r636 | ||||
r547 | try: | |||
r884 | #reditect to given revision from form | |||
post_revision = request.POST.get('at_rev', None) | ||||
if post_revision: | ||||
post_revision = c.repo.get_changeset(post_revision).raw_id | ||||
redirect(url('files_home', repo_name=c.repo_name, | ||||
revision=post_revision, f_path=f_path)) | ||||
c.branch = request.GET.get('branch', None) | ||||
c.f_path = f_path | ||||
r662 | c.changeset = c.repo.get_changeset(revision) | |||
r644 | cur_rev = c.changeset.revision | |||
r636 | ||||
r884 | #prev link | |||
r883 | try: | |||
r884 | prev_rev = c.repo.get_changeset(cur_rev).prev(c.branch).raw_id | |||
r883 | c.url_prev = url('files_home', repo_name=c.repo_name, | |||
r636 | revision=prev_rev, f_path=f_path) | |||
r884 | if c.branch: | |||
c.url_prev += '?branch=%s' % c.branch | ||||
r883 | except ChangesetDoesNotExistError: | |||
c.url_prev = '#' | ||||
r884 | #next link | |||
r883 | try: | |||
r884 | next_rev = c.repo.get_changeset(cur_rev).next(c.branch).raw_id | |||
r883 | c.url_next = url('files_home', repo_name=c.repo_name, | |||
r644 | revision=next_rev, f_path=f_path) | |||
r884 | if c.branch: | |||
c.url_next += '?branch=%s' % c.branch | ||||
r883 | except ChangesetDoesNotExistError: | |||
c.url_next = '#' | ||||
r636 | ||||
r884 | #files | |||
r644 | try: | |||
c.files_list = c.changeset.get_node(f_path) | ||||
r662 | c.file_history = self._get_history(c.repo, c.files_list, f_path) | |||
r644 | except RepositoryError, e: | |||
h.flash(str(e), category='warning') | ||||
redirect(h.url('files_home', repo_name=repo_name, revision=revision)) | ||||
r636 | ||||
r915 | except EmptyRepositoryError, e: | |||
h.flash(_('There are no files yet'), category='warning') | ||||
redirect(h.url('summary_home', repo_name=repo_name)) | ||||
r644 | except RepositoryError, e: | |||
h.flash(str(e), category='warning') | ||||
redirect(h.url('files_home', repo_name=repo_name, revision='tip')) | ||||
r636 | ||||
r547 | return render('files/files.html') | |||
def rawfile(self, repo_name, revision, f_path): | ||||
r1038 | c.repo, dbrepo = ScmModel().get(c.repo_name, retval='repo') | |||
r547 | file_node = c.repo.get_changeset(revision).get_node(f_path) | |||
response.content_type = file_node.mimetype | ||||
response.content_disposition = 'attachment; filename=%s' \ | ||||
r636 | % f_path.split('/')[-1] | |||
r547 | return file_node.content | |||
def raw(self, repo_name, revision, f_path): | ||||
r1038 | c.repo, dbrepo = ScmModel().get(c.repo_name, retval='repo') | |||
r547 | file_node = c.repo.get_changeset(revision).get_node(f_path) | |||
response.content_type = 'text/plain' | ||||
r636 | ||||
r547 | return file_node.content | |||
r636 | ||||
r547 | def annotate(self, repo_name, revision, f_path): | |||
r1038 | c.repo, dbrepo = ScmModel().get(c.repo_name, retval='repo') | |||
r774 | ||||
try: | ||||
c.cs = c.repo.get_changeset(revision) | ||||
c.file = c.cs.get_node(f_path) | ||||
except RepositoryError, e: | ||||
h.flash(str(e), category='warning') | ||||
redirect(h.url('files_home', repo_name=repo_name, revision=revision)) | ||||
r662 | c.file_history = self._get_history(c.repo, c.file, f_path) | |||
r547 | c.f_path = f_path | |||
return render('files/files_annotate.html') | ||||
r636 | ||||
r872 | def archivefile(self, repo_name, fname): | |||
r945 | ||||
r942 | fileformat = None | |||
revision = None | ||||
r948 | ext = None | |||
r945 | ||||
r948 | for a_type, ext_data in ARCHIVE_SPECS.items(): | |||
archive_spec = fname.split(ext_data[1]) | ||||
if len(archive_spec) == 2 and archive_spec[1] == '': | ||||
fileformat = a_type or ext_data[1] | ||||
r942 | revision = archive_spec[0] | |||
r948 | ext = ext_data[1] | |||
r872 | ||||
try: | ||||
r1038 | repo, dbrepo = ScmModel().get(repo_name) | |||
r962 | ||||
r1038 | if dbrepo.enable_downloads is False: | |||
r962 | return _('downloads disabled') | |||
r948 | cs = repo.get_changeset(revision) | |||
r950 | content_type = ARCHIVE_SPECS[fileformat][0] | |||
r872 | except ChangesetDoesNotExistError: | |||
return _('Unknown revision %s') % revision | ||||
r945 | except EmptyRepositoryError: | |||
return _('Empty repository') | ||||
r961 | except (ImproperArchiveTypeError, KeyError): | |||
r948 | return _('Unknown archive type') | |||
r872 | ||||
r950 | response.content_type = content_type | |||
response.content_disposition = 'attachment; filename=%s-%s%s' \ | ||||
% (repo_name, revision, ext) | ||||
r636 | ||||
r948 | return cs.get_chunked_archive(kind=fileformat) | |||
r636 | ||||
r547 | def diff(self, repo_name, f_path): | |||
diff1 = request.GET.get('diff1') | ||||
diff2 = request.GET.get('diff2') | ||||
c.action = request.GET.get('diff') | ||||
c.no_changes = diff1 == diff2 | ||||
c.f_path = f_path | ||||
r1038 | c.repo, dbrepo = ScmModel().get(c.repo_name, retval='repo') | |||
r547 | ||||
try: | ||||
if diff1 not in ['', None, 'None', '0' * 12, '0' * 40]: | ||||
c.changeset_1 = c.repo.get_changeset(diff1) | ||||
node1 = c.changeset_1.get_node(f_path) | ||||
else: | ||||
c.changeset_1 = EmptyChangeset() | ||||
node1 = FileNode('.', '', changeset=c.changeset_1) | ||||
r636 | ||||
r547 | if diff2 not in ['', None, 'None', '0' * 12, '0' * 40]: | |||
c.changeset_2 = c.repo.get_changeset(diff2) | ||||
node2 = c.changeset_2.get_node(f_path) | ||||
else: | ||||
c.changeset_2 = EmptyChangeset() | ||||
node2 = FileNode('.', '', changeset=c.changeset_2) | ||||
except RepositoryError: | ||||
return redirect(url('files_home', | ||||
repo_name=c.repo_name, f_path=f_path)) | ||||
f_udiff = differ.get_udiff(node1, node2) | ||||
diff = differ.DiffProcessor(f_udiff) | ||||
r636 | ||||
r547 | if c.action == 'download': | |||
diff_name = '%s_vs_%s.diff' % (diff1, diff2) | ||||
response.content_type = 'text/plain' | ||||
response.content_disposition = 'attachment; filename=%s' \ | ||||
r636 | % diff_name | |||
r547 | return diff.raw_diff() | |||
r636 | ||||
r547 | elif c.action == 'raw': | |||
r649 | response.content_type = 'text/plain' | |||
return diff.raw_diff() | ||||
r662 | ||||
r547 | elif c.action == 'diff': | |||
r812 | if node1.size > self.cut_off_limit or node2.size > self.cut_off_limit: | |||
r547 | c.cur_diff = _('Diff is to big to display') | |||
else: | ||||
c.cur_diff = diff.as_html() | ||||
else: | ||||
#default option | ||||
r812 | if node1.size > self.cut_off_limit or node2.size > self.cut_off_limit: | |||
r547 | c.cur_diff = _('Diff is to big to display') | |||
else: | ||||
c.cur_diff = diff.as_html() | ||||
r636 | ||||
if not c.cur_diff: c.no_changes = True | ||||
r547 | return render('files/file_diff.html') | |||
r636 | ||||
r547 | def _get_history(self, repo, node, f_path): | |||
from vcs.nodes import NodeKind | ||||
if not node.kind is NodeKind.FILE: | ||||
return [] | ||||
changesets = node.history | ||||
hist_l = [] | ||||
r774 | ||||
changesets_group = ([], _("Changesets")) | ||||
branches_group = ([], _("Branches")) | ||||
tags_group = ([], _("Tags")) | ||||
r547 | for chs in changesets: | |||
n_desc = 'r%s:%s' % (chs.revision, chs.short_id) | ||||
r774 | changesets_group[0].append((chs.raw_id, n_desc,)) | |||
hist_l.append(changesets_group) | ||||
for name, chs in c.repository_branches.items(): | ||||
#chs = chs.split(':')[-1] | ||||
branches_group[0].append((chs, name),) | ||||
hist_l.append(branches_group) | ||||
for name, chs in c.repository_tags.items(): | ||||
#chs = chs.split(':')[-1] | ||||
tags_group[0].append((chs, name),) | ||||
hist_l.append(tags_group) | ||||
r547 | return hist_l | |||
r774 | ||||