@@ -44,49 +44,49 @@ from rhodecode.lib.odict import OrderedD
from rhodecode.lib.celerylib import run_task
from rhodecode.lib.celerylib.tasks import get_commits_stats, \
LANGUAGES_EXTENSIONS_MAP
from rhodecode.lib.helpers import RepoPage
try:
import json
except ImportError:
#python 2.5 compatibility
import simplejson as json
log = logging.getLogger(__name__)
class SummaryController(BaseRepoController):
@LoginRequired()
@HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
'repository.admin')
def __before__(self):
super(SummaryController, self).__before__()
def index(self, repo_name):
e = request.environ
c.dbrepo = dbrepo = Repository.by_repo_name(repo_name)
c.dbrepo = dbrepo = c.rhodecode_db_repo
c.following = self.scm_model.is_following_repo(repo_name,
self.rhodecode_user.user_id)
def url_generator(**kw):
return url('shortlog_home', repo_name=repo_name, size=10, **kw)
c.repo_changesets = RepoPage(c.rhodecode_repo, page=1,
items_per_page=10, url=url_generator)
if self.rhodecode_user.username == 'default':
#for default(anonymous) user we don't need to pass credentials
username = ''
password = ''
else:
username = str(self.rhodecode_user.username)
password = '@'
if e.get('wsgi.url_scheme') == 'https':
split_s = 'https://'
split_s = 'http://'
qualified_uri = [split_s] + [url.current(qualified=True)\
@@ -62,61 +62,74 @@ LANGUAGES_EXTENSIONS_MAP = __get_lem()
# Additional mappings that are not present in the pygments lexers
# NOTE: that this will overide any mappings in LANGUAGES_EXTENSIONS_MAP
ADDITIONAL_MAPPINGS = {'xaml': 'XAML'}
LANGUAGES_EXTENSIONS_MAP.update(ADDITIONAL_MAPPINGS)
def str2bool(_str):
"""
returs True/False value from given string, it tries to translate the
string into boolean
:param _str: string value to translate into boolean
:rtype: boolean
:returns: boolean from given string
if _str is None:
return False
if _str in (True, False):
return _str
_str = str(_str).strip().lower()
return _str in ('t', 'true', 'y', 'yes', 'on', '1')
def convert_line_endings(temp, mode):
def convert_line_endings(line, mode):
Converts a given line "line end" accordingly to given mode
Available modes are::
0 - Unix
1 - Mac
2 - DOS
:param line: given line to convert
:param mode: mode to convert to
:rtype: str
:return: converted line according to mode
from string import replace
#modes: 0 - Unix, 1 - Mac, 2 - DOS
if mode == 0:
temp = replace(temp, '\r\n', '\n')
temp = replace(temp, '\r', '\n')
line = replace(line, '\r\n', '\n')
line = replace(line, '\r', '\n')
elif mode == 1:
temp = replace(temp, '\r\n', '\r')
temp = replace(temp, '\n', '\r')
line = replace(line, '\r\n', '\r')
line = replace(line, '\n', '\r')
elif mode == 2:
import re
temp = re.sub("\r(?!\n)|(?<!\r)\n", "\r\n", temp)
return temp
line = re.sub("\r(?!\n)|(?<!\r)\n", "\r\n", line)
return line
def detect_mode(line, default):
Detects line break for given line, if line break couldn't be found
given default value is returned
:param line: str line
:param default: default
:rtype: int
:return: value of line end on of 0 - Unix, 1 - Mac, 2 - DOS
if line.endswith('\r\n'):
return 2
elif line.endswith('\n'):
return 0
elif line.endswith('\r'):
return 1
return default
def generate_api_key(username, salt=None):
@@ -233,54 +246,70 @@ def age(curdate):
if not curdate:
return ''
agescales = [(_(u"year"), 3600 * 24 * 365),
(_(u"month"), 3600 * 24 * 30),
(_(u"day"), 3600 * 24),
(_(u"hour"), 3600),
(_(u"minute"), 60),
(_(u"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 '%s %s' % (time_ago_in_words(curdate,
agescales[pos][0]), _('ago'))
pos += 1
return _(u'just now')
def credentials_hidder(uri):
def uri_filter(uri):
Removes user:password from given url string
:param uri:
:rtype: unicode
:returns: filtered list of strings
if not uri:
proto = ''
for pat in ('https://', 'http://'):
if uri.startswith(pat):
uri = uri[len(pat):]
proto = pat
break
# remove passwords and username
uri = uri[uri.find('@') + 1:]
# get the port
cred_pos = uri.find(':')
if cred_pos == -1:
host, port = uri, None
host, port = uri[:cred_pos], uri[cred_pos + 1:]
return filter(None, [proto, host, port])
def credentials_filter(uri):
Returns a url with removed credentials
uri = uri_filter(uri)
#check if we have port
if len(uri) > 2 and uri[2]:
uri[2] = ':' + uri[2]
return ''.join(uri)
"""The base Controller API
Provides the BaseController class for subclassing.
from pylons import config, tmpl_context as c, request, session
import logging
from pylons import config, tmpl_context as c, request, session, url
from pylons.controllers import WSGIController
from pylons.controllers.util import redirect
from pylons.templating import render_mako as render
from rhodecode import __version__
from rhodecode.lib.auth import AuthUser
from rhodecode.lib.utils import get_repo_slug
from rhodecode.model import meta
from rhodecode.model.scm import ScmModel
from rhodecode import BACKENDS
from rhodecode.model.db import Repository
class BaseController(WSGIController):
c.rhodecode_version = __version__
c.rhodecode_name = config.get('rhodecode_title')
c.ga_code = config.get('rhodecode_ga_code')
c.repo_name = get_repo_slug(request)
c.backends = BACKENDS.keys()
self.cut_off_limit = int(config.get('cut_off_limit'))
self.sa = meta.Session()
self.scm_model = ScmModel(self.sa)
#c.unread_journal = scm_model.get_unread_journal()
def __call__(self, environ, start_response):
"""Invoke the Controller"""
# WSGIController.__call__ dispatches to the Controller method
# the request is routed to. This routing information is
# available in environ['pylons.routes_dict']
# putting this here makes sure that we update permissions each time
api_key = request.GET.get('api_key')
@@ -42,34 +46,37 @@ class BaseController(WSGIController):
self.rhodecode_user = c.rhodecode_user = AuthUser(user_id, api_key)
self.rhodecode_user.set_authenticated(
getattr(session.get('rhodecode_user'),
'is_authenticated', False))
session['rhodecode_user'] = self.rhodecode_user
session.save()
return WSGIController.__call__(self, environ, start_response)
finally:
meta.Session.remove()
class BaseRepoController(BaseController):
Base class for controllers responsible for loading all needed data
for those controllers, loaded items are
c.rhodecode_repo: instance of scm repository (taken from cache)
super(BaseRepoController, self).__before__()
if c.repo_name:
c.rhodecode_repo = Repository.by_repo_name(c.repo_name).scm_instance
c.rhodecode_db_repo = Repository.by_repo_name(c.repo_name)
c.rhodecode_repo = c.rhodecode_db_repo.scm_instance
if c.rhodecode_repo is None:
log.error('%s this repository is present in database but it '
'cannot be created as an scm instance', c.repo_name)
if c.rhodecode_repo is not None:
c.repository_followers = \
self.scm_model.get_followers(c.repo_name)
c.repository_forks = self.scm_model.get_forks(c.repo_name)
c.repository_followers = 0
c.repository_forks = 0
redirect(url('home'))
@@ -300,57 +300,57 @@ 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))
cs = repo.get_changeset(rev)
except RepositoryError:
from rhodecode.lib.utils import EmptyChangeset
cs = EmptyChangeset()
return cs
def is_following_repo(repo_name, user_id):
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_hidder, age as _age
from rhodecode.lib import credentials_filter, age as _age
age = lambda x:_age(x)
capitalize = lambda x: x.capitalize()
email = author_email
email_or_none = lambda x: email(x) if email(x) != x else None
person = lambda x: author_name(x)
short_id = lambda x: x[:12]
hide_credentials = lambda x: ''.join(credentials_hidder(x))
hide_credentials = lambda x: ''.join(credentials_filter(x))
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
@@ -91,49 +91,53 @@ class CachedRepoList(object):
dbr = db_repo
# invalidate the repo cache if needed before getting the
# scm instance
scm_invalidate = False
if self.invalidation_list is not None:
scm_invalidate = dbr.repo_name in self.invalidation_list
if scm_invalidate:
log.info('invalidating cache for repository %s',
dbr.repo_name)
db_repo.set_invalidate
scmr = db_repo.scm_instance_cached
#check permission at this level
if not HasRepoPermissionAny('repository.read',
'repository.write',
'repository.admin')(dbr.repo_name,
'get repo check'):
continue
if not scmr:
'cannot be created as an scm instance',
last_change = scmr.last_change
tip = h.get_changeset_safe(scmr, 'tip')
tmp_d = {}
tmp_d['name'] = dbr.repo_name
tmp_d['name_sort'] = tmp_d['name'].lower()
tmp_d['description'] = dbr.description
tmp_d['description_sort'] = tmp_d['description']
tmp_d['last_change'] = last_change
tmp_d['last_change_sort'] = time.mktime(last_change \
.timetuple())
tmp_d['tip'] = tip.raw_id
tmp_d['tip_sort'] = tip.revision
tmp_d['rev'] = tip.revision
tmp_d['contact'] = dbr.user.full_contact
tmp_d['contact_sort'] = tmp_d['contact']
tmp_d['owner_sort'] = tmp_d['contact']
tmp_d['repo_archives'] = list(scmr._get_archives())
tmp_d['last_msg'] = tip.message
tmp_d['repo'] = scmr
tmp_d['dbrepo'] = dbr.get_dict()
tmp_d['dbrepo_fork'] = dbr.fork.get_dict() if dbr.fork \
import unittest
from rhodecode.tests import *
@@ -119,79 +119,91 @@ beaker.session.timeout = 36000
##auto save the session to not to use .save()
beaker.session.auto = False
##true exire at browser close
#beaker.session.cookie_expires = 3600
################################################################################
## WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* ##
## Debug mode will enable the interactive debugging tool, allowing ANYONE to ##
## execute malicious code after an exception is raised. ##
#set debug = false
##################################
### LOGVIEW CONFIG ###
logview.sqlalchemy = #faa
logview.pylons.templating = #bfb
logview.pylons.util = #eee
#########################################################
### DB CONFIGS - EACH DB WILL HAVE IT'S OWN CONFIG ###
#sqlalchemy.db1.url = sqlite:///%(here)s/test.db
sqlalchemy.db1.url = postgresql://postgres:qwe@localhost/rhodecode_tests
sqlalchemy.db1.url = sqlite:///%(here)s/test.db
#sqlalchemy.db1.url = postgresql://postgres:qwe@localhost/rhodecode_tests
#sqlalchemy.db1.echo = False
#sqlalchemy.db1.pool_recycle = 3600
sqlalchemy.convert_unicode = true
################################
### LOGGING CONFIGURATION ####
[loggers]
keys = root, routes, rhodecode, sqlalchemy,beaker,templates
[handlers]
keys = console
[formatters]
keys = generic,color_formatter
#############
## LOGGERS ##
[logger_root]
level = ERROR
handlers = console
[logger_routes]
qualname = routes.middleware
# "level = DEBUG" logs the route matched and routing variables.
[logger_beaker]
level = DEBUG
handlers =
qualname = beaker.container
propagate = 1
[logger_templates]
level = INFO
qualname = pylons.templating
[logger_rhodecode]
qualname = rhodecode
propagate = 0
[logger_sqlalchemy]
qualname = sqlalchemy.engine
##############
## HANDLERS ##
[handler_console]
class = StreamHandler
args = (sys.stderr,)
level = NOTSET
formatter = color_formatter
################
## FORMATTERS ##
Status change: