files.py
207 lines
| 8.0 KiB
| text/x-python
|
PythonLexer
r252 | #!/usr/bin/env python | |||
# encoding: utf-8 | ||||
# files controller for pylons | ||||
# Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com> | ||||
r318 | ||||
r252 | # 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. | ||||
""" | ||||
Created on April 21, 2010 | ||||
files controller for pylons | ||||
@author: marcink | ||||
""" | ||||
r318 | from mercurial import archival | |||
from pylons import request, response, session, tmpl_context as c, url | ||||
from pylons.controllers.util import redirect | ||||
from pylons_app.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator | ||||
from pylons_app.lib.base import BaseController, render | ||||
r438 | from pylons_app.lib.utils import EmptyChangeset | |||
r318 | from pylons_app.model.hg_model import HgModel | |||
from vcs.exceptions import RepositoryError, ChangesetError | ||||
from vcs.nodes import FileNode | ||||
from vcs.utils import diffs as differ | ||||
import logging | ||||
import pylons_app.lib.helpers as h | ||||
import tempfile | ||||
r131 | ||||
r93 | log = logging.getLogger(__name__) | |||
class FilesController(BaseController): | ||||
r191 | ||||
@LoginRequired() | ||||
r318 | @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', | |||
'repository.admin') | ||||
r99 | def __before__(self): | |||
r191 | super(FilesController, self).__before__() | |||
r485 | c.file_size_limit = 250 * 1024 #limit of file size to display | |||
r93 | ||||
r99 | def index(self, repo_name, revision, f_path): | |||
hg_model = HgModel() | ||||
c.repo = repo = hg_model.get_repo(c.repo_name) | ||||
r149 | revision = request.POST.get('at_rev', None) or revision | |||
r145 | ||||
r149 | def get_next_rev(cur): | |||
r145 | max_rev = len(c.repo.revisions) - 1 | |||
r149 | r = cur + 1 | |||
if r > max_rev: | ||||
r = max_rev | ||||
return r | ||||
def get_prev_rev(cur): | ||||
r = cur - 1 | ||||
return r | ||||
r99 | c.f_path = f_path | |||
r149 | ||||
r147 | ||||
r138 | try: | |||
r149 | cur_rev = repo.get_changeset(revision).revision | |||
r512 | prev_rev = repo.get_changeset(get_prev_rev(cur_rev)).short_id | |||
next_rev = repo.get_changeset(get_next_rev(cur_rev)).short_id | ||||
r149 | ||||
c.url_prev = url('files_home', repo_name=c.repo_name, | ||||
revision=prev_rev, f_path=f_path) | ||||
c.url_next = url('files_home', repo_name=c.repo_name, | ||||
revision=next_rev, f_path=f_path) | ||||
c.changeset = repo.get_changeset(revision) | ||||
r147 | ||||
r512 | c.cur_rev = c.changeset.short_id | |||
r142 | c.rev_nr = c.changeset.revision | |||
r138 | c.files_list = c.changeset.get_node(f_path) | |||
c.file_history = self._get_history(repo, c.files_list, f_path) | ||||
r147 | ||||
r145 | except (RepositoryError, ChangesetError): | |||
r138 | c.files_list = None | |||
r99 | ||||
r128 | return render('files/files.html') | |||
r147 | def rawfile(self, repo_name, revision, f_path): | |||
hg_model = HgModel() | ||||
c.repo = hg_model.get_repo(c.repo_name) | ||||
file_node = c.repo.get_changeset(revision).get_node(f_path) | ||||
r160 | response.content_type = file_node.mimetype | |||
response.content_disposition = 'attachment; filename=%s' \ | ||||
r147 | % f_path.split('/')[-1] | |||
return file_node.content | ||||
r485 | ||||
def raw(self, repo_name, revision, f_path): | ||||
hg_model = HgModel() | ||||
c.repo = hg_model.get_repo(c.repo_name) | ||||
file_node = c.repo.get_changeset(revision).get_node(f_path) | ||||
response.content_type = 'text/plain' | ||||
return file_node.content | ||||
r147 | ||||
r191 | def annotate(self, repo_name, revision, f_path): | |||
hg_model = HgModel() | ||||
c.repo = hg_model.get_repo(c.repo_name) | ||||
cs = c.repo.get_changeset(revision) | ||||
c.file = cs.get_node(f_path) | ||||
c.file_msg = cs.get_file_message(f_path) | ||||
r512 | c.cur_rev = cs.short_id | |||
r375 | c.rev_nr = cs.revision | |||
r191 | c.f_path = f_path | |||
r335 | ||||
r191 | return render('files/files_annotate.html') | |||
r149 | def archivefile(self, repo_name, revision, fileformat): | |||
r209 | archive_specs = { | |||
'.tar.bz2': ('application/x-tar', 'tbz2'), | ||||
'.tar.gz': ('application/x-tar', 'tgz'), | ||||
'.zip': ('application/zip', 'zip'), | ||||
} | ||||
if not archive_specs.has_key(fileformat): | ||||
return 'Unknown archive type %s' % fileformat | ||||
def read_in_chunks(file_object, chunk_size=1024 * 40): | ||||
"""Lazy function (generator) to read a file piece by piece. | ||||
Default chunk size: 40k.""" | ||||
while True: | ||||
data = file_object.read(chunk_size) | ||||
if not data: | ||||
break | ||||
yield data | ||||
archive = tempfile.TemporaryFile() | ||||
repo = HgModel().get_repo(repo_name).repo | ||||
fname = '%s-%s%s' % (repo_name, revision, fileformat) | ||||
archival.archive(repo, archive, revision, archive_specs[fileformat][1], | ||||
prefix='%s-%s' % (repo_name, revision)) | ||||
response.content_type = archive_specs[fileformat][0] | ||||
response.content_disposition = 'attachment; filename=%s' % fname | ||||
archive.seek(0) | ||||
return read_in_chunks(archive) | ||||
r149 | ||||
r129 | def diff(self, repo_name, f_path): | |||
hg_model = HgModel() | ||||
diff1 = request.GET.get('diff1') | ||||
diff2 = request.GET.get('diff2') | ||||
r337 | c.action = request.GET.get('diff') | |||
r131 | c.no_changes = diff1 == diff2 | |||
r129 | c.f_path = f_path | |||
c.repo = hg_model.get_repo(c.repo_name) | ||||
r152 | ||||
r275 | try: | |||
if diff1 not in ['', None, 'None', '0' * 12]: | ||||
c.changeset_1 = c.repo.get_changeset(diff1) | ||||
node1 = c.changeset_1.get_node(f_path) | ||||
else: | ||||
c.changeset_1 = EmptyChangeset() | ||||
node1 = FileNode('.', '') | ||||
if diff2 not in ['', None, 'None', '0' * 12]: | ||||
c.changeset_2 = c.repo.get_changeset(diff2) | ||||
node2 = c.changeset_2.get_node(f_path) | ||||
else: | ||||
c.changeset_2 = EmptyChangeset() | ||||
node2 = FileNode('.', '') | ||||
except RepositoryError: | ||||
return redirect(url('files_home', | ||||
repo_name=c.repo_name, f_path=f_path)) | ||||
r512 | c.diff1 = 'r%s:%s' % (c.changeset_1.revision, c.changeset_1.short_id) | |||
c.diff2 = 'r%s:%s' % (c.changeset_2.revision, c.changeset_2.short_id) | ||||
r275 | f_udiff = differ.get_udiff(node1, node2) | |||
r160 | ||||
diff = differ.DiffProcessor(f_udiff) | ||||
r337 | if c.action == 'download': | |||
r160 | diff_name = '%s_vs_%s.diff' % (diff1, diff2) | |||
response.content_type = 'text/plain' | ||||
response.content_disposition = 'attachment; filename=%s' \ | ||||
% diff_name | ||||
return diff.raw_diff() | ||||
r337 | elif c.action == 'raw': | |||
r272 | c.cur_diff = '<pre class="raw">%s</pre>' % h.escape(diff.raw_diff()) | |||
r337 | elif c.action == 'diff': | |||
r160 | c.cur_diff = diff.as_html() | |||
r337 | else: | |||
#default option | ||||
c.cur_diff = diff.as_html() | ||||
r349 | ||||
if not c.cur_diff: c.no_changes = True | ||||
r129 | return render('files/file_diff.html') | |||
r128 | def _get_history(self, repo, node, f_path): | |||
from vcs.nodes import NodeKind | ||||
if not node.kind is NodeKind.FILE: | ||||
return [] | ||||
r129 | changesets = node.history | |||
r128 | hist_l = [] | |||
for chs in changesets: | ||||
r512 | n_desc = 'r%s:%s' % (chs.revision, chs.short_id) | |||
hist_l.append((chs.short_id, n_desc,)) | ||||
r128 | return hist_l | |||