@@ -19,142 +19,163 @@ from rhodecode.model.forms import ReposG
log = logging.getLogger(__name__)
class ReposGroupsController(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('repos_group', 'repos_groups')
def __load_defaults(self):
c.repo_groups = [('', '')]
parents_link = lambda k: h.literal('»'.join(
map(lambda k: k.group_name,
k.parents + [k])
)
c.repo_groups.extend([(x.group_id, parents_link(x)) for \
x in self.sa.query(Group).all()])
c.repo_groups = sorted(c.repo_groups,
key=lambda t: t[1].split('»')[0])
c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
@LoginRequired()
def __before__(self):
super(ReposGroupsController, self).__before__()
@HasPermissionAnyDecorator('hg.admin')
def index(self, format='html'):
"""GET /repos_groups: All items in the collection"""
# url('repos_groups')
sk = lambda g:g.parents[0].group_name if g.parents else g.group_name
c.groups = sorted(Group.query().all(), key=sk)
return render('admin/repos_groups/repos_groups_show.html')
def create(self):
"""POST /repos_groups: Create a new item"""
self.__load_defaults()
repos_group_model = ReposGroupModel()
repos_group_form = ReposGroupForm(available_groups=
c.repo_groups_choices)()
try:
form_result = repos_group_form.to_python(dict(request.POST))
repos_group_model.create(form_result)
h.flash(_('created repos group %s') \
% form_result['repos_group_name'], category='success')
#TODO: in futureaction_logger(, '', '', '', self.sa)
except formencode.Invalid, errors:
return htmlfill.render(
render('admin/repos_groups/repos_groups_add.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 occurred during creation of repos group %s') \
% request.POST.get('repos_group_name'), category='error')
return redirect(url('repos_groups'))
def new(self, format='html'):
"""GET /repos_groups/new: Form to create a new item"""
# url('new_repos_group')
return render('admin/repos_groups/repos_groups_add.html')
def update(self, id):
"""PUT /repos_groups/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('repos_group', id=ID),
# method='put')
# url('repos_group', id=ID)
def delete(self, id):
"""DELETE /repos_groups/id: Delete an existing item"""
# <input type="hidden" name="_method" value="DELETE" />
# method='delete')
gr = Group.get(id)
repos = gr.repositories.all()
if repos:
h.flash(_('This group contains %s repositores and cannot be '
'deleted' % len(repos)),
category='error')
repos_group_model.delete(id)
h.flash(_('removed repos group %s' % gr.group_name), category='success')
h.flash(_('error occurred during deletion of repos group %s' % gr.group_name),
def show(self, id, format='html'):
"""GET /repos_groups/id: Show a specific item"""
c.group = Group.get(id)
if c.group:
c.group_repos = c.group.repositories.all()
else:
return redirect(url('repos_group'))
sortables = ['name', 'description', 'last_change', 'tip', 'owner']
current_sort = request.GET.get('sort', 'name')
current_sort_slug = current_sort.replace('-', '')
if current_sort_slug not in sortables:
c.sort_by = 'name'
current_sort_slug = c.sort_by
c.sort_by = current_sort
c.sort_slug = current_sort_slug
sort_key = current_sort_slug + '_sort'
#overwrite our cached list with current filter
gr_filter = [r.repo_name for r in c.group_repos]
c.cached_repo_list = self.scm_model.get_repos(all_repos=gr_filter)
if c.sort_by.startswith('-'):
c.repos_list = sorted(c.cached_repo_list, key=itemgetter(sort_key),
reverse=True)
reverse=False)
c.repo_cnt = len(c.repos_list)
c.groups = self.sa.query(Group).order_by(Group.group_name)\
.filter(Group.group_parent_id == id).all()
return render('admin/repos_groups/repos_groups.html')
def edit(self, id, format='html'):
"""GET /repos_groups/id/edit: Form to edit an existing item"""
# url('edit_repos_group', id=ID)
@@ -227,211 +227,216 @@ class UsersGroupMember(Base):
def __init__(self, gr_id='', u_id=''):
self.users_group_id = gr_id
self.user_id = u_id
class Repository(Base):
__tablename__ = 'repositories'
__table_args__ = (UniqueConstraint('repo_name'), {'useexisting':True},)
__mapper_args__ = {'extension':RepositoryMapper()}
repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
clone_uri = Column("clone_uri", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
private = Column("private", Boolean(), nullable=True, unique=None, default=None)
enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
description = Column("description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
user = relationship('User')
fork = relationship('Repository', remote_side=repo_id)
group = relationship('Group')
repo_to_perm = relationship('RepoToPerm', cascade='all', order_by='RepoToPerm.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', cascade='all')
def __repr__(self):
return "<%s('%s:%s')>" % (self.__class__.__name__,
self.repo_id, self.repo_name)
@classmethod
def by_repo_name(cls, repo_name):
return Session.query(cls).filter(cls.repo_name == repo_name).one()
def get_repo_forks(cls, repo_id):
return Session.query(cls).filter(Repository.fork_id == repo_id)
@property
def just_name(self):
return self.repo_name.split(os.sep)[-1]
def groups_with_parents(self):
groups = []
if self.group is None:
return groups
cur_gr = self.group
groups.insert(0, cur_gr)
while 1:
gr = getattr(cur_gr, 'parent_group', None)
cur_gr = cur_gr.parent_group
if gr is None:
break
groups.insert(0, gr)
def groups_and_repo(self):
return self.groups_with_parents, self.just_name
class Group(Base):
__tablename__ = 'groups'
__table_args__ = (UniqueConstraint('group_name', 'group_parent_id'), {'useexisting':True},)
__mapper_args__ = {'order_by':'group_name'}
group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
group_name = Column("group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
group_description = Column("group_description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
parent_group = relationship('Group', remote_side=group_id)
def __init__(self, group_name='', parent_group=None):
self.group_name = group_name
self.parent_group = parent_group
return "<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
self.group_name)
def url_sep(cls):
return '/'
def parents(self):
if self.parent_group is None:
cur_gr = self.parent_group
def full_path(self):
return '/'.join([g.group_name for g in self.parents] +
return Group.url_sep().join([g.group_name for g in self.parents] +
[self.group_name])
def repositories(self):
return Session.query(Repository).filter(Repository.group == self)
class Permission(Base):
__tablename__ = 'permissions'
__table_args__ = {'useexisting':True}
permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
permission_name = Column("permission_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
self.permission_id, self.permission_name)
def get_by_key(cls, key):
return Session.query(cls).filter(cls.permission_name == key).scalar()
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)
user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
permission = relationship('Permission')
repository = relationship('Repository')
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)
def has_perm(cls, user_id, perm):
if not isinstance(perm, Permission):
raise Exception('perm needs to be an instance of Permission class')
return Session.query(cls).filter(cls.user_id == user_id)\
.filter(cls.permission == perm).scalar() is not None
def grant_perm(cls, user_id, perm):
new = cls()
new.user_id = user_id
new.permission = perm
Session.add(new)
Session.commit()
except:
Session.rollback()
def revoke_perm(cls, user_id, perm):
Session.query(cls).filter(cls.user_id == user_id)\
.filter(cls.permission == perm).delete()
class UsersGroupRepoToPerm(Base):
__tablename__ = 'users_group_repo_to_perm'
__table_args__ = (UniqueConstraint('users_group_id', 'permission_id'), {'useexisting':True})
users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
users_group = relationship('UsersGroup')
class UsersGroupToPerm(Base):
__tablename__ = 'users_group_to_perm'
@@ -144,195 +144,195 @@ class ValidPassword(formencode.validator
if value:
if value.get('password'):
value['password'] = get_crypt_password(value['password'])
except UnicodeEncodeError:
e_dict = {'password':_('Invalid characters in password')}
raise formencode.Invalid('', value, state, error_dict=e_dict)
if value.get('password_confirmation'):
value['password_confirmation'] = \
get_crypt_password(value['password_confirmation'])
e_dict = {'password_confirmation':_('Invalid characters in password')}
if value.get('new_password'):
value['new_password'] = \
get_crypt_password(value['new_password'])
e_dict = {'new_password':_('Invalid characters in password')}
return value
class ValidPasswordsMatch(formencode.validators.FancyValidator):
def validate_python(self, value, state):
if value['password'] != value['password_confirmation']:
e_dict = {'password_confirmation':
_('Password do not match')}
class ValidAuth(formencode.validators.FancyValidator):
messages = {
'invalid_password':_('invalid password'),
'invalid_login':_('invalid user name'),
'disabled_account':_('Your account is disabled')
}
#error mapping
e_dict = {'username':messages['invalid_login'],
'password':messages['invalid_password']}
e_dict_disable = {'username':messages['disabled_account']}
password = value['password']
username = value['username']
user = UserModel().get_by_username(username)
if authenticate(username, password):
if user and user.active is False:
log.warning('user %s is disabled', username)
raise formencode.Invalid(self.message('disabled_account',
state=State_obj),
value, state,
error_dict=self.e_dict_disable)
log.warning('user %s not authenticated', username)
raise formencode.Invalid(self.message('invalid_password',
state=State_obj), value, state,
error_dict=self.e_dict)
class ValidRepoUser(formencode.validators.FancyValidator):
def to_python(self, value, state):
self.user_db = User.query()\
.filter(User.active == True)\
.filter(User.username == value).one()
raise formencode.Invalid(_('This username is not valid'),
value, state)
def ValidRepoName(edit, old_data):
class _ValidRepoName(formencode.validators.FancyValidator):
repo_name = value.get('repo_name')
slug = repo_name_slug(repo_name)
if slug in ['_admin', '']:
e_dict = {'repo_name': _('This repository name is disallowed')}
if value.get('repo_group'):
gr = Group.get(value.get('repo_group'))
group_path = gr.full_path
# value needs to be aware of group name
# it has to use '/'
repo_name_full = group_path + '/' + repo_name
# value needs to be aware of group name in order to check
# db key
repo_name_full = group_path + Group.url_sep() + repo_name
group_path = ''
repo_name_full = repo_name
value['repo_name_full'] = repo_name_full
if old_data.get('repo_name') != repo_name_full or not edit:
if group_path != '':
if RepoModel().get_by_repo_name(repo_name_full,):
e_dict = {'repo_name':_('This repository already '
'exists in group "%s"') %
gr.group_name}
raise formencode.Invalid('', value, state,
error_dict=e_dict)
if RepoModel().get_by_repo_name(repo_name_full):
e_dict = {'repo_name':_('This repository '
'already exists')}
return _ValidRepoName
def SlugifyName():
class _SlugifyName(formencode.validators.FancyValidator):
return repo_name_slug(value)
return _SlugifyName
def ValidCloneUri():
from mercurial.httprepo import httprepository, httpsrepository
from rhodecode.lib.utils import make_ui
class _ValidCloneUri(formencode.validators.FancyValidator):
if not value:
pass
elif value.startswith('https'):
httpsrepository(make_ui('db'), value).capabilities
except Exception, e:
raise formencode.Invalid(_('invalid clone url'), value,
state)
elif value.startswith('http'):
httprepository(make_ui('db'), value).capabilities
raise formencode.Invalid(_('Invalid clone url, provide a '
'valid clone http\s url'), value,
return _ValidCloneUri
def ValidForkType(old_data):
class _ValidForkType(formencode.validators.FancyValidator):
if old_data['repo_type'] != value:
raise formencode.Invalid(_('Fork have to be the same '
'type as original'), value, state)
return _ValidForkType
class ValidPerms(formencode.validators.FancyValidator):
messages = {'perm_new_member_name':_('This username or users group name'
' is not valid')}
perms_update = []
perms_new = []
#build a list of permission to update and new permission to create
for k, v in value.items():
#means new added member to permissions
if k.startswith('perm_new_member'):
new_perm = value.get('perm_new_member', False)
new_member = value.get('perm_new_member_name', False)
new_type = value.get('perm_new_member_type')
if new_member and new_perm:
if (new_member, new_perm, new_type) not in perms_new:
perms_new.append((new_member, new_perm, new_type))
elif k.startswith('u_perm_') or k.startswith('g_perm_'):
member = k[7:]
# -*- coding: utf-8 -*-
"""
rhodecode.model.user_group
~~~~~~~~~~~~~~~~~~~~~~~~~~
users groups model for RhodeCode
:created_on: Jan 25, 2011
:author: marcink
:copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
:license: GPLv3, see COPYING for more details.
# 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, either version 3 of the License, or
# (at your option) any later version.
#
# 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, see <http://www.gnu.org/licenses/>.
import os
import logging
import traceback
from pylons.i18n.translation import _
from vcs.utils.lazy import LazyProperty
from rhodecode.model import BaseModel
from rhodecode.model.caching_query import FromCache
from rhodecode.model.db import Group, RhodeCodeUi
class ReposGroupModel(BaseModel):
@LazyProperty
def repos_path(self):
Get's the repositories root path from database
q = RhodeCodeUi.get_by_key('/').one()
return q.ui_value
def __create_group(self, group_name, parent_id):
makes repositories group on filesystem
:param repo_name:
:param parent_id:
if parent_id:
parent_group_name = Group.get(parent_id).group_name
paths = Group.get(parent_id).full_path.split(Group.url_sep())
parent_path = os.sep.join(paths)
parent_group_name = ''
parent_path = ''
create_path = os.path.join(self.repos_path, parent_group_name,
group_name)
create_path = os.path.join(self.repos_path, parent_path, group_name)
log.debug('creating new group in %s', create_path)
if os.path.isdir(create_path):
raise Exception('That directory already exists !')
os.makedirs(create_path)
def __rename_group(self, group_name):
Renames a group on filesystem
:param group_name:
def __delete_group(self, group_name):
def __delete_group(self, group):
Deletes a group from a filesystem
:param group: instance of group from database
paths = group.full_path.split(Group.url_sep())
paths = os.sep.join(paths)
rm_path = os.path.join(self.repos_path, paths)
os.rmdir(rm_path)
def create(self, form_data):
new_repos_group = Group()
new_repos_group.group_name = form_data['repos_group_name']
new_repos_group.group_description = \
form_data['repos_group_description']
new_repos_group.group_parent_id = form_data['repos_group_parent']
self.sa.add(new_repos_group)
self.__create_group(form_data['repos_group_name'],
form_data['repos_group_parent'])
self.sa.commit()
self.sa.rollback()
raise
def update(self, repos_group_id, form_data):
repos_group = Group.get(repos_group_id)
self.sa.add(repos_group)
def delete(self, users_group_id):
users_group = self.get(users_group_id, cache=False)
users_group = Group.get(users_group_id)
self.sa.delete(users_group)
self.__delete_group(users_group)
Status change: