@@ -259,404 +259,420 @@ def pygmentize_annotation(repo_name, fil
return t, p, v
if i == 5:
return v, p, q
golden_ratio = 0.618033988749895
h = 0.22717784590367374
for _ in xrange(n):
h += golden_ratio
h %= 1
HSV_tuple = [h, 0.95, 0.95]
RGB_tuple = hsv_to_rgb(*HSV_tuple)
yield map(lambda x: str(int(x * 256)), RGB_tuple)
cgenerator = gen_color()
def get_color_string(cs):
if cs in color_dict:
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
short_id = lambda x: x[:12]
hide_credentials = lambda x: ''.join(credentials_filter(x))
def is_git(repository):
if hasattr(repository, 'alias'):
_type = repository.alias
elif hasattr(repository, 'repo_type'):
_type = repository.repo_type
_type = repository
return _type == 'git'
def is_hg(repository):
return _type == 'hg'
def email_or_none(author):
_email = email(author)
if _email != '':
return _email
# See if it contains a username we can get an email from
user = User.get_by_username(author_name(author), 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(author):
# attr to return from fetched user
person_getter = lambda usr: usr.username
# Valid email in the attribute passed, see if they're in the system
user = User.get_by_email(_email, case_insensitive=True, cache=True)
return person_getter(user)
# Maybe it's a username?
_author = author_name(author)
user = User.get_by_username(_author, case_insensitive=True,
# Still nothing? Just pass back the author name then
return _author
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,
revision=rev), title=tooltip(message(rev)),
class_='tooltip') for rev in revs[:revs_limit] ]))
compare_view = (' <div class="compare_view tooltip" title="%s">'
'<a href="%s">%s</a> '
'</div>' % (_('Show all combined changesets %s->%s' \
% (revs[0], revs[-1])),
revision='%s...%s' % (revs[0], revs[-1])
),
_('compare view'))
if len(revs) > revs_limit:
# if we have exactly one more than normally displayed:
# just display it, takes less space than displaying "and 1 more revisions"
if len(revs) == revs_limit + 1:
rev = revs[revs_limit]
cs_links.append(", " + link_to(rev,
class_='tooltip') )
# hidden-by-default ones
if len(revs) > revs_limit + 1:
uniq_id = revs[0]
html_tmpl = ('<span> %s '
'<a class="show_more" id="_%s" href="#more">%s</a> '
'%s</span>')
if not feed:
cs_links.append(html_tmpl % (_('and'), uniq_id, _('%s more') \
% (len(revs) - revs_limit),
_('revisions')))
html_tmpl = '<span id="%s" style="display:none"> %s </span>'
html_tmpl = '<span id="%s" style="display:none">, %s </span>'
html_tmpl = '<span id="%s"> %s </span>'
cs_links.append(html_tmpl % (uniq_id, ', '.join([link_to(rev,
morelinks = ', '.join([link_to(rev,
repo_name=repo_name, revision=rev),
title=message(rev), class_='tooltip')
for rev in revs[revs_limit:revs_top_limit]])))
for rev in revs[revs_limit:revs_top_limit]])
if len(revs) > revs_top_limit:
morelinks += ', ...'
cs_links.append(html_tmpl % (uniq_id, morelinks))
if len(revs) > 1:
cs_links.append(compare_view)
return ''.join(cs_links)
def get_fork_name():
repo_name = action_params
return _('fork name ') + str(link_to(action_params, url('summary_home',
repo_name=repo_name,)))
action_map = {'user_deleted_repo':(_('[deleted] repository'), None),
'user_created_repo':(_('[created] repository'), None),
'user_created_fork':(_('[created] repository as fork'), None),
'user_forked_repo':(_('[forked] repository'), 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] into'), get_cs_links),
'push_local':(_('[committed via RhodeCode] into'), get_cs_links),
'push_remote':(_('[pulled from remote] into'), get_cs_links),
'pull':(_('[pulled] from'), None),
'started_following_repo':(_('[started following] repository'), None),
'stopped_following_repo':(_('[stopped following] repository'), None),
}
action_str = action_map.get(action, action)
if feed:
action = action_str[0].replace('[', '').replace(']', '')
action = action_str[0].replace('[', '<span class="journal_highlight">')\
.replace(']', '</span>')
action_params_func = lambda :""
if callable(action_str[1]):
action_params_func = action_str[1]
return [literal(action), action_params_func]
def action_parser_icon(user_log):
action_params = None
tmpl = """<img src="%s%s" alt="%s"/>"""
map = {'user_deleted_repo':'database_delete.png',
'user_created_repo':'database_add.png',
'user_created_fork':'arrow_divide.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_local':'script_edit.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
def gravatar_url(email_address, size=30):
if (not str2bool(config['app_conf'].get('use_gravatar')) or
not email_address or email_address == 'anonymous@rhodecode.org'):
f=lambda a,l:min(l,key=lambda x:abs(x-a))
return url("/images/user%s.png" % f(size, [14, 16, 20, 24, 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 = safe_str(email_address)
# construct the url
gravatar_url = baseurl + hashlib.md5(email_address.lower()).hexdigest() + "?"
gravatar_url += urllib.urlencode({'d':default, 's':str(size)})
return gravatar_url
# REPO PAGER, PAGER FOR REPOSITORY
class RepoPage(Page):
def __init__(self, collection, page=1, items_per_page=20,
item_count=None, url=None, **kwargs):
"""Create a "RepoPage" instance. special pager for paging
repository
self._url_generator = url
# Safe the kwargs class-wide so they can be used in the pager() method
self.kwargs = kwargs
# Save a reference to the collection
self.original_collection = collection
self.collection = collection
# The self.page is the number of the current page.
# The first page has the number 1!
try:
self.page = int(page) # make it int() if we get it as a string
except (ValueError, TypeError):
self.page = 1
self.items_per_page = items_per_page
# Unless the user tells us how many items the collections has
# we calculate that ourselves.
if item_count is not None:
self.item_count = item_count
self.item_count = len(self.collection)
# Compute the number of the first and last available page
if self.item_count > 0:
self.first_page = 1
self.page_count = int(math.ceil(float(self.item_count) /
self.items_per_page))
self.last_page = self.first_page + self.page_count - 1
# Make sure that the requested page number is the range of
# valid pages
if self.page > self.last_page:
self.page = self.last_page
elif self.page < self.first_page:
self.page = self.first_page
# Note: the number of items on this page can be less than
# items_per_page if the last page is not full
self.first_item = max(0, (self.item_count) - (self.page *
items_per_page))
self.last_item = ((self.item_count - 1) - items_per_page *
(self.page - 1))
self.items = list(self.collection[self.first_item:self.last_item + 1])
# Links to previous and next page
if self.page > self.first_page:
self.previous_page = self.page - 1
self.previous_page = None
if self.page < self.last_page:
self.next_page = self.page + 1
self.next_page = None
# No items available
self.first_page = None
self.page_count = 0
self.last_page = None
self.first_item = None
self.last_item = None
self.items = []
# This is a subclass of the 'list' type. Initialise the list now.
list.__init__(self, reversed(self.items))
def changed_tooltip(nodes):
Generates a html string for changed nodes in changeset page.
Status change: