#!/usr/bin/env python
# encoding: utf-8
# changeset controller for pylons
# Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
# 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.
from rhodecode.lib.utils import EmptyChangeset
"""
Created on April 25, 2010
changeset controller for pylons
@author: marcink
from pylons import tmpl_context as c, url, request, response
from pylons.i18n.translation import _
from pylons.controllers.util import redirect
from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
from rhodecode.lib.base import BaseController, render
import rhodecode.lib.helpers as h
from rhodecode.model.hg import HgModel
from vcs.exceptions import RepositoryError, ChangesetError
from vcs.nodes import FileNode
from vcs.utils import diffs as differ
import logging
import traceback
log = logging.getLogger(__name__)
class ChangesetController(BaseController):
@LoginRequired()
@HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
'repository.admin')
def __before__(self):
super(ChangesetController, self).__before__()
def index(self, revision):
hg_model = HgModel()
cut_off_limit = 1024 * 250
def wrap_to_table(str):
return '''<table class="code-difftable">
<tr class="line">
<td class="lineno new"></td>
<td class="code"><pre>%s</pre></td>
</tr>
</table>''' % str
try:
c.changeset = hg_model.get_repo(c.repo_name).get_changeset(revision)
except RepositoryError:
except RepositoryError, e:
log.error(traceback.format_exc())
h.flash(str(e), category='warning')
return redirect(url('home'))
else:
c.changeset_old = c.changeset.parents[0]
except IndexError:
c.changeset_old = None
c.changes = []
#===================================================================
# ADDED FILES
c.sum_added = 0
for node in c.changeset.added:
filenode_old = FileNode(node.path, '', EmptyChangeset())
if filenode_old.is_binary or node.is_binary:
diff = wrap_to_table(_('binary file'))
c.sum_added += node.size
if c.sum_added < cut_off_limit:
f_udiff = differ.get_udiff(filenode_old, node)
diff = differ.DiffProcessor(f_udiff).as_html()
diff = wrap_to_table(_('Changeset is to big and was cut'
' off, see raw changeset instead'))
cs1 = None
cs2 = node.last_changeset.raw_id
c.changes.append(('added', node, diff, cs1, cs2))
# CHANGED FILES
c.sum_removed = 0
for node in c.changeset.changed:
filenode_old = c.changeset_old.get_node(node.path)
except ChangesetError:
if c.sum_removed < cut_off_limit:
if diff:
c.sum_removed += len(diff)
cs1 = filenode_old.last_changeset.raw_id
c.changes.append(('changed', node, diff, cs1, cs2))
# REMOVED FILES
for node in c.changeset.removed:
c.changes.append(('removed', node, None, None, None))
return render('changeset/changeset.html')
def raw_changeset(self, revision):
method = request.GET.get('diff', 'show')
filenode_old = FileNode(node.path, '')
diff = _('binary file')
diff = differ.DiffProcessor(f_udiff).raw_diff()
response.content_type = 'text/plain'
if method == 'download':
response.content_disposition = 'attachment; filename=%s.patch' % revision
parent = True if len(c.changeset.parents) > 0 else False
c.parent_tmpl = 'Parent %s' % c.changeset.parents[0].raw_id if parent else ''
c.diffs = ''
for x in c.changes:
c.diffs += x[2]
return render('changeset/raw_changeset.html')
# files controller for pylons
Created on April 21, 2010
files controller for pylons
from mercurial import archival
from pylons import request, response, session, tmpl_context as c, url
import tempfile
class FilesController(BaseController):
super(FilesController, self).__before__()
c.file_size_limit = 250 * 1024 #limit of file size to display
def index(self, repo_name, revision, f_path):
c.repo = repo = hg_model.get_repo(c.repo_name)
revision = request.POST.get('at_rev', None) or revision
def get_next_rev(cur):
max_rev = len(c.repo.revisions) - 1
r = cur + 1
if r > max_rev:
r = max_rev
return r
def get_prev_rev(cur):
r = cur - 1
c.f_path = f_path
cur_rev = repo.get_changeset(revision).revision
c.changeset = repo.get_changeset(revision)
cur_rev = c.changeset.revision
prev_rev = repo.get_changeset(get_prev_rev(cur_rev)).raw_id
next_rev = repo.get_changeset(get_next_rev(cur_rev)).raw_id
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.files_list = c.changeset.get_node(f_path)
c.file_history = self._get_history(repo, c.files_list, f_path)
c.cur_rev = c.changeset.raw_id
c.rev_nr = c.changeset.revision
redirect(h.url('files_home', repo_name=repo_name, revision=revision))
except (RepositoryError, ChangesetError):
c.files_list = None
redirect(h.url('files_home', repo_name=repo_name, revision='tip'))
return render('files/files.html')
def rawfile(self, repo_name, revision, f_path):
c.repo = hg_model.get_repo(c.repo_name)
file_node = c.repo.get_changeset(revision).get_node(f_path)
response.content_type = file_node.mimetype
response.content_disposition = 'attachment; filename=%s' \
% f_path.split('/')[-1]
return file_node.content
def raw(self, repo_name, revision, f_path):
def annotate(self, repo_name, revision, f_path):
cs = c.repo.get_changeset(revision)
c.file = cs.get_node(f_path)
c.file_msg = cs.get_file_message(f_path)
c.cur_rev = cs.raw_id
c.rev_nr = cs.revision
return render('files/files_annotate.html')
def archivefile(self, repo_name, revision, fileformat):
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)
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
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)
c.changeset_1 = EmptyChangeset()
node1 = FileNode('.', '', changeset=c.changeset_1)
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)
c.changeset_2 = EmptyChangeset()
node2 = FileNode('.', '', changeset=c.changeset_2)
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)
if c.action == 'download':
diff_name = '%s_vs_%s.diff' % (diff1, diff2)
% diff_name
return diff.raw_diff()
elif c.action == 'raw':
c.cur_diff = '<pre class="raw">%s</pre>' % h.escape(diff.raw_diff())
elif c.action == 'diff':
if node1.size > c.file_size_limit or node2.size > c.file_size_limit:
c.cur_diff = _('Diff is to big to display')
c.cur_diff = diff.as_html()
#default option
if not c.cur_diff: c.no_changes = True
return render('files/file_diff.html')
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 = []
for chs in changesets:
n_desc = 'r%s:%s' % (chs.revision, chs.short_id)
hist_l.append((chs.raw_id, n_desc,))
return hist_l
<%inherit file="/base/base.html"/>
<%def name="title()">
${c.repo_name} ${_('Files')} - ${c.rhodecode_name}
</%def>
<%def name="breadcrumbs_links()">
${h.link_to(u'Home',h.url('/'))}
»
${h.link_to(c.repo_name,h.url('files_home',repo_name=c.repo_name))}
${_('files')}
%if c.files_list:
@ R${c.rev_nr}:${h.short_id(c.cur_rev)}
@ r${c.changeset.revision}:${h.short_id(c.changeset.raw_id)}
%endif
<%def name="page_nav()">
${self.menu('files')}
<%def name="main()">
<div class="box">
<!-- box / title -->
<div class="title">
${self.breadcrumbs()}
<ul class="links">
<li>
<span style="text-transform: uppercase;"><a href="#">${_('branch')}: ${c.changeset.branch}</a></span>
</li>
</ul>
</div>
<div class="table">
<div id="files_data">
<h3 class="files_location">${_('Location')}: ${h.files_breadcrumbs(c.repo_name,c.cur_rev,c.files_list.path)}</h3>
<h3 class="files_location">
${_('Location')}: ${h.files_breadcrumbs(c.repo_name,c.changeset.raw_id,c.files_list.path)}
</h3>
%if c.files_list.is_dir():
<%include file='files_browser.html'/>
%else:
<%include file='files_source.html'/>
<h2>
<a href="#" onClick="javascript:parent.history.back();" target="main">${_('Go back')}</a>
${_('No files at given path')}: "${c.f_path or "/"}"
</h2>
\ No newline at end of file
<%def name="file_class(node)">
%if node.is_file():
<%return "browser-file" %>
<%return "browser-dir"%>
<div id="body" class="browserblock">
<div class="browser-header">
${h.form(h.url.current())}
<div class="info_box">
<span >${_('view')}@rev</span>
<a href="${c.url_prev}">«</a>
${h.text('at_rev',value=c.rev_nr,size=3)}
<a href="${c.url_next}">»</a>
<a href="${c.url_prev}" title="${_('previous revision')}">«</a>
${h.text('at_rev',value=c.changeset.revision,size=3)}
<a href="${c.url_next}" title="${_('next revision')}">»</a>
${h.submit('view','view')}
${h.end_form()}
<div class="browser-body">
<table class="code-browser">
<thead>
<tr>
<th>${_('Name')}</th>
<th>${_('Size')}</th>
<th>${_('Mimetype')}</th>
<th>${_('Revision')}</th>
<th>${_('Last modified')}</th>
<th>${_('Last commiter')}</th>
</thead>
%if c.files_list.parent:
<tr class="parity0">
<td>
${h.link_to('..',h.url('files_home',repo_name=c.repo_name,revision=c.cur_rev,f_path=c.files_list.parent.path),class_="browser-dir")}
${h.link_to('..',h.url('files_home',repo_name=c.repo_name,revision=c.changeset.raw_id,f_path=c.files_list.parent.path),class_="browser-dir")}
</td>
<td></td>
%for cnt,node in enumerate(c.files_list,1):
<tr class="parity${cnt%2}">
${h.link_to(node.name,h.url('files_home',repo_name=c.repo_name,revision=c.cur_rev,f_path=node.path),class_=file_class(node))}
${h.link_to(node.name,h.url('files_home',repo_name=c.repo_name,revision=c.changeset.raw_id,f_path=node.path),class_=file_class(node))}
${h.format_byte_size(node.size,binary=True)}
${node.mimetype}
${node.last_changeset.revision}
<span class="tooltip" tooltip_title="${node.last_changeset.raw_id}">${node.last_changeset.revision}</span>
${h.age(node.last_changeset.date)} - ${node.last_changeset.date}
${node.last_changeset.date} - ${h.age(node.last_changeset.date)} ${_('ago')}
${node.last_changeset.author}
%endfor
</table>
<dl>
<dt>${_('Last revision')}</dt>
<dd>
${h.link_to("r%s:%s" % (c.files_list.last_changeset.revision,h.short_id(c.files_list.last_changeset.raw_id)),
h.url('files_home',repo_name=c.repo_name,revision=c.files_list.last_changeset.raw_id,f_path=c.f_path))}
</dd>
<dt>${_('Size')}</dt>
<dd>${h.format_byte_size(c.files_list.size,binary=True)}</dd>
<dt>${_('Mimetype')}</dt>
<dd>${c.files_list.mimetype}</dd>
<dt>${_('Options')}</dt>
<dd>${h.link_to(_('show annotation'),
h.url('files_annotate_home',repo_name=c.repo_name,revision=c.cur_rev,f_path=c.f_path))}
h.url('files_annotate_home',repo_name=c.repo_name,revision=c.changeset.raw_id,f_path=c.f_path))}
/ ${h.link_to(_('show as raw'),
h.url('files_raw_home',repo_name=c.repo_name,revision=c.cur_rev,f_path=c.f_path))}
h.url('files_raw_home',repo_name=c.repo_name,revision=c.changeset.raw_id,f_path=c.f_path))}
/ ${h.link_to(_('download as raw'),
h.url('files_rawfile_home',repo_name=c.repo_name,revision=c.cur_rev,f_path=c.f_path))}
h.url('files_rawfile_home',repo_name=c.repo_name,revision=c.changeset.raw_id,f_path=c.f_path))}
<dt>${_('History')}</dt>
<div>
${h.form(h.url('files_diff_home',repo_name=c.repo_name,f_path=c.f_path),method='get')}
${h.hidden('diff2',c.files_list.last_changeset.raw_id)}
${h.select('diff1',c.files_list.last_changeset.raw_id,c.file_history)}
${h.submit('diff','diff to revision',class_="ui-button ui-widget ui-state-default ui-corner-all")}
${h.submit('show_rev','show at revision',class_="ui-button ui-widget ui-state-default ui-corner-all")}
</dl>
<div id="body" class="codeblock">
<div class="code-header">
<div class="revision">${c.files_list.name}@r${c.files_list.last_changeset.revision}:${h.short_id(c.files_list.last_changeset.raw_id)}</div>
<div class="commit">"${c.files_list.last_changeset.message}"</div>
<div class="code-body">
% if c.files_list.size < c.file_size_limit:
${h.pygmentize(c.files_list,linenos=True,anchorlinenos=True,lineanchors='S',cssclass="code-highlight")}
${_('File is to big to display')} ${h.link_to(_('show as raw'),
<script type="text/javascript">
YAHOO.util.Event.onDOMReady(function(){
YAHOO.util.Event.addListener('show_rev','click',function(e){
YAHOO.util.Event.preventDefault(e);
var cs = YAHOO.util.Dom.get('diff1').value;
var url = "${h.url('files_home',repo_name=c.repo_name,revision='__CS__',f_path=c.f_path)}".replace('__CS__',cs);
window.location = url;
});
</script>
Status change: