#!/usr/bin/env python
# encoding: utf-8
# permissions controller for pylons
# Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; version 2
# of the License or (at your opinion) any later version of the license.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301, USA.
"""
Created on April 27, 2010
permissions controller for pylons
@author: marcink
from formencode import htmlfill
from pylons import request, session, tmpl_context as c, url
from pylons.controllers.util import abort, redirect
from pylons.i18n.translation import _
from rhodecode.lib import helpers as h
from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
from rhodecode.lib.base import BaseController, render
from rhodecode.model.forms import UserForm, DefaultPermissionsForm
from rhodecode.model.permission_model import PermissionModel
from rhodecode.model.permission import PermissionModel
from rhodecode.model.user import UserModel
import formencode
import logging
import traceback
log = logging.getLogger(__name__)
class PermissionsController(BaseController):
"""REST Controller styled on the Atom Publishing Protocol"""
# To properly map this controller, ensure your config/routing.py
# file has a resource setup:
# map.resource('permission', 'permissions')
@LoginRequired()
@HasPermissionAllDecorator('hg.admin')
def __before__(self):
c.admin_user = session.get('admin_user')
c.admin_username = session.get('admin_username')
super(PermissionsController, self).__before__()
self.perms_choices = [('repository.none', _('None'),),
('repository.read', _('Read'),),
('repository.write', _('Write'),),
('repository.admin', _('Admin'),)]
self.register_choices = [
('hg.register.none',
_('disabled')),
('hg.register.manual_activate',
_('allowed with manual account activation')),
('hg.register.auto_activate',
_('allowed with automatic account activation')), ]
self.create_choices = [('hg.create.none', _('Disabled')),
('hg.create.repository', _('Enabled'))]
def index(self, format='html'):
"""GET /permissions: All items in the collection"""
# url('permissions')
def create(self):
"""POST /permissions: Create a new item"""
def new(self, format='html'):
"""GET /permissions/new: Form to create a new item"""
# url('new_permission')
def update(self, id):
"""PUT /permissions/id: Update an existing item"""
# Forms posted to this method should contain a hidden field:
# <input type="hidden" name="_method" value="PUT" />
# Or using helpers:
# h.form(url('permission', id=ID),
# method='put')
# url('permission', id=ID)
permission_model = PermissionModel()
_form = DefaultPermissionsForm([x[0] for x in self.perms_choices],
[x[0] for x in self.register_choices],
[x[0] for x in self.create_choices])()
try:
form_result = _form.to_python(dict(request.POST))
form_result.update({'perm_user_name':id})
permission_model.update(form_result)
h.flash(_('Default permissions updated succesfully'),
category='success')
except formencode.Invalid, errors:
c.perms_choices = self.perms_choices
c.register_choices = self.register_choices
c.create_choices = self.create_choices
return htmlfill.render(
render('admin/permissions/permissions.html'),
defaults=errors.value,
errors=errors.error_dict or {},
prefix_error=False,
encoding="UTF-8")
except Exception:
log.error(traceback.format_exc())
h.flash(_('error occured during update of permissions'),
category='error')
return redirect(url('edit_permission', id=id))
def delete(self, id):
"""DELETE /permissions/id: Delete an existing item"""
# <input type="hidden" name="_method" value="DELETE" />
@@ -40,208 +40,208 @@ from rhodecode.model.repo import RepoMod
class ReposController(BaseController):
# map.resource('repo', 'repos')
@HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
super(ReposController, self).__before__()
"""GET /repos: All items in the collection"""
# url('repos')
cached_repo_list = ScmModel().get_repos()
c.repos_list = sorted(cached_repo_list, key=itemgetter('name_sort'))
return render('admin/repos/repos.html')
"""POST /repos: Create a new item"""
repo_model = RepoModel()
_form = RepoForm()()
form_result = {}
repo_model.create(form_result, c.rhodecode_user)
h.flash(_('created repository %s') % form_result['repo_name'],
if request.POST.get('user_created'):
action_logger(self.rhodecode_user, 'user_created_repo',
form_result['repo_name'], '', self.sa)
else:
action_logger(self.rhodecode_user, 'admin_created_repo',
c.new_repo = errors.value['repo_name']
r = render('admin/repos/repo_add_create_repository.html')
r = render('admin/repos/repo_add.html')
r,
msg = _('error occured during creation of repository %s') \
% form_result.get('repo_name')
h.flash(msg, category='error')
return redirect(url('home'))
return redirect(url('repos'))
"""GET /repos/new: Form to create a new item"""
new_repo = request.GET.get('repo', '')
c.new_repo = h.repo_name_slug(new_repo)
return render('admin/repos/repo_add.html')
def update(self, repo_name):
"""PUT /repos/repo_name: Update an existing item"""
# h.form(url('repo', repo_name=ID),
# url('repo', repo_name=ID)
changed_name = repo_name
_form = RepoForm(edit=True, old_data={'repo_name':repo_name})()
repo_model.update(repo_name, form_result)
invalidate_cache('get_repo_cached_%s' % repo_name)
h.flash(_('Repository %s updated succesfully' % repo_name),
h.flash(_('Repository %s updated successfully' % repo_name),
changed_name = form_result['repo_name']
action_logger(self.rhodecode_user, 'admin_updated_repo',
changed_name, '', self.sa)
c.repo_info = repo_model.get(repo_name)
c.users_array = repo_model.get_users_js()
errors.value.update({'user':c.repo_info.user.username})
render('admin/repos/repo_edit.html'),
h.flash(_('error occured during update of repository %s') \
h.flash(_('error occurred during update of repository %s') \
% repo_name, category='error')
return redirect(url('edit_repo', repo_name=changed_name))
def delete(self, repo_name):
"""DELETE /repos/repo_name: Delete an existing item"""
# method='delete')
repo = repo_model.get(repo_name)
if not repo:
h.flash(_('%s repository is not mapped to db perhaps'
' it was moved or renamed from the filesystem'
' please run the application again'
' in order to rescan repositories') % repo_name,
action_logger(self.rhodecode_user, 'admin_deleted_repo',
repo_name, '', self.sa)
repo_model.delete(repo)
h.flash(_('deleted repository %s') % repo_name, category='success')
except Exception, e:
h.flash(_('An error occured during deletion of %s') % repo_name,
def delete_perm_user(self, repo_name):
DELETE an existing repository permission user
:param repo_name:
repo_model.delete_perm_user(request.POST, repo_name)
h.flash(_('An error occured during deletion of repository user'),
raise HTTPInternalServerError()
def show(self, repo_name, format='html'):
"""GET /repos/repo_name: Show a specific item"""
def edit(self, repo_name, format='html'):
"""GET /repos/repo_name/edit: Form to edit an existing item"""
# url('edit_repo', repo_name=ID)
c.repo_info = repo = repo_model.get(repo_name)
' it was created or renamed from the filesystem'
defaults = c.repo_info.__dict__
if c.repo_info.user:
defaults.update({'user':c.repo_info.user.username})
replacement_user = self.sa.query(User)\
.filter(User.admin == True).first().username
defaults.update({'user':replacement_user})
for p in c.repo_info.repo_to_perm:
defaults.update({'perm_%s' % p.user.username:
p.permission.permission_name})
defaults=defaults,
encoding="UTF-8",
force_defaults=False
)
@@ -155,165 +155,165 @@ class SettingsController(BaseController)
if setting_id == 'mercurial':
application_form = ApplicationUiSettingsForm()()
form_result = application_form.to_python(dict(request.POST))
hgsettings1 = self.sa.query(RhodeCodeUi)\
.filter(RhodeCodeUi.ui_key == 'push_ssl').one()
hgsettings1.ui_value = form_result['web_push_ssl']
hgsettings2 = self.sa.query(RhodeCodeUi)\
.filter(RhodeCodeUi.ui_key == '/').one()
hgsettings2.ui_value = form_result['paths_root_path']
#HOOKS
hgsettings3 = self.sa.query(RhodeCodeUi)\
.filter(RhodeCodeUi.ui_key == 'changegroup.update').one()
hgsettings3.ui_active = bool(form_result['hooks_changegroup_update'])
hgsettings4 = self.sa.query(RhodeCodeUi)\
.filter(RhodeCodeUi.ui_key == 'changegroup.repo_size').one()
hgsettings4.ui_active = bool(form_result['hooks_changegroup_repo_size'])
hgsettings5 = self.sa.query(RhodeCodeUi)\
.filter(RhodeCodeUi.ui_key == 'pretxnchangegroup.push_logger').one()
hgsettings5.ui_active = bool(form_result['hooks_pretxnchangegroup_push_logger'])
hgsettings6 = self.sa.query(RhodeCodeUi)\
.filter(RhodeCodeUi.ui_key == 'preoutgoing.pull_logger').one()
hgsettings6.ui_active = bool(form_result['hooks_preoutgoing_pull_logger'])
self.sa.add(hgsettings1)
self.sa.add(hgsettings2)
self.sa.add(hgsettings3)
self.sa.add(hgsettings4)
self.sa.add(hgsettings5)
self.sa.add(hgsettings6)
self.sa.commit()
h.flash(_('Updated mercurial settings'),
except:
h.flash(_('error occurred during updating application settings'),
self.sa.rollback()
render('admin/settings/settings.html'),
return redirect(url('admin_settings'))
def delete(self, setting_id):
"""DELETE /admin/settings/setting_id: Delete an existing item"""
# h.form(url('admin_setting', setting_id=ID),
# url('admin_setting', setting_id=ID)
def show(self, setting_id, format='html'):
"""GET /admin/settings/setting_id: Show a specific item"""
def edit(self, setting_id, format='html'):
"""GET /admin/settings/setting_id/edit: Form to edit an existing item"""
# url('admin_edit_setting', setting_id=ID)
def my_account(self):
GET /_admin/my_account Displays info about my account
# url('admin_settings_my_account')
c.user = UserModel(self.sa).get(c.rhodecode_user.user_id, cache=False)
c.user = UserModel().get(c.rhodecode_user.user_id, cache=False)
all_repos = self.sa.query(Repository)\
.filter(Repository.user_id == c.user.user_id)\
.order_by(func.lower(Repository.repo_name))\
.all()
c.user_repos = ScmModel().get_repos(all_repos)
if c.user.username == 'default':
h.flash(_("You can't edit this user since it's"
" crucial for entire application"), category='warning')
return redirect(url('users'))
defaults = c.user.__dict__
render('admin/users/user_edit_my_account.html'),
def my_account_update(self):
"""PUT /_admin/my_account_update: Update an existing item"""
# h.form(url('admin_settings_my_account_update'),
# url('admin_settings_my_account_update', id=ID)
user_model = UserModel()
uid = c.rhodecode_user.user_id
_form = UserForm(edit=True, old_data={'user_id':uid,
'email':c.rhodecode_user.email})()
user_model.update_my_account(uid, form_result)
h.flash(_('Your account was updated succesfully'),
c.user = user_model.get(c.rhodecode_user.user_id, cache=False)
h.flash(_('error occured during update of user %s') \
% form_result.get('username'), category='error')
return redirect(url('my_account'))
def create_repository(self):
"""GET /_admin/create_repository: Form to create a new item"""
return render('admin/repos/repo_add_create_repository.html')
# users controller for pylons
from rhodecode.lib.utils import action_logger
Created on April 4, 2010
users controller for pylons
from rhodecode.model.db import User, UserLog
from rhodecode.model.forms import UserForm
from rhodecode.model.user import UserModel, DefaultUserException
class UsersController(BaseController):
# map.resource('user', 'users')
super(UsersController, self).__before__()
"""GET /users: All items in the collection"""
# url('users')
c.users_list = self.sa.query(User).all()
return render('admin/users/users.html')
"""POST /users: Create a new item"""
login_form = UserForm()()
form_result = login_form.to_python(dict(request.POST))
user_model.create(form_result)
h.flash(_('created user %s') % form_result['username'],
#action_logger(self.rhodecode_user, 'new_user', '', '', self.sa)
render('admin/users/user_add.html'),
h.flash(_('error occured during creation of user %s') \
% request.POST.get('username'), category='error')
"""GET /users/new: Form to create a new item"""
# url('new_user')
return render('admin/users/user_add.html')
"""PUT /users/id: Update an existing item"""
# h.form(url('user', id=ID),
# url('user', id=ID)
c.user = user_model.get(id)
_form = UserForm(edit=True, old_data={'user_id':id,
'email':c.user.email})()
user_model.update(id, form_result)
h.flash(_('User updated succesfully'), category='success')
render('admin/users/user_edit.html'),
@@ -50,193 +50,193 @@ class PasswordGenerator(object):
ALPHABETS_BIG = r'''QWERTYUIOPASDFGHJKLZXCVBNM'''#[2]
ALPHABETS_SPECIAL = r'''`-=[]\;',./~!@#$%^&*()_+{}|:"<>?''' #[3]
ALPHABETS_FULL = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM + ALPHABETS_SPECIAL#[4]
ALPHABETS_ALPHANUM = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM#[5]
ALPHABETS_BIG_SMALL = ALPHABETS_BIG + ALPHABETS_SMALL
ALPHABETS_ALPHANUM_BIG = ALPHABETS_BIG + ALPHABETS_NUM#[6]
ALPHABETS_ALPHANUM_SMALL = ALPHABETS_SMALL + ALPHABETS_NUM#[7]
def __init__(self, passwd=''):
self.passwd = passwd
def gen_password(self, len, type):
self.passwd = ''.join([random.choice(type) for _ in xrange(len)])
return self.passwd
def get_crypt_password(password):
"""Cryptographic function used for password hashing based on sha1
:param password: password to hash
return bcrypt.hashpw(password, bcrypt.gensalt(10))
def check_password(password, hashed):
return bcrypt.hashpw(password, hashed) == hashed
def authfunc(environ, username, password):
user = UserModel().get_by_username(username, cache=False)
if user:
if user.active:
if user.username == 'default' and user.active:
log.info('user %s authenticated correctly', username)
return True
elif user.username == username and check_password(password, user.password):
log.error('user %s is disabled', username)
return False
class AuthUser(object):
A simple object that handles a mercurial username for authentication
def __init__(self):
self.username = 'None'
self.name = ''
self.lastname = ''
self.email = ''
self.user_id = None
self.is_authenticated = False
self.is_admin = False
self.permissions = {}
def __repr__(self):
return "<AuthUser('id:%s:%s')>" % (self.user_id, self.username)
def set_available_permissions(config):
This function will propagate pylons globals with all available defined
permission given in db. We don't wannt to check each time from db for new
permissions since adding a new permission also requires application restart
ie. to decorate new views with the newly created permission
:param config:
log.info('getting information about all available permissions')
sa = meta.Session()
all_perms = sa.query(Permission).all()
pass
finally:
meta.Session.remove()
config['available_permissions'] = [x.permission_name for x in all_perms]
def set_base_path(config):
config['base_path'] = config['pylons.app_globals'].base_path
def fill_perms(user):
Fills user permission attribute with permissions taken from database
:param user:
user.permissions['repositories'] = {}
user.permissions['global'] = set()
#===========================================================================
# fetch default permissions
default_user = UserModel(sa).get_by_username('default', cache=True)
default_user = UserModel().get_by_username('default', cache=True)
default_perms = sa.query(RepoToPerm, Repository, Permission)\
.join((Repository, RepoToPerm.repository_id == Repository.repo_id))\
.join((Permission, RepoToPerm.permission_id == Permission.permission_id))\
.filter(RepoToPerm.user == default_user).all()
if user.is_admin:
#=======================================================================
# #admin have all default rights set to admin
user.permissions['global'].add('hg.admin')
for perm in default_perms:
p = 'repository.admin'
user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
# set default permissions
#default global
default_global_perms = sa.query(UserToPerm)\
.filter(UserToPerm.user == sa.query(User).filter(User.username ==
'default').one())
for perm in default_global_perms:
user.permissions['global'].add(perm.permission.permission_name)
#default repositories
if perm.Repository.private and not perm.Repository.user_id == user.user_id:
#disable defaults for private repos,
p = 'repository.none'
elif perm.Repository.user_id == user.user_id:
#set admin if owner
p = perm.Permission.permission_name
# #overwrite default with user permissions if any
user_perms = sa.query(RepoToPerm, Permission, Repository)\
.filter(RepoToPerm.user_id == user.user_id).all()
for perm in user_perms:
if perm.Repository.user_id == user.user_id:#set admin if owner
return user
def get_user(session):
Gets user from session, and wraps permissions into user
:param session:
user = session.get('rhodecode_user', AuthUser())
#if the user is not logged in we check for anonymous access
#if user is logged and it's a default user check if we still have anonymous
#access enabled
if user.user_id is None or user.username == 'default':
anonymous_user = UserModel().get_by_username('default', cache=True)
if anonymous_user.active is True:
#then we set this user is logged in
user.is_authenticated = True
user.user_id = anonymous_user.user_id
user.is_authenticated = False
if user.is_authenticated:
user = UserModel().fill_data(user)
user = fill_perms(user)
session['rhodecode_user'] = user
session.save()
#===============================================================================
# CHECK DECORATORS
class LoginRequired(object):
"""Must be logged in to execute this function else redirect to login page"""
def __call__(self, func):
return decorator(self.__wrapper, func)
def __wrapper(self, func, *fargs, **fkwargs):
from celery.decorators import task
import os
from time import mktime
from operator import itemgetter
from rhodecode.lib.celerylib import run_task, locked_task
from rhodecode.lib.helpers import person
from rhodecode.lib.smtp_mailer import SmtpMailer
from rhodecode.lib.utils import OrderedDict
from vcs.backends import get_repo
from rhodecode.model.scm import ScmModel
from rhodecode.model.db import RhodeCodeUi
import json
except ImportError:
#python 2.5 compatibility
import simplejson as json
from celeryconfig import PYLONS_CONFIG as config
celery_on = True
#if celeryconfig is not present let's just load our pylons
#config instead
from pylons import config
celery_on = False
__all__ = ['whoosh_index', 'get_commits_stats',
'reset_user_password', 'send_email']
def get_session():
if celery_on:
from sqlalchemy import engine_from_config
from sqlalchemy.orm import sessionmaker, scoped_session
engine = engine_from_config(dict(config.items('app:main')),
'sqlalchemy.db1.')
sa = scoped_session(sessionmaker(bind=engine))
#If we don't use celery reuse our current application Session
from rhodecode.model.meta import Session
sa = Session()
return sa
def get_repos_path():
sa = get_session()
q = sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
return q.ui_value
@task
@locked_task
def whoosh_index(repo_location, full_index):
log = whoosh_index.get_logger()
from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
index_location = dict(config.items('app:main'))['index_dir']
WhooshIndexingDaemon(index_location=index_location,
repo_location=repo_location).run(full_index=full_index)
def get_commits_stats(repo_name, ts_min_y, ts_max_y):
from rhodecode.model.db import Statistics, Repository
log = get_commits_stats.get_logger()
author_key_cleaner = lambda k: person(k).replace('"', "") #for js data compatibilty
commits_by_day_author_aggregate = {}
commits_by_day_aggregate = {}
repos_path = ScmModel().repos_path
repos_path = get_repos_path()
p = os.path.join(repos_path, repo_name)
repo = get_repo(p)
skip_date_limit = True
parse_limit = 250 #limit for single task changeset parsing optimal for
last_rev = 0
last_cs = None
timegetter = itemgetter('time')
dbrepo = sa.query(Repository)\
.filter(Repository.repo_name == repo_name).scalar()
cur_stats = sa.query(Statistics)\
.filter(Statistics.repository == dbrepo).scalar()
if cur_stats:
last_rev = cur_stats.stat_on_revision
if not repo.revisions:
if last_rev == repo.revisions[-1] and len(repo.revisions) > 1:
#pass silently without any work if we're not on first revision or current
#state of parsing revision(from db marker) is the last revision
commits_by_day_aggregate = OrderedDict(
json.loads(
cur_stats.commit_activity_combined))
commits_by_day_author_aggregate = json.loads(cur_stats.commit_activity)
log.debug('starting parsing %s', parse_limit)
lmktime = mktime
for cnt, rev in enumerate(repo.revisions[last_rev:]):
last_cs = cs = repo.get_changeset(rev)
k = '%s-%s-%s' % (cs.date.timetuple()[0], cs.date.timetuple()[1],
cs.date.timetuple()[2])
timetupple = [int(x) for x in k.split('-')]
timetupple.extend([0 for _ in xrange(6)])
k = lmktime(timetupple)
if commits_by_day_author_aggregate.has_key(author_key_cleaner(cs.author)):
l = [timegetter(x) for x in commits_by_day_author_aggregate\
[author_key_cleaner(cs.author)]['data']]
time_pos = l.index(k)
except ValueError:
time_pos = False
if time_pos >= 0 and time_pos is not False:
datadict = commits_by_day_author_aggregate\
[author_key_cleaner(cs.author)]['data'][time_pos]
datadict["commits"] += 1
datadict["added"] += len(cs.added)
datadict["changed"] += len(cs.changed)
datadict["removed"] += len(cs.removed)
if k >= ts_min_y and k <= ts_max_y or skip_date_limit:
datadict = {"time":k,
"commits":1,
"added":len(cs.added),
"changed":len(cs.changed),
"removed":len(cs.removed),
}
commits_by_day_author_aggregate\
[author_key_cleaner(cs.author)]['data'].append(datadict)
commits_by_day_author_aggregate[author_key_cleaner(cs.author)] = {
"label":author_key_cleaner(cs.author),
"data":[{"time":k,
}],
"schema":["commits"],
#gather all data by day
if commits_by_day_aggregate.has_key(k):
commits_by_day_aggregate[k] += 1
commits_by_day_aggregate[k] = 1
if cnt >= parse_limit:
#don't fetch to much data since we can freeze application
break
overview_data = []
for k, v in commits_by_day_aggregate.items():
overview_data.append([k, v])
@@ -178,138 +185,138 @@ def get_commits_stats(repo_name, ts_min_
if last_rev == 0 or leftovers < parse_limit:
stats.languages = json.dumps(__get_codes_stats(repo_name))
stats.repository = dbrepo
stats.stat_on_revision = last_cs.revision
sa.add(stats)
sa.commit()
sa.rollback()
if len(repo.revisions) > 1:
run_task(get_commits_stats, repo_name, ts_min_y, ts_max_y)
def reset_user_password(user_email):
log = reset_user_password.get_logger()
from rhodecode.lib import auth
from rhodecode.model.db import User
user = sa.query(User).filter(User.email == user_email).scalar()
new_passwd = auth.PasswordGenerator().gen_password(8,
auth.PasswordGenerator.ALPHABETS_BIG_SMALL)
user.password = auth.get_crypt_password(new_passwd)
sa.add(user)
log.info('change password for %s', user_email)
if new_passwd is None:
raise Exception('unable to generate new password')
run_task(send_email, user_email,
"Your new rhodecode password",
'Your new rhodecode password:%s' % (new_passwd))
log.info('send new password mail to %s', user_email)
log.error('Failed to update user password')
def send_email(recipients, subject, body):
Sends an email with defined parameters from the .ini files.
:param recipients: list of recipients, it this is empty the defined email
address from field 'email_to' is used instead
:param subject: subject of the mail
:param body: body of the mail
log = send_email.get_logger()
email_config = dict(config.items('DEFAULT'))
if not recipients:
recipients = [email_config.get('email_to')]
def str2bool(v):
return v.lower() in ["yes", "true", "t", "1"]
mail_from = email_config.get('app_email_from')
user = email_config.get('smtp_username')
passwd = email_config.get('smtp_password')
mail_server = email_config.get('smtp_server')
mail_port = email_config.get('smtp_port')
tls = str2bool(email_config.get('smtp_use_tls'))
ssl = str2bool(email_config.get('smtp_use_ssl'))
m = SmtpMailer(mail_from, user, passwd, mail_server,
mail_port, ssl, tls)
m.send(recipients, subject, body)
log.error('Mail sending failed')
def create_repo_fork(form_data, cur_user):
from rhodecode.model.repo import RepoModel
from vcs import get_backend
log = create_repo_fork.get_logger()
repo_model = RepoModel(get_session())
repo_model.create(form_data, cur_user, just_db=True, fork=True)
repo_name = form_data['repo_name']
repo_path = os.path.join(repos_path, repo_name)
repo_fork_path = os.path.join(repos_path, form_data['fork_name'])
alias = form_data['repo_type']
log.info('creating repo fork %s as %s', repo_name, repo_path)
backend = get_backend(alias)
backend(str(repo_fork_path), create=True, src_url=str(repo_path))
def __get_codes_stats(repo_name):
LANGUAGES_EXTENSIONS = ['action', 'adp', 'ashx', 'asmx',
'aspx', 'asx', 'axd', 'c', 'cfg', 'cfm', 'cpp', 'cs', 'diff', 'do', 'el',
'erl', 'h', 'java', 'js', 'jsp', 'jspx', 'lisp', 'lua', 'm', 'mako', 'ml',
'pas', 'patch', 'php', 'php3', 'php4', 'phtml', 'pm', 'py', 'rb', 'rst',
's', 'sh', 'tpl', 'txt', 'vim', 'wss', 'xhtml', 'xml', 'xsl', 'xslt', 'yaws']
tip = repo.get_changeset()
code_stats = {}
def aggregate(cs):
for f in cs[2]:
k = f.mimetype
if f.extension in LANGUAGES_EXTENSIONS:
if code_stats.has_key(k):
code_stats[k] += 1
code_stats[k] = 1
map(aggregate, tip.walk('/'))
return code_stats or {}
# Utilities for RhodeCode
Created on April 18, 2010
Utilities for RhodeCode
from UserDict import DictMixin
from mercurial import ui, config, hg
from mercurial.error import RepoError
from rhodecode.model import meta
from rhodecode.model.caching_query import FromCache
from rhodecode.model.db import Repository, User, RhodeCodeUi, RhodeCodeSettings, \
UserLog
from vcs.backends.base import BaseChangeset
from paste.script import command
import ConfigParser
from vcs.utils.lazy import LazyProperty
import datetime
def get_repo_slug(request):
return request.environ['pylons.routes_dict'].get('repo_name')
def is_mercurial(environ):
Returns True if request's target is mercurial server - header
``HTTP_ACCEPT`` of such request would start with ``application/mercurial``.
http_accept = environ.get('HTTP_ACCEPT')
if http_accept and http_accept.startswith('application/mercurial'):
def is_git(environ):
Returns True if request's target is git server. ``HTTP_USER_AGENT`` would
then have git client version given.
:param environ:
http_user_agent = environ.get('HTTP_USER_AGENT')
if http_user_agent and http_user_agent.startswith('git'):
def action_logger(user, action, repo, ipaddr='', sa=None):
Action logger for various action made by users
:param user: user that made this action, can be a string unique username or
object containing user_id attribute
:param action: action to log, should be on of predefined unique actions for
easy translations
:param repo: repository that action was made on
:param ipaddr: optional ip address from what the action was made
:param sa: optional sqlalchemy session
if not sa:
if hasattr(user, 'user_id'):
user_obj = user
elif isinstance(user, basestring):
user_obj = UserModel(sa).get_by_username(user, cache=False)
user_obj = UserModel().get_by_username(user, cache=False)
raise Exception('You have to provide user object or username')
if repo:
repo_name = repo.lstrip('/')
repository = RepoModel(sa).get(repo_name, cache=False)
repository = RepoModel().get(repo_name, cache=False)
if not repository:
raise Exception('You have to provide valid repository')
raise Exception('You have to provide repository to action logger')
user_log = UserLog()
user_log.user_id = user_obj.user_id
user_log.action = action
user_log.repository_name = repo_name
user_log.repository = repository
user_log.action_date = datetime.datetime.now()
user_log.user_ip = ipaddr
sa.add(user_log)
log.info('Adding user %s, action %s on %s',
user_obj.username, action, repo)
def get_repos(path, recursive=False, initial=False):
Scans given path for repos and return (name,(type,path)) tuple
:param prefix:
:param path:
:param recursive:
:param initial:
from vcs.utils.helpers import get_scm
from vcs.exceptions import VCSError
scm = get_scm(path)
raise Exception('The given path %s should not be a repository got %s',
path, scm)
for dirpath in os.listdir(path):
yield dirpath, get_scm(os.path.join(path, dirpath))
except VCSError:
if __name__ == '__main__':
get_repos('', '/home/marcink/workspace-python')
def check_repo_fast(repo_name, base_path):
if os.path.isdir(os.path.join(base_path, repo_name)):return False
def check_repo(repo_name, base_path, verify=True):
repo_path = os.path.join(base_path, repo_name)
if not check_repo_fast(repo_name, base_path):
r = hg.repository(ui.ui(), repo_path)
if verify:
hg.verify(r)
#here we hnow that repo exists it was verified
log.info('%s repo is already created', repo_name)
except RepoError:
#it means that there is no valid repo there...
log.info('%s repo is free for creation', repo_name)
def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
while True:
ok = raw_input(prompt)
if ok in ('y', 'ye', 'yes'): return True
if ok in ('n', 'no', 'nop', 'nope'): return False
retries = retries - 1
if retries < 0: raise IOError
print complaint
def get_hg_ui_cached():
sa = meta.Session
ret = sa.query(RhodeCodeUi)\
.options(FromCache("sql_cache_short", "get_hg_ui_settings"))\
return ret
def get_hg_settings():
@@ -200,240 +200,244 @@ def get_hg_settings():
.options(FromCache("sql_cache_short", "get_hg_settings"))\
if not ret:
raise Exception('Could not get application settings !')
settings = {}
for each in ret:
settings['rhodecode_' + each.app_settings_name] = each.app_settings_value
return settings
def get_hg_ui_settings():
ret = sa.query(RhodeCodeUi).all()
raise Exception('Could not get application ui settings !')
k = each.ui_key
v = each.ui_value
if k == '/':
k = 'root_path'
if k.find('.') != -1:
k = k.replace('.', '_')
if each.ui_section == 'hooks':
v = each.ui_active
settings[each.ui_section + '_' + k] = v
#propagated from mercurial documentation
ui_sections = ['alias', 'auth',
'decode/encode', 'defaults',
'diff', 'email',
'extensions', 'format',
'merge-patterns', 'merge-tools',
'hooks', 'http_proxy',
'smtp', 'patch',
'paths', 'profiling',
'server', 'trusted',
'ui', 'web', ]
def make_ui(read_from='file', path=None, checkpaths=True):
A function that will read python rc files or database
and make an mercurial ui object from read options
:param path: path to mercurial config file
:param checkpaths: check the path
:param read_from: read from 'file' or 'db'
baseui = ui.ui()
if read_from == 'file':
if not os.path.isfile(path):
log.warning('Unable to read config file %s' % path)
log.debug('reading hgrc from %s', path)
cfg = config.config()
cfg.read(path)
for section in ui_sections:
for k, v in cfg.items(section):
baseui.setconfig(section, k, v)
log.debug('settings ui from file[%s]%s:%s', section, k, v)
elif read_from == 'db':
hg_ui = get_hg_ui_cached()
for ui_ in hg_ui:
if ui_.ui_active:
log.debug('settings ui from db[%s]%s:%s', ui_.ui_section, ui_.ui_key, ui_.ui_value)
baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
return baseui
def set_rhodecode_config(config):
hgsettings = get_hg_settings()
for k, v in hgsettings.items():
config[k] = v
def invalidate_cache(name, *args):
def invalidate_cache(cache_key, *args):
Puts cache invalidation task into db for
further global cache invalidation
if cache_key.startswith('get_repo_cached_'):
name = cache_key.split('get_repo_cached_')[-1]
ScmModel().mark_for_invalidation(name)
class EmptyChangeset(BaseChangeset):
An dummy empty changeset. It's possible to pass hash when creating
an EmptyChangeset
def __init__(self, cs='0' * 40):
self._empty_cs = cs
self.revision = -1
self.message = ''
self.author = ''
self.date = ''
@LazyProperty
def raw_id(self):
Returns raw string identifying this changeset, useful for web
representation.
return self._empty_cs
def short_id(self):
return self.raw_id[:12]
def get_file_changeset(self, path):
return self
def get_file_content(self, path):
return u''
def get_file_size(self, path):
return 0
def repo2db_mapper(initial_repo_list, remove_obsolete=False):
maps all found repositories into db
rm = RepoModel(sa)
rm = RepoModel()
user = sa.query(User).filter(User.admin == True).first()
for name, repo in initial_repo_list.items():
if not rm.get(name, cache=False):
log.info('repository %s not found creating default', name)
form_data = {
'repo_name':name,
'repo_type':repo.alias,
'description':repo.description \
if repo.description != 'unknown' else \
'%s repository' % name,
'private':False
rm.create(form_data, user, just_db=True)
if remove_obsolete:
#remove from database those repositories that are not in the filesystem
for repo in sa.query(Repository).all():
if repo.repo_name not in initial_repo_list.keys():
sa.delete(repo)
class OrderedDict(dict, DictMixin):
def __init__(self, *args, **kwds):
if len(args) > 1:
raise TypeError('expected at most 1 arguments, got %d' % len(args))
self.__end
except AttributeError:
self.clear()
self.update(*args, **kwds)
def clear(self):
self.__end = end = []
end += [None, end, end] # sentinel node for doubly linked list
self.__map = {} # key --> [key, prev, next]
dict.clear(self)
def __setitem__(self, key, value):
if key not in self:
end = self.__end
curr = end[1]
curr[2] = end[1] = self.__map[key] = [key, curr, end]
dict.__setitem__(self, key, value)
def __delitem__(self, key):
dict.__delitem__(self, key)
key, prev, next = self.__map.pop(key)
prev[2] = next
next[1] = prev
def __iter__(self):
curr = end[2]
while curr is not end:
yield curr[0]
curr = curr[2]
def __reversed__(self):
curr = curr[1]
def popitem(self, last=True):
if not self:
raise KeyError('dictionary is empty')
if last:
key = reversed(self).next()
key = iter(self).next()
value = self.pop(key)
return key, value
def __reduce__(self):
items = [[k, self[k]] for k in self]
tmp = self.__map, self.__end
del self.__map, self.__end
inst_dict = vars(self).copy()
self.__map, self.__end = tmp
if inst_dict:
return (self.__class__, (items,), inst_dict)
return self.__class__, (items,)
def keys(self):
return list(self)
setdefault = DictMixin.setdefault
update = DictMixin.update
pop = DictMixin.pop
values = DictMixin.values
items = DictMixin.items
iterkeys = DictMixin.iterkeys
from rhodecode.model.meta import Base
from sqlalchemy import *
from sqlalchemy.orm import relation, backref
from sqlalchemy.orm.session import Session
class RhodeCodeSettings(Base):
__tablename__ = 'rhodecode_settings'
__table_args__ = (UniqueConstraint('app_settings_name'), {'useexisting':True})
app_settings_id = Column("app_settings_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
app_settings_name = Column("app_settings_name", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
app_settings_value = Column("app_settings_value", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
class RhodeCodeUi(Base):
__tablename__ = 'rhodecode_ui'
__table_args__ = {'useexisting':True}
ui_id = Column("ui_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
ui_section = Column("ui_section", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
ui_key = Column("ui_key", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
ui_value = Column("ui_value", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
ui_active = Column("ui_active", BOOLEAN(), nullable=True, unique=None, default=True)
class User(Base):
__tablename__ = 'users'
__table_args__ = (UniqueConstraint('username'), UniqueConstraint('email'), {'useexisting':True})
user_id = Column("user_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
username = Column("username", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
password = Column("password", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
active = Column("active", BOOLEAN(), nullable=True, unique=None, default=None)
admin = Column("admin", BOOLEAN(), nullable=True, unique=None, default=False)
name = Column("name", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
lastname = Column("lastname", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
email = Column("email", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
last_login = Column("last_login", DATETIME(timezone=False), nullable=True, unique=None, default=None)
user_log = relation('UserLog')
user_perms = relation('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id")
def full_contact(self):
return '%s %s <%s>' % (self.name, self.lastname, self.email)
return "<User('id:%s:%s')>" % (self.user_id, self.username)
def update_lastlogin(self):
"""Update user lastlogin"""
session = Session.object_session(self)
self.last_login = datetime.datetime.now()
session.add(self)
session.commit()
log.debug('updated user %s lastlogin', self.username)
session.rollback()
class UserLog(Base):
__tablename__ = 'user_logs'
user_log_id = Column("user_log_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
user_id = Column("user_id", INTEGER(), ForeignKey(u'users.user_id'), nullable=False, unique=None, default=None)
repository_id = Column("repository_id", INTEGER(length=None, convert_unicode=False, assert_unicode=None), ForeignKey(u'repositories.repo_id'), nullable=False, unique=None, default=None)
repository_name = Column("repository_name", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
user_ip = Column("user_ip", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
action = Column("action", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
action_date = Column("action_date", DATETIME(timezone=False), nullable=True, unique=None, default=None)
user = relation('User')
repository = relation('Repository')
class Repository(Base):
__tablename__ = 'repositories'
__table_args__ = (UniqueConstraint('repo_name'), {'useexisting':True},)
repo_id = Column("repo_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
repo_name = Column("repo_name", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
repo_type = Column("repo_type", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
user_id = Column("user_id", INTEGER(), ForeignKey(u'users.user_id'), nullable=False, unique=False, default=None)
private = Column("private", BOOLEAN(), nullable=True, unique=None, default=None)
description = Column("description", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
fork_id = Column("fork_id", INTEGER(), ForeignKey(u'repositories.repo_id'), nullable=True, unique=False, default=None)
fork = relation('Repository', remote_side=repo_id)
repo_to_perm = relation('RepoToPerm', cascade='all')
stats = relation('Statistics', cascade='all')
return "<Repository('id:%s:%s')>" % (self.repo_id, self.repo_name)
return "<Repository('%s:%s')>" % (self.repo_id, self.repo_name)
class Permission(Base):
__tablename__ = 'permissions'
permission_id = Column("permission_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
permission_name = Column("permission_name", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
permission_longname = Column("permission_longname", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
return "<Permission('%s:%s')>" % (self.permission_id, self.permission_name)
class RepoToPerm(Base):
__tablename__ = 'repo_to_perm'
__table_args__ = (UniqueConstraint('user_id', 'repository_id'), {'useexisting':True})
repo_to_perm_id = Column("repo_to_perm_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
permission_id = Column("permission_id", INTEGER(), ForeignKey(u'permissions.permission_id'), nullable=False, unique=None, default=None)
repository_id = Column("repository_id", INTEGER(), ForeignKey(u'repositories.repo_id'), nullable=False, unique=None, default=None)
permission = relation('Permission')
class UserToPerm(Base):
__tablename__ = 'user_to_perm'
__table_args__ = (UniqueConstraint('user_id', 'permission_id'), {'useexisting':True})
user_to_perm_id = Column("user_to_perm_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
class Statistics(Base):
__tablename__ = 'statistics'
__table_args__ = (UniqueConstraint('repository_id'), {'useexisting':True})
stat_id = Column("stat_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
repository_id = Column("repository_id", INTEGER(), ForeignKey(u'repositories.repo_id'), nullable=False, unique=True, default=None)
stat_on_revision = Column("stat_on_revision", INTEGER(), nullable=False)
commit_activity = Column("commit_activity", BLOB(), nullable=False)#JSON data
commit_activity_combined = Column("commit_activity_combined", BLOB(), nullable=False)#JSON data
languages = Column("languages", BLOB(), nullable=False)#JSON data
repository = relation('Repository', single_parent=True)
class CacheInvalidation(Base):
__tablename__ = 'cache_invalidation'
__table_args__ = (UniqueConstraint('cache_key'), {'useexisting':True})
cache_id = Column("cache_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
cache_key = Column("cache_key", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
cache_args = Column("cache_args", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
cache_active = Column("cache_active", BOOLEAN(), nullable=True, unique=None, default=None)
cache_active = Column("cache_active", BOOLEAN(), nullable=True, unique=None, default=False)
def __init__(self, cache_key, cache_args=''):
self.cache_key = cache_key
self.cache_args = cache_args
self.cache_active = False
return "<CacheInvaidation('%s:%s')>" % (self.cache_id, self.cache_key)
file renamed from rhodecode/model/permission_model.py to rhodecode/model/permission.py
# Model for permissions
Created on Aug 20, 2010
Model for permissions
from rhodecode.model.db import User, Permission, UserToPerm, RepoToPerm
class PermissionModel(object):
def __init__(self, sa=None):
self.sa = Session()
self.sa = sa
def get_permission(self, permission_id, cache=False):
perm = self.sa.query(Permission)
if cache:
perm = perm.options(FromCache("sql_cache_short",
"get_permission_%s" % permission_id))
return perm.get(permission_id)
def get_permission_by_name(self, name, cache=False):
perm = self.sa.query(Permission)\
.filter(Permission.permission_name == name)
"get_permission_%s" % name))
return perm.scalar()
def update(self, form_result):
perm_user = self.sa.query(User)\
.filter(User.username == form_result['perm_user_name']).scalar()
u2p = self.sa.query(UserToPerm).filter(UserToPerm.user == perm_user).all()
if len(u2p) != 3:
raise Exception('Defined: %s should be 3 permissions for default'
' user. This should not happen please verify'
' your database' % len(u2p))
#stage 1 change defaults
for p in u2p:
if p.permission.permission_name.startswith('repository.'):
p.permission = self.get_permission_by_name(
form_result['default_perm'])
self.sa.add(p)
if p.permission.permission_name.startswith('hg.register.'):
form_result['default_register'])
if p.permission.permission_name.startswith('hg.create.'):
form_result['default_create'])
#stage 2 update all default permissions for repos if checked
if form_result['overwrite_default'] == 'true':
for r2p in self.sa.query(RepoToPerm)\
.filter(RepoToPerm.user == perm_user).all():
r2p.permission = self.get_permission_by_name(
self.sa.add(r2p)
#stage 3 set anonymous access
if perm_user.username == 'default':
perm_user.active = bool(form_result['anonymous'])
self.sa.add(perm_user)
raise
# model for handling repositories actions
Created on Jun 5, 2010
model for handling repositories actions
:author: marcink
from vcs.backends import get_repo, get_backend
from datetime import datetime
from pylons import app_globals as g
from rhodecode.model.db import Repository, RepoToPerm, User, Permission
import shutil
class RepoModel(object):
def get(self, repo_id, cache=False):
repo = self.sa.query(Repository)\
.filter(Repository.repo_name == repo_id)
repo = repo.options(FromCache("sql_cache_short",
"get_repo_%s" % repo))
return repo.scalar()
def get_users_js(self):
users = self.sa.query(User).filter(User.active == True).all()
u_tmpl = '''{id:%s, fname:"%s", lname:"%s", nname:"%s"},'''
users_array = '[%s];' % '\n'.join([u_tmpl % (u.user_id, u.name,
u.lastname, u.username)
for u in users])
return users_array
def update(self, repo_name, form_data):
#update permissions
for username, perm in form_data['perms_updates']:
r2p = self.sa.query(RepoToPerm)\
.filter(RepoToPerm.user == UserModel(self.sa).get_by_username(username, cache=False))\
.filter(RepoToPerm.user == UserModel().get_by_username(username, cache=False))\
.filter(RepoToPerm.repository == self.get(repo_name))\
.one()
r2p.permission_id = self.sa.query(Permission).filter(
Permission.permission_name ==
perm).one().permission_id
#set new permissions
for username, perm in form_data['perms_new']:
r2p = RepoToPerm()
r2p.repository = self.get(repo_name)
r2p.user = UserModel(self.sa).get_by_username(username, cache=False)
r2p.user = UserModel().get_by_username(username, cache=False)
Permission.permission_name == perm)\
.one().permission_id
#update current repo
cur_repo = self.get(repo_name, cache=False)
for k, v in form_data.items():
if k == 'user':
cur_repo.user_id = v
setattr(cur_repo, k, v)
self.sa.add(cur_repo)
if repo_name != form_data['repo_name']:
#rename our data
self.__rename_repo(repo_name, form_data['repo_name'])
def create(self, form_data, cur_user, just_db=False, fork=False):
if fork:
#force str since hg doesn't go with unicode
repo_name = str(form_data['fork_name'])
org_name = str(form_data['repo_name'])
org_name = repo_name = str(form_data['repo_name'])
new_repo = Repository()
if k == 'repo_name':
v = repo_name
setattr(new_repo, k, v)
parent_repo = self.sa.query(Repository)\
.filter(Repository.repo_name == org_name).scalar()
new_repo.fork = parent_repo
new_repo.user_id = cur_user.user_id
self.sa.add(new_repo)
#create default permission
repo_to_perm = RepoToPerm()
default = 'repository.read'
for p in UserModel(self.sa).get_by_username('default', cache=False).user_perms:
for p in UserModel().get_by_username('default', cache=False).user_perms:
default = p.permission.permission_name
default_perm = 'repository.none' if form_data['private'] else default
repo_to_perm.permission_id = self.sa.query(Permission)\
.filter(Permission.permission_name == default_perm)\
repo_to_perm.repository_id = new_repo.repo_id
repo_to_perm.user_id = UserModel(self.sa).get_by_username('default', cache=False).user_id
repo_to_perm.user_id = UserModel().get_by_username('default', cache=False).user_id
self.sa.add(repo_to_perm)
if not just_db:
self.__create_repo(repo_name, form_data['repo_type'])
def create_fork(self, form_data, cur_user):
from rhodecode.lib.celerylib import tasks, run_task
run_task(tasks.create_repo_fork, form_data, cur_user)
def delete(self, repo):
self.sa.delete(repo)
self.__delete_repo(repo)
def delete_perm_user(self, form_data, repo_name):
self.sa.query(RepoToPerm)\
.filter(RepoToPerm.user_id == form_data['user_id']).delete()
def __create_repo(self, repo_name, alias):
from rhodecode.lib.utils import check_repo
repo_path = os.path.join(g.base_path, repo_name)
if check_repo(repo_name, g.base_path):
log.info('creating repo %s in %s', repo_name, repo_path)
backend(repo_path, create=True)
def __rename_repo(self, old, new):
log.info('renaming repo from %s to %s', old, new)
old_path = os.path.join(g.base_path, old)
new_path = os.path.join(g.base_path, new)
if os.path.isdir(new_path):
raise Exception('Was trying to rename to already existing dir %s',
new_path)
shutil.move(old_path, new_path)
def __delete_repo(self, repo):
rm_path = os.path.join(g.base_path, repo.repo_name)
log.info("Removing %s", rm_path)
#disable hg/git
alias = repo.repo_type
shutil.move(os.path.join(rm_path, '.%s' % alias),
os.path.join(rm_path, 'rm__.%s' % alias))
#disable repo
shutil.move(rm_path, os.path.join(g.base_path, 'rm__%s__%s' \
% (datetime.today(), repo.repo_name)))
# Model for RhodeCode
Created on April 9, 2010
Model for RhodeCode
from beaker.cache import cache_region, region_invalidate
from mercurial import ui
from rhodecode.lib.auth import HasRepoPermissionAny
from rhodecode.lib.utils import get_repos
from rhodecode.model.db import Repository, User, RhodeCodeUi
from rhodecode.model.db import Repository, User, RhodeCodeUi, CacheInvalidation
from sqlalchemy.orm import joinedload
from sqlalchemy.orm.session import make_transient
from vcs.exceptions import RepositoryError, VCSError
import time
class ScmModel(object):
Mercurial Model
self.sa = meta.Session()
def repos_path(self):
Get's the repositories root path from database
q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
def repo_scan(self, repos_path, baseui, initial=False):
Listing of repositories in given path. This path should not be a
repository itself. Return a dictionary of repository objects
:param repos_path: path to directory containing repositories
:param baseui
:param initial: initial scan
log.info('scanning for repositories in %s', repos_path)
if not isinstance(baseui, ui.ui):
repos_list = {}
for name, path in get_repos(repos_path):
if repos_list.has_key(name):
raise RepositoryError('Duplicate repository name %s '
'found in %s' % (name, path))
klass = get_backend(path[0])
if path[0] == 'hg':
repos_list[name] = klass(path[1], baseui=baseui)
if path[0] == 'git':
repos_list[name] = klass(path[1])
except OSError:
continue
return repos_list
def get_repos(self, all_repos=None):
Get all repos from db and for each repo create it's backend instance.
and fill that backed with information from database
:param all_repos: give specific repositories list, good for filtering
if not all_repos:
all_repos = self.sa.query(Repository).all()
for r in all_repos:
repo = self.get(r.repo_name)
if repo is not None:
last_change = repo.last_change
tip = h.get_changeset_safe(repo, 'tip')
tmp_d = {}
tmp_d['name'] = repo.name
tmp_d['name_sort'] = tmp_d['name'].lower()
tmp_d['description'] = repo.dbrepo.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'] = repo.dbrepo.user.full_contact
tmp_d['contact_sort'] = tmp_d['contact']
tmp_d['repo_archives'] = list(repo._get_archives())
tmp_d['last_msg'] = tip.message
tmp_d['repo'] = repo
yield tmp_d
def get_repo(self, repo_name):
return self.get(repo_name)
def get(self, repo_name):
Get's repository from given name, creates BackendInstance and
propagates it's data from database with all additional information
if not HasRepoPermissionAny('repository.read', 'repository.write',
'repository.admin')(repo_name, 'get repo check'):
return
@cache_region('long_term', 'get_repo_cached_%s' % repo_name)
@cache_region('long_term')
def _get_repo(repo_name):
repo_path = os.path.join(self.repos_path, repo_name)
alias = get_scm(repo_path)[0]
log.debug('Creating instance of %s repository', alias)
if alias == 'hg':
repo = backend(repo_path, create=False, baseui=None)
#skip hidden web repository
if repo._get_hidden():
repo = backend(repo_path, create=False)
dbrepo = self.sa.query(Repository)\
.options(joinedload(Repository.fork))\
.options(joinedload(Repository.user))\
.filter(Repository.repo_name == repo_name)\
.scalar()
make_transient(dbrepo)
repo.dbrepo = dbrepo
return repo
invalidate = False
invalidate = self._should_invalidate(repo_name)
if invalidate:
log.info('INVALIDATING CACHE FOR %s', repo_name)
log.info('invalidating cache for repository %s', repo_name)
region_invalidate(_get_repo, None, repo_name)
self._mark_invalidated(invalidate)
return _get_repo(repo_name)
def mark_for_invalidation(self, repo_name):
:param repo_name: this repo that should invalidation take place
log.debug('marking %s for invalidation', repo_name)
cache = self.sa.query(CacheInvalidation)\
.filter(CacheInvalidation.cache_key == repo_name).scalar()
#mark this cache as inactive
cache.cache_active = False
log.debug('cache key not found in invalidation db -> creating one')
cache = CacheInvalidation(repo_name)
self.sa.add(cache)
def _should_invalidate(self, repo_name):
Looks up database for invalidation signals for this repo_name
ret = self.sa.query(CacheInvalidation)\
.options(FromCache('sql_cache_short',
'get_invalidation_%s' % repo_name))\
.filter(CacheInvalidation.cache_key == repo_name)\
.filter(CacheInvalidation.cache_active == False)\
def _mark_invalidated(self, cache_key):
Marks all occurences of cache to invaldation as already invalidated
@param repo_name:
if cache_key:
log.debug('marking %s as already invalidated', cache_key)
cache_key.cache_active = True
self.sa.add(cache_key)
# Model for users
Model for users
class DefaultUserException(Exception):pass
class UserModel(object):
def get(self, user_id, cache=False):
user = self.sa.query(User)
user = user.options(FromCache("sql_cache_short",
"get_user_%s" % user_id))
return user.get(user_id)
def get_by_username(self, username, cache=False):
user = self.sa.query(User)\
.filter(User.username == username)
"get_user_%s" % username))
return user.scalar()
def create(self, form_data):
new_user = User()
setattr(new_user, k, v)
self.sa.add(new_user)
def create_registration(self, form_data):
if k != 'admin':
body = ('New user registration\n'
'username: %s\n'
'email: %s\n')
body = body % (form_data['username'], form_data['email'])
run_task(tasks.send_email, None,
_('[RhodeCode] New User registration'),
body)
def update(self, user_id, form_data):
new_user = self.get(user_id, cache=False)
if new_user.username == 'default':
raise DefaultUserException(
_("You can't Edit this user since it's"
" crucial for entire application"))
if k == 'new_password' and v != '':
new_user.password = v
def update_my_account(self, user_id, form_data):
if k not in ['admin', 'active']:
def delete(self, user_id):
Status change: