Files
@ 73434499fa72
Branch filter:
Location: kallithea/rhodecode/lib/helpers.py - annotation
73434499fa72
14.7 KiB
text/x-python
merges for stable
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 | 1e757ac98988 1e757ac98988 1e757ac98988 1e757ac98988 1e757ac98988 49eb69d78988 49eb69d78988 3d9da7893fdb 3d9da7893fdb 3d9da7893fdb 3d9da7893fdb 1e757ac98988 1e757ac98988 1e757ac98988 1e757ac98988 3d9da7893fdb 1e757ac98988 1e757ac98988 1e757ac98988 1e757ac98988 1e757ac98988 1e757ac98988 1e757ac98988 1e757ac98988 1e757ac98988 1e757ac98988 1e757ac98988 1e757ac98988 1e757ac98988 1e757ac98988 1e757ac98988 fd63782c4426 3d9da7893fdb 4cd0709b6d4b 4cd0709b6d4b 4cd0709b6d4b 3d9da7893fdb 3d9da7893fdb 3d9da7893fdb 3d9da7893fdb 4cd0709b6d4b 3d9da7893fdb 3d9da7893fdb 3d9da7893fdb 4cd0709b6d4b 4cd0709b6d4b 4cd0709b6d4b 4cd0709b6d4b 4cd0709b6d4b 4cd0709b6d4b 4cd0709b6d4b 49eb69d78988 49eb69d78988 49eb69d78988 49eb69d78988 49eb69d78988 49eb69d78988 49eb69d78988 49eb69d78988 49eb69d78988 49eb69d78988 49eb69d78988 49eb69d78988 49eb69d78988 49eb69d78988 49eb69d78988 49eb69d78988 49eb69d78988 3d9da7893fdb 3d9da7893fdb 3d9da7893fdb 1e757ac98988 3d9da7893fdb 3d9da7893fdb 3d9da7893fdb 1e757ac98988 1e757ac98988 1e757ac98988 1e757ac98988 1e757ac98988 1e757ac98988 1e757ac98988 1e757ac98988 1e757ac98988 05528ad948c4 1e757ac98988 3d9da7893fdb 3d9da7893fdb 3d9da7893fdb 5cc96df705b9 1e757ac98988 05528ad948c4 1e757ac98988 1e757ac98988 05528ad948c4 1e757ac98988 3d9da7893fdb 3d9da7893fdb 3d9da7893fdb 1e757ac98988 05528ad948c4 1e757ac98988 1e757ac98988 1e757ac98988 1e757ac98988 1e757ac98988 3d9da7893fdb 1e757ac98988 af6ca51fb80f 3d9da7893fdb 1e757ac98988 1e757ac98988 1e757ac98988 1e757ac98988 1e757ac98988 3d9da7893fdb 1e757ac98988 3d9da7893fdb 3d9da7893fdb 1e757ac98988 1e757ac98988 1e757ac98988 1e757ac98988 1e757ac98988 1e757ac98988 1e757ac98988 1e757ac98988 05528ad948c4 1e757ac98988 1e757ac98988 1e757ac98988 1e757ac98988 1e757ac98988 05528ad948c4 1e757ac98988 93b980ebee55 3d9da7893fdb 1e757ac98988 1e757ac98988 1e757ac98988 1e757ac98988 9279dfedcf93 1e757ac98988 1e757ac98988 1e757ac98988 1e757ac98988 9279dfedcf93 1e757ac98988 1e757ac98988 1e757ac98988 1e757ac98988 3d9da7893fdb 1e757ac98988 3d9da7893fdb 3d9da7893fdb 1e757ac98988 1e757ac98988 1e757ac98988 1e757ac98988 1e757ac98988 9279dfedcf93 1e757ac98988 9279dfedcf93 1e757ac98988 1e757ac98988 1e757ac98988 1e757ac98988 5cc96df705b9 1e757ac98988 1e757ac98988 1e757ac98988 1e757ac98988 1e757ac98988 1e757ac98988 1e757ac98988 5cc96df705b9 1e757ac98988 05528ad948c4 1e757ac98988 3d9da7893fdb 3d9da7893fdb 3d9da7893fdb 3d9da7893fdb 3d9da7893fdb 05528ad948c4 1e757ac98988 1e757ac98988 1e757ac98988 3d9da7893fdb 1e757ac98988 1e757ac98988 1e757ac98988 1e757ac98988 1e757ac98988 05528ad948c4 1e757ac98988 1e757ac98988 05528ad948c4 1e757ac98988 1e757ac98988 1e757ac98988 1e757ac98988 1e757ac98988 1e757ac98988 05528ad948c4 1e757ac98988 1e757ac98988 05528ad948c4 05528ad948c4 1e757ac98988 1e757ac98988 1e757ac98988 373ee7031003 373ee7031003 1e757ac98988 1e757ac98988 1e757ac98988 ffd07396d315 ffd07396d315 1e757ac98988 3d9da7893fdb 1e757ac98988 05528ad948c4 1e757ac98988 05528ad948c4 1e757ac98988 05528ad948c4 1e757ac98988 1e757ac98988 1e757ac98988 1e757ac98988 1e757ac98988 1e757ac98988 05528ad948c4 1e757ac98988 1e757ac98988 1e757ac98988 1e757ac98988 1e757ac98988 1e757ac98988 1e757ac98988 1e757ac98988 1e757ac98988 1e757ac98988 1e757ac98988 fd63782c4426 1e757ac98988 fd63782c4426 1e757ac98988 fd63782c4426 fd63782c4426 fd63782c4426 fd63782c4426 df61378032f3 9dc1d92d82ed 9dc1d92d82ed 1e757ac98988 fd63782c4426 4a3291628f09 4a3291628f09 4a3291628f09 4a3291628f09 4a3291628f09 fd63782c4426 fd63782c4426 fd63782c4426 fd63782c4426 fd63782c4426 fd63782c4426 4a3291628f09 dff6d5cb8bba fd63782c4426 fd63782c4426 dff6d5cb8bba dff6d5cb8bba fd63782c4426 1e757ac98988 1e757ac98988 1e757ac98988 1e757ac98988 ffd07396d315 1e757ac98988 df61378032f3 131c1e335fa7 3d9da7893fdb 131c1e335fa7 3d9da7893fdb 131c1e335fa7 131c1e335fa7 131c1e335fa7 131c1e335fa7 3d9da7893fdb 3d9da7893fdb 131c1e335fa7 131c1e335fa7 3d9da7893fdb 3d9da7893fdb 131c1e335fa7 131c1e335fa7 131c1e335fa7 131c1e335fa7 df61378032f3 df61378032f3 df61378032f3 df61378032f3 df61378032f3 df61378032f3 df61378032f3 df61378032f3 ee6f345736a6 df61378032f3 df61378032f3 df61378032f3 df61378032f3 df61378032f3 df61378032f3 7df3855bf6c7 bdc438fb4fe4 bdc438fb4fe4 3d9da7893fdb 3d9da7893fdb 3d9da7893fdb bdc438fb4fe4 bdc438fb4fe4 bdc438fb4fe4 bdc438fb4fe4 bdc438fb4fe4 bdc438fb4fe4 bdc438fb4fe4 bdc438fb4fe4 1af15d66838f bdc438fb4fe4 3d9da7893fdb bdc438fb4fe4 bdc438fb4fe4 bdc438fb4fe4 49eb69d78988 bdc438fb4fe4 49eb69d78988 7df3855bf6c7 af6ca51fb80f af6ca51fb80f af6ca51fb80f 4fa80e0484ef af6ca51fb80f af6ca51fb80f bdc438fb4fe4 af6ca51fb80f af6ca51fb80f af6ca51fb80f af6ca51fb80f af6ca51fb80f bdc438fb4fe4 bdc438fb4fe4 af6ca51fb80f af6ca51fb80f dbec976d9975 df61378032f3 5c87d5ad1b5e af6ca51fb80f af6ca51fb80f af6ca51fb80f af6ca51fb80f af6ca51fb80f af6ca51fb80f af6ca51fb80f 4fa80e0484ef 1af15d66838f 1af15d66838f 1af15d66838f 1af15d66838f 1af15d66838f 1af15d66838f 1af15d66838f 1af15d66838f 1af15d66838f 93b980ebee55 1af15d66838f 1af15d66838f 1af15d66838f 1af15d66838f 1af15d66838f af6ca51fb80f 1af15d66838f 1af15d66838f 1af15d66838f 3d9da7893fdb 1af15d66838f 1af15d66838f 1af15d66838f 1af15d66838f af6ca51fb80f af6ca51fb80f df61378032f3 df61378032f3 fd63782c4426 1e757ac98988 fd63782c4426 1e757ac98988 1e757ac98988 1e757ac98988 fd63782c4426 1e757ac98988 fd63782c4426 1e757ac98988 1e757ac98988 1e757ac98988 1e757ac98988 1e757ac98988 af6ca51fb80f 1e757ac98988 1e757ac98988 1e757ac98988 1e757ac98988 05528ad948c4 3d9da7893fdb 3d9da7893fdb 3d9da7893fdb 1e757ac98988 1e757ac98988 1e757ac98988 1e757ac98988 1e757ac98988 1e757ac98988 af6ca51fb80f af6ca51fb80f af6ca51fb80f af6ca51fb80f af6ca51fb80f af6ca51fb80f 3d9da7893fdb af6ca51fb80f af6ca51fb80f | """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
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 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
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"])
return HTML.input(**attrs)
reset = _reset
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):
"""Special function just to wrap our text into nice formatted
autowrapped text
:param tooltip_title:
"""
return wrap_paragraphs(escape(tooltip_title), trim_at)\
.replace('\n', '<br/>')
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
"""
js = '''
YAHOO.util.Event.onDOMReady(function(){
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
};
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,
});
});
'''
return literal(js)
tooltip = _ToolTip()
class _FilesBreadCrumbs(object):
def __call__(self, repo_name, rev, paths):
if isinstance(paths, str):
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',
repo_name=repo_name,
revision=rev,
f_path='/'.join(paths_l[:cnt + 1]))))
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)))
def _wrap_code(self, source):
for cnt, it in enumerate(source):
i, t = it
t = '<div id="#S-%s">%s</div>' % (cnt + 1, t)
yield i, t
def pygmentize(filenode, **kwargs):
"""
pygmentize function using pygments
:param filenode:
"""
return literal(code_highlight(filenode.content,
filenode.lexer, CodeHtmlFormatter(**kwargs)))
def pygmentize_annotation(filenode, **kwargs):
"""
pygmentize function for annotation
:param filenode:
"""
color_dict = {}
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
golden_ratio = 0.618033988749895
h = 0.22717784590367374
for c in xrange(n):
h += golden_ratio
h %= 1
HSV_tuple = [h, 0.95, 0.95]
RGB_tuple = colorsys.hsv_to_rgb(*HSV_tuple)
yield map(lambda x:str(int(x * 256)), RGB_tuple)
cgenerator = gen_color()
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(changeset):
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 % (changeset.author,
changeset.date,
tooltip(changeset.message))
lnk_format = '%5s:%s' % ('r%s' % changeset.revision,
short_id(changeset.raw_id))
uri = link_to(
lnk_format,
url('changeset_home', repo_name=changeset.repository.name,
revision=changeset.raw_id),
style=get_color_string(changeset.raw_id),
class_='tooltip',
title=tooltip_html
)
uri += '\n'
return uri
return literal(annotate_highlight(filenode, url_func, **kwargs))
def get_changeset_safe(repo, rev):
from vcs.backends.base import BaseRepository
from vcs.exceptions import RepositoryError
if not isinstance(repo, BaseRepository):
raise Exception('You must pass an Repository '
'object as first argument got %s', type(repo))
try:
cs = repo.get_changeset(rev)
except RepositoryError:
from rhodecode.lib.utils import EmptyChangeset
cs = EmptyChangeset()
return cs
flash = _Flash()
#==============================================================================
# MERCURIAL FILTERS available via h.
#==============================================================================
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 ''
agescales = [("year", 3600 * 24 * 365),
("month", 3600 * 24 * 30),
("day", 3600 * 24),
("hour", 3600),
("minute", 60),
("second", 1), ]
age = datetime.now() - curdate
age_seconds = (age.days * agescales[2][1]) + age.seconds
pos = 1
for scale in agescales:
if scale[1] <= age_seconds:
if pos == 6:pos = 5
return time_ago_in_words(curdate, agescales[pos][0]) + ' ' + _('ago')
pos += 1
return _('just now')
age = lambda x:_age(x)
capitalize = lambda x: x.capitalize()
email = util.email
email_or_none = lambda x: util.email(x) if util.email(x) != x else None
person = lambda x: _person(x)
short_id = lambda x: x[:12]
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):
"""
This helper will map the specified string action into translated
fancy names with icons and links
@param action:
"""
action = user_log.action
action_params = ' '
x = action.split(':')
if len(x) > 1:
action, action_params = x
def get_cs_links():
revs_limit = 5
revs = action_params.split(',')
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 = ('<span> %s '
'<a class="show_more" id="_%s" href="#">%s</a> '
'%s</span>')
cs_links += html_tmpl % (_('and'), uniq_id, _('%s more') \
% (len(revs) - revs_limit),
_('revisions'))
html_tmpl = '<span id="%s" style="display:none"> %s </span>'
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:] ]))
return cs_links
def get_fork_name():
repo_name = action_params
return str(link_to(action_params, url('summary_home',
repo_name=repo_name,)))
map = {'user_deleted_repo':(_('[deleted] repository'), None),
'user_created_repo':(_('[created] repository'), None),
'user_forked_repo':(_('[forked] repository as'), get_fork_name),
'user_updated_repo':(_('[updated] repository'), None),
'admin_deleted_repo':(_('[delete] repository'), None),
'admin_created_repo':(_('[created] repository'), None),
'admin_forked_repo':(_('[forked] repository'), None),
'admin_updated_repo':(_('[updated] repository'), None),
'push':(_('[pushed] '), get_cs_links),
'pull':(_('[pulled] '), None),
'started_following_repo':(_('[started following] repository'), None),
'stopped_following_repo':(_('[stopped following] repository'), None),
}
action_str = map.get(action, action)
action = action_str[0].replace('[', '<span class="journal_highlight">')\
.replace(']', '</span>')
action_params_func = lambda :""
if action_str[1] is not None:
action_params_func = action_str[1]
return literal(action + " " + action_params_func())
def action_parser_icon(user_log):
action = user_log.action
action_params = None
x = action.split(':')
if len(x) > 1:
action, action_params = x
tmpl = """<img src="%s%s" alt="%s"/>"""
map = {'user_deleted_repo':'database_delete.png',
'user_created_repo':'database_add.png',
'user_forked_repo':'arrow_divide.png',
'user_updated_repo':'database_edit.png',
'admin_deleted_repo':'database_delete.png',
'admin_created_repo':'database_add.png',
'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',
}
return literal(tmpl % ((url('/images/icons/')),
map.get(action, action), action))
#==============================================================================
# PERMS
#==============================================================================
from rhodecode.lib.auth import HasPermissionAny, HasPermissionAll, \
HasRepoPermissionAny, HasRepoPermissionAll
#==============================================================================
# GRAVATAR URL
#==============================================================================
import hashlib
import urllib
from pylons import request
def gravatar_url(email_address, size=30):
ssl_enabled = 'https' == request.environ.get('wsgi.url_scheme')
default = 'identicon'
baseurl_nossl = "http://www.gravatar.com/avatar/"
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 changed_tooltip(nodes):
if nodes:
pref = ': <br/> '
suf = ''
if len(nodes) > 30:
suf = '<br/>' + _(' and %s more') % (len(nodes) - 30)
return literal(pref + '<br/> '.join([safe_unicode(x.path) for x in nodes[:30]]) + suf)
else:
return ': ' + _('No Files')
|