"""Helper functions
Consists of functions to typically be used within templates, but also
available to Controllers. This module is available to both as 'h'.
"""
import random
import hashlib
import StringIO
import urllib
import math
from datetime import datetime
from pygments.formatters.html import HtmlFormatter
from pygments import highlight as code_highlight
from pylons import url, request, config
from pylons.i18n.translation import _, ungettext
from webhelpers.html import literal, HTML, escape
from webhelpers.html.tools import *
from webhelpers.html.builder import make_tag
from webhelpers.html.tags import auto_discovery_link, checkbox, css_classes, \
end_form, file, form, hidden, image, javascript_link, link_to, link_to_if, \
link_to_unless, ol, required_legend, select, stylesheet_link, submit, text, \
password, textarea, title, ul, xml_declaration, radio
from webhelpers.html.tools import auto_link, button_to, highlight, js_obfuscate, \
mail_to, strip_links, strip_tags, tag_re
from webhelpers.number import format_byte_size, format_bit_size
from webhelpers.pylonslib import Flash as _Flash
from webhelpers.pylonslib.secure_form import secure_form
from webhelpers.text import chop_at, collapse, convert_accented_entities, \
convert_misc_entities, lchop, plural, rchop, remove_formatting, \
replace_whitespace, urlify, truncate, wrap_paragraphs
from webhelpers.date import time_ago_in_words
from webhelpers.paginate import Page
from webhelpers.html.tags import _set_input_attrs, _set_id_attr, \
convert_boolean_attrs, NotGiven, _make_safe_id_component
from rhodecode.lib.annotate import annotate_highlight
from rhodecode.lib.utils import repo_name_slug
from rhodecode.lib import str2bool, safe_unicode, safe_str, get_changeset_safe
from rhodecode.lib.markup_renderer import MarkupRenderer
def _reset(name, value=None, id=NotGiven, type="reset", **attrs):
Reset button
_set_input_attrs(attrs, type, name, value)
_set_id_attr(attrs, id, name)
convert_boolean_attrs(attrs, ["disabled"])
return HTML.input(**attrs)
reset = _reset
safeid = _make_safe_id_component
def get_token():
"""Return the current authentication token, creating one if one doesn't
already exist.
token_key = "_authentication_token"
from pylons import session
if not token_key in session:
try:
token = hashlib.sha1(str(random.getrandbits(128))).hexdigest()
except AttributeError: # Python < 2.4
token = hashlib.sha1(str(random.randrange(2 ** 128))).hexdigest()
session[token_key] = token
if hasattr(session, 'save'):
session.save()
return session[token_key]
class _GetError(object):
"""Get error from form_errors, and represent it as span wrapped error
message
:param field_name: field to fetch errors for
:param form_errors: form errors dict
def __call__(self, field_name, form_errors):
tmpl = """<span class="error_msg">%s</span>"""
if form_errors and form_errors.has_key(field_name):
return literal(tmpl % form_errors.get(field_name))
get_error = _GetError()
class _ToolTip(object):
def __call__(self, tooltip_title, trim_at=50):
@@ -243,105 +242,140 @@ def pygmentize_annotation(repo_name, fil
def get_color_string(cs):
if color_dict.has_key(cs):
col = color_dict[cs]
else:
col = color_dict[cs] = cgenerator.next()
return "color: rgb(%s)! important;" % (', '.join(col))
def url_func(repo_name):
def _url_func(changeset):
author = changeset.author
date = changeset.date
message = tooltip(changeset.message)
tooltip_html = ("<div style='font-size:0.8em'><b>Author:</b>"
" %s<br/><b>Date:</b> %s</b><br/><b>Message:"
"</b> %s<br/></div>")
tooltip_html = tooltip_html % (author, date, message)
lnk_format = '%5s:%s' % ('r%s' % changeset.revision,
short_id(changeset.raw_id))
uri = link_to(
lnk_format,
url('changeset_home', repo_name=repo_name,
revision=changeset.raw_id),
style=get_color_string(changeset.raw_id),
class_='tooltip',
title=tooltip_html
)
uri += '\n'
return uri
return _url_func
return literal(annotate_highlight(filenode, url_func(repo_name), **kwargs))
def is_following_repo(repo_name, user_id):
from rhodecode.model.scm import ScmModel
return ScmModel().is_following_repo(repo_name, user_id)
flash = _Flash()
#==============================================================================
# SCM FILTERS available via h.
from vcs.utils import author_name, author_email
from rhodecode.lib import credentials_filter, age as _age
from rhodecode.model.db import User
age = lambda x:_age(x)
capitalize = lambda x: x.capitalize()
email = author_email
email_or_none = lambda x: email(x) if email(x) != x else None
person = lambda x: author_name(x)
short_id = lambda x: x[:12]
hide_credentials = lambda x: ''.join(credentials_filter(x))
def email_or_none(x):
if email(x) != '':
return email(x)
# See if it contains a username we can get an email from
user = User.get_by_username(author_name(x), case_insensitive=True,
cache=True)
if user is not None:
return user.email
# No valid email, not a valid user in the system, none!
return None
def person(x):
# Valid email in the attribute passed, see if they're in the system
# attr to return from fetched user
person_getter = lambda usr: usr.username
user = User.get_by_email(email(x), case_insensitive=True, cache=True)
return person_getter(user)
# Maybe it's a username?
# Still nothing? Just pass back the author name then
return author_name(x)
def bool2icon(value):
"""Returns True/False values represented as small html image of true/false
icons
:param value: bool value
if value is True:
return HTML.tag('img', src=url("/images/icons/accept.png"),
alt=_('True'))
if value is False:
return HTML.tag('img', src=url("/images/icons/cancel.png"),
alt=_('False'))
return value
def action_parser(user_log, feed=False):
"""This helper will action_map the specified string action into translated
fancy names with icons and links
:param user_log: user log instance
:param feed: use output for feeds (no html and fancy icons)
action = user_log.action
action_params = ' '
x = action.split(':')
if len(x) > 1:
action, action_params = x
def get_cs_links():
revs_limit = 3 #display this amount always
revs_top_limit = 50 #show upto this amount of changesets hidden
revs = action_params.split(',')
repo_name = user_log.repository.repo_name
repo = user_log.repository.scm_instance
message = lambda rev: get_changeset_safe(repo, rev).message
cs_links = []
cs_links.append(" " + ', '.join ([link_to(rev,
url('changeset_home',
repo_name=repo_name,
@@ -9,98 +9,97 @@ ${c.repo_name} ${_('Changelog')} - ${c.r
<%def name="breadcrumbs_links()">
${h.link_to(u'Home',h.url('/'))}
»
${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
${_('Changelog')} - ${_('showing ')} ${c.size if c.size <= c.total_cs else c.total_cs} ${_('out of')} ${c.total_cs} ${_('revisions')}
</%def>
<%def name="page_nav()">
${self.menu('changelog')}
<%def name="main()">
<div class="box">
<!-- box / title -->
<div class="title">
${self.breadcrumbs()}
</div>
<div class="table">
% if c.pagination:
<div id="graph">
<div id="graph_nodes">
<canvas id="graph_canvas"></canvas>
<div id="graph_content">
<div class="container_header">
${h.form(h.url.current(),method='get')}
<div class="info_box" style="float:left">
${h.submit('set',_('Show'),class_="ui-btn")}
${h.text('size',size=1,value=c.size)}
<span class="rev">${_('revisions')}</span>
${h.end_form()}
<div style="float:right">${h.select('branch_filter',c.branch_name,c.branch_filters)}</div>
<div id="rev_range_container" style="display:none"></div>
%for cnt,cs in enumerate(c.pagination):
<div id="chg_${cnt+1}" class="container">
<div class="left">
<div class="date">
${h.checkbox(cs.short_id,class_="changeset_range")}
<span>${_('commit')} ${cs.revision}: ${h.short_id(cs.raw_id)}@${cs.date}</span>
<div class="author">
<div class="gravatar">
<img alt="gravatar" src="${h.gravatar_url(h.email(cs.author),16)}"/>
<div title="${h.email_or_none(cs.author)}" class="user">${h.person(cs.author)}</div>
##<span><a href="mailto:${h.email_or_none(cs.author)}">${h.email_or_none(cs.author)}</a></span><br/>
<div title="${cs.author}" class="user">${h.person(cs.author)}</div>
<div class="message">${h.link_to(h.wrap_paragraphs(cs.message),h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id))}</div>
<div class="right">
<div id="${cs.raw_id}_changes_info" class="changes">
<span id="${cs.raw_id}" class="changed_total tooltip" title="${_('Affected number of files, click to show more details')}">${len(cs.affected_files)}</span>
%if len(cs.parents)>1:
<div class="merge">${_('merge')}</div>
%endif
%if cs.parents:
%for p_cs in reversed(cs.parents):
<div class="parent">${_('Parent')} ${p_cs.revision}: ${h.link_to(h.short_id(p_cs.raw_id),
h.url('changeset_home',repo_name=c.repo_name,revision=p_cs.raw_id),title=p_cs.message)}
%endfor
%else:
<div class="parent">${_('No parents')}</div>
<span class="logtags">
%if cs.branch:
<span class="branchtag" title="${'%s %s' % (_('branch'),cs.branch)}">
${h.link_to(cs.branch,h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id))}</span>
%for tag in cs.tags:
<span class="tagtag" title="${'%s %s' % (_('tag'),tag)}">
${h.link_to(tag,h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id))}</span>
</span>
<div class="pagination-wh pagination-left">
${c.pagination.pager('$link_previous ~2~ $link_next')}
<script type="text/javascript" src="${h.url('/js/graph.js')}"></script>
<script type="text/javascript">
YAHOO.util.Event.onDOMReady(function(){
//Monitor range checkboxes and build a link to changesets
//ranges
var checkboxes = YUD.getElementsByClassName('changeset_range');
var url_tmpl = "${h.url('changeset_home',repo_name=c.repo_name,revision='__REVRANGE__')}";
@@ -19,97 +19,97 @@
<ul class="links">
<li>
<span style="text-transform: uppercase;"><a href="#">${_('branch')}: ${c.cs.branch}</a></span>
</li>
</ul>
<div id="files_data">
<h3 class="files_location">${_('Location')}: ${h.files_breadcrumbs(c.repo_name,c.cs.revision,c.file.path)}</h3>
<dl>
<dt style="padding-top:10px;font-size:16px">${_('History')}</dt>
<dd>
<div>
${h.form(h.url('files_diff_home',repo_name=c.repo_name,f_path=c.f_path),method='get')}
${h.hidden('diff2',c.file.last_changeset.raw_id)}
${h.select('diff1',c.file.last_changeset.raw_id,c.file_history)}
${h.submit('diff','diff to revision',class_="ui-btn")}
${h.submit('show_rev','show at revision',class_="ui-btn")}
</dd>
</dl>
<div id="body" class="codeblock">
<div class="code-header">
<div class="stats">
<div class="left"><img src="${h.url('/images/icons/file.png')}"/></div>
<div class="left item">${h.link_to("r%s:%s" % (c.file.last_changeset.revision,h.short_id(c.file.last_changeset.raw_id)),h.url('changeset_home',repo_name=c.repo_name,revision=c.file.last_changeset.raw_id))}</div>
<div class="left item">${h.format_byte_size(c.file.size,binary=True)}</div>
<div class="left item last">${c.file.mimetype}</div>
<div class="buttons">
${h.link_to(_('show source'),h.url('files_home',repo_name=c.repo_name,revision=c.cs.raw_id,f_path=c.f_path),class_="ui-btn")}
${h.link_to(_('show as raw'),h.url('files_raw_home',repo_name=c.repo_name,revision=c.cs.raw_id,f_path=c.f_path),class_="ui-btn")}
${h.link_to(_('download as raw'),h.url('files_rawfile_home',repo_name=c.repo_name,revision=c.cs.raw_id,f_path=c.f_path),class_="ui-btn")}
% if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name):
% if not c.file.is_binary:
${h.link_to(_('edit'),h.url('files_edit_home',repo_name=c.repo_name,revision=c.cs.raw_id,f_path=c.f_path),class_="ui-btn")}
% endif
<img alt="gravatar" src="${h.gravatar_url(h.email(c.cs.author),16)}"/>
<div title="${h.email_or_none(c.cs.author)}" class="user">${h.person(c.cs.author)}</div>
<div title="${c.cs.author}" class="user">${h.person(c.cs.author)}</div>
<div class="commit">${c.file.last_changeset.message}</div>
<div class="code-body">
%if c.file.is_binary:
${_('Binary file (%s)') % c.file.mimetype}
% if c.file.size < c.cut_off_limit:
${h.pygmentize_annotation(c.repo_name,c.file,linenos=True,anchorlinenos=True,lineanchors='L',cssclass="code-highlight")}
${_('File is too big to display')} ${h.link_to(_('show as raw'),
h.url('files_raw_home',repo_name=c.repo_name,revision=c.cs.revision,f_path=c.f_path))}
function highlight_lines(lines){
for(pos in lines){
YUD.setStyle('L'+lines[pos],'background-color','#FFFFBE');
}
page_highlights = location.href.substring(location.href.indexOf('#')+1).split('L');
if (page_highlights.length == 2){
highlight_ranges = page_highlights[1].split(",");
var h_lines = [];
for (pos in highlight_ranges){
var _range = highlight_ranges[pos].split('-');
if(_range.length == 2){
var start = parseInt(_range[0]);
var end = parseInt(_range[1]);
if (start < end){
for(var i=start;i<=end;i++){
h_lines.push(i);
else{
h_lines.push(parseInt(highlight_ranges[pos]));
highlight_lines(h_lines);
//remember original location
var old_hash = location.href.substring(location.href.indexOf('#'));
// this makes a jump to anchor moved by 3 posstions for padding
window.location.hash = '#L'+Math.max(parseInt(h_lines[0])-3,1);
//sets old anchor
@@ -51,59 +51,61 @@
<th>${_('Last modified')}</th>
<th>${_('Last commiter')}</th>
</tr>
</thead>
<tbody id="tbody">
%if c.file.parent:
<tr class="parity0">
<td>
${h.link_to('..',h.url('files_home',repo_name=c.repo_name,revision=c.changeset.raw_id,f_path=c.file.parent.path),class_="browser-dir ypjax-link")}
</td>
<td></td>
%for cnt,node in enumerate(c.file):
<tr class="parity${cnt%2}">
${h.link_to(node.name,h.url('files_home',repo_name=c.repo_name,revision=c.changeset.raw_id,f_path=h.safe_unicode(node.path)),class_=file_class(node)+" ypjax-link")}
%if node.is_file():
${h.format_byte_size(node.size,binary=True)}
${node.mimetype}
<span class="tooltip" title="${node.last_changeset.message}">
${'r%s:%s' % (node.last_changeset.revision,node.last_changeset.short_id)}</span>
<span class="tooltip" title="${node.last_changeset.date}">
${h.age(node.last_changeset.date)}</span>
${node.last_changeset.author}
<span title="${node.last_changeset.author}">
${h.person(node.last_changeset.author)}
</tbody>
<tbody id="tbody_filtered" style="display:none">
</table>
\ No newline at end of file
${h.link_to(_('show annotation'),h.url('files_annotate_home',repo_name=c.repo_name,revision=c.changeset.raw_id,f_path=c.f_path),class_="ui-btn")}
${h.link_to(_('show as raw'),h.url('files_raw_home',repo_name=c.repo_name,revision=c.changeset.raw_id,f_path=c.f_path),class_="ui-btn")}
${h.link_to(_('download as raw'),h.url('files_rawfile_home',repo_name=c.repo_name,revision=c.changeset.raw_id,f_path=c.f_path),class_="ui-btn")}
${h.link_to(_('edit'),h.url('files_edit_home',repo_name=c.repo_name,revision=c.changeset.raw_id,f_path=c.f_path),class_="ui-btn")}
<img alt="gravatar" src="${h.gravatar_url(h.email(c.changeset.author),16)}"/>
<div title="${h.email_or_none(c.changeset.author)}" class="user">${h.person(c.changeset.author)}</div>
<div title="${c.changeset.author}" class="user">${h.person(c.changeset.author)}</div>
${h.pygmentize(c.file,linenos=True,anchorlinenos=True,lineanchors='L',cssclass="code-highlight")}
h.url('files_raw_home',repo_name=c.repo_name,revision=c.changeset.raw_id,f_path=c.f_path))}
Status change: