@@ -141,26 +141,27 @@ class ReposController(BaseController):
total_records = len(c.repos_list)
_tmpl_lookup = rhodecode.CONFIG['pylons.app_globals'].mako_lookup
template = _tmpl_lookup.get_template('data_table/_dt_elements.html')
quick_menu = lambda repo_name: (template.get_def("quick_menu")
.render(repo_name, _=_, h=h))
.render(repo_name, _=_, h=h, c=c))
repo_lnk = lambda name, rtype, private, fork_of: (
template.get_def("repo_name")
.render(name, rtype, private, fork_of, short_name=False,
admin=True, _=_, h=h))
admin=True, _=_, h=h, c=c))
repo_actions = lambda repo_name: (template.get_def("repo_actions")
for repo in c.repos_list:
repos_data.append({
"menu": quick_menu(repo.repo_name),
"raw_name": repo.repo_name,
"name": repo_lnk(repo.repo_name, repo.repo_type, repo.private, repo.fork),
"name": repo_lnk(repo.repo_name, repo.repo_type,
repo.private, repo.fork),
"desc": repo.description,
"owner": repo.user.username,
"action": repo_actions(repo.repo_name),
})
c.data = json.dumps({
@@ -42,13 +42,13 @@ from rhodecode.lib.base import BaseContr
from rhodecode.lib.celerylib import tasks, run_task
from rhodecode.lib.utils import repo2db_mapper, invalidate_cache, \
set_rhodecode_config, repo_name_slug
from rhodecode.model.db import RhodeCodeUi, Repository, RepoGroup, \
RhodeCodeSetting, PullRequest, PullRequestReviewers
from rhodecode.model.forms import UserForm, ApplicationSettingsForm, \
ApplicationUiSettingsForm
ApplicationUiSettingsForm, ApplicationVisualisationForm
from rhodecode.model.scm import ScmModel
from rhodecode.model.user import UserModel
from rhodecode.model.db import User
from rhodecode.model.notification import EmailNotificationModel
from rhodecode.model.meta import Session
@@ -140,21 +140,21 @@ class SettingsController(BaseController)
errors=errors.error_dict or {},
prefix_error=False,
encoding="UTF-8"
)
try:
sett1 = RhodeCodeSetting.get_by_name('title')
sett1 = RhodeCodeSetting.get_by_name_or_create('title')
sett1.app_settings_value = form_result['rhodecode_title']
Session().add(sett1)
sett2 = RhodeCodeSetting.get_by_name('realm')
sett2 = RhodeCodeSetting.get_by_name_or_create('realm')
sett2.app_settings_value = form_result['rhodecode_realm']
Session().add(sett2)
sett3 = RhodeCodeSetting.get_by_name('ga_code')
sett3 = RhodeCodeSetting.get_by_name_or_create('ga_code')
sett3.app_settings_value = form_result['rhodecode_ga_code']
Session().add(sett3)
Session().commit()
set_rhodecode_config(config)
h.flash(_('Updated application settings'), category='success')
@@ -162,12 +162,53 @@ class SettingsController(BaseController)
except Exception:
log.error(traceback.format_exc())
h.flash(_('error occurred during updating '
'application settings'),
category='error')
if setting_id == 'visual':
application_form = ApplicationVisualisationForm()()
form_result = application_form.to_python(dict(request.POST))
except formencode.Invalid, errors:
return htmlfill.render(
render('admin/settings/settings.html'),
defaults=errors.value,
sett1 = RhodeCodeSetting.get_by_name_or_create('show_public_icon')
sett1.app_settings_value = \
form_result['rhodecode_show_public_icon']
sett2 = RhodeCodeSetting.get_by_name_or_create('show_private_icon')
sett2.app_settings_value = \
form_result['rhodecode_show_private_icon']
sett3 = RhodeCodeSetting.get_by_name_or_create('stylify_metatags')
sett3.app_settings_value = \
form_result['rhodecode_stylify_metatags']
h.flash(_('Updated visualisation settings'),
category='success')
'visualisation settings'),
if setting_id == 'vcs':
application_form = ApplicationUiSettingsForm()()
@@ -75,21 +75,21 @@ class UsersController(BaseController):
total_records = len(c.users_list)
grav_tmpl = lambda user_email, size: (
template.get_def("user_gravatar")
.render(user_email, size, _=_, h=h))
.render(user_email, size, _=_, h=h, c=c))
user_lnk = lambda user_id, username: (
template.get_def("user_name")
.render(user_id, username, _=_, h=h))
.render(user_id, username, _=_, h=h, c=c))
user_actions = lambda user_id, username: (
template.get_def("user_actions")
for user in c.users_list:
users_data.append({
"gravatar": grav_tmpl(user. email, 24),
"raw_username": user.username,
"username": user_lnk(user.user_id, user.username),
@@ -14,13 +14,13 @@ from pylons import config, tmpl_context
from pylons.controllers import WSGIController
from pylons.controllers.util import redirect
from pylons.templating import render_mako as render
from rhodecode import __version__, BACKENDS
from rhodecode.lib.utils2 import str2bool, safe_unicode
from rhodecode.lib.utils2 import str2bool, safe_unicode, AttributeDict
from rhodecode.lib.auth import AuthUser, get_container_username, authfunc,\
HasPermissionAnyMiddleware, CookieStoreWrapper
from rhodecode.lib.utils import get_repo_slug, invalidate_cache
from rhodecode.model import meta
from rhodecode.model.db import Repository, RhodeCodeUi
@@ -155,13 +155,13 @@ class BaseVCSController(object):
require_ssl = str2bool(RhodeCodeUi.get_by_key('push_ssl')\
.scalar().ui_value)
if require_ssl and org_proto == 'http':
log.debug('proto is %s and SSL is required BAD REQUEST !'
% org_proto)
return False
return True
def __call__(self, environ, start_response):
start = time.time()
return self._handle_request(environ, start_response)
finally:
@@ -175,12 +175,18 @@ class BaseController(WSGIController):
def __before__(self):
c.rhodecode_version = __version__
c.rhodecode_instanceid = config.get('instance_id')
c.rhodecode_name = config.get('rhodecode_title')
c.use_gravatar = str2bool(config.get('use_gravatar'))
c.ga_code = config.get('rhodecode_ga_code')
# Visual options
c.visual = AttributeDict({})
c.visual.show_public_icon = str2bool(config.get('rhodecode_show_public_icon'))
c.visual.show_private_icon = str2bool(config.get('rhodecode_show_private_icon'))
c.visual.stylify_metatags = str2bool(config.get('rhodecode_stylify_metatags'))
c.repo_name = get_repo_slug(request)
c.backends = BACKENDS.keys()
c.unread_notifications = NotificationModel()\
.get_unread_cnt_for_user(c.rhodecode_user.user_id)
self.cut_off_limit = int(config.get('cut_off_limit'))
@@ -6,12 +6,13 @@ available to Controllers. This module is
import random
import hashlib
import StringIO
import urllib
import math
import logging
import re
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
@@ -427,12 +428,32 @@ def person(author):
return person_getter(user)
# Still nothing? Just pass back the author name then
return _author
def desc_stylize(value):
"""
converts tags from value into html equivalent
:param value:
value = re.sub(r'\[see\ \=\>\ *([a-zA-Z0-9\/\=\?\&\ \:\/\.\-]*)\]',
'<div class="metatag" tag="see">see => \\1 </div>', value)
value = re.sub(r'\[license\ \=\>\ *([a-zA-Z0-9\/\=\?\&\ \:\/\.\-]*)\]',
'<div class="metatag" tag="license"><a href="http:\/\/www.opensource.org/licenses/\\1">\\1</a></div>', value)
value = re.sub(r'\[(requires|recommends|conflicts|base)\ \=\>\ *([a-zA-Z\-\/]*)\]',
'<div class="metatag" tag="\\1">\\1 => <a href="/\\2">\\2</a></div>', value)
value = re.sub(r'\[(lang|language)\ \=\>\ *([a-zA-Z\-\/]*)\]',
'<div class="metatag" tag="lang">\\2</div>', value)
value = re.sub(r'\[([a-z]+)\]',
'<div class="metatag" tag="\\1">\\1</div>', value)
return value
def bool2icon(value):
"""Returns True/False values represented as small html image of true/false
icons
:param value: bool value
@@ -440,6 +440,12 @@ def extract_mentioned_users(s):
usrs = set()
for username in re.findall(MENTIONS_REGEX, s):
usrs.add(username)
return sorted(list(usrs), key=lambda k: k.lower())
class AttributeDict(dict):
def __getattr__(self, attr):
return self.get(attr, None)
__setattr__ = dict.__setitem__
__delattr__ = dict.__delitem__
@@ -179,15 +179,22 @@ class RhodeCodeSetting(Base, BaseModel):
return u"<%s('%s:%s')>" % (
self.__class__.__name__,
self.app_settings_name, self.app_settings_value
@classmethod
def get_by_name(cls, ldap_key):
def get_by_name(cls, key):
return cls.query()\
.filter(cls.app_settings_name == ldap_key).scalar()
.filter(cls.app_settings_name == key).scalar()
def get_by_name_or_create(cls, key):
res = cls.get_by_name(key)
if not res:
res = cls(key)
return res
def get_app_settings(cls, cache=False):
ret = cls.query()
@@ -586,14 +593,14 @@ class Repository(Base, BaseModel):
fork = relationship('Repository', remote_side=repo_id)
group = relationship('RepoGroup')
repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
stats = relationship('Statistics', cascade='all', uselist=False)
followers = relationship('UserFollowing',
primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id',
cascade='all')
logs = relationship('UserLog')
comments = relationship('ChangesetComment', cascade="all, delete, delete-orphan")
pull_requests_org = relationship('PullRequest',
@@ -1544,13 +1551,13 @@ class PullRequest(Base, BaseModel):
@revisions.setter
def revisions(self, val):
self._revisions = ':'.join(val)
author = relationship('User', lazy='joined')
reviewers = relationship('PullRequestReviewers',
cascade="all, delete, delete-orphan")
org_repo = relationship('Repository', primaryjoin='PullRequest.org_repo_id==Repository.repo_id')
other_repo = relationship('Repository', primaryjoin='PullRequest.other_repo_id==Repository.repo_id')
statuses = relationship('ChangesetStatus')
comments = relationship('ChangesetComment',
@@ -239,39 +239,46 @@ def ApplicationSettingsForm():
rhodecode_realm = v.UnicodeString(strip=True, min=1, not_empty=True)
rhodecode_ga_code = v.UnicodeString(strip=True, min=1, not_empty=False)
return _ApplicationSettingsForm
def ApplicationVisualisationForm():
class _ApplicationVisualisationForm(formencode.Schema):
allow_extra_fields = True
filter_extra_fields = False
rhodecode_show_public_icon = v.StringBoolean(if_missing=False)
rhodecode_show_private_icon = v.StringBoolean(if_missing=False)
rhodecode_stylify_metatags = v.StringBoolean(if_missing=False)
return _ApplicationVisualisationForm
def ApplicationUiSettingsForm():
class _ApplicationUiSettingsForm(formencode.Schema):
web_push_ssl = v.OneOf(['true', 'false'], if_missing='false')
web_push_ssl = v.StringBoolean(if_missing=False)
paths_root_path = All(
v.ValidPath(),
v.UnicodeString(strip=True, min=1, not_empty=True)
hooks_changegroup_update = v.OneOf(['True', 'False'],
if_missing=False)
hooks_changegroup_repo_size = v.OneOf(['True', 'False'],
hooks_changegroup_push_logger = v.OneOf(['True', 'False'],
hooks_preoutgoing_pull_logger = v.OneOf(['True', 'False'],
hooks_changegroup_update = v.StringBoolean(if_missing=False)
hooks_changegroup_repo_size = v.StringBoolean(if_missing=False)
hooks_changegroup_push_logger = v.StringBoolean(if_missing=False)
hooks_preoutgoing_pull_logger = v.StringBoolean(if_missing=False)
return _ApplicationUiSettingsForm
def DefaultPermissionsForm(perms_choices, register_choices, create_choices):
class _DefaultPermissionsForm(formencode.Schema):
filter_extra_fields = True
overwrite_default = v.StringBoolean(if_missing=False)
anonymous = v.OneOf(['True', 'False'], if_missing=False)
anonymous = v.StringBoolean(if_missing=False)
default_perm = v.OneOf(perms_choices)
default_register = v.OneOf(register_choices)
default_create = v.OneOf(create_choices)
return _DefaultPermissionsForm
@@ -1824,12 +1824,87 @@ div.form div.fields div.field div.button
}
#summary {
#summary .metatag {
display: inline-block;
padding: 3px 5px;
margin-bottom: 3px;
margin-right: 1px;
border-radius: 5px;
#content div.box #summary p {
margin-bottom: -5px;
width: 600px;
white-space: pre-wrap;
#content div.box #summary p:last-child {
margin-bottom: 9px;
#content div.box #summary p:first-of-type {
margin-top: 9px;
.metatag {
-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;
border: solid 1px #9CF;
padding: 2px 3px 2px 3px !important;
background-color: #DEF;
.metatag[tag="dead"] {
background-color: #E44;
.metatag[tag="stale"] {
background-color: #EA4;
.metatag[tag="featured"] {
background-color: #AEA;
.metatag[tag="requires"] {
background-color: #9CF;
.metatag[tag="recommends"] {
background-color: #BDF;
.metatag[tag="lang"] {
background-color: #FAF474;
.metatag[tag="license"] {
target-new: tab !important;
.metatag[tag="see"] {
border: solid 1px #CBD;
background-color: #EDF;
a.metatag[tag="license"]:hover {
background-color: #003367;
color: #FFF;
text-decoration: none;
#summary .desc {
white-space: pre;
width: 100%;
#summary .repo_name {
@@ -113,12 +113,69 @@
${h.reset('reset',_('Reset'),class_="ui-btn large")}
</div>
${h.end_form()}
<h3>${_('Visualisation settings')}</h3>
${h.form(url('admin_setting', setting_id='visual'),method='put')}
<div class="form">
<!-- fields -->
<div class="fields">
<div class="field">
<div class="label label-checkbox">
<label>${_('Icons')}:</label>
<div class="checkboxes">
<div class="checkbox">
${h.checkbox('rhodecode_show_public_icon','True')}
<label for="rhodecode_show_public_icon">${_('Show public repo icon on repositories')}</label>
${h.checkbox('rhodecode_show_private_icon','True')}
<label for="rhodecode_show_private_icon">${_('Show private repo icon on repositories')}</label>
<label>${_('Meta-Tagging')}:</label>
${h.checkbox('rhodecode_stylify_metatags','True')}
<label for="rhodecode_stylify_metatags">${_('Stylify recognised metatags:')}</label>
<div style="padding-left: 20px;">
<ul> <!-- Fix style here -->
<li>[featured] <span class="metatag" tag="featured">featured</span></li>
<li>[stale] <span class="metatag" tag="stale">stale</span></li>
<li>[dead] <span class="metatag" tag="dead">dead</span></li>
<li>[lang => lang] <span class="metatag" tag="lang" >lang</span></li>
<li>[license => License] <span class="metatag" tag="license"><a href="http://www.opensource.org/licenses/License" >License</a></span></li>
<li>[requires => Repo] <span class="metatag" tag="requires" >requires => <a href="#" >Repo</a></span></li>
<li>[recommends => Repo] <span class="metatag" tag="recommends" >recommends => <a href="#" >Repo</a></span></li>
<li>[see => URI] <span class="metatag" tag="see">see => <a href="#">URI</a> </span></li>
</ul>
<div class="buttons">
${h.submit('save',_('Save settings'),class_="ui-btn large")}
<h3>${_('VCS settings')}</h3>
${h.form(url('admin_setting', setting_id='vcs'),method='put')}
@@ -60,16 +60,16 @@
<img class="icon" title="${_('Mercurial repository')}" alt="${_('Mercurial repository')}" src="${h.url('/images/icons/hgicon.png')}"/>
%elif h.is_git(rtype):
<img class="icon" title="${_('Git repository')}" alt="${_('Git repository')}" src="${h.url('/images/icons/giticon.png')}"/>
%endif
##PRIVATE/PUBLIC
%if private:
<img class="icon" title="${_('private repository')}" alt="${_('private repository')}" src="${h.url('/images/icons/lock.png')}"/>
%else:
<img class="icon" title="${_('public repository')}" alt="${_('public repository')}" src="${h.url('/images/icons/lock_open.png')}"/>
%if private and c.visual.show_private_icon:
%elif not private and c.visual.show_public_icon:
##NAME
%if admin:
${h.link_to(get_name(name),h.url('edit_repo',repo_name=name),class_="repo_name")}
@@ -105,7 +105,6 @@
</%def>
<%def name="user_name(user_id, username)">
${h.link_to(username,h.url('edit_user', id=user_id))}
@@ -38,13 +38,17 @@
<td>
<div style="white-space: nowrap">
<img class="icon" alt="${_('Repositories group')}" src="${h.url('/images/icons/database_link.png')}"/>
${h.link_to(gr.name,url('repos_group_home',group_name=gr.group_name))}
</td>
<td>${gr.group_description}</td>
%if c.visual.stylify_metatags:
<td>${h.desc_stylize(gr.group_description)}</td>
## this is commented out since for multi nested repos can be HEAVY!
## in number of executed queries during traversing uncomment at will
##<td><b>${gr.repositories_recursive_count}</b></td>
</tr>
% endfor
@@ -82,13 +86,17 @@
##REPO NAME AND ICONS
<td class="reponame">
${dt.repo_name(repo['name'],repo['dbrepo']['repo_type'],repo['dbrepo']['private'],repo['dbrepo_fork'].get('repo_name'),pageargs.get('short_repo_names'))}
##DESCRIPTION
<td><span class="tooltip" title="${h.tooltip(repo['description'])}">
${h.urlify_text(h.desc_stylize(h.truncate(repo['description'],60)))}</span>
${h.truncate(repo['description'],60)}</span>
##LAST CHANGE DATE
<span class="tooltip" date="${repo['last_change']}" title="${h.tooltip(h.fmt_date(repo['last_change']))}">${h.age(repo['last_change'])}</span>
##LAST REVISION
@@ -125,15 +125,15 @@
%if h.is_hg(entry.follows_repository):
%elif h.is_git(entry.follows_repository):
%if entry.follows_repository.private:
%if entry.follows_repository.private and c.visual.show_private_icon:
%elif not entry.follows_repository.private and c.visual.show_public_icon:
<span class="watched_repo">
${h.link_to(entry.follows_repository.repo_name,h.url('summary_home',repo_name=entry.follows_repository.repo_name))}
</span>
@@ -3,18 +3,18 @@
<li class="qfilter_rs">
<input type="text" style="border:0;width:100%" value="${_('quick filter...')}" name="filter" id="q_filter_rs" />
</li>
%for repo in c.repos_list:
%if repo['dbrepo']['private']:
%if repo['dbrepo']['private'] and c.visual.show_private_icon:
<li>
<img src="${h.url('/images/icons/lock.png')}" alt="${_('Private repository')}" class="repo_switcher_type"/>
${h.link_to(repo['name'],h.url('summary_home',repo_name=repo['name']),class_="repo_name %s" % repo['dbrepo']['repo_type'])}
%elif not repo['dbrepo']['private'] and c.visual.show_public_icon:
<img src="${h.url('/images/icons/lock_open.png')}" alt="${_('Public repository')}" class="repo_switcher_type" />
%endfor
@@ -101,13 +101,17 @@
<div class="label-summary">
<label>${_('Description')}:</label>
<div class="input ${summary(c.show_stats)} desc">${h.urlify_text(c.dbrepo.description)}</div>
<div class="input ${summary(c.show_stats)} desc">${h.urlify_text(h.desc_stylize(c.dbrepo.description))}</div>
<label>${_('Contact')}:</label>
@@ -128,6 +128,22 @@ class TestLibs(unittest.TestCase):
self.assertEqual(age(n - delt(hours=1)), u'1 hour ago')
self.assertEqual(age(n - delt(hours=24)), u'1 day ago')
self.assertEqual(age(n - delt(hours=24 * 5)), u'5 days ago')
self.assertEqual(age(n - delt(hours=24 * (calendar.mdays[n.month-1] + 2))),
u'1 month and 2 days ago')
self.assertEqual(age(n - delt(hours=24 * 400)), u'1 year and 1 month ago')
def test_tag_exctrator(self):
sample = (
"hello pta[tag] gog [[]] [[] sda ero[or]d [me =>>< sa]"
"[requires] [stale] [see<>=>] [see => http://url.com]"
"[requires => url] [lang => python] [just a tag]"
"[,d] [ => ULR ] [obsolete] [desc]]"
from rhodecode.lib.helpers import desc_stylize
res = desc_stylize(sample)
self.assertTrue('<div class="metatag" tag="tag">tag</div>' in res)
self.assertTrue('<div class="metatag" tag="obsolete">obsolete</div>' in res)
self.assertTrue('<div class="metatag" tag="stale">stale</div>' in res)
self.assertTrue('<div class="metatag" tag="lang">python</div>' in res)
self.assertTrue('<div class="metatag" tag="requires">requires => <a href="/url">url</a></div>' in res)
Status change: