@@ -571,356 +571,356 @@ def action_parser_icon(user_log):
}
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
else:
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.
It limits the output to 30 entries
:param nodes: LazyNodesGenerator
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)
return ': ' + _('No Files')
def repo_link(groups_and_repos):
Makes a breadcrumbs link to repo within a group
joins » on each group to create a fancy link
ex::
group >> subgroup >> repo
:param groups_and_repos:
groups, repo_name = groups_and_repos
if not groups:
return repo_name
def make_link(group):
return link_to(group.name, url('repos_group_home',
group_name=group.group_name))
return literal(' » '.join(map(make_link, groups)) + \
" » " + repo_name)
def fancy_file_stats(stats):
Displays a fancy two colored bar for number of added/deleted
lines of code on file
:param stats: two element list of added/deleted lines of code
a, d, t = stats[0], stats[1], stats[0] + stats[1]
width = 100
unit = float(width) / (t or 1)
# needs > 9% of width to be visible or 0 to be hidden
a_p = max(9, unit * a) if a > 0 else 0
d_p = max(9, unit * d) if d > 0 else 0
p_sum = a_p + d_p
if p_sum > width:
#adjust the percentage to be == 100% since we adjusted to 9
if a_p > d_p:
a_p = a_p - (p_sum - width)
d_p = d_p - (p_sum - width)
a_v = a if a > 0 else ''
d_v = d if d > 0 else ''
def cgen(l_type):
mapping = {'tr': 'top-right-rounded-corner',
'tl': 'top-left-rounded-corner',
'br': 'bottom-right-rounded-corner',
'bl': 'bottom-left-rounded-corner'}
mapping = {'tr': 'top-right-rounded-corner-mid',
'tl': 'top-left-rounded-corner-mid',
'br': 'bottom-right-rounded-corner-mid',
'bl': 'bottom-left-rounded-corner-mid'}
map_getter = lambda x: mapping[x]
if l_type == 'a' and d_v:
#case when added and deleted are present
return ' '.join(map(map_getter, ['tl', 'bl']))
if l_type == 'a' and not d_v:
return ' '.join(map(map_getter, ['tr', 'br', 'tl', 'bl']))
if l_type == 'd' and a_v:
return ' '.join(map(map_getter, ['tr', 'br']))
if l_type == 'd' and not a_v:
d_a = '<div class="added %s" style="width:%s%%">%s</div>' % (
cgen('a'), a_p, a_v
)
d_d = '<div class="deleted %s" style="width:%s%%">%s</div>' % (
cgen('d'), d_p, d_v
return literal('<div style="width:%spx">%s%s</div>' % (width, d_a, d_d))
def urlify_text(text_):
import re
url_pat = re.compile(r'''(http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]'''
'''|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+)''')
def url_func(match_obj):
url_full = match_obj.groups()[0]
return '<a href="%(url)s">%(url)s</a>' % ({'url': url_full})
return literal(url_pat.sub(url_func, text_))
def urlify_changesets(text_, repository):
Extract revision ids from changeset and make link from them
:param text_:
:param repository:
URL_PAT = re.compile(r'([0-9a-fA-F]{12,})')
rev = match_obj.groups()[0]
pref = ''
if match_obj.group().startswith(' '):
pref = ' '
tmpl = (
'%(pref)s<a class="%(cls)s" href="%(url)s">'
'%(rev)s'
'</a>'
return tmpl % {
'pref': pref,
'cls': 'revision-link',
'url': url('changeset_home', repo_name=repository, revision=rev),
'rev': rev,
newtext = URL_PAT.sub(url_func, text_)
return newtext
def urlify_commit(text_, repository=None, link_=None):
Parses given text message and makes proper links.
issues are linked to given issue-server, and rest is a changeset link
if link_ is given, in other case it's a plain text
:param link_: changeset link
import traceback
def escaper(string):
return string.replace('<', '<').replace('>', '>')
def linkify_others(t, l):
urls = re.compile(r'(\<a.*?\<\/a\>)',)
links = []
for e in urls.split(t):
if not urls.match(e):
links.append('<a class="message-link" href="%s">%s</a>' % (l, e))
links.append(e)
return ''.join(links)
# urlify changesets - extrac revisions and make link out of them
text_ = urlify_changesets(escaper(text_), repository)
conf = config['app_conf']
URL_PAT = re.compile(r'%s' % conf.get('issue_pat'))
if URL_PAT:
ISSUE_SERVER_LNK = conf.get('issue_server_link')
ISSUE_PREFIX = conf.get('issue_prefix')
issue_id = ''.join(match_obj.groups())
'%(issue-prefix)s%(id-repr)s'
url = ISSUE_SERVER_LNK.replace('{id}', issue_id)
if repository:
url = url.replace('{repo}', repository)
'cls': 'issue-tracker-link',
'url': url,
'id-repr': issue_id,
'issue-prefix': ISSUE_PREFIX,
'serv': ISSUE_SERVER_LNK,
if link_:
# wrap not links into final link => link_
newtext = linkify_others(newtext, link_)
return literal(newtext)
except:
log.error(traceback.format_exc())
pass
return text_
def rst(source):
return literal('<div class="rst-block">%s</div>' %
MarkupRenderer.rst(source))
def rst_w_mentions(source):
Wrapped rst renderer with @mention highlighting
:param source:
MarkupRenderer.rst_with_mentions(source))
html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,font,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td
{
border: 0;
outline: 0;
font-size: 100%;
vertical-align: baseline;
background: transparent;
margin: 0;
padding: 0;
body {
line-height: 1;
height: 100%;
background: url("../images/background.png") repeat scroll 0 0 #B0B0B0;
font-family: Lucida Grande, Verdana, Lucida Sans Regular,
Lucida Sans Unicode, Arial, sans-serif; font-size : 12px;
color: #000;
font-size: 12px;
ol,ul {
list-style: none;
blockquote,q {
quotes: none;
blockquote:before,blockquote:after,q:before,q:after {
content: none;
:focus {
del {
text-decoration: line-through;
table {
border-collapse: collapse;
border-spacing: 0;
html {
a {
color: #003367;
text-decoration: none;
cursor: pointer;
a:hover {
color: #316293;
text-decoration: underline;
h1,h2,h3,h4,h5,h6 {
color: #292929;
font-weight: 700;
h1 {
font-size: 22px;
h2 {
font-size: 20px;
h3 {
font-size: 18px;
h4 {
font-size: 16px;
h5 {
font-size: 14px;
h6 {
font-size: 11px;
ul.circle {
list-style-type: circle;
ul.disc {
list-style-type: disc;
ul.square {
list-style-type: square;
ol.lower-roman {
list-style-type: lower-roman;
ol.upper-roman {
list-style-type: upper-roman;
ol.lower-alpha {
list-style-type: lower-alpha;
ol.upper-alpha {
list-style-type: upper-alpha;
ol.decimal {
list-style-type: decimal;
div.color {
clear: both;
overflow: hidden;
position: absolute;
background: #FFF;
margin: 7px 0 0 60px;
padding: 1px 1px 1px 0;
div.color a {
width: 15px;
height: 15px;
display: block;
float: left;
margin: 0 0 0 1px;
div.options {
margin: 7px 0 0 162px;
div.options a {
height: 1%;
padding: 3px 8px;
.top-left-rounded-corner {
-webkit-border-top-left-radius: 8px;
-khtml-border-radius-topleft: 8px;
-moz-border-radius-topleft: 8px;
border-top-left-radius: 8px;
.top-right-rounded-corner {
-webkit-border-top-right-radius: 8px;
-khtml-border-radius-topright: 8px;
-moz-border-radius-topright: 8px;
border-top-right-radius: 8px;
.bottom-left-rounded-corner {
-webkit-border-bottom-left-radius: 8px;
-khtml-border-radius-bottomleft: 8px;
-moz-border-radius-bottomleft: 8px;
border-bottom-left-radius: 8px;
.bottom-right-rounded-corner {
-webkit-border-bottom-right-radius: 8px;
-khtml-border-radius-bottomright: 8px;
-moz-border-radius-bottomright: 8px;
border-bottom-right-radius: 8px;
.top-left-rounded-corner-mid {
-webkit-border-top-left-radius: 4px;
-khtml-border-radius-topleft: 4px;
-moz-border-radius-topleft: 4px;
border-top-left-radius: 4px;
.top-right-rounded-corner-mid {
-webkit-border-top-right-radius: 4px;
-khtml-border-radius-topright: 4px;
-moz-border-radius-topright: 4px;
border-top-right-radius: 4px;
.bottom-left-rounded-corner-mid {
-webkit-border-bottom-left-radius: 4px;
-khtml-border-radius-bottomleft: 4px;
-moz-border-radius-bottomleft: 4px;
border-bottom-left-radius: 4px;
.bottom-right-rounded-corner-mid {
-webkit-border-bottom-right-radius: 4px;
-khtml-border-radius-bottomright: 4px;
-moz-border-radius-bottomright: 4px;
border-bottom-right-radius: 4px;
.help-block {
color: #999999;
margin-bottom: 0;
margin-top: 5px;
#header {
padding: 0 10px;
#header ul#logged-user {
margin-bottom: 5px !important;
-webkit-border-radius: 0px 0px 8px 8px;
-khtml-border-radius: 0px 0px 8px 8px;
-moz-border-radius: 0px 0px 8px 8px;
border-radius: 0px 0px 8px 8px;
height: 37px;
background-color: #eedc94;
background-repeat: repeat-x;
background-image: -khtml-gradient(linear, left top, left bottom, from(#fceec1),
to(#eedc94) );
background-image: -moz-linear-gradient(top, #003b76, #00376e);
background-image: -ms-linear-gradient(top, #003b76, #00376e);
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #003b76), color-stop(100%, #00376e) );
background-image: -webkit-linear-gradient(top, #003b76, #00376e);
background-image: -o-linear-gradient(top, #003b76, #00376e);
background-image: linear-gradient(top, #003b76, #00376e);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#003b76',
endColorstr='#00376e', GradientType=0 );
box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
#header ul#logged-user li {
margin: 8px 0 0;
padding: 4px 12px;
border-left: 1px solid #316293;
#header ul#logged-user li.first {
border-left: none;
margin: 4px;
#header ul#logged-user li.first div.gravatar {
margin-top: -2px;
#header ul#logged-user li.first div.account {
padding-top: 4px;
#header ul#logged-user li.last {
border-right: none;
#header ul#logged-user li a {
color: #fff;
#header ul#logged-user li a:hover {
#header ul#logged-user li.highlight a {
#header ul#logged-user li.highlight a:hover {
color: #FFF;
#header #header-inner {
min-height: 44px;
position: relative;
background-image: -khtml-gradient(linear, left top, left bottom, from(#fceec1),to(#eedc94) );
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #003b76),color-stop(100%, #00376e) );
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#003b76',endColorstr='#00376e', GradientType=0 );
-webkit-border-radius: 4px 4px 4px 4px;
-khtml-border-radius: 4px 4px 4px 4px;
-moz-border-radius: 4px 4px 4px 4px;
border-radius: 4px 4px 4px 4px;
#header #header-inner.hover{
position: fixed !important;
width: 100% !important;
margin-left: -10px !important;
z-index: 10000;
-webkit-border-radius: 0px 0px 0px 0px;
-khtml-border-radius: 0px 0px 0px 0px;
-moz-border-radius: 0px 0px 0px 0px;
border-radius: 0px 0px 0px 0px;
#header #header-inner #home a {
height: 40px;
width: 46px;
background: url("../images/button_home.png");
background-position: 0 0;
#header #header-inner #home a:hover {
background-position: 0 -40px;
#header #header-inner #logo {
#header #header-inner #logo h1 {
margin: 12px 0 0 13px;
#header #header-inner #logo a {
#header #header-inner #logo a:hover {
color: #bfe3ff;
#header #header-inner #quick,#header #header-inner #quick ul {
float: right;
list-style-type: none;
list-style-position: outside;
margin: 8px 8px 0 0;
#header #header-inner #quick li {
margin: 0 5px 0 0;
#header #header-inner #quick li a.menu_link {
top: 0;
left: 0;
background: #369;
#header #header-inner #quick li span.short {
padding: 9px 6px 8px 6px;
#header #header-inner #quick li span {
right: 0;
border-left: 1px solid #3f6f9f;
Status change: