diff --git a/rhodecode/lib/helpers.py b/rhodecode/lib/helpers.py
--- a/rhodecode/lib/helpers.py
+++ b/rhodecode/lib/helpers.py
@@ -5,11 +5,15 @@ available to Controllers. This module is
"""
import random
import hashlib
+import StringIO
+import urllib
+
+from datetime import datetime
from pygments.formatters import HtmlFormatter
from pygments import highlight as code_highlight
from pylons import url, app_globals as g
from pylons.i18n.translation import _, ungettext
-from vcs.utils.annotate import annotate_highlight
+
from webhelpers.html import literal, HTML, escape
from webhelpers.html.tools import *
from webhelpers.html.builder import make_tag
@@ -26,11 +30,18 @@ from webhelpers.text import chop_at, col
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
+from vcs.utils.annotate import annotate_highlight
+from rhodecode.lib.utils import repo_name_slug
+from rhodecode.lib import str2bool, safe_unicode
+
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"])
@@ -55,24 +66,13 @@ def get_token():
session.save()
return session[token_key]
-
-#Custom helpers here :)
-class _Link(object):
- '''
- Make a url based on label and url with help of url_for
- :param label:name of link if not defined url is used
- :param url: the url for link
- '''
+class _GetError(object):
+ """Get error from form_errors, and represent it as span wrapped error
+ message
- def __call__(self, label='', *url_, **urlargs):
- if label is None or '':
- label = url
- link_fn = link_to(label, url(*url_, **urlargs))
- return link_fn
-
-link = _Link()
-
-class _GetError(object):
+ :param field_name: field to fetch errors for
+ :param form_errors: form errors dict
+ """
def __call__(self, field_name, form_errors):
tmpl = """%s"""
@@ -81,29 +81,12 @@ class _GetError(object):
get_error = _GetError()
-def recursive_replace(str, replace=' '):
- """
- Recursive replace of given sign to just one instance
- :param str: given string
- :param replace:char to find and replace multiple instances
-
- Examples::
- >>> recursive_replace("Mighty---Mighty-Bo--sstones",'-')
- 'Mighty-Mighty-Bo-sstones'
- """
-
- if str.find(replace * 2) == -1:
- return str
- else:
- str = str.replace(replace * 2, replace)
- return recursive_replace(str, replace)
-
class _ToolTip(object):
def __call__(self, tooltip_title, trim_at=50):
- """
- Special function just to wrap our text into nice formatted autowrapped
- text
+ """Special function just to wrap our text into nice formatted
+ autowrapped text
+
:param tooltip_title:
"""
@@ -111,11 +94,9 @@ class _ToolTip(object):
.replace('\n', '
')
def activate(self):
- """
- Adds tooltip mechanism to the given Html all tooltips have to have
- set class tooltip and set attribute tooltip_title.
- Then a tooltip will be generated based on that
- All with yui js tooltip
+ """Adds tooltip mechanism to the given Html all tooltips have to have
+ set class `tooltip` and set attribute `tooltip_title`.
+ Then a tooltip will be generated based on that. All with yui js tooltip
"""
js = '''
@@ -123,104 +104,26 @@ class _ToolTip(object):
function toolTipsId(){
var ids = [];
var tts = YAHOO.util.Dom.getElementsByClassName('tooltip');
-
+
for (var i = 0; i < tts.length; i++) {
//if element doesn't not have and id autogenerate one for tooltip
-
+
if (!tts[i].id){
tts[i].id='tt'+i*100;
}
ids.push(tts[i].id);
}
- return ids
+ return ids
};
- var myToolTips = new YAHOO.widget.Tooltip("tooltip", {
- context: toolTipsId(),
+ var myToolTips = new YAHOO.widget.Tooltip("tooltip", {
+ context: [[toolTipsId()],"tl","bl",null,[0,5]],
monitorresize:false,
xyoffset :[0,0],
autodismissdelay:300000,
hidedelay:5,
showdelay:20,
});
-
- //Mouse Over event disabled for new repositories since they don't
- //have last commit message
- myToolTips.contextMouseOverEvent.subscribe(
- function(type, args) {
- var context = args[0];
- var txt = context.getAttribute('tooltip_title');
- if(txt){
- return true;
- }
- else{
- return false;
- }
- });
-
-
- // Set the text for the tooltip just before we display it. Lazy method
- myToolTips.contextTriggerEvent.subscribe(
- function(type, args) {
-
- var context = args[0];
-
- var txt = context.getAttribute('tooltip_title');
- this.cfg.setProperty("text", txt);
-
-
- // positioning of tooltip
- var tt_w = this.element.clientWidth;
- var tt_h = this.element.clientHeight;
-
- var context_w = context.offsetWidth;
- var context_h = context.offsetHeight;
-
- var pos_x = YAHOO.util.Dom.getX(context);
- var pos_y = YAHOO.util.Dom.getY(context);
-
- var display_strategy = 'top';
- var xy_pos = [0,0];
- switch (display_strategy){
-
- case 'top':
- var cur_x = (pos_x+context_w/2)-(tt_w/2);
- var cur_y = (pos_y-tt_h-4);
- xy_pos = [cur_x,cur_y];
- break;
- case 'bottom':
- var cur_x = (pos_x+context_w/2)-(tt_w/2);
- var cur_y = pos_y+context_h+4;
- xy_pos = [cur_x,cur_y];
- break;
- case 'left':
- var cur_x = (pos_x-tt_w-4);
- var cur_y = pos_y-((tt_h/2)-context_h/2);
- xy_pos = [cur_x,cur_y];
- break;
- case 'right':
- var cur_x = (pos_x+context_w+4);
- var cur_y = pos_y-((tt_h/2)-context_h/2);
- xy_pos = [cur_x,cur_y];
- break;
- default:
- var cur_x = (pos_x+context_w/2)-(tt_w/2);
- var cur_y = pos_y-tt_h-4;
- xy_pos = [cur_x,cur_y];
- break;
-
- }
-
- this.cfg.setProperty("xy",xy_pos);
-
- });
-
- //Mouse out
- myToolTips.contextMouseOutEvent.subscribe(
- function(type, args) {
- var context = args[0];
-
- });
});
'''
return literal(js)
@@ -231,12 +134,11 @@ class _FilesBreadCrumbs(object):
def __call__(self, repo_name, rev, paths):
if isinstance(paths, str):
- paths = paths.decode('utf-8', 'replace')
+ paths = safe_unicode(paths)
url_l = [link_to(repo_name, url('files_home',
repo_name=repo_name,
revision=rev, f_path=''))]
paths_l = paths.split('/')
-
for cnt, p in enumerate(paths_l):
if p != '':
url_l.append(link_to(p, url('files_home',
@@ -247,7 +149,10 @@ class _FilesBreadCrumbs(object):
return literal('/'.join(url_l))
files_breadcrumbs = _FilesBreadCrumbs()
+
class CodeHtmlFormatter(HtmlFormatter):
+ """My code Html Formatter for source codes
+ """
def wrap(self, source, outfile):
return self._wrap_div(self._wrap_pre(self._wrap_code(source)))
@@ -272,15 +177,16 @@ def pygmentize_annotation(filenode, **kw
"""
color_dict = {}
- def gen_color():
- """generator for getting 10k of evenly distibuted colors using hsv color
- and golden ratio.
+ def gen_color(n=10000):
+ """generator for getting n of evenly distributed colors using
+ hsv color and golden ratio. It always return same order of colors
+
+ :returns: RGB tuple
"""
import colorsys
- n = 10000
golden_ratio = 0.618033988749895
h = 0.22717784590367374
- #generate 10k nice web friendly colors in the same order
+
for c in xrange(n):
h += golden_ratio
h %= 1
@@ -312,27 +218,13 @@ def pygmentize_annotation(filenode, **kw
revision=changeset.raw_id),
style=get_color_string(changeset.raw_id),
class_='tooltip',
- tooltip_title=tooltip_html
+ title=tooltip_html
)
uri += '\n'
return uri
return literal(annotate_highlight(filenode, url_func, **kwargs))
-def repo_name_slug(value):
- """Return slug of name of repository
- This function is called on each creation/modification
- of repository to prevent bad names in repo
- """
- slug = remove_formatting(value)
- slug = strip_tags(slug)
-
- for c in """=[]\;'"<>,/~!@#$%^&*()+{}|: """:
- slug = slug.replace(c, '-')
- slug = recursive_replace(slug, '-')
- slug = collapse(slug, '-')
- return slug
-
def get_changeset_safe(repo, rev):
from vcs.backends.base import BaseRepository
from vcs.exceptions import RepositoryError
@@ -357,16 +249,12 @@ flash = _Flash()
from mercurial import util
from mercurial.templatefilters import person as _person
-
-
def _age(curdate):
"""turns a datetime into an age string."""
if not curdate:
return ''
- from datetime import timedelta, datetime
-
agescales = [("year", 3600 * 24 * 365),
("month", 3600 * 24 * 30),
("day", 3600 * 24),
@@ -394,17 +282,19 @@ short_id = lambda x: x[:12]
def bool2icon(value):
- """
- Returns True/False values represented as small html image of true/false
+ """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'))
+ 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 HTML.tag('img', src=url("/images/icons/cancel.png"),
+ alt=_('False'))
return value
@@ -427,10 +317,9 @@ def action_parser(user_log):
def get_cs_links():
revs_limit = 5
revs = action_params.split(',')
- cs_links = " " + ', '.join ([link(rev,
- url('changeset_home',
- repo_name=user_log.repository.repo_name,
- revision=rev)) for rev in revs[:revs_limit] ])
+ cs_links = " " + ', '.join ([link_to(rev, url('changeset_home',
+ repo_name=user_log.repository.repo_name,
+ revision=rev)) for rev in revs[:revs_limit]])
if len(revs) > revs_limit:
uniq_id = revs[0]
html_tmpl = (' %s '
@@ -441,7 +330,7 @@ def action_parser(user_log):
_('revisions'))
html_tmpl = ' %s '
- cs_links += html_tmpl % (uniq_id, ', '.join([link(rev,
+ cs_links += html_tmpl % (uniq_id, ', '.join([link_to(rev,
url('changeset_home',
repo_name=user_log.repository.repo_name,
revision=rev)) for rev in revs[revs_limit:] ]))
@@ -495,6 +384,7 @@ def action_parser_icon(user_log):
'admin_forked_repo':'arrow_divide.png',
'admin_updated_repo':'database_edit.png',
'push':'script_add.png',
+ 'push_remote':'connect.png',
'pull':'down_16.png',
'started_following_repo':'heart_add.png',
'stopped_following_repo':'heart_delete.png',
@@ -523,35 +413,21 @@ def gravatar_url(email_address, size=30)
baseurl_ssl = "https://secure.gravatar.com/avatar/"
baseurl = baseurl_ssl if ssl_enabled else baseurl_nossl
-
+ if isinstance(email_address, unicode):
+ #hashlib crashes on unicode items
+ email_address = email_address.encode('utf8', 'replace')
# construct the url
gravatar_url = baseurl + hashlib.md5(email_address.lower()).hexdigest() + "?"
gravatar_url += urllib.urlencode({'d':default, 's':str(size)})
return gravatar_url
-def safe_unicode(str):
- """safe unicode function. In case of UnicodeDecode error we try to return
- unicode with errors replace, if this failes we return unicode with
- string_escape decoding """
-
- try:
- u_str = unicode(str)
- except UnicodeDecodeError:
- try:
- u_str = unicode(str, 'utf-8', 'replace')
- except UnicodeDecodeError:
- #incase we have a decode error just represent as byte string
- u_str = unicode(str(str).encode('string_escape'))
-
- return u_str
-
def changed_tooltip(nodes):
if nodes:
pref = ':
'
suf = ''
if len(nodes) > 30:
suf = '
' + _(' and %s more') % (len(nodes) - 30)
- return literal(pref + '
'.join([x.path.decode('utf-8', 'replace') for x in nodes[:30]]) + suf)
+ return literal(pref + '
'.join([safe_unicode(x.path) for x in nodes[:30]]) + suf)
else:
return ': ' + _('No Files')