# -*- coding: utf-8 -*-
"""
rhodecode.controllers.admin.repos
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Admin controller for RhodeCode
:created_on: Apr 7, 2010
: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; 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.
import logging
import traceback
import formencode
from operator import itemgetter
from formencode import htmlfill
from paste.httpexceptions import HTTPInternalServerError
from pylons import request, response, 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, \
HasPermissionAnyDecorator
from rhodecode.lib.base import BaseController, render
from rhodecode.lib.utils import invalidate_cache, action_logger, repo_name_slug
from rhodecode.lib.helpers import get_token
from rhodecode.model.db import User, Repository, UserFollowing
from rhodecode.model.db import User, Repository, UserFollowing, Group
from rhodecode.model.forms import RepoForm
from rhodecode.model.scm import ScmModel
from rhodecode.model.repo import RepoModel
log = logging.getLogger(__name__)
class ReposController(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('repo', 'repos')
@LoginRequired()
@HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
def __before__(self):
c.admin_user = session.get('admin_user')
c.admin_username = session.get('admin_username')
super(ReposController, self).__before__()
def __load_data(self, repo_name):
Load defaults settings for edit, and update
:param repo_name:
repo, dbrepo = ScmModel().get(repo_name, retval='repo')
repo_model = RepoModel()
c.repo_info = repo_model.get_by_repo_name(repo_name)
if c.repo_info is None:
h.flash(_('%s repository is not mapped to db perhaps'
' it was created or renamed from the filesystem'
' please run the application again'
' in order to rescan repositories') % repo_name,
category='error')
return redirect(url('repos'))
c.repo_groups = [('', '')]
c.repo_groups.extend([(x.group_id, x.group_name) for x in self.sa.query(Group).all()])
c.default_user_id = User.by_username('default').user_id
c.in_public_journal = self.sa.query(UserFollowing)\
.filter(UserFollowing.user_id == c.default_user_id)\
.filter(UserFollowing.follows_repository == c.repo_info).scalar()
if c.repo_info.stats:
last_rev = c.repo_info.stats.stat_on_revision
else:
last_rev = 0
c.stats_revision = last_rev
c.repo_last_rev = repo.count() - 1 if repo.revisions else 0
if last_rev == 0 or c.repo_last_rev == 0:
c.stats_percentage = 0
c.stats_percentage = '%.2f' % ((float((last_rev)) /
c.repo_last_rev) * 100)
c.users_array = repo_model.get_users_js()
c.users_groups_array = repo_model.get_users_groups_js()
defaults = c.repo_info.get_dict()
group, repo_name = c.repo_info.groups_and_repo
defaults['repo_name'] = repo_name
defaults['repo_group'] = getattr(group, 'group_id', None)
#fill owner
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})
#fill repository users
for p in c.repo_info.repo_to_perm:
defaults.update({'u_perm_%s' % p.user.username:
p.permission.permission_name})
#fill repository groups
for p in c.repo_info.users_group_to_perm:
defaults.update({'g_perm_%s' % p.users_group.users_group_name:
return defaults
@HasPermissionAllDecorator('hg.admin')
def index(self, format='html'):
"""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')
def create(self):
POST /repos: Create a new item"""
_form = RepoForm()()
form_result = {}
try:
form_result = _form.to_python(dict(request.POST))
form_result = RepoForm()(repo_groups=c.repo_groups).to_python(dict(request.POST))
repo_model.create(form_result, c.rhodecode_user)
h.flash(_('created repository %s') % form_result['repo_name'],
if form_result['clone_uri']:
h.flash(_('created repository %s from %s') \
% (form_result['repo_name'], form_result['clone_uri']),
category='success')
if request.POST.get('user_created'):
action_logger(self.rhodecode_user, 'user_created_repo',
form_result['repo_name'], '', self.sa)
action_logger(self.rhodecode_user, 'admin_created_repo',
except formencode.Invalid, errors:
c.new_repo = errors.value['repo_name']
r = render('admin/repos/repo_add_create_repository.html')
r = render('admin/repos/repo_add.html')
return htmlfill.render(
r,
defaults=errors.value,
errors=errors.error_dict or {},
prefix_error=False,
encoding="UTF-8")
except Exception:
log.error(traceback.format_exc())
msg = _('error occurred during creation of repository %s') \
% form_result.get('repo_name')
h.flash(msg, category='error')
return redirect(url('home'))
def new(self, format='html'):
"""GET /repos/new: Form to create a new item"""
new_repo = request.GET.get('repo', '')
c.new_repo = 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"""
# Forms posted to this method should contain a hidden field:
# <input type="hidden" name="_method" value="PUT" />
# Or using helpers:
# h.form(url('repo', repo_name=ID),
# method='put')
# 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 successfully' % repo_name),
changed_name = form_result['repo_name']
action_logger(self.rhodecode_user, 'admin_updated_repo',
changed_name, '', self.sa)
if last_rev == 0:
errors.value.update({'user':c.repo_info.user.username})
defaults = self.__load_data(repo_name)
defaults.update(errors.value)
render('admin/repos/repo_edit.html'),
defaults=defaults,
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"""
# <input type="hidden" name="_method" value="DELETE" />
# method='delete')
repo = repo_model.get_by_repo_name(repo_name)
if not repo:
' it was moved or renamed from the filesystem'
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 occurred during deletion of %s') % repo_name,
def delete_perm_user(self, repo_name):
DELETE an existing repository permission user
repo_model.delete_perm_user(request.POST, repo_name)
h.flash(_('An error occurred during deletion of repository user'),
raise HTTPInternalServerError()
def delete_perm_users_group(self, repo_name):
DELETE an existing repository permission users group
repo_model.delete_perm_users_group(request.POST, repo_name)
h.flash(_('An error occurred during deletion of repository'
' users groups'),
def repo_stats(self, repo_name):
DELETE an existing repository statistics
repo_model.delete_stats(repo_name)
h.flash(_('An error occurred during deletion of repository stats'),
return redirect(url('edit_repo', repo_name=repo_name))
def repo_cache(self, repo_name):
INVALIDATE existing repository cache
ScmModel().mark_for_invalidation(repo_name)
h.flash(_('An error occurred during cache invalidation'),
def repo_public_journal(self, repo_name):
Set's this repository to be visible in public journal,
in other words assing default user to follow this repo
cur_token = request.POST.get('auth_token')
token = get_token()
if cur_token == token:
repo_id = Repository.by_repo_name(repo_name).repo_id
user_id = User.by_username('default').user_id
self.scm_model.toggle_following_repo(repo_id, user_id)
h.flash(_('Updated repository visibility in public journal'),
except:
h.flash(_('An error occurred during setting this'
' repository in public journal'),
h.flash(_('Token mismatch'), category='error')
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)
encoding="UTF-8",
force_defaults=False
)
rhodecode.controllers.admin.settings
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
settings controller for rhodecode admin
:created_on: Jul 14, 2010
from sqlalchemy import func
from pylons import request, session, tmpl_context as c, url, config
HasPermissionAnyDecorator, NotAnonymous
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
from rhodecode.model.db import RhodeCodeUi, Repository, Group
from rhodecode.model.forms import UserForm, ApplicationSettingsForm, \
ApplicationUiSettingsForm
from rhodecode.model.settings import SettingsModel
from rhodecode.model.user import UserModel
class SettingsController(BaseController):
"""REST Controller styled on the Atom Publishing Protocol"""
# map.resource('setting', 'settings', controller='admin/settings',
# path_prefix='/admin', name_prefix='admin_')
super(SettingsController, self).__before__()
"""GET /admin/settings: All items in the collection"""
# url('admin_settings')
defaults = SettingsModel().get_app_settings()
defaults.update(self.get_hg_ui_settings())
render('admin/settings/settings.html'),
"""POST /admin/settings: Create a new item"""
"""GET /admin/settings/new: Form to create a new item"""
# url('admin_new_setting')
def update(self, setting_id):
"""PUT /admin/settings/setting_id: Update an existing item"""
# h.form(url('admin_setting', setting_id=ID),
# url('admin_setting', setting_id=ID)
if setting_id == 'mapping':
rm_obsolete = request.POST.get('destroy', False)
log.debug('Rescanning directories with destroy=%s', rm_obsolete)
initial = ScmModel().repo_scan()
log.debug('invalidating all repositories')
for repo_name in initial.keys():
added, removed = repo2db_mapper(initial, rm_obsolete)
h.flash(_('Repositories successfully'
' rescanned added: %s,removed: %s') % (added, removed)
, category='success')
if setting_id == 'whoosh':
repo_location = self.get_hg_ui_settings()['paths_root_path']
full_index = request.POST.get('full_index', False)
task = run_task(tasks.whoosh_index, repo_location, full_index)
h.flash(_('Whoosh reindex task scheduled'), category='success')
if setting_id == 'global':
application_form = ApplicationSettingsForm()()
form_result = application_form.to_python(dict(request.POST))
settings_model = SettingsModel()
hgsettings1 = settings_model.get('title')
hgsettings1.app_settings_value = form_result['rhodecode_title']
hgsettings2 = settings_model.get('realm')
hgsettings2.app_settings_value = form_result['rhodecode_realm']
hgsettings3 = settings_model.get('ga_code')
hgsettings3.app_settings_value = form_result['rhodecode_ga_code']
self.sa.add(hgsettings1)
self.sa.add(hgsettings2)
self.sa.add(hgsettings3)
self.sa.commit()
set_rhodecode_config(config)
h.flash(_('Updated application settings'),
h.flash(_('error occurred during updating application settings'),
self.sa.rollback()
if setting_id == 'mercurial':
application_form = ApplicationUiSettingsForm()()
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(hgsettings4)
self.sa.add(hgsettings5)
self.sa.add(hgsettings6)
h.flash(_('Updated mercurial settings'),
return redirect(url('admin_settings'))
def delete(self, setting_id):
"""DELETE /admin/settings/setting_id: Delete an existing item"""
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)
@NotAnonymous()
def my_account(self):
GET /_admin/my_account Displays info about my account
# url('admin_settings_my_account')
c.user = UserModel().get(c.rhodecode_user.user_id, cache=False)
all_repos = [r.repo_name for r in 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.get_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 successfully'),
c.user = user_model.get(c.rhodecode_user.user_id, cache=False)
all_repos = self.sa.query(Repository)\
.order_by(func.lower(Repository.repo_name))\
.all()
h.flash(_('error occurred 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')
def get_hg_ui_settings(self):
ret = self.sa.query(RhodeCodeUi).all()
if not ret:
raise Exception('Could not get application ui settings !')
settings = {}
for each in ret:
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
return settings
@@ -261,291 +261,295 @@ class ValidPerms(formencode.validators.F
if member == 'default':
if value['private']:
#set none for default when updating to private repo
v = 'repository.none'
perms_update.append((member, v, t))
value['perms_updates'] = perms_update
value['perms_new'] = perms_new
#update permissions
sa = meta.Session
for k, v, t in perms_new:
if t is 'user':
self.user_db = sa.query(User)\
.filter(User.active == True)\
.filter(User.username == k).one()
if t is 'users_group':
self.user_db = sa.query(UsersGroup)\
.filter(UsersGroup.users_group_active == True)\
.filter(UsersGroup.users_group_name == k).one()
msg = self.message('perm_new_member_name',
state=State_obj)
raise formencode.Invalid(msg, value, state,
error_dict={'perm_new_member_name':msg})
return value
class ValidSettings(formencode.validators.FancyValidator):
def to_python(self, value, state):
#settings form can't edit user
if value.has_key('user'):
del['value']['user']
class ValidPath(formencode.validators.FancyValidator):
if not os.path.isdir(value):
msg = _('This is not a valid path')
error_dict={'paths_root_path':msg})
def UniqSystemEmail(old_data):
class _UniqSystemEmail(formencode.validators.FancyValidator):
value = value.lower()
if old_data.get('email') != value:
sa = meta.Session()
user = sa.query(User).filter(User.email == value).scalar()
if user:
raise formencode.Invalid(_("This e-mail address is already taken") ,
value, state)
finally:
meta.Session.remove()
return _UniqSystemEmail
class ValidSystemEmail(formencode.validators.FancyValidator):
if user is None:
raise formencode.Invalid(_("This e-mail address doesn't exist.") ,
class LdapLibValidator(formencode.validators.FancyValidator):
import ldap
except ImportError:
raise LdapImportError
class AttrLoginValidator(formencode.validators.FancyValidator):
if not value or not isinstance(value, (str, unicode)):
raise formencode.Invalid(_("The LDAP Login attribute of the CN "
"must be specified - this is the name "
"of the attribute that is equivalent "
"to 'username'"),
#===============================================================================
# FORMS
class LoginForm(formencode.Schema):
allow_extra_fields = True
filter_extra_fields = True
username = UnicodeString(
strip=True,
min=1,
not_empty=True,
messages={
'empty':_('Please enter a login'),
'tooShort':_('Enter a value %(min)i characters long or more')}
password = UnicodeString(
min=6,
'empty':_('Please enter a password'),
'tooShort':_('Enter %(min)i characters or more')}
#chained validators have access to all data
chained_validators = [ValidAuth]
def UserForm(edit=False, old_data={}):
class _UserForm(formencode.Schema):
username = All(UnicodeString(strip=True, min=1, not_empty=True),
ValidUsername(edit, old_data))
if edit:
new_password = All(UnicodeString(strip=True, min=6, not_empty=False))
admin = StringBoolean(if_missing=False)
password = All(UnicodeString(strip=True, min=6, not_empty=True))
active = StringBoolean(if_missing=False)
name = UnicodeString(strip=True, min=1, not_empty=True)
lastname = UnicodeString(strip=True, min=1, not_empty=True)
email = All(Email(not_empty=True), UniqSystemEmail(old_data))
chained_validators = [ValidPassword]
return _UserForm
def UsersGroupForm(edit=False, old_data={}, available_members=[]):
class _UsersGroupForm(formencode.Schema):
users_group_name = All(UnicodeString(strip=True, min=1, not_empty=True),
ValidUsersGroup(edit, old_data))
users_group_active = StringBoolean(if_missing=False)
users_group_members = OneOf(available_members, hideList=False,
testValueList=True,
if_missing=None, not_empty=False)
return _UsersGroupForm
def RegisterForm(edit=False, old_data={}):
class _RegisterForm(formencode.Schema):
username = All(ValidUsername(edit, old_data),
UnicodeString(strip=True, min=1, not_empty=True))
password_confirmation = All(UnicodeString(strip=True, min=6, not_empty=True))
chained_validators = [ValidPasswordsMatch, ValidPassword]
return _RegisterForm
def PasswordResetForm():
class _PasswordResetForm(formencode.Schema):
email = All(ValidSystemEmail(), Email(not_empty=True))
return _PasswordResetForm
def RepoForm(edit=False, old_data={}, supported_backends=BACKENDS.keys()):
def RepoForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
repo_groups=[]):
class _RepoForm(formencode.Schema):
filter_extra_fields = False
repo_name = All(UnicodeString(strip=True, min=1, not_empty=True),
ValidRepoName(edit, old_data))
clone_uri = UnicodeString(strip=True, min=1, not_empty=False)
repo_group = OneOf(repo_groups)
repo_type = OneOf(supported_backends)
description = UnicodeString(strip=True, min=1, not_empty=True)
private = StringBoolean(if_missing=False)
enable_statistics = StringBoolean(if_missing=False)
enable_downloads = StringBoolean(if_missing=False)
#this is repo owner
user = All(Int(not_empty=True), ValidRepoUser)
chained_validators = [ValidPerms]
return _RepoForm
def RepoForkForm(edit=False, old_data={}, supported_backends=BACKENDS.keys()):
class _RepoForkForm(formencode.Schema):
fork_name = All(UnicodeString(strip=True, min=1, not_empty=True),
repo_type = All(ValidForkType(old_data), OneOf(supported_backends))
return _RepoForkForm
def RepoSettingsForm(edit=False, old_data={}):
chained_validators = [ValidPerms, ValidSettings]
def ApplicationSettingsForm():
class _ApplicationSettingsForm(formencode.Schema):
rhodecode_title = UnicodeString(strip=True, min=1, not_empty=True)
rhodecode_realm = UnicodeString(strip=True, min=1, not_empty=True)
rhodecode_ga_code = UnicodeString(strip=True, min=1, not_empty=False)
return _ApplicationSettingsForm
def ApplicationUiSettingsForm():
class _ApplicationUiSettingsForm(formencode.Schema):
web_push_ssl = OneOf(['true', 'false'], if_missing='false')
paths_root_path = All(ValidPath(), UnicodeString(strip=True, min=1, not_empty=True))
hooks_changegroup_update = OneOf(['True', 'False'], if_missing=False)
hooks_changegroup_repo_size = OneOf(['True', 'False'], if_missing=False)
hooks_pretxnchangegroup_push_logger = OneOf(['True', 'False'], if_missing=False)
hooks_preoutgoing_pull_logger = OneOf(['True', 'False'], if_missing=False)
return _ApplicationUiSettingsForm
def DefaultPermissionsForm(perms_choices, register_choices, create_choices):
class _DefaultPermissionsForm(formencode.Schema):
overwrite_default = StringBoolean(if_missing=False)
anonymous = OneOf(['True', 'False'], if_missing=False)
default_perm = OneOf(perms_choices)
default_register = OneOf(register_choices)
default_create = OneOf(create_choices)
return _DefaultPermissionsForm
def LdapSettingsForm(tls_reqcert_choices, search_scope_choices):
class _LdapSettingsForm(formencode.Schema):
pre_validators = [LdapLibValidator]
ldap_active = StringBoolean(if_missing=False)
ldap_host = UnicodeString(strip=True,)
ldap_port = Number(strip=True,)
ldap_ldaps = StringBoolean(if_missing=False)
ldap_tls_reqcert = OneOf(tls_reqcert_choices)
ldap_dn_user = UnicodeString(strip=True,)
ldap_dn_pass = UnicodeString(strip=True,)
ldap_base_dn = UnicodeString(strip=True,)
ldap_filter = UnicodeString(strip=True,)
ldap_search_scope = OneOf(search_scope_choices)
ldap_attr_login = All(AttrLoginValidator, UnicodeString(strip=True,))
ldap_attr_firstname = UnicodeString(strip=True,)
ldap_attr_lastname = UnicodeString(strip=True,)
ldap_attr_email = UnicodeString(strip=True,)
return _LdapSettingsForm
@@ -45,303 +45,305 @@ from rhodecode.model.users_group import
class RepoModel(BaseModel):
@LazyProperty
def repos_path(self):
"""Get's the repositories root path from database
q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
return q.ui_value
def get(self, repo_id, cache=False):
repo = self.sa.query(Repository)\
.filter(Repository.repo_id == repo_id)
if cache:
repo = repo.options(FromCache("sql_cache_short",
"get_repo_%s" % repo_id))
return repo.scalar()
def get_by_repo_name(self, repo_name, cache=False):
.filter(Repository.repo_name == repo_name)
"get_repo_%s" % repo_name))
def get_full(self, repo_name, cache=False, invalidate=False):
.options(joinedload(Repository.fork))\
.options(joinedload(Repository.user))\
.filter(Repository.repo_name == repo_name)\
repo = repo.options(FromCache("sql_cache_long",
"get_repo_full_%s" % repo_name))
if invalidate and cache:
repo.invalidate()
ret = repo.scalar()
#make transient for sake of errors
make_transient(ret)
for k in ['fork', 'user']:
attr = getattr(ret, k, False)
if attr:
make_transient(attr)
return ret
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 get_users_groups_js(self):
users_groups = self.sa.query(UsersGroup)\
.filter(UsersGroup.users_group_active == True).all()
g_tmpl = '''{id:%s, grname:"%s",grmembers:"%s"},'''
users_groups_array = '[%s]' % '\n'.join([g_tmpl % \
(gr.users_group_id, gr.users_group_name,
len(gr.members))
for gr in users_groups])
return users_groups_array
def update(self, repo_name, form_data):
cur_repo = self.get_by_repo_name(repo_name, cache=False)
user_model = UserModel(self.sa)
users_group_model = UsersGroupModel(self.sa)
for member, perm, member_type in form_data['perms_updates']:
if member_type == 'user':
r2p = self.sa.query(RepoToPerm)\
.filter(RepoToPerm.user == user_model.get_by_username(member))\
.filter(RepoToPerm.repository == cur_repo)\
.one()
r2p.permission = self.sa.query(Permission)\
.filter(Permission.permission_name == perm)\
.scalar()
self.sa.add(r2p)
g2p = self.sa.query(UsersGroupToPerm)\
.filter(UsersGroupToPerm.users_group == users_group_model.get_by_groupname(member))\
.filter(UsersGroupToPerm.repository == cur_repo)\
g2p.permission = self.sa.query(Permission)\
self.sa.add(g2p)
#set new permissions
for member, perm, member_type in form_data['perms_new']:
r2p = RepoToPerm()
r2p.repository = cur_repo
r2p.user = user_model.get_by_username(member)
g2p = UsersGroupToPerm()
g2p.repository = cur_repo
g2p.users_group = users_group_model.get_by_groupname(member)
#update current repo
for k, v in form_data.items():
if k == 'user':
cur_repo.user = user_model.get(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'])
raise
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()
new_repo.enable_statistics = False
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:
if p.permission.permission_name.startswith('repository.'):
default = p.permission.permission_name
break
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)\
.one().permission_id
repo_to_perm.repository = new_repo
repo_to_perm.user_id = UserModel(self.sa)\
.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'])
self.__create_repo(repo_name, form_data['repo_type'],
form_data['clone_uri'])
#now automatically start following this repository as owner
ScmModel(self.sa).toggle_following_repo(new_repo.repo_id,
cur_user.user_id)
def create_fork(self, form_data, cur_user):
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.repository \
== self.get_by_repo_name(repo_name))\
.filter(RepoToPerm.user_id == form_data['user_id']).delete()
def delete_perm_users_group(self, form_data, repo_name):
self.sa.query(UsersGroupToPerm)\
.filter(UsersGroupToPerm.repository \
.filter(UsersGroupToPerm.users_group_id \
== form_data['users_group_id']).delete()
def delete_stats(self, repo_name):
self.sa.query(Statistics)\
.filter(Statistics.repository == \
self.get_by_repo_name(repo_name)).delete()
def __create_repo(self, repo_name, alias):
def __create_repo(self, repo_name, alias, clone_uri=False):
makes repository on filesystem
:param alias:
from rhodecode.lib.utils import check_repo
repo_path = os.path.join(self.repos_path, repo_name)
if check_repo(repo_name, self.repos_path):
log.info('creating repo %s in %s', repo_name, repo_path)
log.info('creating repo %s in %s @ %s', repo_name, repo_path,
clone_uri)
backend = get_backend(alias)
backend(repo_path, create=True)
backend(repo_path, create=True, src_url=clone_uri)
def __rename_repo(self, old, new):
renames repository on filesystem
:param old: old name
:param new: new name
log.info('renaming repo from %s to %s', old, new)
old_path = os.path.join(self.repos_path, old)
new_path = os.path.join(self.repos_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):
removes repo from filesystem, the removal is acctually made by
added rm__ prefix into dir, and rename internat .hg/.git dirs so this
repository is no longer valid for rhodecode, can be undeleted later on
by reverting the renames on this repository
:param repo: repo object
rm_path = os.path.join(self.repos_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(self.repos_path, 'rm__%s__%s' \
% (datetime.today().isoformat(),
repo.repo_name)))
## -*- coding: utf-8 -*-
${h.form(url('repos'))}
<div class="form">
<!-- fields -->
<div class="fields">
<div class="field">
<div class="label">
<label for="repo_name">${_('Name')}:</label>
</div>
<div class="input">
${h.text('repo_name',c.new_repo,class_="small")}
<label for="repo_name">${_('Clone from')}:</label>
<label for="clone_uri">${_('Clone from')}:</label>
${h.text('clone_uri',c.new_repo,class_="small")}
${h.text('clone_uri',class_="small")}
<label for="repo_group">${_('Repository group')}:</label>
${h.select('repo_group','',c.repo_groups,class_="medium")}
<span>${h.link_to(_('add new group'),h.url(''))}</span>
<label for="repo_type">${_('Type')}:</label>
${h.select('repo_type','hg',c.backends,class_="small")}
<div class="label label-textarea">
<label for="description">${_('Description')}:</label>
<div class="textarea text-area editor">
${h.textarea('description',cols=23,rows=5)}
<div class="label label-checkbox">
<label for="private">${_('Private')}:</label>
<div class="checkboxes">
${h.checkbox('private',value="True")}
<div class="buttons">
${h.submit('add','add',class_="ui-button")}
${h.end_form()}
<%inherit file="/base/base.html"/>
<%def name="title()">
${_('Edit repository')} ${c.repo_info.repo_name} - ${c.rhodecode_name}
</%def>
<%def name="breadcrumbs_links()">
${h.link_to(_('Admin'),h.url('admin_home'))}
»
${h.link_to(_('Repositories'),h.url('repos'))}
${_('edit')} » ${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
<%def name="page_nav()">
${self.menu('admin')}
<%def name="main()">
<div class="box box-left">
<!-- box / title -->
<div class="title">
${self.breadcrumbs()}
${h.form(url('repo', repo_name=c.repo_info.repo_name),method='put')}
${h.text('repo_name',class_="medium")}
<label for="clone_uri">${_('Clone uri')}:</label>
${h.select('repo_type','hg',c.backends,class_="medium")}
<label for="enable_statistics">${_('Enable statistics')}:</label>
${h.checkbox('enable_statistics',value="True")}
<label for="enable_downloads">${_('Enable downloads')}:</label>
${h.checkbox('enable_downloads',value="True")}
<label for="user">${_('Owner')}:</label>
<div class="input input-small ac">
<div class="perm_ac">
${h.text('user',class_='yui-ac-input')}
<div id="owner_container"></div>
<label for="input">${_('Permissions')}:</label>
<%include file="repo_edit_perms.html"/>
${h.submit('save','Save',class_="ui-button")}
${h.reset('reset','Reset',class_="ui-button")}
<script type="text/javascript">
YAHOO.util.Event.onDOMReady(function(){
var D = YAHOO.util.Dom;
if(!D.hasClass('perm_new_member_name','error')){
D.setStyle('add_perm_input','display','none');
}
YAHOO.util.Event.addListener('add_perm','click',function(){
D.setStyle('add_perm_input','display','');
D.setStyle('add_perm','opacity','0.6');
D.setStyle('add_perm','cursor','default');
});
</script>
YAHOO.example.FnMultipleFields = function(){
var myUsers = ${c.users_array|n};
var myGroups = ${c.users_groups_array|n};
// Define a custom search function for the DataSource of users
var matchUsers = function(sQuery) {
// Case insensitive matching
var query = sQuery.toLowerCase();
var i=0;
var l=myUsers.length;
var matches = [];
// Match against each name of each contact
for(; i<l; i++) {
contact = myUsers[i];
if((contact.fname.toLowerCase().indexOf(query) > -1) ||
(contact.lname.toLowerCase().indexOf(query) > -1) ||
(contact.nname && (contact.nname.toLowerCase().indexOf(query) > -1))) {
matches[matches.length] = contact;
return matches;
};
// Define a custom search function for the DataSource of usersGroups
var matchGroups = function(sQuery) {
var l=myGroups.length;
matched_group = myGroups[i];
if(matched_group.grname.toLowerCase().indexOf(query) > -1) {
matches[matches.length] = matched_group;
//match all
var matchAll = function(sQuery){
u = matchUsers(sQuery);
g = matchGroups(sQuery);
return u.concat(g);
// DataScheme for members
var memberDS = new YAHOO.util.FunctionDataSource(matchAll);
memberDS.responseSchema = {
fields: ["id", "fname", "lname", "nname", "grname", "grmembers"]
// DataScheme for owner
var ownerDS = new YAHOO.util.FunctionDataSource(matchUsers);
ownerDS.responseSchema = {
fields: ["id", "fname", "lname", "nname"]
// Instantiate AutoComplete for perms
var membersAC = new YAHOO.widget.AutoComplete("perm_new_member_name", "perm_container", memberDS);
membersAC.useShadow = false;
membersAC.resultTypeList = false;
// Instantiate AutoComplete for owner
var ownerAC = new YAHOO.widget.AutoComplete("user", "owner_container", ownerDS);
ownerAC.useShadow = false;
ownerAC.resultTypeList = false;
// Helper highlight function for the formatter
var highlightMatch = function(full, snippet, matchindex) {
return full.substring(0, matchindex) +
"<span class='match'>" +
full.substr(matchindex, snippet.length) +
"</span>" +
full.substring(matchindex + snippet.length);
// Custom formatter to highlight the matching letters
var custom_formatter = function(oResultData, sQuery, sResultMatch) {
if (oResultData.grname != undefined){
var grname = oResultData.grname;
var grmembers = oResultData.grmembers;
var grnameMatchIndex = grname.toLowerCase().indexOf(query);
var grprefix = "${_('Group')}: ";
var grsuffix = " ("+grmembers+" ${_('members')})";
if (grnameMatchIndex > -1){
return grprefix+highlightMatch(grname,query,grnameMatchIndex)+grsuffix;
return grprefix+oResultData.grname+grsuffix;
else if(oResultData.fname != undefined){
var fname = oResultData.fname,
lname = oResultData.lname,
nname = oResultData.nname || "", // Guard against null value
fnameMatchIndex = fname.toLowerCase().indexOf(query),
lnameMatchIndex = lname.toLowerCase().indexOf(query),
nnameMatchIndex = nname.toLowerCase().indexOf(query),
displayfname, displaylname, displaynname;
if(fnameMatchIndex > -1) {
displayfname = highlightMatch(fname, query, fnameMatchIndex);
else {
displayfname = fname;
if(lnameMatchIndex > -1) {
displaylname = highlightMatch(lname, query, lnameMatchIndex);
${c.repo_name} ${_('Summary')} - ${c.rhodecode_name}
${h.link_to(u'Home',h.url('/'))}
${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
${_('summary')}
${self.menu('summary')}
<!-- end box / title -->
<label>${_('Name')}:</label>
<div class="input-short">
%if c.dbrepo.repo_type =='hg':
<img style="margin-bottom:2px" class="icon" title="${_('Mercurial repository')}" alt="${_('Mercurial repository')}" src="${h.url("/images/icons/hgicon.png")}"/>
%endif
%if c.dbrepo.repo_type =='git':
<img style="margin-bottom:2px" class="icon" title="${_('Git repository')}" alt="${_('Git repository')}" src="${h.url("/images/icons/giticon.png")}"/>
%if c.dbrepo.private:
<img style="margin-bottom:2px" class="icon" title="${_('private repository')}" alt="${_('private repository')}" src="${h.url("/images/icons/lock.png")}"/>
%else:
<img style="margin-bottom:2px" class="icon" title="${_('public repository')}" alt="${_('public repository')}" src="${h.url("/images/icons/lock_open.png")}"/>
<span style="font-size: 1.6em;font-weight: bold;vertical-align: baseline;">${c.repo.name}</span>
%if c.rhodecode_user.username != 'default':
%if c.following:
<span id="follow_toggle" class="following" title="${_('Stop following this repository')}"
onclick="javascript:toggleFollowingRepo(this,${c.dbrepo.repo_id},'${str(h.get_token())}')">
</span>
<span id="follow_toggle" class="follow" title="${_('Start following this repository')}"
%endif:
<br/>
%if c.dbrepo.fork:
<span style="margin-top:5px">
<a href="${h.url('summary_home',repo_name=c.dbrepo.fork.repo_name)}">
<img class="icon" alt="${_('public')}"
title="${_('Fork of')} ${c.dbrepo.fork.repo_name}"
src="${h.url("/images/icons/arrow_divide.png")}"/>
${_('Fork of')} ${c.dbrepo.fork.repo_name}
</a>
%if c.dbrepo.clone_uri:
<a href="${h.url(str(c.dbrepo.clone_uri))}">
<img class="icon" alt="${_('remote clone')}"
title="${_('Clone from')} ${c.dbrepo.clone_uri}"
src="${h.url("/images/icons/connect.png")}"/>
${_('Clone from')} ${c.dbrepo.clone_uri}
<label>${_('Description')}:</label>
${c.dbrepo.description}
<label>${_('Contact')}:</label>
<div class="gravatar">
<img alt="gravatar" src="${h.gravatar_url(c.dbrepo.user.email)}"/>
${_('Username')}: ${c.dbrepo.user.username}<br/>
${_('Name')}: ${c.dbrepo.user.name} ${c.dbrepo.user.lastname}<br/>
${_('Email')}: <a href="mailto:${c.dbrepo.user.email}">${c.dbrepo.user.email}</a>
<label>${_('Last change')}:</label>
${h.age(c.repo.last_change)} - ${c.repo.last_change}
${_('by')} ${h.get_changeset_safe(c.repo,'tip').author}
<label>${_('Clone url')}:</label>
<input type="text" id="clone_url" readonly="readonly" value="hg clone ${c.clone_repo_url}" size="70"/>
<label>${_('Trending source files')}:</label>
<div id="lang_stats"></div>
<label>${_('Download')}:</label>
%if len(c.repo.revisions) == 0:
${_('There are no downloads yet')}
%elif c.enable_downloads is False:
${_('Downloads are disabled for this repository')}
%if h.HasPermissionAll('hg.admin')('enable stats on from summary'):
[${h.link_to(_('enable'),h.url('edit_repo',repo_name=c.repo_name))}]
${h.select('download_options',c.repo.get_changeset().raw_id,c.download_options)}
%for cnt,archive in enumerate(c.repo._get_archives()):
%if cnt >=1:
|
<span class="tooltip" title="${_('Download %s as %s') %('tip',archive['type'])}"
id="${archive['type']+'_link'}">${h.link_to(archive['type'],
h.url('files_archive_home',repo_name=c.repo.name,
fname='tip'+archive['extension']),class_="archive_icon")}</span>
%endfor
<label>${_('Feeds')}:</label>
${h.link_to(_('RSS'),h.url('rss_feed_home',repo_name=c.repo.name),class_='rss_icon')}
${h.link_to(_('Atom'),h.url('atom_feed_home',repo_name=c.repo.name),class_='atom_icon')}
YUE.onDOMReady(function(e){
id = 'clone_url';
YUE.on(id,'click',function(e){
YUD.get('clone_url').select();
})
var data = ${c.trending_languages|n};
var total = 0;
var no_data = true;
for (k in data){
total += data[k];
no_data = false;
var tbl = document.createElement('table');
tbl.setAttribute('class','trending_language_tbl');
var cnt =0;
cnt+=1;
var hide = cnt>2;
var tr = document.createElement('tr');
if (hide){
tr.setAttribute('style','display:none');
tr.setAttribute('class','stats_hidden');
var percentage = Math.round((data[k]/total*100),2);
var value = data[k];
var td1 = document.createElement('td');
td1.width=150;
var trending_language_label = document.createElement('div');
trending_language_label.innerHTML = k;
td1.appendChild(trending_language_label);
var td2 = document.createElement('td');
td2.setAttribute('style','padding-right:14px !important');
var trending_language = document.createElement('div');
var nr_files = value+" ${_('files')}";
trending_language.title = k+" "+nr_files;
if (percentage>20){
trending_language.innerHTML = "<b style='font-size:0.8em'>"+percentage+"% "+nr_files+ "</b>";
else{
trending_language.innerHTML = "<b style='font-size:0.8em'>"+percentage+"%</b>";
trending_language.setAttribute("class", 'trending_language top-right-rounded-corner bottom-right-rounded-corner');
trending_language.style.width=percentage+"%";
td2.appendChild(trending_language);
tr.appendChild(td1);
tr.appendChild(td2);
tbl.appendChild(tr);
if(cnt == 2){
var show_more = document.createElement('tr');
var td=document.createElement('td');
lnk = document.createElement('a');
lnk.href='#';
lnk.innerHTML = "${_("show more")}";
lnk.id='code_stats_show_more';
td.appendChild(lnk);
show_more.appendChild(td);
show_more.appendChild(document.createElement('td'));
tbl.appendChild(show_more);
if(no_data){
td1.innerHTML = "${c.no_data_msg}";
YUD.get('lang_stats').appendChild(tbl);
YUE.on('code_stats_show_more','click',function(){
l = YUD.getElementsByClassName('stats_hidden')
for (e in l){
YUD.setStyle(l[e],'display','');
YUD.setStyle(YUD.get('code_stats_show_more'),
'display','none');
YUE.on('download_options','change',function(e){
var new_cs = e.target.options[e.target.selectedIndex];
var tmpl_links = {}
tmpl_links['${archive['type']}'] = '${h.link_to(archive['type'],
fname='__CS__'+archive['extension']),class_="archive_icon")}';
for(k in tmpl_links){
Status change: