@@ -22,56 +22,149 @@
# 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, see <http://www.gnu.org/licenses/>.
import logging
import traceback
from collections import defaultdict
from webob.exc import HTTPForbidden
from pylons import tmpl_context as c, url, request, response
from pylons.i18n.translation import _
from pylons.controllers.util import redirect
from pylons.decorators import jsonify
from vcs.exceptions import RepositoryError, ChangesetError, \
ChangesetDoesNotExistError
from vcs.nodes import FileNode
import rhodecode.lib.helpers as h
from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
from rhodecode.lib.base import BaseRepoController, render
from rhodecode.lib.utils import EmptyChangeset
from rhodecode.lib.compat import OrderedDict
from rhodecode.lib import diffs
from rhodecode.model.db import ChangesetComment
from rhodecode.model.comment import ChangesetCommentsModel
from rhodecode.model.meta import Session
log = logging.getLogger(__name__)
def anchor_url(revision,path):
fid = h.FID(revision, path)
return h.url.current(anchor=fid,**request.GET)
def get_ignore_ws(fid, GET):
ig_ws_global = request.GET.get('ignorews')
ig_ws = filter(lambda k:k.startswith('WS'),GET.getall(fid))
if ig_ws:
try:
return int(ig_ws[0].split(':')[-1])
except:
pass
return ig_ws_global
def _ignorews_url(fileid=None):
params = defaultdict(list)
lbl = _('show white space')
ig_ws = get_ignore_ws(fileid, request.GET)
ln_ctx = get_line_ctx(fileid, request.GET)
# global option
if fileid is None:
if ig_ws is None:
params['ignorews'] += [1]
lbl = _('ignore white space')
ctx_key = 'context'
ctx_val = ln_ctx
# per file options
else:
params[fileid] += ['WS:1']
ctx_key = fileid
ctx_val = 'C:%s' % ln_ctx
# if we have passed in ln_ctx pass it along to our params
if ln_ctx:
params[ctx_key] += [ctx_val]
params['anchor'] = fileid
return h.link_to(lbl, h.url.current(**params))
def get_line_ctx(fid, GET):
ln_ctx_global = request.GET.get('context')
ln_ctx = filter(lambda k:k.startswith('C'),GET.getall(fid))
retval = ln_ctx[0].split(':')[-1]
retval = ln_ctx_global
return int(retval)
return
def _context_url(fileid=None):
"""
Generates url for context lines
:param fileid:
ln_ctx = (get_line_ctx(fileid, request.GET) or 3) * 2
if ln_ctx > 0:
params['context'] += [ln_ctx]
ig_ws_key = 'ignorews'
ig_ws_val = 1
# per file option
params[fileid] += ['C:%s' % ln_ctx]
ig_ws_key = fileid
ig_ws_val = 'WS:%s' % 1
params[ig_ws_key] += [ig_ws_val]
lbl = _('%s line context') % ln_ctx
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_
class ChangesetController(BaseRepoController):
@LoginRequired()
@HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
'repository.admin')
def __before__(self):
super(ChangesetController, self).__before__()
c.affected_files_cut_off = 60
def index(self, revision):
ignore_whitespace = request.GET.get('ignorews') == '1'
line_context = request.GET.get('context', 3)
def wrap_to_table(str):
</table>''' % str
c.anchor_url = anchor_url
c.ignorews_url = _ignorews_url
c.context_url = _context_url
#get ranges of revisions if preset
rev_range = revision.split('...')[:2]
if len(rev_range) == 2:
@@ -127,16 +220,19 @@ class ChangesetController(BaseRepoContro
st = (0, 0)
# in this case node.size is good parameter since those are
# added nodes and their size defines how many changes were
# made
c.sum_added += node.size
fid = h.FID(revision, node.path)
line_context_lcl = get_line_ctx(fid, request.GET)
ignore_whitespace_lcl = get_ignore_ws(fid, request.GET)
if c.sum_added < self.cut_off_limit:
f_gitdiff = diffs.get_gitdiff(filenode_old, node,
ignore_whitespace=ignore_whitespace,
context=line_context)
ignore_whitespace=ignore_whitespace_lcl,
context=line_context_lcl)
d = diffs.DiffProcessor(f_gitdiff, format='gitdiff')
st = d.stat()
diff = d.as_html()
@@ -168,15 +264,18 @@ class ChangesetController(BaseRepoContro
if filenode_old.is_binary or node.is_binary:
diff = wrap_to_table(_('binary file'))
if c.sum_removed < self.cut_off_limit:
ignore_whitespace_lcl = get_ignore_ws(fid, request.GET,)
d = diffs.DiffProcessor(f_gitdiff,
format='gitdiff')
if (st[0] + st[1]) * 256 > self.cut_off_limit:
diff = wrap_to_table(_('Diff is to big '
'and was cut off, see '
@@ -49,12 +49,23 @@ def _reset(name, value=None, id=NotGiven
convert_boolean_attrs(attrs, ["disabled"])
return HTML.input(**attrs)
reset = _reset
safeid = _make_safe_id_component
def FID(raw_id,path):
Creates a uniqe ID for filenode based on it's path and revision
:param raw_id:
:param path:
return 'C-%s-%s' % (short_id(raw_id), safeid(safe_unicode(path)))
def get_token():
"""Return the current authentication token, creating one if one doesn't
already exist.
token_key = "_authentication_token"
from pylons import session
@@ -1887,13 +1887,15 @@ h3.files_location {
#changeset_content .container .left .message {
font-style: italic;
color: #556CB5;
white-space: pre-wrap;
}
#changeset_content .container .left .message a:hover {
text-decoration: none;
.cs_files .cur_cs {
margin: 10px 2px;
font-weight: bold;
.cs_files .node {
@@ -2251,33 +2253,33 @@ table.code-browser .browser-file {
padding: 2px 0px 2px 22px;
.diffblock .diff-menu-wrapper{
float: left;
.diffblock .changeset_header .diff-menu{
.diffblock .diff-menu{
position: absolute;
background: none repeat scroll 0 0 #FFFFFF;
border-color: #003367 #666666 #666666;
border-right: 1px solid #666666;
border-style: solid solid solid;
border-width: 1px;
box-shadow: 2px 8px 4px rgba(0, 0, 0, 0.2);
margin-top:5px;
margin-left:1px;
.diffblock .changeset_header .diff-menu ul li {
.diffblock .diff-menu ul li {
padding: 0px 0px 0px 0px !important;
.diffblock .changeset_header .diff-menu ul li a{
.diffblock .diff-menu ul li a{
display: block;
padding: 3px 8px 3px 8px !important;
.diffblock .changeset_header .diff-menu ul li a:hover{
.diffblock .diff-menu ul li a:hover{
background-color: #EEEEEE;
table.code-browser .browser-dir {
background: url("../images/icons/folder_16.png") no-repeat scroll 3px;
height: 16px;
@@ -3686,12 +3688,17 @@ div.diffblock.margined{
margin: 0px 20px 0px 20px;
div.diffblock .code-header{
border-bottom: 1px solid #CCCCCC;
background: #EEEEEE;
padding:10px 0 10px 0;
height: 14px;
div.diffblock .code-header .date{
float:left;
text-transform: uppercase;
div.diffblock .code-header div{
margin-left:4px;
font-size: 14px;
@@ -15,40 +15,39 @@
</%def>
<%def name="page_nav()">
${self.menu('changelog')}
<%def name="fid(raw_id,path)" filter="strip">
<%
return 'C-%s-%s' % (h.short_id(raw_id),h.safeid(h.safe_unicode(path)))
%>
<%def name="main()">
<div class="box">
<!-- box / title -->
<div class="title">
${self.breadcrumbs()}
</div>
<div class="table">
<div class="diffblock">
<div class="code-header">
<div>
<span>${h.link_to(_('raw diff'),
h.url('raw_changeset_home',repo_name=c.repo_name,revision=c.changeset.raw_id,diff='show'))}</span>
» <span>${h.link_to(_('download diff'),
h.url('raw_changeset_home',repo_name=c.repo_name,revision=c.changeset.raw_id,diff='download'))}</span>
<div class="comments-number" style="float:right;padding-right:5px">${len(c.comments)} comment(s) (${c.inline_cnt} ${_('inline')})</div>
<div class="date">${_('commit')} ${c.changeset.revision}:${h.short_id(c.changeset.raw_id)} ${c.changeset.date}</div>
<div class="diff-menu-wrapper">
<img class="diff-menu-activate" style="cursor: pointer" alt="diff-menu" src="${h.url('/images/icons/script_gear.png')}" />
<div class="diff-menu" style="display:none">
<ul>
<li>${h.link_to(_('raw diff'),h.url('raw_changeset_home',repo_name=c.repo_name,revision=c.changeset.raw_id,diff='show'))}</li>
<li>${h.link_to(_('download diff'),h.url('raw_changeset_home',repo_name=c.repo_name,revision=c.changeset.raw_id,diff='download'))}</li>
<li>${c.ignorews_url()}</li>
<li>${c.context_url()}</li>
</ul>
<div id="changeset_content">
<div class="container">
<div class="left">
<div class="date">${_('commit')} ${c.changeset.revision}: ${h.short_id(c.changeset.raw_id)}@${c.changeset.date}</div>
<div class="author">
<div class="gravatar">
<img alt="gravatar" src="${h.gravatar_url(h.email(c.changeset.author),20)}"/>
<span>${h.person(c.changeset.author)}</span><br/>
<span><a href="mailto:${h.email_or_none(c.changeset.author)}">${h.email_or_none(c.changeset.author)}</a></span><br/>
@@ -97,15 +96,15 @@
</span>
<div class="cs_files">
%for change,filenode,diff,cs1,cs2,stat in c.changes:
<div class="cs_${change}">
<div class="node">
%if change != 'removed':
${h.link_to(h.safe_unicode(filenode.path),h.url.current(anchor=self.fid(filenode.changeset.raw_id,filenode.path)))}
${h.link_to(h.safe_unicode(filenode.path),c.anchor_url(filenode.changeset.raw_id,filenode.path))}
%else:
${h.link_to(h.safe_unicode(filenode.path),h.url.current(anchor=self.fid('',filenode.path)))}
${h.link_to(h.safe_unicode(filenode.path),h.url.current(anchor=h.FID('',filenode.path)))}
%endif
<div class="changes">${h.fancy_file_stats(stat)}</div>
%endfor
% if c.cut_off:
@@ -115,14 +114,14 @@
%if change !='removed':
<div style="clear:both;height:10px"></div>
<div class="diffblock margined comm" id="${self.fid(filenode.changeset.raw_id,filenode.path)}">
<div id="${h.FID(filenode.changeset.raw_id,filenode.path)}" style="clear:both;height:90px;margin-top:-60px"></div>
<div class="diffblock margined comm">
<div class="changeset_header">
<div class="changeset_file">
${h.link_to_if(change!='removed',h.safe_unicode(filenode.path),h.url('files_home',repo_name=c.repo_name,
revision=filenode.changeset.raw_id,f_path=h.safe_unicode(filenode.path)))}
@@ -130,19 +129,21 @@
<img class="diff-menu-activate" style="margin-bottom:-6px;cursor: pointer" alt="diff-menu" src="${h.url('/images/icons/script_gear.png')}" />
<li>${h.link_to(_('diff'),h.url('files_diff_home',repo_name=c.repo_name,f_path=h.safe_unicode(filenode.path),diff2=cs2,diff1=cs1,diff='diff'))}</li>
<li>${h.link_to(_('raw diff'),h.url('files_diff_home',repo_name=c.repo_name,f_path=h.safe_unicode(filenode.path),diff2=cs2,diff1=cs1,diff='raw'))}</li>
<li>${h.link_to(_('download diff'),h.url('files_diff_home',repo_name=c.repo_name,f_path=h.safe_unicode(filenode.path),diff2=cs2,diff1=cs1,diff='download'))}</li>
<li>${c.ignorews_url(h.FID(filenode.changeset.raw_id,filenode.path))}</li>
<li>${c.context_url(h.FID(filenode.changeset.raw_id,filenode.path))}</li>
<span style="float:right;margin-top:-3px">
<label>
${_('show inline comments')}
${h.checkbox('',checked="checked",class_="show-inline-comments",id_for=self.fid(filenode.changeset.raw_id,filenode.path))}
${h.checkbox('',checked="checked",class_="show-inline-comments",id_for=h.FID(filenode.changeset.raw_id,filenode.path))}
</label>
<div class="code-body">
<div class="full_f_path" path="${filenode.path}"></div>
@@ -161,13 +162,13 @@
${comment.comment_inline_form()}
<div class="comments">
<div class="comments-number">${len(c.comments)} comment(s) (${c.inline_cnt} ${_('inline')})</div>
%for path, lines in c.inline_comments:
<div style="display:none" class="inline-comment-placeholder" path="${path}" target_id="${self.fid(c.changeset.raw_id,path)}">
<div style="display:none" class="inline-comment-placeholder" path="${path}" target_id="${h.FID(c.changeset.raw_id,path)}">
% for line,comments in lines.iteritems():
<div class="inline-comment-placeholder-line" line="${line}" target_id="${h.safeid(h.safe_unicode(path))}">
%for co in comments:
${comment.comment_block(co)}
Status change: