@@ -262,6 +262,10 @@ def make_map(config):
rmap.resource('permission', 'permissions',
controller='admin/permissions', path_prefix=ADMIN_PREFIX)
#ADMIN DEFAULTS REST ROUTES
rmap.resource('default', 'defaults',
controller='admin/defaults', path_prefix=ADMIN_PREFIX)
##ADMIN LDAP SETTINGS
rmap.connect('ldap_settings', '%s/ldap' % ADMIN_PREFIX,
controller='admin/ldap_settings', action='ldap_settings',
new file 100644
# -*- coding: utf-8 -*-
"""
rhodecode.controllers.admin.defaults
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
default settings controller for Rhodecode
:created_on: Apr 27, 2010
:author: marcink
:copyright: (C) 2010-2012 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 logging
import traceback
import formencode
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 DefaultsForm
from rhodecode.model.meta import Session
from rhodecode import BACKENDS
from rhodecode.model.db import RhodeCodeSetting
log = logging.getLogger(__name__)
class DefaultsController(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('default', 'defaults')
@LoginRequired()
@HasPermissionAllDecorator('hg.admin')
def __before__(self):
super(DefaultsController, self).__before__()
def index(self, format='html'):
"""GET /defaults: All items in the collection"""
# url('defaults')
c.backends = BACKENDS.keys()
defaults = RhodeCodeSetting.get_default_repo_settings()
return htmlfill.render(
render('admin/defaults/defaults.html'),
defaults=defaults,
encoding="UTF-8",
force_defaults=False
)
def create(self):
"""POST /defaults: Create a new item"""
def new(self, format='html'):
"""GET /defaults/new: Form to create a new item"""
# url('new_default')
def update(self, id):
"""PUT /defaults/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('default', id=ID),
# method='put')
# url('default', id=ID)
_form = DefaultsForm()()
try:
form_result = _form.to_python(dict(request.POST))
for k, v in form_result.iteritems():
setting = RhodeCodeSetting.get_by_name_or_create(k)
setting.app_settings_value = v
Session().add(setting)
Session().commit()
h.flash(_('Default settings updated successfully'),
category='success')
except formencode.Invalid, errors:
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 update of defaults'),
category='error')
return redirect(url('defaults'))
def delete(self, id):
"""DELETE /defaults/id: Delete an existing item"""
# <input type="hidden" name="_method" value="DELETE" />
# method='delete')
def show(self, id, format='html'):
"""GET /defaults/id: Show a specific item"""
def edit(self, id, format='html'):
"""GET /defaults/id/edit: Form to edit an existing item"""
# url('edit_default', id=ID)
@@ -42,7 +42,8 @@ from rhodecode.lib.base import BaseContr
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, RepoGroup
from rhodecode.model.db import User, Repository, UserFollowing, RepoGroup,\
RhodeCodeSetting
from rhodecode.model.forms import RepoForm
from rhodecode.model.scm import ScmModel
from rhodecode.model.repo import RepoModel
@@ -481,7 +481,15 @@ class SettingsController(BaseController)
new_repo = request.GET.get('repo', '')
c.new_repo = repo_name_slug(new_repo)
return render('admin/repos/repo_add_create_repository.html')
## apply the defaults from defaults page
defaults = RhodeCodeSetting.get_default_repo_settings(strip_prefix=True)
render('admin/repos/repo_add_create_repository.html'),
errors={},
encoding="UTF-8"
def _get_hg_ui_settings(self):
ret = RhodeCodeUi.query().all()
@@ -273,6 +273,7 @@ class DbManage(object):
def step_8(self):
self.klass.populate_default_permissions()
self.klass.create_default_options(skip_existing=True)
upgrade_steps = [0] + range(curr_version + 1, __dbversion__ + 1)
@@ -482,6 +483,22 @@ class DbManage(object):
setting = RhodeCodeSetting(k, v)
self.sa.add(setting)
def create_default_options(self, skip_existing=False):
"""Creates default settings"""
for k, v in [
('default_repo_enable_locking', False),
('default_repo_enable_downloads', False),
('default_repo_enable_statistics', False),
('default_repo_private', False),
('default_repo_type', 'hg')]:
if skip_existing and RhodeCodeSetting.get_by_name(k) != None:
log.debug('Skipping option %s' % k)
continue
def fixup_groups(self):
def_usr = User.get_by_username('default')
for g in RepoGroup.query().all():
@@ -622,6 +639,7 @@ class DbManage(object):
self.sa.add(sett6)
self.create_ldap_options()
self.create_default_options()
log.info('created ui config')
@@ -23,6 +23,7 @@ def upgrade(migrate_engine):
pass
def downgrade(migrate_engine):
meta = MetaData()
meta.bind = migrate_engine
@@ -287,7 +287,7 @@ def remove_suffix(s, suffix):
def remove_prefix(s, prefix):
if s.startswith(prefix):
s = s[:-1 * len(prefix)]
s = s[len(prefix):]
return s
@@ -46,7 +46,7 @@ from rhodecode.lib.vcs.exceptions import
from rhodecode.lib.vcs.utils.lazy import LazyProperty
from rhodecode.lib.utils2 import str2bool, safe_str, get_changeset_safe, \
safe_unicode, remove_suffix
safe_unicode, remove_suffix, remove_prefix
from rhodecode.lib.compat import json
from rhodecode.lib.caching_query import FromCache
@@ -167,7 +167,11 @@ class RhodeCodeSetting(Base, BaseModel):
@hybrid_property
def app_settings_value(self):
v = self._app_settings_value
if self.app_settings_name == 'ldap_active':
if self.app_settings_name in ["ldap_active",
"default_repo_enable_statistics",
"default_repo_enable_locking",
"default_repo_private",
"default_repo_enable_downloads"]:
v = str2bool(v)
return v
@@ -225,6 +229,19 @@ class RhodeCodeSetting(Base, BaseModel):
return fd
@classmethod
def get_default_repo_settings(cls, cache=False, strip_prefix=False):
ret = cls.query()\
.filter(cls.app_settings_name.startswith('default_')).all()
fd = {}
for row in ret:
key = row.app_settings_name
if strip_prefix:
key = remove_prefix(key, prefix='default_')
fd.update({key: row.app_settings_value})
class RhodeCodeUi(Base, BaseModel):
__tablename__ = 'rhodecode_ui'
@@ -173,19 +173,19 @@ def RepoForm(edit=False, old_data={}, su
repo_groups=[], landing_revs=[]):
class _RepoForm(formencode.Schema):
allow_extra_fields = True
filter_extra_fields = False
filter_extra_fields = True
repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
v.SlugifyName())
clone_uri = All(v.UnicodeString(strip=True, min=1, not_empty=False))
repo_group = All(v.CanWriteGroup(),
v.OneOf(repo_groups, hideList=True))
repo_type = v.OneOf(supported_backends)
description = v.UnicodeString(strip=True, min=1, not_empty=False)
private = v.StringBoolean(if_missing=False)
enable_statistics = v.StringBoolean(if_missing=False)
enable_downloads = v.StringBoolean(if_missing=False)
enable_locking = v.StringBoolean(if_missing=False)
landing_rev = v.OneOf(landing_revs, hideList=True)
repo_description = v.UnicodeString(strip=True, min=1, not_empty=False)
repo_private = v.StringBoolean(if_missing=False)
repo_enable_statistics = v.StringBoolean(if_missing=False)
repo_enable_downloads = v.StringBoolean(if_missing=False)
repo_enable_locking = v.StringBoolean(if_missing=False)
repo_landing_rev = v.OneOf(landing_revs, hideList=True)
if edit:
#this is repo owner
@@ -218,9 +218,8 @@ def RepoForkForm(edit=False, old_data={}
return _RepoForkForm
def RepoSettingsForm(edit=False, old_data={},
supported_backends=BACKENDS.keys(), repo_groups=[],
landing_revs=[]):
def RepoSettingsForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
@@ -282,8 +281,8 @@ def ApplicationUiSettingsForm():
return _ApplicationUiSettingsForm
def DefaultPermissionsForm(repo_perms_choices, group_perms_choices, register_choices, create_choices,
fork_choices):
def DefaultPermissionsForm(repo_perms_choices, group_perms_choices,
register_choices, create_choices, fork_choices):
class _DefaultPermissionsForm(formencode.Schema):
@@ -299,6 +298,19 @@ def DefaultPermissionsForm(repo_perms_ch
return _DefaultPermissionsForm
def DefaultsForm(edit=False, old_data={}, supported_backends=BACKENDS.keys()):
class _DefaultsForm(formencode.Schema):
default_repo_type = v.OneOf(supported_backends)
default_repo_private = v.StringBoolean(if_missing=False)
default_repo_enable_statistics = v.StringBoolean(if_missing=False)
default_repo_enable_downloads = v.StringBoolean(if_missing=False)
default_repo_enable_locking = v.StringBoolean(if_missing=False)
return _DefaultsForm
def LdapSettingsForm(tls_reqcert_choices, search_scope_choices,
tls_kind_choices):
class _LdapSettingsForm(formencode.Schema):
@@ -37,7 +37,8 @@ from rhodecode.lib.hooks import log_crea
from rhodecode.model import BaseModel
from rhodecode.model.db import Repository, UserRepoToPerm, User, Permission, \
Statistics, UsersGroup, UsersGroupRepoToPerm, RhodeCodeUi, RepoGroup
Statistics, UsersGroup, UsersGroupRepoToPerm, RhodeCodeUi, RepoGroup,\
@@ -205,7 +206,8 @@ class RepoModel(BaseModel):
def create_repo(self, repo_name, repo_type, description, owner,
private=False, clone_uri=None, repos_group=None,
landing_rev='tip', just_db=False, fork_of=None,
copy_fork_permissions=False):
copy_fork_permissions=False, enable_statistics=False,
enable_locking=False, enable_downloads=False):
Create repository
@@ -234,6 +236,10 @@ class RepoModel(BaseModel):
new_repo.clone_uri = clone_uri
new_repo.landing_rev = landing_rev
new_repo.enable_statistics = enable_statistics
new_repo.enable_locking = enable_locking
new_repo.enable_downloads = enable_downloads
if repos_group:
new_repo.enable_locking = repos_group.enable_locking
@@ -307,20 +313,27 @@ class RepoModel(BaseModel):
:param just_db:
:param fork:
owner = cur_user
repo_name = form_data['repo_name_full']
repo_type = form_data['repo_type']
description = form_data['description']
private = form_data['private']
description = form_data['repo_description']
private = form_data['repo_private']
clone_uri = form_data.get('clone_uri')
repos_group = form_data['repo_group']
landing_rev = form_data['landing_rev']
landing_rev = form_data['repo_landing_rev']
copy_fork_permissions = form_data.get('copy_permissions')
fork_of = form_data.get('fork_parent_id')
##defaults
defs = RhodeCodeSetting.get_default_repo_settings(strip_prefix=True)
enable_statistics = defs.get('repo_enable_statistic')
enable_locking = defs.get('repo_enable_locking')
enable_downloads = defs.get('repo_enable_downloads')
return self.create_repo(
repo_name, repo_type, description, owner, private, clone_uri,
repos_group, landing_rev, just_db, fork_of, copy_fork_permissions
repos_group, landing_rev, just_db, fork_of, copy_fork_permissions,
enable_statistics, enable_locking, enable_downloads
def create_fork(self, form_data, cur_user):
@@ -626,6 +626,14 @@ div:hover > a.permalink {
padding: 12px 9px 7px 24px;
}
#header #header-inner #quick li ul li a.defaults,#header #header-inner #quick li ul li a.defaults:hover
{
background: #FFF url("../images/icons/wrench.png") no-repeat 4px 9px;
width: 167px;
margin: 0;
#header #header-inner #quick li ul li a.settings,#header #header-inner #quick li ul li a.settings:hover
background: #FFF url("../images/icons/cog.png") no-repeat 4px 9px;
## -*- coding: utf-8 -*-
<%inherit file="/base/base.html"/>
<%def name="title()">
${_('Repositories defaults')} - ${c.rhodecode_name}
</%def>
<%def name="breadcrumbs_links()">
${h.link_to(_('Admin'),h.url('admin_home'))}
»
${_('Defaults')}
<%def name="page_nav()">
${self.menu('admin')}
<%def name="main()">
<div class="box">
<!-- box / title -->
<div class="title">
${self.breadcrumbs()}
</div>
<h3>${_('Repositories defaults')}</h3>
${h.form(url('default', id='defaults'),method='put')}
<div class="form">
<!-- fields -->
<div class="fields">
<div class="field">
<div class="label">
<label for="default_repo_type">${_('Type')}:</label>
<div class="input">
${h.select('default_repo_type','hg',c.backends,class_="medium")}
<div class="label label-checkbox">
<label for="default_repo_private">${_('Private repository')}:</label>
<div class="checkboxes">
${h.checkbox('default_repo_private',value="True")}
<span class="help-block">${_('Private repositories are only visible to people explicitly added as collaborators.')}</span>
<label for="default_repo_enable_statistics">${_('Enable statistics')}:</label>
${h.checkbox('default_repo_enable_statistics',value="True")}
<span class="help-block">${_('Enable statistics window on summary page.')}</span>
<label for="default_repo_enable_downloads">${_('Enable downloads')}:</label>
${h.checkbox('default_repo_enable_downloads',value="True")}
<span class="help-block">${_('Enable download menu on summary page.')}</span>
<label for="default_repo_enable_locking">${_('Enable locking')}:</label>
${h.checkbox('default_repo_enable_locking',value="True")}
<span class="help-block">${_('Enable lock-by-pulling on repository.')}</span>
<div class="buttons">
${h.submit('save',_('Save'),class_="ui-btn large")}
${h.end_form()}
##<h3>${_('Groups defaults')}</h3>
@@ -12,7 +12,7 @@ ${h.form(url('repos'))}
${h.text('repo_name',c.new_repo,class_="small")}
%if not h.HasPermissionAll('hg.admin')('repo create form'):
${h.hidden('user_created',True)}
%endif
@@ -44,28 +44,28 @@ ${h.form(url('repos'))}
<label for="landing_rev">${_('Landing revision')}:</label>
<label for="repo_landing_rev">${_('Landing revision')}:</label>
${h.select('landing_rev','',c.landing_revs,class_="medium")}
${h.select('repo_landing_rev','',c.landing_revs,class_="medium")}
<span class="help-block">${_('Default revision for files page, downloads, whoosh and readme')}</span>
<div class="label label-textarea">
<label for="description">${_('Description')}:</label>
<label for="repo_description">${_('Description')}:</label>
<div class="textarea text-area editor">
${h.textarea('description')}
${h.textarea('repo_description')}
<span class="help-block">${_('Keep it short and to the point. Use a README file for longer descriptions.')}</span>
<label for="private">${_('Private repository')}:</label>
<label for="repo_private">${_('Private repository')}:</label>
${h.checkbox('private',value="True")}
${h.checkbox('repo_private',value="True")}
@@ -236,6 +236,7 @@
<li>${h.link_to(_('users groups'),h.url('users_groups'),class_='groups')}</li>
<li>${h.link_to(_('permissions'),h.url('edit_permission',id='default'),class_='permissions')}</li>
<li>${h.link_to(_('ldap'),h.url('ldap_home'),class_='ldap')}</li>
<li>${h.link_to(_('defaults'),h.url('defaults'),class_='defaults')}</li>
<li class="last">${h.link_to(_('settings'),h.url('admin_settings'),class_='settings')}</li>
</ul>
@@ -47,7 +47,7 @@ __all__ = [
'TEST_USER_REGULAR2_PASS', 'TEST_USER_REGULAR2_EMAIL', 'TEST_HG_REPO',
'TEST_HG_REPO_CLONE', 'TEST_HG_REPO_PULL', 'TEST_GIT_REPO',
'TEST_GIT_REPO_CLONE', 'TEST_GIT_REPO_PULL', 'HG_REMOTE_REPO',
'GIT_REMOTE_REPO', 'SCM_TESTS',
'GIT_REMOTE_REPO', 'SCM_TESTS', '_get_repo_create_params'
]
# Invoke websetup with the current config file
@@ -163,3 +163,24 @@ class TestController(TestCase):
'msg `%s` not found in session flash: got `%s` instead' % (
msg, response.session['flash'])
## HELPERS ##
def _get_repo_create_params(**custom):
defs = {
'repo_name': None,
'repo_type': 'hg',
'clone_uri': '',
'repo_group': '',
'repo_description': 'DESC',
'repo_private': False,
'repo_landing_rev': 'tip'
defs.update(custom)
if 'repo_name_full' not in custom:
defs.update({'repo_name_full': defs['repo_name']})
return defs
@@ -61,15 +61,10 @@ def destroy_users_group(name=TEST_USERS_
def create_repo(repo_name, repo_type):
# create new repo
form_data = dict(repo_name=repo_name,
repo_name_full=repo_name,
fork_name=None,
description='description %s' % repo_name,
repo_group=None,
private=False,
repo_type=repo_type,
clone_uri=None,
landing_rev='tip')
form_data = _get_repo_create_params(
repo_description='description %s' % repo_name,
cur_user = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
r = RepoModel().create(form_data, cur_user)
from rhodecode.tests import *
class TestDefaultsController(TestController):
def test_index(self):
self.log_user()
response = self.app.get(url('defaults'))
response.mustcontain('default_repo_private')
response.mustcontain('default_repo_enable_statistics')
response.mustcontain('default_repo_enable_downloads')
response.mustcontain('default_repo_enable_locking')
def test_index_as_xml(self):
response = self.app.get(url('formatted_defaults', format='xml'))
def test_create(self):
response = self.app.post(url('defaults'))
def test_new(self):
response = self.app.get(url('new_default'))
def test_new_as_xml(self):
response = self.app.get(url('formatted_new_default', format='xml'))
def test_update(self):
params = {
'default_repo_enable_locking': True,
'default_repo_enable_downloads': True,
'default_repo_enable_statistics': True,
'default_repo_private': True,
'default_repo_type': 'hg',
response = self.app.put(url('default', id='default'), params=params)
self.checkSessionFlash(response, 'Default settings updated successfully')
defs = RhodeCodeSetting.get_default_repo_settings()
self.assertEqual(params, defs)
'default_repo_enable_locking': False,
'default_repo_enable_downloads': False,
'default_repo_enable_statistics': False,
'default_repo_private': False,
'default_repo_type': 'git',
def test_update_browser_fakeout(self):
response = self.app.post(url('default', id=1), params=dict(_method='put'))
def test_delete(self):
response = self.app.delete(url('default', id=1))
def test_delete_browser_fakeout(self):
response = self.app.post(url('default', id=1), params=dict(_method='delete'))
def test_show(self):
response = self.app.get(url('default', id=1))
def test_show_as_xml(self):
response = self.app.get(url('formatted_default', id=1, format='xml'))
def test_edit(self):
response = self.app.get(url('edit_default', id=1))
def test_edit_as_xml(self):
response = self.app.get(url('formatted_edit_default', id=1, format='xml'))
@@ -26,14 +26,10 @@ class TestAdminReposController(TestContr
repo_name = NEW_HG_REPO
description = 'description for newly created repo'
private = False
response = self.app.post(url('repos'), {'repo_name': repo_name,
'description': description,
'private': private,
'landing_rev': 'tip'})
response = self.app.post(url('repos'),
_get_repo_create_params(repo_private=False,
repo_name=repo_name,
repo_description=description))
self.checkSessionFlash(response,
'created repository %s' % (repo_name))
@@ -63,13 +59,10 @@ class TestAdminReposController(TestContr
description = 'description for newly created repo' + non_ascii
description_unicode = description.decode('utf8')
'created repository %s' % (repo_name_unicode))
@@ -103,14 +96,12 @@ class TestAdminReposController(TestContr
repo_name = 'ingroup'
repo_name_full = RepoGroup.url_sep().join([group_name, repo_name])
'repo_group': gr.group_id,
repo_description=description,
repo_group=gr.group_id,))
@@ -142,14 +133,12 @@ class TestAdminReposController(TestContr
repo_name = NEW_GIT_REPO
'repo_type': 'git',
repo_type='git',
@@ -179,13 +168,12 @@ class TestAdminReposController(TestContr
@@ -226,13 +214,12 @@ class TestAdminReposController(TestContr
repo_name = 'vcs_test_new_to_delete'
repo_type='hg',
@@ -275,13 +262,12 @@ class TestAdminReposController(TestContr
from rhodecode.model.db import Repository
ARCHIVE_SPECS = {
'.tar.bz2': ('application/x-bzip2', 'tbz2', ''),
@@ -7,6 +9,13 @@ ARCHIVE_SPECS = {
def _set_downloads(repo_name, set_to):
repo = Repository.get_by_repo_name(repo_name)
repo.enable_downloads = set_to
Session().add(repo)
class TestFilesController(TestController):
@@ -216,7 +225,7 @@ removed extra unicode conversion in diff
def test_archival(self):
_set_downloads(HG_REPO, set_to=True)
for arch_ext, info in ARCHIVE_SPECS.items():
short = '27cd5cce30c9%s' % arch_ext
fname = '27cd5cce30c96924232dffcd24178a07ffeb5dfc%s' % arch_ext
@@ -237,7 +246,7 @@ removed extra unicode conversion in diff
def test_archival_wrong_ext(self):
for arch_ext in ['tar', 'rar', 'x', '..ax', '.zipz']:
@@ -249,7 +258,7 @@ removed extra unicode conversion in diff
def test_archival_wrong_revision(self):
for rev in ['00x000000', 'tar', 'wrong', '@##$@$42413232', '232dffcd']:
fname = '%s.zip' % rev
@@ -4,7 +4,7 @@ from rhodecode.tests import *
from rhodecode.model.repos_group import ReposGroupModel
from rhodecode.model.db import RepoGroup, User, Repository
from rhodecode.model.db import RepoGroup, User
from sqlalchemy.exc import IntegrityError
@@ -125,17 +125,7 @@ class TestReposGroups(unittest.TestCase)
g2 = _make_group('g2')
form_data = dict(repo_name='john',
repo_name_full='john',
description=None,
landing_rev='tip',
enable_locking=False,
recursive=False)
form_data = _get_repo_create_params(repo_name='john')
cur_user = User.get_by_username(TEST_USER_ADMIN_LOGIN)
Status change: