repositories: rewrote whole admin section to pyramid.

- fixed few small found problems
- code cleanups
This commit is contained in:
Marcin Kuzminski 2017-08-11 13:00:47 +02:00
parent fadfe2a810
commit 928129ed6c
53 changed files with 1557 additions and 1604 deletions

View file

@ -205,6 +205,19 @@ def admin_routes(config):
name='edit_user_group_perms_summary_json',
pattern='/user_groups/{user_group_id:\d+}/edit/permissions_summary/json')
# repos admin
config.add_route(
name='repos',
pattern='/repos')
config.add_route(
name='repo_new',
pattern='/repos/new')
config.add_route(
name='repo_create',
pattern='/repos/create')
def includeme(config):
settings = config.get_settings()

View file

@ -0,0 +1,509 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2010-2017 RhodeCode GmbH
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License, version 3
# (only), as published by the Free Software Foundation.
#
# 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 Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# This program is dual-licensed. If you wish to learn more about the
# RhodeCode Enterprise Edition, including its added features, Support services,
# and proprietary license terms, please see https://rhodecode.com/licenses/
import urllib
import mock
import pytest
from rhodecode.apps._base import ADMIN_PREFIX
from rhodecode.lib import auth
from rhodecode.lib.utils2 import safe_str
from rhodecode.lib import helpers as h
from rhodecode.model.db import (
Repository, RepoGroup, UserRepoToPerm, User, Permission)
from rhodecode.model.meta import Session
from rhodecode.model.repo import RepoModel
from rhodecode.model.repo_group import RepoGroupModel
from rhodecode.model.user import UserModel
from rhodecode.tests import (
login_user_session, assert_session_flash, TEST_USER_ADMIN_LOGIN,
TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
from rhodecode.tests.fixture import Fixture, error_function
from rhodecode.tests.utils import AssertResponse, repo_on_filesystem
fixture = Fixture()
def route_path(name, params=None, **kwargs):
import urllib
base_url = {
'repos': ADMIN_PREFIX + '/repos',
'repo_new': ADMIN_PREFIX + '/repos/new',
'repo_create': ADMIN_PREFIX + '/repos/create',
'repo_creating_check': '/{repo_name}/repo_creating_check',
}[name].format(**kwargs)
if params:
base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
return base_url
def _get_permission_for_user(user, repo):
perm = UserRepoToPerm.query()\
.filter(UserRepoToPerm.repository ==
Repository.get_by_repo_name(repo))\
.filter(UserRepoToPerm.user == User.get_by_username(user))\
.all()
return perm
@pytest.mark.usefixtures("app")
class TestAdminRepos(object):
def test_repo_list(self, autologin_user, user_util):
repo = user_util.create_repo()
response = self.app.get(
route_path('repos'), status=200)
response.mustcontain(repo.repo_name)
def test_create_page_restricted_to_single_backend(self, autologin_user, backend):
with mock.patch('rhodecode.BACKENDS', {'git': 'git'}):
response = self.app.get(route_path('repo_new'), status=200)
assert_response = AssertResponse(response)
element = assert_response.get_element('#repo_type')
assert element.text_content() == '\ngit\n'
def test_create_page_non_restricted_backends(self, autologin_user, backend):
response = self.app.get(route_path('repo_new'), status=200)
assert_response = AssertResponse(response)
assert_response.element_contains('#repo_type', 'git')
assert_response.element_contains('#repo_type', 'svn')
assert_response.element_contains('#repo_type', 'hg')
@pytest.mark.parametrize(
"suffix", [u'', u'xxa'], ids=['', 'non-ascii'])
def test_create(self, autologin_user, backend, suffix, csrf_token):
repo_name_unicode = backend.new_repo_name(suffix=suffix)
repo_name = repo_name_unicode.encode('utf8')
description_unicode = u'description for newly created repo' + suffix
description = description_unicode.encode('utf8')
response = self.app.post(
route_path('repo_create'),
fixture._get_repo_create_params(
repo_private=False,
repo_name=repo_name,
repo_type=backend.alias,
repo_description=description,
csrf_token=csrf_token),
status=302)
self.assert_repository_is_created_correctly(
repo_name, description, backend)
def test_create_numeric_name(self, autologin_user, backend, csrf_token):
numeric_repo = '1234'
repo_name = numeric_repo
description = 'description for newly created repo' + numeric_repo
self.app.post(
route_path('repo_create'),
fixture._get_repo_create_params(
repo_private=False,
repo_name=repo_name,
repo_type=backend.alias,
repo_description=description,
csrf_token=csrf_token))
self.assert_repository_is_created_correctly(
repo_name, description, backend)
@pytest.mark.parametrize("suffix", [u'', u'ąćę'], ids=['', 'non-ascii'])
def test_create_in_group(
self, autologin_user, backend, suffix, csrf_token):
# create GROUP
group_name = 'sometest_%s' % backend.alias
gr = RepoGroupModel().create(group_name=group_name,
group_description='test',
owner=TEST_USER_ADMIN_LOGIN)
Session().commit()
repo_name = u'ingroup' + suffix
repo_name_full = RepoGroup.url_sep().join(
[group_name, repo_name])
description = u'description for newly created repo'
self.app.post(
route_path('repo_create'),
fixture._get_repo_create_params(
repo_private=False,
repo_name=safe_str(repo_name),
repo_type=backend.alias,
repo_description=description,
repo_group=gr.group_id,
csrf_token=csrf_token))
# TODO: johbo: Cleanup work to fixture
try:
self.assert_repository_is_created_correctly(
repo_name_full, description, backend)
new_repo = RepoModel().get_by_repo_name(repo_name_full)
inherited_perms = UserRepoToPerm.query().filter(
UserRepoToPerm.repository_id == new_repo.repo_id).all()
assert len(inherited_perms) == 1
finally:
RepoModel().delete(repo_name_full)
RepoGroupModel().delete(group_name)
Session().commit()
def test_create_in_group_numeric_name(
self, autologin_user, backend, csrf_token):
# create GROUP
group_name = 'sometest_%s' % backend.alias
gr = RepoGroupModel().create(group_name=group_name,
group_description='test',
owner=TEST_USER_ADMIN_LOGIN)
Session().commit()
repo_name = '12345'
repo_name_full = RepoGroup.url_sep().join([group_name, repo_name])
description = 'description for newly created repo'
self.app.post(
route_path('repo_create'),
fixture._get_repo_create_params(
repo_private=False,
repo_name=repo_name,
repo_type=backend.alias,
repo_description=description,
repo_group=gr.group_id,
csrf_token=csrf_token))
# TODO: johbo: Cleanup work to fixture
try:
self.assert_repository_is_created_correctly(
repo_name_full, description, backend)
new_repo = RepoModel().get_by_repo_name(repo_name_full)
inherited_perms = UserRepoToPerm.query()\
.filter(UserRepoToPerm.repository_id == new_repo.repo_id).all()
assert len(inherited_perms) == 1
finally:
RepoModel().delete(repo_name_full)
RepoGroupModel().delete(group_name)
Session().commit()
def test_create_in_group_without_needed_permissions(self, backend):
session = login_user_session(
self.app, TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
csrf_token = auth.get_csrf_token(session)
# revoke
user_model = UserModel()
# disable fork and create on default user
user_model.revoke_perm(User.DEFAULT_USER, 'hg.create.repository')
user_model.grant_perm(User.DEFAULT_USER, 'hg.create.none')
user_model.revoke_perm(User.DEFAULT_USER, 'hg.fork.repository')
user_model.grant_perm(User.DEFAULT_USER, 'hg.fork.none')
# disable on regular user
user_model.revoke_perm(TEST_USER_REGULAR_LOGIN, 'hg.create.repository')
user_model.grant_perm(TEST_USER_REGULAR_LOGIN, 'hg.create.none')
user_model.revoke_perm(TEST_USER_REGULAR_LOGIN, 'hg.fork.repository')
user_model.grant_perm(TEST_USER_REGULAR_LOGIN, 'hg.fork.none')
Session().commit()
# create GROUP
group_name = 'reg_sometest_%s' % backend.alias
gr = RepoGroupModel().create(group_name=group_name,
group_description='test',
owner=TEST_USER_ADMIN_LOGIN)
Session().commit()
group_name_allowed = 'reg_sometest_allowed_%s' % backend.alias
gr_allowed = RepoGroupModel().create(
group_name=group_name_allowed,
group_description='test',
owner=TEST_USER_REGULAR_LOGIN)
Session().commit()
repo_name = 'ingroup'
description = 'description for newly created repo'
response = self.app.post(
route_path('repo_create'),
fixture._get_repo_create_params(
repo_private=False,
repo_name=repo_name,
repo_type=backend.alias,
repo_description=description,
repo_group=gr.group_id,
csrf_token=csrf_token))
response.mustcontain('Invalid value')
# user is allowed to create in this group
repo_name = 'ingroup'
repo_name_full = RepoGroup.url_sep().join(
[group_name_allowed, repo_name])
description = 'description for newly created repo'
response = self.app.post(
route_path('repo_create'),
fixture._get_repo_create_params(
repo_private=False,
repo_name=repo_name,
repo_type=backend.alias,
repo_description=description,
repo_group=gr_allowed.group_id,
csrf_token=csrf_token))
# TODO: johbo: Cleanup in pytest fixture
try:
self.assert_repository_is_created_correctly(
repo_name_full, description, backend)
new_repo = RepoModel().get_by_repo_name(repo_name_full)
inherited_perms = UserRepoToPerm.query().filter(
UserRepoToPerm.repository_id == new_repo.repo_id).all()
assert len(inherited_perms) == 1
assert repo_on_filesystem(repo_name_full)
finally:
RepoModel().delete(repo_name_full)
RepoGroupModel().delete(group_name)
RepoGroupModel().delete(group_name_allowed)
Session().commit()
def test_create_in_group_inherit_permissions(self, autologin_user, backend,
csrf_token):
# create GROUP
group_name = 'sometest_%s' % backend.alias
gr = RepoGroupModel().create(group_name=group_name,
group_description='test',
owner=TEST_USER_ADMIN_LOGIN)
perm = Permission.get_by_key('repository.write')
RepoGroupModel().grant_user_permission(
gr, TEST_USER_REGULAR_LOGIN, perm)
# add repo permissions
Session().commit()
repo_name = 'ingroup_inherited_%s' % backend.alias
repo_name_full = RepoGroup.url_sep().join([group_name, repo_name])
description = 'description for newly created repo'
self.app.post(
route_path('repo_create'),
fixture._get_repo_create_params(
repo_private=False,
repo_name=repo_name,
repo_type=backend.alias,
repo_description=description,
repo_group=gr.group_id,
repo_copy_permissions=True,
csrf_token=csrf_token))
# TODO: johbo: Cleanup to pytest fixture
try:
self.assert_repository_is_created_correctly(
repo_name_full, description, backend)
except Exception:
RepoGroupModel().delete(group_name)
Session().commit()
raise
# check if inherited permissions are applied
new_repo = RepoModel().get_by_repo_name(repo_name_full)
inherited_perms = UserRepoToPerm.query().filter(
UserRepoToPerm.repository_id == new_repo.repo_id).all()
assert len(inherited_perms) == 2
assert TEST_USER_REGULAR_LOGIN in [
x.user.username for x in inherited_perms]
assert 'repository.write' in [
x.permission.permission_name for x in inherited_perms]
RepoModel().delete(repo_name_full)
RepoGroupModel().delete(group_name)
Session().commit()
@pytest.mark.xfail_backends(
"git", "hg", reason="Missing reposerver support")
def test_create_with_clone_uri(self, autologin_user, backend, reposerver,
csrf_token):
source_repo = backend.create_repo(number_of_commits=2)
source_repo_name = source_repo.repo_name
reposerver.serve(source_repo.scm_instance())
repo_name = backend.new_repo_name()
response = self.app.post(
route_path('repo_create'),
fixture._get_repo_create_params(
repo_private=False,
repo_name=repo_name,
repo_type=backend.alias,
repo_description='',
clone_uri=reposerver.url,
csrf_token=csrf_token),
status=302)
# Should be redirected to the creating page
response.mustcontain('repo_creating')
# Expecting that both repositories have same history
source_repo = RepoModel().get_by_repo_name(source_repo_name)
source_vcs = source_repo.scm_instance()
repo = RepoModel().get_by_repo_name(repo_name)
repo_vcs = repo.scm_instance()
assert source_vcs[0].message == repo_vcs[0].message
assert source_vcs.count() == repo_vcs.count()
assert source_vcs.commit_ids == repo_vcs.commit_ids
@pytest.mark.xfail_backends("svn", reason="Depends on import support")
def test_create_remote_repo_wrong_clone_uri(self, autologin_user, backend,
csrf_token):
repo_name = backend.new_repo_name()
description = 'description for newly created repo'
response = self.app.post(
route_path('repo_create'),
fixture._get_repo_create_params(
repo_private=False,
repo_name=repo_name,
repo_type=backend.alias,
repo_description=description,
clone_uri='http://repo.invalid/repo',
csrf_token=csrf_token))
response.mustcontain('invalid clone url')
@pytest.mark.xfail_backends("svn", reason="Depends on import support")
def test_create_remote_repo_wrong_clone_uri_hg_svn(
self, autologin_user, backend, csrf_token):
repo_name = backend.new_repo_name()
description = 'description for newly created repo'
response = self.app.post(
route_path('repo_create'),
fixture._get_repo_create_params(
repo_private=False,
repo_name=repo_name,
repo_type=backend.alias,
repo_description=description,
clone_uri='svn+http://svn.invalid/repo',
csrf_token=csrf_token))
response.mustcontain('invalid clone url')
def test_create_with_git_suffix(
self, autologin_user, backend, csrf_token):
repo_name = backend.new_repo_name() + ".git"
description = 'description for newly created repo'
response = self.app.post(
route_path('repo_create'),
fixture._get_repo_create_params(
repo_private=False,
repo_name=repo_name,
repo_type=backend.alias,
repo_description=description,
csrf_token=csrf_token))
response.mustcontain('Repository name cannot end with .git')
def test_default_user_cannot_access_private_repo_in_a_group(
self, autologin_user, user_util, backend):
group = user_util.create_repo_group()
repo = backend.create_repo(
repo_private=True, repo_group=group, repo_copy_permissions=True)
permissions = _get_permission_for_user(
user='default', repo=repo.repo_name)
assert len(permissions) == 1
assert permissions[0].permission.permission_name == 'repository.none'
assert permissions[0].repository.private is True
def test_create_on_top_level_without_permissions(self, backend):
session = login_user_session(
self.app, TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
csrf_token = auth.get_csrf_token(session)
# revoke
user_model = UserModel()
# disable fork and create on default user
user_model.revoke_perm(User.DEFAULT_USER, 'hg.create.repository')
user_model.grant_perm(User.DEFAULT_USER, 'hg.create.none')
user_model.revoke_perm(User.DEFAULT_USER, 'hg.fork.repository')
user_model.grant_perm(User.DEFAULT_USER, 'hg.fork.none')
# disable on regular user
user_model.revoke_perm(TEST_USER_REGULAR_LOGIN, 'hg.create.repository')
user_model.grant_perm(TEST_USER_REGULAR_LOGIN, 'hg.create.none')
user_model.revoke_perm(TEST_USER_REGULAR_LOGIN, 'hg.fork.repository')
user_model.grant_perm(TEST_USER_REGULAR_LOGIN, 'hg.fork.none')
Session().commit()
repo_name = backend.new_repo_name()
description = 'description for newly created repo'
response = self.app.post(
route_path('repo_create'),
fixture._get_repo_create_params(
repo_private=False,
repo_name=repo_name,
repo_type=backend.alias,
repo_description=description,
csrf_token=csrf_token))
response.mustcontain(
u"You do not have the permission to store repositories in "
u"the root location.")
@mock.patch.object(RepoModel, '_create_filesystem_repo', error_function)
def test_create_repo_when_filesystem_op_fails(
self, autologin_user, backend, csrf_token):
repo_name = backend.new_repo_name()
description = 'description for newly created repo'
response = self.app.post(
route_path('repo_create'),
fixture._get_repo_create_params(
repo_private=False,
repo_name=repo_name,
repo_type=backend.alias,
repo_description=description,
csrf_token=csrf_token))
assert_session_flash(
response, 'Error creating repository %s' % repo_name)
# repo must not be in db
assert backend.repo is None
# repo must not be in filesystem !
assert not repo_on_filesystem(repo_name)
def assert_repository_is_created_correctly(
self, repo_name, description, backend):
repo_name_utf8 = safe_str(repo_name)
# run the check page that triggers the flash message
response = self.app.get(
route_path('repo_creating_check', repo_name=safe_str(repo_name)))
assert response.json == {u'result': True}
flash_msg = u'Created repository <a href="/{}">{}</a>'.format(
urllib.quote(repo_name_utf8), repo_name)
assert_session_flash(response, flash_msg)
# test if the repo was created in the database
new_repo = RepoModel().get_by_repo_name(repo_name)
assert new_repo.repo_name == repo_name
assert new_repo.description == description
# test if the repository is visible in the list ?
response = self.app.get(
h.route_path('repo_summary', repo_name=safe_str(repo_name)))
response.mustcontain(repo_name)
response.mustcontain(backend.alias)
assert repo_on_filesystem(repo_name)

View file

@ -48,7 +48,7 @@ class OpenSourceLicensesAdminSettingsView(BaseAppView):
c = self.load_default_context()
c.active = 'open_source'
c.navlist = navigation_list(self.request)
c.opensource_licenses = collections.OrderedDict(
sorted(read_opensource_licenses().items(), key=lambda t: t[0]))
items = sorted(read_opensource_licenses().items(), key=lambda t: t[0])
c.opensource_licenses = collections.OrderedDict(items)
return self._get_template_context(c)

View file

@ -0,0 +1,180 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2016-2017 RhodeCode GmbH
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License, version 3
# (only), as published by the Free Software Foundation.
#
# 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 Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# This program is dual-licensed. If you wish to learn more about the
# RhodeCode Enterprise Edition, including its added features, Support services,
# and proprietary license terms, please see https://rhodecode.com/licenses/
import logging
import formencode
from pyramid.httpexceptions import HTTPFound, HTTPForbidden
from pyramid.view import view_config
from pyramid.renderers import render
from pyramid.response import Response
from rhodecode.apps._base import BaseAppView, DataGridAppView
from rhodecode.lib.ext_json import json
from rhodecode.lib.auth import (
LoginRequired, CSRFRequired, NotAnonymous,
HasPermissionAny, HasRepoGroupPermissionAny)
from rhodecode.lib import helpers as h
from rhodecode.lib.utils import repo_name_slug
from rhodecode.lib.utils2 import safe_int, safe_unicode
from rhodecode.model.forms import RepoForm
from rhodecode.model.repo import RepoModel
from rhodecode.model.scm import RepoList, RepoGroupList, ScmModel
from rhodecode.model.settings import SettingsModel
from rhodecode.model.db import Repository, RepoGroup
log = logging.getLogger(__name__)
class AdminReposView(BaseAppView, DataGridAppView):
def load_default_context(self):
c = self._get_local_tmpl_context()
self._register_global_c(c)
return c
def _load_form_data(self, c):
acl_groups = RepoGroupList(RepoGroup.query().all(),
perm_set=['group.write', 'group.admin'])
c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
c.repo_groups_choices = map(lambda k: safe_unicode(k[0]), c.repo_groups)
c.landing_revs_choices, c.landing_revs = \
ScmModel().get_repo_landing_revs()
c.personal_repo_group = self._rhodecode_user.personal_repo_group
@LoginRequired()
@NotAnonymous()
@view_config(
route_name='repos', request_method='GET',
renderer='rhodecode:templates/admin/repos/repos.mako')
def repository_list(self):
c = self.load_default_context()
repo_list = Repository.get_all_repos()
c.repo_list = RepoList(repo_list, perm_set=['repository.admin'])
repos_data = RepoModel().get_repos_as_dict(
repo_list=c.repo_list, admin=True, super_user_actions=True)
# json used to render the grid
c.data = json.dumps(repos_data)
return self._get_template_context(c)
@LoginRequired()
@NotAnonymous()
# perms check inside
@view_config(
route_name='repo_new', request_method='GET',
renderer='rhodecode:templates/admin/repos/repo_add.mako')
def repository_new(self):
c = self.load_default_context()
new_repo = self.request.GET.get('repo', '')
parent_group = safe_int(self.request.GET.get('parent_group'))
_gr = RepoGroup.get(parent_group)
if not HasPermissionAny('hg.admin', 'hg.create.repository')():
# you're not super admin nor have global create permissions,
# but maybe you have at least write permission to a parent group ?
gr_name = _gr.group_name if _gr else None
# create repositories with write permission on group is set to true
create_on_write = HasPermissionAny('hg.create.write_on_repogroup.true')()
group_admin = HasRepoGroupPermissionAny('group.admin')(group_name=gr_name)
group_write = HasRepoGroupPermissionAny('group.write')(group_name=gr_name)
if not (group_admin or (group_write and create_on_write)):
raise HTTPForbidden()
self._load_form_data(c)
c.new_repo = repo_name_slug(new_repo)
# apply the defaults from defaults page
defaults = SettingsModel().get_default_repo_settings(strip_prefix=True)
# set checkbox to autochecked
defaults['repo_copy_permissions'] = True
parent_group_choice = '-1'
if not self._rhodecode_user.is_admin and self._rhodecode_user.personal_repo_group:
parent_group_choice = self._rhodecode_user.personal_repo_group
if parent_group and _gr:
if parent_group in [x[0] for x in c.repo_groups]:
parent_group_choice = safe_unicode(parent_group)
defaults.update({'repo_group': parent_group_choice})
data = render('rhodecode:templates/admin/repos/repo_add.mako',
self._get_template_context(c), self.request)
html = formencode.htmlfill.render(
data,
defaults=defaults,
encoding="UTF-8",
force_defaults=False
)
return Response(html)
@LoginRequired()
@NotAnonymous()
@CSRFRequired()
# perms check inside
@view_config(
route_name='repo_create', request_method='POST',
renderer='rhodecode:templates/admin/repos/repos.mako')
def repository_create(self):
c = self.load_default_context()
form_result = {}
task_id = None
self._load_form_data(c)
try:
# CanWriteToGroup validators checks permissions of this POST
form_result = RepoForm(repo_groups=c.repo_groups_choices,
landing_revs=c.landing_revs_choices)()\
.to_python(dict(self.request.POST))
# create is done sometimes async on celery, db transaction
# management is handled there.
task = RepoModel().create(form_result, self._rhodecode_user.user_id)
from celery.result import BaseAsyncResult
if isinstance(task, BaseAsyncResult):
task_id = task.task_id
except formencode.Invalid as errors:
data = render('rhodecode:templates/admin/repos/repo_add.mako',
self._get_template_context(c), self.request)
html = formencode.htmlfill.render(
data,
defaults=errors.value,
errors=errors.error_dict or {},
prefix_error=False,
encoding="UTF-8",
force_defaults=False
)
return Response(html)
except Exception as e:
msg = self._log_creation_exception(e, form_result.get('repo_name'))
h.flash(msg, category='error')
raise HTTPFound(h.route_path('home'))
raise HTTPFound(
h.route_path('repo_creating',
repo_name=form_result['repo_name_full'],
_query=dict(task_id=task_id)))

View file

@ -19,7 +19,6 @@
# and proprietary license terms, please see https://rhodecode.com/licenses/
import logging
import datetime
from pyramid.httpexceptions import HTTPFound
from pyramid.view import view_config
@ -28,12 +27,11 @@ from rhodecode.model.scm import UserGroupList
from rhodecode.apps._base import BaseAppView, DataGridAppView
from rhodecode.lib.auth import (
LoginRequired, HasPermissionAllDecorator, CSRFRequired, NotAnonymous,
LoginRequired, NotAnonymous,
HasUserGroupPermissionAnyDecorator)
from rhodecode.lib import helpers as h
from rhodecode.lib.utils import PartialRenderer
from rhodecode.lib.utils2 import safe_int, safe_unicode
from rhodecode.model.user_group import UserGroupModel
from rhodecode.lib.utils2 import safe_unicode
from rhodecode.model.db import (
joinedload, or_, count, User, UserGroup, UserGroupMember,
UserGroupRepoToPerm, UserGroupRepoGroupToPerm)

View file

View file

@ -337,7 +337,76 @@ def includeme(config):
name='edit_repo_perms',
pattern='/{repo_name:.*?[^/]}/settings/permissions', repo_route=True)
# Repo Review Rules
# Maintenance
config.add_route(
name='edit_repo_maintenance',
pattern='/{repo_name:.*?[^/]}/settings/maintenance', repo_route=True)
config.add_route(
name='edit_repo_maintenance_execute',
pattern='/{repo_name:.*?[^/]}/settings/maintenance/execute', repo_route=True)
# Fields
config.add_route(
name='edit_repo_fields',
pattern='/{repo_name:.*?[^/]}/settings/fields', repo_route=True)
config.add_route(
name='edit_repo_fields_create',
pattern='/{repo_name:.*?[^/]}/settings/fields/create', repo_route=True)
config.add_route(
name='edit_repo_fields_delete',
pattern='/{repo_name:.*?[^/]}/settings/fields/{field_id}/delete', repo_route=True)
# Locking
config.add_route(
name='repo_edit_toggle_locking',
pattern='/{repo_name:.*?[^/]}/settings/toggle_locking', repo_route=True)
# Remote
config.add_route(
name='edit_repo_remote',
pattern='/{repo_name:.*?[^/]}/settings/remote', repo_route=True)
config.add_route(
name='edit_repo_remote_pull',
pattern='/{repo_name:.*?[^/]}/settings/remote/pull', repo_route=True)
# Statistics
config.add_route(
name='edit_repo_statistics',
pattern='/{repo_name:.*?[^/]}/settings/statistics', repo_route=True)
config.add_route(
name='edit_repo_statistics_reset',
pattern='/{repo_name:.*?[^/]}/settings/statistics/update', repo_route=True)
# Issue trackers
config.add_route(
name='edit_repo_issuetracker',
pattern='/{repo_name:.*?[^/]}/settings/issue_trackers', repo_route=True)
config.add_route(
name='edit_repo_issuetracker_test',
pattern='/{repo_name:.*?[^/]}/settings/issue_trackers/test', repo_route=True)
config.add_route(
name='edit_repo_issuetracker_delete',
pattern='/{repo_name:.*?[^/]}/settings/issue_trackers/delete', repo_route=True)
config.add_route(
name='edit_repo_issuetracker_update',
pattern='/{repo_name:.*?[^/]}/settings/issue_trackers/update', repo_route=True)
# VCS Settings
config.add_route(
name='edit_repo_vcs',
pattern='/{repo_name:.*?[^/]}/settings/vcs', repo_route=True)
config.add_route(
name='edit_repo_vcs_update',
pattern='/{repo_name:.*?[^/]}/settings/vcs/update', repo_route=True)
# svn pattern
config.add_route(
name='edit_repo_vcs_svn_pattern_delete',
pattern='/{repo_name:.*?[^/]}/settings/vcs/svn_pattern/delete', repo_route=True)
# Repo Review Rules (EE feature)
config.add_route(
name='repo_reviewers',
pattern='/{repo_name:.*?[^/]}/settings/review/rules', repo_route=True)
@ -346,18 +415,9 @@ def includeme(config):
name='repo_default_reviewers_data',
pattern='/{repo_name:.*?[^/]}/settings/review/default-reviewers', repo_route=True)
# Maintenance
config.add_route(
name='repo_maintenance',
pattern='/{repo_name:.*?[^/]}/settings/maintenance', repo_route=True)
config.add_route(
name='repo_maintenance_execute',
pattern='/{repo_name:.*?[^/]}/settings/maintenance/execute', repo_route=True)
# Strip
config.add_route(
name='strip',
name='edit_repo_strip',
pattern='/{repo_name:.*?[^/]}/settings/strip', repo_route=True)
config.add_route(

View file

@ -24,23 +24,38 @@ from rhodecode.lib.utils2 import md5
from rhodecode.model.db import Repository
from rhodecode.model.meta import Session
from rhodecode.model.settings import SettingsModel, IssueTrackerSettingsModel
from rhodecode.tests import url
def route_path(name, params=None, **kwargs):
import urllib
base_url = {
'repo_summary': '/{repo_name}',
'edit_repo_issuetracker': '/{repo_name}/settings/issue_trackers',
'edit_repo_issuetracker_test': '/{repo_name}/settings/issue_trackers/test',
'edit_repo_issuetracker_delete': '/{repo_name}/settings/issue_trackers/delete',
'edit_repo_issuetracker_update': '/{repo_name}/settings/issue_trackers/update',
}[name].format(**kwargs)
if params:
base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
return base_url
@pytest.mark.usefixtures("app")
class TestAdminRepos:
class TestRepoIssueTracker(object):
def test_issuetracker_index(self, autologin_user, backend):
repo = backend.create_repo()
response = self.app.get(url('repo_settings_issuetracker',
response = self.app.get(route_path('edit_repo_issuetracker',
repo_name=repo.repo_name))
assert response.status_code == 200
def test_add_issuetracker_patterns(
self, autologin_user, backend, csrf_token, request):
def test_add_and_test_issuetracker_patterns(
self, autologin_user, backend, csrf_token, request, xhr_header):
pattern = 'issuetracker_pat'
another_pattern = pattern+'1'
post_url = url('repo_issuetracker_save',
repo_name=backend.repo.repo_name)
post_url = route_path(
'edit_repo_issuetracker_update', repo_name=backend.repo.repo_name)
post_data = {
'new_pattern_pattern_0': pattern,
'new_pattern_url_0': 'url',
@ -60,6 +75,17 @@ class TestAdminRepos:
self.another_uid = md5(another_pattern)
assert settings[self.another_uid]['pat'] == another_pattern
# test pattern
data = {'test_text': 'example of issuetracker_pat replacement',
'csrf_token': csrf_token}
response = self.app.post(
route_path('edit_repo_issuetracker_test',
repo_name=backend.repo.repo_name),
extra_environ=xhr_header, params=data)
assert response.body == \
'example of <a class="issue-tracker-link" href="url">prefix</a> replacement'
@request.addfinalizer
def cleanup():
self.settings_model.delete_entries(self.uid)
@ -76,8 +102,8 @@ class TestAdminRepos:
entry_key+old_uid, old_pattern, 'unicode')
Session().add(sett)
Session().commit()
post_url = url('repo_issuetracker_save',
repo_name=backend.repo.repo_name)
post_url = route_path(
'edit_repo_issuetracker_update', repo_name=backend.repo.repo_name)
post_data = {
'new_pattern_pattern_0': pattern,
'new_pattern_url_0': 'url',
@ -92,7 +118,7 @@ class TestAdminRepos:
self.uid = md5(pattern)
assert settings[self.uid]['pat'] == pattern
with pytest.raises(KeyError):
settings[old_uid]
key = settings[old_uid]
@request.addfinalizer
def cleanup():
@ -110,10 +136,10 @@ class TestAdminRepos:
value=entry_key, type_='unicode', cleanup=False)
self.app.post(
url('repo_issuetracker_delete',
route_path(
'edit_repo_issuetracker_delete',
repo_name=backend.repo.repo_name),
{
'_method': 'delete',
'uid': uid,
'csrf_token': csrf_token
}, status=302)

View file

@ -26,8 +26,7 @@ from rhodecode.lib.vcs.exceptions import RepositoryRequirementError
from rhodecode.model.db import Repository, UserRepoToPerm, Permission, User
from rhodecode.model.meta import Session
from rhodecode.tests import (
url, HG_REPO, TEST_USER_ADMIN_LOGIN, TEST_USER_REGULAR_LOGIN,
assert_session_flash)
TEST_USER_ADMIN_LOGIN, TEST_USER_REGULAR_LOGIN, assert_session_flash)
from rhodecode.tests.fixture import Fixture
fixture = Fixture()
@ -41,6 +40,11 @@ def route_path(name, params=None, **kwargs):
'edit_repo_advanced': '/{repo_name}/settings/advanced',
'edit_repo_caches': '/{repo_name}/settings/caches',
'edit_repo_perms': '/{repo_name}/settings/permissions',
'edit_repo_vcs': '/{repo_name}/settings/vcs',
'edit_repo_issuetracker': '/{repo_name}/settings/issue_trackers',
'edit_repo_fields': '/{repo_name}/settings/fields',
'edit_repo_remote': '/{repo_name}/settings/remote',
'edit_repo_statistics': '/{repo_name}/settings/statistics',
}[name].format(**kwargs)
if params:
@ -64,6 +68,11 @@ class TestAdminRepoSettings(object):
'edit_repo_caches',
'edit_repo_perms',
'edit_repo_advanced',
'edit_repo_vcs',
'edit_repo_issuetracker',
'edit_repo_fields',
'edit_repo_remote',
'edit_repo_statistics',
])
def test_show_page(self, urlname, app, backend):
app.get(route_path(urlname, repo_name=backend.repo_name), status=200)
@ -75,16 +84,6 @@ class TestAdminRepoSettings(object):
with scm_patcher:
self.app.get(route_path('edit_repo', repo_name=backend_hg.repo_name))
@pytest.mark.parametrize('urlname', [
'repo_vcs_settings',
'repo_settings_issuetracker',
'edit_repo_fields',
'edit_repo_remote',
'edit_repo_statistics',
])
def test_show_page_pylons(self, urlname, app):
app.get(url(urlname, repo_name=HG_REPO))
@pytest.mark.parametrize('update_settings', [
{'repo_description': 'alter-desc'},
{'repo_owner': TEST_USER_REGULAR_LOGIN},

View file

@ -18,26 +18,21 @@
# RhodeCode Enterprise Edition, including its added features, Support services,
# and proprietary license terms, please see https://rhodecode.com/licenses/
import urllib
import mock
import pytest
from rhodecode.lib import auth
from rhodecode.lib.utils2 import safe_str, str2bool
from rhodecode.lib import helpers as h
from rhodecode.lib.utils2 import str2bool
from rhodecode.model.db import (
Repository, RepoGroup, UserRepoToPerm, User, Permission)
Repository, UserRepoToPerm, User)
from rhodecode.model.meta import Session
from rhodecode.model.repo import RepoModel
from rhodecode.model.repo_group import RepoGroupModel
from rhodecode.model.settings import SettingsModel, VcsSettingsModel
from rhodecode.model.user import UserModel
from rhodecode.tests import (
login_user_session, url, assert_session_flash, TEST_USER_ADMIN_LOGIN,
TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS, logout_user_session)
from rhodecode.tests.fixture import Fixture, error_function
from rhodecode.tests.utils import AssertResponse, repo_on_filesystem
login_user_session, logout_user_session,
TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
from rhodecode.tests.fixture import Fixture
from rhodecode.tests.utils import AssertResponse
fixture = Fixture()
@ -48,6 +43,10 @@ def route_path(name, params=None, **kwargs):
base_url = {
'repo_summary': '/{repo_name}',
'repo_creating_check': '/{repo_name}/repo_creating_check',
'edit_repo': '/{repo_name}/settings',
'edit_repo_vcs': '/{repo_name}/settings/vcs',
'edit_repo_vcs_update': '/{repo_name}/settings/vcs/update',
'edit_repo_vcs_svn_pattern_delete': '/{repo_name}/settings/vcs/svn_pattern/delete'
}[name].format(**kwargs)
if params:
@ -55,447 +54,6 @@ def route_path(name, params=None, **kwargs):
return base_url
@pytest.mark.usefixtures("app")
class TestAdminRepos(object):
def test_index(self):
self.app.get(url('repos'))
def test_create_page_restricted(self, autologin_user, backend):
with mock.patch('rhodecode.BACKENDS', {'git': 'git'}):
response = self.app.get(url('new_repo'), status=200)
assert_response = AssertResponse(response)
element = assert_response.get_element('#repo_type')
assert element.text_content() == '\ngit\n'
def test_create_page_non_restricted(self, autologin_user, backend):
response = self.app.get(url('new_repo'), status=200)
assert_response = AssertResponse(response)
assert_response.element_contains('#repo_type', 'git')
assert_response.element_contains('#repo_type', 'svn')
assert_response.element_contains('#repo_type', 'hg')
@pytest.mark.parametrize("suffix",
[u'', u'xxa'], ids=['', 'non-ascii'])
def test_create(self, autologin_user, backend, suffix, csrf_token):
repo_name_unicode = backend.new_repo_name(suffix=suffix)
repo_name = repo_name_unicode.encode('utf8')
description_unicode = u'description for newly created repo' + suffix
description = description_unicode.encode('utf8')
response = self.app.post(
url('repos'),
fixture._get_repo_create_params(
repo_private=False,
repo_name=repo_name,
repo_type=backend.alias,
repo_description=description,
csrf_token=csrf_token),
status=302)
self.assert_repository_is_created_correctly(
repo_name, description, backend)
def test_create_numeric(self, autologin_user, backend, csrf_token):
numeric_repo = '1234'
repo_name = numeric_repo
description = 'description for newly created repo' + numeric_repo
self.app.post(
url('repos'),
fixture._get_repo_create_params(
repo_private=False,
repo_name=repo_name,
repo_type=backend.alias,
repo_description=description,
csrf_token=csrf_token))
self.assert_repository_is_created_correctly(
repo_name, description, backend)
@pytest.mark.parametrize("suffix", [u'', u'ąćę'], ids=['', 'non-ascii'])
def test_create_in_group(
self, autologin_user, backend, suffix, csrf_token):
# create GROUP
group_name = 'sometest_%s' % backend.alias
gr = RepoGroupModel().create(group_name=group_name,
group_description='test',
owner=TEST_USER_ADMIN_LOGIN)
Session().commit()
repo_name = u'ingroup' + suffix
repo_name_full = RepoGroup.url_sep().join(
[group_name, repo_name])
description = u'description for newly created repo'
self.app.post(
url('repos'),
fixture._get_repo_create_params(
repo_private=False,
repo_name=safe_str(repo_name),
repo_type=backend.alias,
repo_description=description,
repo_group=gr.group_id,
csrf_token=csrf_token))
# TODO: johbo: Cleanup work to fixture
try:
self.assert_repository_is_created_correctly(
repo_name_full, description, backend)
new_repo = RepoModel().get_by_repo_name(repo_name_full)
inherited_perms = UserRepoToPerm.query().filter(
UserRepoToPerm.repository_id == new_repo.repo_id).all()
assert len(inherited_perms) == 1
finally:
RepoModel().delete(repo_name_full)
RepoGroupModel().delete(group_name)
Session().commit()
def test_create_in_group_numeric(
self, autologin_user, backend, csrf_token):
# create GROUP
group_name = 'sometest_%s' % backend.alias
gr = RepoGroupModel().create(group_name=group_name,
group_description='test',
owner=TEST_USER_ADMIN_LOGIN)
Session().commit()
repo_name = '12345'
repo_name_full = RepoGroup.url_sep().join([group_name, repo_name])
description = 'description for newly created repo'
self.app.post(
url('repos'),
fixture._get_repo_create_params(
repo_private=False,
repo_name=repo_name,
repo_type=backend.alias,
repo_description=description,
repo_group=gr.group_id,
csrf_token=csrf_token))
# TODO: johbo: Cleanup work to fixture
try:
self.assert_repository_is_created_correctly(
repo_name_full, description, backend)
new_repo = RepoModel().get_by_repo_name(repo_name_full)
inherited_perms = UserRepoToPerm.query()\
.filter(UserRepoToPerm.repository_id == new_repo.repo_id).all()
assert len(inherited_perms) == 1
finally:
RepoModel().delete(repo_name_full)
RepoGroupModel().delete(group_name)
Session().commit()
def test_create_in_group_without_needed_permissions(self, backend):
session = login_user_session(
self.app, TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
csrf_token = auth.get_csrf_token(session)
# revoke
user_model = UserModel()
# disable fork and create on default user
user_model.revoke_perm(User.DEFAULT_USER, 'hg.create.repository')
user_model.grant_perm(User.DEFAULT_USER, 'hg.create.none')
user_model.revoke_perm(User.DEFAULT_USER, 'hg.fork.repository')
user_model.grant_perm(User.DEFAULT_USER, 'hg.fork.none')
# disable on regular user
user_model.revoke_perm(TEST_USER_REGULAR_LOGIN, 'hg.create.repository')
user_model.grant_perm(TEST_USER_REGULAR_LOGIN, 'hg.create.none')
user_model.revoke_perm(TEST_USER_REGULAR_LOGIN, 'hg.fork.repository')
user_model.grant_perm(TEST_USER_REGULAR_LOGIN, 'hg.fork.none')
Session().commit()
# create GROUP
group_name = 'reg_sometest_%s' % backend.alias
gr = RepoGroupModel().create(group_name=group_name,
group_description='test',
owner=TEST_USER_ADMIN_LOGIN)
Session().commit()
group_name_allowed = 'reg_sometest_allowed_%s' % backend.alias
gr_allowed = RepoGroupModel().create(
group_name=group_name_allowed,
group_description='test',
owner=TEST_USER_REGULAR_LOGIN)
Session().commit()
repo_name = 'ingroup'
description = 'description for newly created repo'
response = self.app.post(
url('repos'),
fixture._get_repo_create_params(
repo_private=False,
repo_name=repo_name,
repo_type=backend.alias,
repo_description=description,
repo_group=gr.group_id,
csrf_token=csrf_token))
response.mustcontain('Invalid value')
# user is allowed to create in this group
repo_name = 'ingroup'
repo_name_full = RepoGroup.url_sep().join(
[group_name_allowed, repo_name])
description = 'description for newly created repo'
response = self.app.post(
url('repos'),
fixture._get_repo_create_params(
repo_private=False,
repo_name=repo_name,
repo_type=backend.alias,
repo_description=description,
repo_group=gr_allowed.group_id,
csrf_token=csrf_token))
# TODO: johbo: Cleanup in pytest fixture
try:
self.assert_repository_is_created_correctly(
repo_name_full, description, backend)
new_repo = RepoModel().get_by_repo_name(repo_name_full)
inherited_perms = UserRepoToPerm.query().filter(
UserRepoToPerm.repository_id == new_repo.repo_id).all()
assert len(inherited_perms) == 1
assert repo_on_filesystem(repo_name_full)
finally:
RepoModel().delete(repo_name_full)
RepoGroupModel().delete(group_name)
RepoGroupModel().delete(group_name_allowed)
Session().commit()
def test_create_in_group_inherit_permissions(self, autologin_user, backend,
csrf_token):
# create GROUP
group_name = 'sometest_%s' % backend.alias
gr = RepoGroupModel().create(group_name=group_name,
group_description='test',
owner=TEST_USER_ADMIN_LOGIN)
perm = Permission.get_by_key('repository.write')
RepoGroupModel().grant_user_permission(
gr, TEST_USER_REGULAR_LOGIN, perm)
# add repo permissions
Session().commit()
repo_name = 'ingroup_inherited_%s' % backend.alias
repo_name_full = RepoGroup.url_sep().join([group_name, repo_name])
description = 'description for newly created repo'
self.app.post(
url('repos'),
fixture._get_repo_create_params(
repo_private=False,
repo_name=repo_name,
repo_type=backend.alias,
repo_description=description,
repo_group=gr.group_id,
repo_copy_permissions=True,
csrf_token=csrf_token))
# TODO: johbo: Cleanup to pytest fixture
try:
self.assert_repository_is_created_correctly(
repo_name_full, description, backend)
except Exception:
RepoGroupModel().delete(group_name)
Session().commit()
raise
# check if inherited permissions are applied
new_repo = RepoModel().get_by_repo_name(repo_name_full)
inherited_perms = UserRepoToPerm.query().filter(
UserRepoToPerm.repository_id == new_repo.repo_id).all()
assert len(inherited_perms) == 2
assert TEST_USER_REGULAR_LOGIN in [
x.user.username for x in inherited_perms]
assert 'repository.write' in [
x.permission.permission_name for x in inherited_perms]
RepoModel().delete(repo_name_full)
RepoGroupModel().delete(group_name)
Session().commit()
@pytest.mark.xfail_backends(
"git", "hg", reason="Missing reposerver support")
def test_create_with_clone_uri(self, autologin_user, backend, reposerver,
csrf_token):
source_repo = backend.create_repo(number_of_commits=2)
source_repo_name = source_repo.repo_name
reposerver.serve(source_repo.scm_instance())
repo_name = backend.new_repo_name()
response = self.app.post(
url('repos'),
fixture._get_repo_create_params(
repo_private=False,
repo_name=repo_name,
repo_type=backend.alias,
repo_description='',
clone_uri=reposerver.url,
csrf_token=csrf_token),
status=302)
# Should be redirected to the creating page
response.mustcontain('repo_creating')
# Expecting that both repositories have same history
source_repo = RepoModel().get_by_repo_name(source_repo_name)
source_vcs = source_repo.scm_instance()
repo = RepoModel().get_by_repo_name(repo_name)
repo_vcs = repo.scm_instance()
assert source_vcs[0].message == repo_vcs[0].message
assert source_vcs.count() == repo_vcs.count()
assert source_vcs.commit_ids == repo_vcs.commit_ids
@pytest.mark.xfail_backends("svn", reason="Depends on import support")
def test_create_remote_repo_wrong_clone_uri(self, autologin_user, backend,
csrf_token):
repo_name = backend.new_repo_name()
description = 'description for newly created repo'
response = self.app.post(
url('repos'),
fixture._get_repo_create_params(
repo_private=False,
repo_name=repo_name,
repo_type=backend.alias,
repo_description=description,
clone_uri='http://repo.invalid/repo',
csrf_token=csrf_token))
response.mustcontain('invalid clone url')
@pytest.mark.xfail_backends("svn", reason="Depends on import support")
def test_create_remote_repo_wrong_clone_uri_hg_svn(
self, autologin_user, backend, csrf_token):
repo_name = backend.new_repo_name()
description = 'description for newly created repo'
response = self.app.post(
url('repos'),
fixture._get_repo_create_params(
repo_private=False,
repo_name=repo_name,
repo_type=backend.alias,
repo_description=description,
clone_uri='svn+http://svn.invalid/repo',
csrf_token=csrf_token))
response.mustcontain('invalid clone url')
def test_create_with_git_suffix(
self, autologin_user, backend, csrf_token):
repo_name = backend.new_repo_name() + ".git"
description = 'description for newly created repo'
response = self.app.post(
url('repos'),
fixture._get_repo_create_params(
repo_private=False,
repo_name=repo_name,
repo_type=backend.alias,
repo_description=description,
csrf_token=csrf_token))
response.mustcontain('Repository name cannot end with .git')
def test_show(self, autologin_user, backend):
self.app.get(url('repo', repo_name=backend.repo_name))
def test_default_user_cannot_access_private_repo_in_a_group(
self, autologin_user, user_util, backend, csrf_token):
group = user_util.create_repo_group()
repo = backend.create_repo(
repo_private=True, repo_group=group, repo_copy_permissions=True)
permissions = _get_permission_for_user(
user='default', repo=repo.repo_name)
assert len(permissions) == 1
assert permissions[0].permission.permission_name == 'repository.none'
assert permissions[0].repository.private is True
def test_create_on_top_level_without_permissions(self, backend):
session = login_user_session(
self.app, TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
csrf_token = auth.get_csrf_token(session)
# revoke
user_model = UserModel()
# disable fork and create on default user
user_model.revoke_perm(User.DEFAULT_USER, 'hg.create.repository')
user_model.grant_perm(User.DEFAULT_USER, 'hg.create.none')
user_model.revoke_perm(User.DEFAULT_USER, 'hg.fork.repository')
user_model.grant_perm(User.DEFAULT_USER, 'hg.fork.none')
# disable on regular user
user_model.revoke_perm(TEST_USER_REGULAR_LOGIN, 'hg.create.repository')
user_model.grant_perm(TEST_USER_REGULAR_LOGIN, 'hg.create.none')
user_model.revoke_perm(TEST_USER_REGULAR_LOGIN, 'hg.fork.repository')
user_model.grant_perm(TEST_USER_REGULAR_LOGIN, 'hg.fork.none')
Session().commit()
repo_name = backend.new_repo_name()
description = 'description for newly created repo'
response = self.app.post(
url('repos'),
fixture._get_repo_create_params(
repo_private=False,
repo_name=repo_name,
repo_type=backend.alias,
repo_description=description,
csrf_token=csrf_token))
response.mustcontain(
u"You do not have the permission to store repositories in "
u"the root location.")
@mock.patch.object(RepoModel, '_create_filesystem_repo', error_function)
def test_create_repo_when_filesystem_op_fails(
self, autologin_user, backend, csrf_token):
repo_name = backend.new_repo_name()
description = 'description for newly created repo'
response = self.app.post(
url('repos'),
fixture._get_repo_create_params(
repo_private=False,
repo_name=repo_name,
repo_type=backend.alias,
repo_description=description,
csrf_token=csrf_token))
assert_session_flash(
response, 'Error creating repository %s' % repo_name)
# repo must not be in db
assert backend.repo is None
# repo must not be in filesystem !
assert not repo_on_filesystem(repo_name)
def assert_repository_is_created_correctly(
self, repo_name, description, backend):
repo_name_utf8 = safe_str(repo_name)
# run the check page that triggers the flash message
response = self.app.get(
route_path('repo_creating_check', repo_name=safe_str(repo_name)))
assert response.json == {u'result': True}
flash_msg = u'Created repository <a href="/{}">{}</a>'.format(
urllib.quote(repo_name_utf8), repo_name)
assert_session_flash(response, flash_msg)
# test if the repo was created in the database
new_repo = RepoModel().get_by_repo_name(repo_name)
assert new_repo.repo_name == repo_name
assert new_repo.description == description
# test if the repository is visible in the list ?
response = self.app.get(
h.route_path('repo_summary', repo_name=safe_str(repo_name)))
response.mustcontain(repo_name)
response.mustcontain(backend.alias)
assert repo_on_filesystem(repo_name)
@pytest.mark.usefixtures("app")
class TestVcsSettings(object):
FORM_DATA = {
@ -515,7 +73,7 @@ class TestVcsSettings(object):
@pytest.mark.skip_backends('svn')
def test_global_settings_initial_values(self, autologin_user, backend):
repo_name = backend.repo_name
response = self.app.get(url('repo_vcs_settings', repo_name=repo_name))
response = self.app.get(route_path('edit_repo_vcs', repo_name=repo_name))
expected_settings = (
'rhodecode_use_outdated_comments', 'rhodecode_pr_merge_enabled',
@ -533,12 +91,12 @@ class TestVcsSettings(object):
user_util.grant_user_permission_to_repo(repo, user, 'repository.admin')
login_user_session(
self.app, TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
self.app.get(url('repo_vcs_settings', repo_name=repo_name), status=200)
self.app.get(route_path('edit_repo_vcs', repo_name=repo_name), status=200)
def test_inherit_global_settings_flag_is_true_by_default(
self, autologin_user, backend):
repo_name = backend.repo_name
response = self.app.get(url('repo_vcs_settings', repo_name=repo_name))
response = self.app.get(route_path('edit_repo_vcs', repo_name=repo_name))
assert_response = AssertResponse(response)
element = assert_response.get_element('#inherit_global_settings')
@ -551,7 +109,7 @@ class TestVcsSettings(object):
repo_name = repo.repo_name
settings_util.create_repo_rhodecode_setting(
repo, 'inherit_vcs_settings', checked_value, 'bool')
response = self.app.get(url('repo_vcs_settings', repo_name=repo_name))
response = self.app.get(route_path('edit_repo_vcs', repo_name=repo_name))
assert_response = AssertResponse(response)
element = assert_response.get_element('#inherit_global_settings')
@ -564,7 +122,7 @@ class TestVcsSettings(object):
data = self.FORM_DATA.copy()
data['csrf_token'] = csrf_token
self.app.post(
url('repo_vcs_settings', repo_name=repo_name), data, status=302)
route_path('edit_repo_vcs_update', repo_name=repo_name), data, status=302)
settings = SettingsModel(repo=repo_name)
try:
for section, key in VcsSettingsModel.HOOKS_SETTINGS:
@ -579,7 +137,7 @@ class TestVcsSettings(object):
data = self.FORM_DATA.copy()
data['csrf_token'] = csrf_token
self.app.post(
url('repo_vcs_settings', repo_name=repo_name), data, status=302)
route_path('edit_repo_vcs_update', repo_name=repo_name), data, status=302)
settings = SettingsModel(repo=repo_name)
try:
for section, key in VcsSettingsModel.HOOKS_SETTINGS:
@ -599,7 +157,7 @@ class TestVcsSettings(object):
data = self.FORM_DATA.copy()
data['csrf_token'] = csrf_token
self.app.post(
url('repo_vcs_settings', repo_name=repo_name), data, status=302)
route_path('edit_repo_vcs_update', repo_name=repo_name), data, status=302)
try:
for section, key in VcsSettingsModel.HOOKS_SETTINGS:
ui = settings.get_ui_by_section_and_key(section, key)
@ -617,7 +175,7 @@ class TestVcsSettings(object):
data = self.FORM_DATA.copy()
data['csrf_token'] = csrf_token
self.app.post(
url('repo_vcs_settings', repo_name=repo_name), data, status=302)
route_path('edit_repo_vcs_update', repo_name=repo_name), data, status=302)
try:
for section, key in VcsSettingsModel.HOOKS_SETTINGS:
ui = settings.get_ui_by_section_and_key(section, key)
@ -632,7 +190,7 @@ class TestVcsSettings(object):
data = self.FORM_DATA.copy()
data['csrf_token'] = csrf_token
self.app.post(
url('repo_vcs_settings', repo_name=repo_name), data, status=302)
route_path('edit_repo_vcs_update', repo_name=repo_name), data, status=302)
settings = SettingsModel(repo=repo_name)
try:
for name in VcsSettingsModel.GENERAL_SETTINGS:
@ -647,7 +205,7 @@ class TestVcsSettings(object):
data = self.FORM_DATA.copy()
data['csrf_token'] = csrf_token
self.app.post(
url('repo_vcs_settings', repo_name=repo_name), data, status=302)
route_path('edit_repo_vcs_update', repo_name=repo_name), data, status=302)
settings = SettingsModel(repo=repo_name)
try:
for name in VcsSettingsModel.GENERAL_SETTINGS:
@ -675,7 +233,7 @@ class TestVcsSettings(object):
try:
self.app.post(
url('repo_vcs_settings', repo_name=repo_name), data,
route_path('edit_repo_vcs_update', repo_name=repo_name), data,
status=302)
finally:
self._cleanup_repo_settings(settings)
@ -691,7 +249,7 @@ class TestVcsSettings(object):
data = self.FORM_DATA.copy()
data['csrf_token'] = csrf_token
self.app.post(
url('repo_vcs_settings', repo_name=repo_name), data, status=302)
route_path('edit_repo_vcs_update', repo_name=repo_name), data, status=302)
try:
for name in VcsSettingsModel.GENERAL_SETTINGS:
setting = settings.get_setting_by_name(name)
@ -709,7 +267,7 @@ class TestVcsSettings(object):
data = self.FORM_DATA.copy()
data['csrf_token'] = csrf_token
self.app.post(
url('repo_vcs_settings', repo_name=repo_name), data, status=302)
route_path('edit_repo_vcs_update', repo_name=repo_name), data, status=302)
try:
for name in VcsSettingsModel.GENERAL_SETTINGS:
setting = settings.get_setting_by_name(name)
@ -733,7 +291,7 @@ class TestVcsSettings(object):
VcsSettingsModel.SVN_TAG_SECTION, 'svn-tag')
self.app.post(
url('repo_vcs_settings', repo_name=repo_name), data, status=302)
route_path('edit_repo_vcs_update', repo_name=repo_name), data, status=302)
settings = SettingsModel(repo=repo_name)
try:
svn_branches = settings.get_ui_by_section(
@ -761,7 +319,7 @@ class TestVcsSettings(object):
repo, VcsSettingsModel.SVN_TAG_SECTION, 'test_tag')
response = self.app.post(
url('repo_vcs_settings', repo_name=repo_name), data, status=200)
route_path('edit_repo_vcs_update', repo_name=repo_name), data, status=200)
response.mustcontain('Pattern already exists')
def test_svn_settings_with_empty_values_are_not_created(
@ -770,7 +328,7 @@ class TestVcsSettings(object):
data = self.FORM_DATA.copy()
data['csrf_token'] = csrf_token
self.app.post(
url('repo_vcs_settings', repo_name=repo_name), data, status=302)
route_path('edit_repo_vcs_update', repo_name=repo_name), data, status=302)
settings = SettingsModel(repo=repo_name)
try:
svn_branches = settings.get_ui_by_section(
@ -786,7 +344,7 @@ class TestVcsSettings(object):
self, autologin_user, backend_svn, csrf_token):
repo_name = backend_svn.repo_name
response = self.app.get(
url('repo_vcs_settings', repo_name=repo_name), status=200)
route_path('edit_repo_vcs', repo_name=repo_name), status=200)
response.mustcontain('Subversion Settings')
@pytest.mark.skip_backends('svn')
@ -796,7 +354,7 @@ class TestVcsSettings(object):
data = self.FORM_DATA.copy()
data['csrf_token'] = csrf_token
self.app.post(
url('repo_vcs_settings', repo_name=repo_name), data, status=302)
route_path('edit_repo_vcs_update', repo_name=repo_name), data, status=302)
settings = SettingsModel(repo=repo_name)
try:
svn_branches = settings.get_ui_by_section(
@ -813,7 +371,7 @@ class TestVcsSettings(object):
self, autologin_user, backend, csrf_token):
repo_name = backend.repo_name
response = self.app.get(
url('repo_vcs_settings', repo_name=repo_name), status=200)
route_path('edit_repo_vcs', repo_name=repo_name), status=200)
response.mustcontain(no='Subversion Settings')
def test_hg_settings_are_created(
@ -824,7 +382,7 @@ class TestVcsSettings(object):
data['new_svn_branch'] = 'svn-branch'
data['csrf_token'] = csrf_token
self.app.post(
url('repo_vcs_settings', repo_name=repo_name), data, status=302)
route_path('edit_repo_vcs_update', repo_name=repo_name), data, status=302)
settings = SettingsModel(repo=repo_name)
try:
largefiles_ui = settings.get_ui_by_section_and_key(
@ -848,7 +406,7 @@ class TestVcsSettings(object):
data = self.FORM_DATA.copy()
data['csrf_token'] = csrf_token
self.app.post(
url('repo_vcs_settings', repo_name=repo_name), data, status=302)
route_path('edit_repo_vcs_update', repo_name=repo_name), data, status=302)
try:
largefiles_ui = settings.get_ui_by_section_and_key(
'extensions', 'largefiles')
@ -863,7 +421,7 @@ class TestVcsSettings(object):
self, autologin_user, backend_hg, csrf_token):
repo_name = backend_hg.repo_name
response = self.app.get(
url('repo_vcs_settings', repo_name=repo_name), status=200)
route_path('edit_repo_vcs', repo_name=repo_name), status=200)
response.mustcontain('Mercurial Settings')
@pytest.mark.skip_backends('hg')
@ -873,7 +431,7 @@ class TestVcsSettings(object):
data = self.FORM_DATA.copy()
data['csrf_token'] = csrf_token
self.app.post(
url('repo_vcs_settings', repo_name=repo_name), data, status=302)
route_path('edit_repo_vcs_update', repo_name=repo_name), data, status=302)
settings = SettingsModel(repo=repo_name)
try:
largefiles_ui = settings.get_ui_by_section_and_key(
@ -890,7 +448,7 @@ class TestVcsSettings(object):
self, autologin_user, backend, csrf_token):
repo_name = backend.repo_name
response = self.app.get(
url('repo_vcs_settings', repo_name=repo_name), status=200)
route_path('edit_repo_vcs', repo_name=repo_name), status=200)
response.mustcontain(no='Mercurial Settings')
@pytest.mark.skip_backends('hg')
@ -906,7 +464,7 @@ class TestVcsSettings(object):
data = self.FORM_DATA.copy()
data['csrf_token'] = csrf_token
self.app.post(
url('repo_vcs_settings', repo_name=repo_name), data, status=302)
route_path('edit_repo_vcs_update', repo_name=repo_name), data, status=302)
try:
largefiles_ui = settings.get_ui_by_section_and_key(
'extensions', 'largefiles')
@ -932,7 +490,7 @@ class TestVcsSettings(object):
for i in range(10)]
response = self.app.get(
url('repo_vcs_settings', repo_name=repo_name), status=200)
route_path('edit_repo_vcs', repo_name=repo_name), status=200)
assert_response = AssertResponse(response)
for branch in branches:
css_selector = '[name=branch_value_{}]'.format(branch.ui_id)
@ -948,7 +506,7 @@ class TestVcsSettings(object):
repo = backend_svn.create_repo()
repo_name = repo.repo_name
response = self.app.get(
url('repo_vcs_settings', repo_name=repo_name), status=200)
route_path('edit_repo_vcs', repo_name=repo_name), status=200)
response.mustcontain(no='<label>Hooks:</label>')
response.mustcontain(no='<label>Pull Request Settings:</label>')
@ -959,7 +517,7 @@ class TestVcsSettings(object):
data['csrf_token'] = csrf_token
data['inherit_global_settings'] = True
self.app.post(
url('repo_vcs_settings', repo_name=repo_name), data, status=302)
route_path('edit_repo_vcs_update', repo_name=repo_name), data, status=302)
settings = SettingsModel(repo=repo_name)
vcs_settings = VcsSettingsModel(repo=repo_name)
@ -977,10 +535,10 @@ class TestVcsSettings(object):
settings = SettingsModel(repo=repo_name)
invalidation_patcher = mock.patch(
'rhodecode.controllers.admin.repos.ScmModel.mark_for_invalidation')
'rhodecode.model.scm.ScmModel.mark_for_invalidation')
with invalidation_patcher as invalidation_mock:
self.app.post(
url('repo_vcs_settings', repo_name=repo_name), data,
route_path('edit_repo_vcs_update', repo_name=repo_name), data,
status=302)
try:
invalidation_mock.assert_called_once_with(repo_name, delete=True)
@ -994,7 +552,7 @@ class TestVcsSettings(object):
data['csrf_token'] = csrf_token
data['inherit_global_settings'] = True
self.app.post(
url('repo_vcs_settings', repo_name=repo_name), data, status=302)
route_path('edit_repo_vcs_update', repo_name=repo_name), data, status=302)
settings = SettingsModel(repo=repo_name)
ui_settings = (
@ -1019,7 +577,7 @@ class TestVcsSettings(object):
self._cleanup_repo_settings(settings)
def test_delete_svn_branch_and_tag_patterns(
self, autologin_user, backend_svn, settings_util, csrf_token):
self, autologin_user, backend_svn, settings_util, csrf_token, xhr_header):
repo = backend_svn.create_repo()
repo_name = repo.repo_name
branch = settings_util.create_repo_rhodecode_ui(
@ -1028,19 +586,18 @@ class TestVcsSettings(object):
tag = settings_util.create_repo_rhodecode_ui(
repo, VcsSettingsModel.SVN_TAG_SECTION, 'test_tag', cleanup=False)
data = {
'_method': 'delete',
'csrf_token': csrf_token
}
for id_ in (branch.ui_id, tag.ui_id):
data['delete_svn_pattern'] = id_,
self.app.post(
url('repo_vcs_settings', repo_name=repo_name), data,
headers={'X-REQUESTED-WITH': 'XMLHttpRequest', }, status=200)
route_path('edit_repo_vcs_svn_pattern_delete', repo_name=repo_name),
data, extra_environ=xhr_header, status=200)
settings = VcsSettingsModel(repo=repo_name)
assert settings.get_repo_svn_branch_patterns() == []
def test_delete_svn_branch_requires_repo_admin_permission(
self, backend_svn, user_util, settings_util, csrf_token):
self, backend_svn, user_util, settings_util, csrf_token, xhr_header):
repo = backend_svn.create_repo()
repo_name = repo.repo_name
@ -1056,36 +613,33 @@ class TestVcsSettings(object):
repo, VcsSettingsModel.SVN_BRANCH_SECTION, 'test_branch',
cleanup=False)
data = {
'_method': 'delete',
'csrf_token': csrf_token,
'delete_svn_pattern': branch.ui_id
}
self.app.post(
url('repo_vcs_settings', repo_name=repo_name), data,
headers={'X-REQUESTED-WITH': 'XMLHttpRequest', }, status=200)
route_path('edit_repo_vcs_svn_pattern_delete', repo_name=repo_name),
data, extra_environ=xhr_header, status=200)
def test_delete_svn_branch_raises_400_when_not_found(
self, autologin_user, backend_svn, settings_util, csrf_token):
self, autologin_user, backend_svn, settings_util, csrf_token, xhr_header):
repo_name = backend_svn.repo_name
data = {
'_method': 'delete',
'delete_svn_pattern': 123,
'csrf_token': csrf_token
}
self.app.post(
url('repo_vcs_settings', repo_name=repo_name), data,
headers={'X-REQUESTED-WITH': 'XMLHttpRequest', }, status=400)
route_path('edit_repo_vcs_svn_pattern_delete', repo_name=repo_name),
data, extra_environ=xhr_header, status=400)
def test_delete_svn_branch_raises_400_when_no_id_specified(
self, autologin_user, backend_svn, settings_util, csrf_token):
self, autologin_user, backend_svn, settings_util, csrf_token, xhr_header):
repo_name = backend_svn.repo_name
data = {
'_method': 'delete',
'csrf_token': csrf_token
}
self.app.post(
url('repo_vcs_settings', repo_name=repo_name), data,
headers={'X-REQUESTED-WITH': 'XMLHttpRequest', }, status=400)
route_path('edit_repo_vcs_svn_pattern_delete', repo_name=repo_name),
data, extra_environ=xhr_header, status=400)
def _cleanup_repo_settings(self, settings_model):
cleanup = []

View file

@ -24,7 +24,6 @@ import pytest
import rhodecode
from rhodecode.model.db import Repository
from rhodecode.model.settings import SettingsModel
from rhodecode.tests import url
from rhodecode.tests.utils import AssertResponse
@ -33,6 +32,8 @@ def route_path(name, params=None, **kwargs):
base_url = {
'edit_repo': '/{repo_name}/settings',
'edit_repo_vcs': '/{repo_name}/settings/vcs',
'edit_repo_vcs_update': '/{repo_name}/settings/vcs/update',
}[name].format(**kwargs)
if params:
@ -51,8 +52,8 @@ class TestAdminRepoVcsSettings(object):
if backend.alias not in setting_backends:
pytest.skip('Setting not available for backend {}'.format(backend))
vcs_settings_url = url(
'repo_vcs_settings', repo_name=backend.repo.repo_name)
vcs_settings_url = route_path(
'edit_repo_vcs', repo_name=backend.repo.repo_name)
with mock.patch.dict(
rhodecode.CONFIG, {'labs_settings_active': 'true'}):
@ -61,24 +62,6 @@ class TestAdminRepoVcsSettings(object):
assertr = AssertResponse(response)
assertr.one_element_exists('#rhodecode_{}'.format(setting_name))
@pytest.mark.parametrize('setting_name, setting_backends', [
('hg_use_rebase_for_merging', ['hg']),
])
def test_labs_settings_not_visible_if_disabled(
self, setting_name, setting_backends, backend):
if backend.alias not in setting_backends:
pytest.skip('Setting not available for backend {}'.format(backend))
vcs_settings_url = url(
'repo_vcs_settings', repo_name=backend.repo.repo_name)
with mock.patch.dict(
rhodecode.CONFIG, {'labs_settings_active': 'false'}):
response = self.app.get(vcs_settings_url)
assertr = AssertResponse(response)
assertr.no_element_exists('#rhodecode_{}'.format(setting_name))
@pytest.mark.parametrize('setting_name, setting_backends', [
('hg_use_rebase_for_merging', ['hg']),
])
@ -91,8 +74,8 @@ class TestAdminRepoVcsSettings(object):
repo_name = repo.repo_name
settings_model = SettingsModel(repo=repo)
vcs_settings_url = url(
'repo_vcs_settings', repo_name=repo_name)
vcs_settings_url = route_path(
'edit_repo_vcs_update', repo_name=repo_name)
self.app.post(
vcs_settings_url,

View file

@ -36,7 +36,7 @@ from rhodecode.lib.auth import (
from rhodecode.lib.compat import OrderedDict
from rhodecode.lib.exceptions import StatusChangeOnClosedPullRequestError
import rhodecode.lib.helpers as h
from rhodecode.lib.utils2 import safe_unicode, safe_int
from rhodecode.lib.utils2 import safe_unicode
from rhodecode.lib.vcs.backends.base import EmptyCommit
from rhodecode.lib.vcs.exceptions import (
RepositoryError, CommitDoesNotExistError, NodeDoesNotExistError)

View file

@ -29,10 +29,9 @@ from webhelpers.feedgenerator import Rss201rev2Feed, Atom1Feed
from rhodecode.apps._base import RepoAppView
from rhodecode.lib import audit_logger
from rhodecode.lib import helpers as h
from rhodecode.lib.auth import (LoginRequired, HasRepoPermissionAnyDecorator,
NotAnonymous, CSRFRequired)
from rhodecode.lib.auth import (
LoginRequired, HasRepoPermissionAnyDecorator)
from rhodecode.lib.diffs import DiffProcessor, LimitedDiffContainer
from rhodecode.lib.ext_json import json
from rhodecode.lib.utils2 import str2bool, safe_int
from rhodecode.model.db import UserApiKeys, CacheKey

View file

@ -23,8 +23,7 @@ import logging
from pyramid.view import view_config
from rhodecode.apps._base import RepoAppView
from rhodecode.lib.auth import (LoginRequired, HasRepoPermissionAnyDecorator,
NotAnonymous)
from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
from rhodecode.lib import repo_maintenance
log = logging.getLogger(__name__)
@ -43,7 +42,7 @@ class RepoMaintenanceView(RepoAppView):
@LoginRequired()
@HasRepoPermissionAnyDecorator('repository.admin')
@view_config(
route_name='repo_maintenance', request_method='GET',
route_name='edit_repo_maintenance', request_method='GET',
renderer='rhodecode:templates/admin/repos/repo_edit.mako')
def repo_maintenance(self):
c = self.load_default_context()
@ -55,7 +54,7 @@ class RepoMaintenanceView(RepoAppView):
@LoginRequired()
@HasRepoPermissionAnyDecorator('repository.admin')
@view_config(
route_name='repo_maintenance_execute', request_method='GET',
route_name='edit_repo_maintenance_execute', request_method='GET',
renderer='json', xhr=True)
def repo_maintenance_execute(self):
c = self.load_default_context()

View file

@ -20,23 +20,17 @@
import logging
import deform
from pyramid.httpexceptions import HTTPFound
from pyramid.view import view_config
from rhodecode.apps._base import RepoAppView
from rhodecode.forms import RcForm
from rhodecode.lib import helpers as h
from rhodecode.lib import audit_logger
from rhodecode.lib.auth import (
LoginRequired, HasRepoPermissionAnyDecorator,
HasRepoPermissionAllDecorator, CSRFRequired)
from rhodecode.model.db import RepositoryField, RepoGroup
LoginRequired, HasRepoPermissionAnyDecorator, CSRFRequired)
from rhodecode.model.forms import RepoPermsForm
from rhodecode.model.meta import Session
from rhodecode.model.repo import RepoModel
from rhodecode.model.scm import RepoGroupList, ScmModel
from rhodecode.model.validation_schema.schemas import repo_schema
log = logging.getLogger(__name__)
@ -63,7 +57,7 @@ class RepoSettingsPermissionsView(RepoAppView):
return self._get_template_context(c)
@LoginRequired()
@HasRepoPermissionAllDecorator('repository.admin')
@HasRepoPermissionAnyDecorator('repository.admin')
@CSRFRequired()
@view_config(
route_name='edit_repo_perms', request_method='POST',
@ -74,7 +68,7 @@ class RepoSettingsPermissionsView(RepoAppView):
c.active = 'permissions'
data = self.request.POST
# store private flag outside of HTML to verify if we can modify
# default user permissions, prevents submition of FAKE post data
# default user permissions, prevents submission of FAKE post data
# into the form for private repos
data['repo_private'] = self.db_repo.private
form = RepoPermsForm()().to_python(data)
@ -95,4 +89,4 @@ class RepoSettingsPermissionsView(RepoAppView):
h.flash(_('Repository permissions updated'), category='success')
raise HTTPFound(
self.request.route_path('edit_repo_perms', repo_name=self.db_repo_name))
h.route_path('edit_repo_perms', repo_name=self.db_repo_name))

View file

@ -29,9 +29,8 @@ from rhodecode.forms import RcForm
from rhodecode.lib import helpers as h
from rhodecode.lib import audit_logger
from rhodecode.lib.auth import (
LoginRequired, HasRepoPermissionAnyDecorator,
HasRepoPermissionAllDecorator, CSRFRequired)
from rhodecode.model.db import RepositoryField, RepoGroup
LoginRequired, HasRepoPermissionAnyDecorator, CSRFRequired)
from rhodecode.model.db import RepositoryField, RepoGroup, Repository
from rhodecode.model.meta import Session
from rhodecode.model.repo import RepoModel
from rhodecode.model.scm import RepoGroupList, ScmModel
@ -109,7 +108,7 @@ class RepoSettingsView(RepoAppView):
return self._get_template_context(c)
@LoginRequired()
@HasRepoPermissionAllDecorator('repository.admin')
@HasRepoPermissionAnyDecorator('repository.admin')
@CSRFRequired()
@view_config(
route_name='edit_repo', request_method='POST',
@ -176,4 +175,80 @@ class RepoSettingsView(RepoAppView):
old_repo_name), category='error')
raise HTTPFound(
self.request.route_path('edit_repo', repo_name=new_repo_name))
h.route_path('edit_repo', repo_name=new_repo_name))
@LoginRequired()
@HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
@view_config(
route_name='repo_edit_toggle_locking', request_method='GET',
renderer='rhodecode:templates/admin/repos/repo_edit.mako')
def toggle_locking(self):
"""
Toggle locking of repository by simple GET call to url
"""
_ = self.request.translate
repo = self.db_repo
try:
if repo.enable_locking:
if repo.locked[0]:
Repository.unlock(repo)
action = _('Unlocked')
else:
Repository.lock(
repo, self._rhodecode_user.user_id,
lock_reason=Repository.LOCK_WEB)
action = _('Locked')
h.flash(_('Repository has been %s') % action,
category='success')
except Exception:
log.exception("Exception during unlocking")
h.flash(_('An error occurred during unlocking'),
category='error')
raise HTTPFound(
h.route_path('repo_summary', repo_name=self.db_repo_name))
@LoginRequired()
@HasRepoPermissionAnyDecorator('repository.admin')
@view_config(
route_name='edit_repo_statistics', request_method='GET',
renderer='rhodecode:templates/admin/repos/repo_edit.mako')
def edit_statistics_form(self):
c = self.load_default_context()
if self.db_repo.stats:
# this is on what revision we ended up so we add +1 for count
last_rev = self.db_repo.stats.stat_on_revision + 1
else:
last_rev = 0
c.active = 'statistics'
c.stats_revision = last_rev
c.repo_last_rev = self.rhodecode_vcs_repo.count()
if last_rev == 0 or c.repo_last_rev == 0:
c.stats_percentage = 0
else:
c.stats_percentage = '%.2f' % (
(float((last_rev)) / c.repo_last_rev) * 100)
return self._get_template_context(c)
@LoginRequired()
@HasRepoPermissionAnyDecorator('repository.admin')
@CSRFRequired()
@view_config(
route_name='edit_repo_statistics_reset', request_method='POST',
renderer='rhodecode:templates/admin/repos/repo_edit.mako')
def repo_statistics_reset(self):
_ = self.request.translate
try:
RepoModel().delete_stats(self.db_repo_name)
Session().commit()
except Exception:
log.exception('Edit statistics failure')
h.flash(_('An error occurred during deletion of repository stats'),
category='error')
raise HTTPFound(
h.route_path('edit_repo_statistics', repo_name=self.db_repo_name))

View file

@ -61,7 +61,7 @@ class RepoSettingsView(RepoAppView):
c.default_user_id = User.get_default_user().user_id
c.in_public_journal = UserFollowing.query() \
.filter(UserFollowing.user_id == c.default_user_id) \
.filter(UserFollowing.follows_repository == c.repo_info).scalar()
.filter(UserFollowing.follows_repository == self.db_repo).scalar()
c.has_origin_repo_read_perm = False
if self.db_repo.fork:

View file

@ -0,0 +1,113 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2017-2017 RhodeCode GmbH
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License, version 3
# (only), as published by the Free Software Foundation.
#
# 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 Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# This program is dual-licensed. If you wish to learn more about the
# RhodeCode Enterprise Edition, including its added features, Support services,
# and proprietary license terms, please see https://rhodecode.com/licenses/
import logging
import formencode
from pyramid.httpexceptions import HTTPFound
from pyramid.view import view_config
from rhodecode.apps._base import RepoAppView
from rhodecode.lib import audit_logger
from rhodecode.lib import helpers as h
from rhodecode.lib.auth import (
LoginRequired, HasRepoPermissionAnyDecorator, CSRFRequired)
from rhodecode.model.db import RepositoryField
from rhodecode.model.forms import RepoFieldForm
from rhodecode.model.meta import Session
from rhodecode.model.repo import RepoModel
log = logging.getLogger(__name__)
class RepoSettingsFieldsView(RepoAppView):
def load_default_context(self):
c = self._get_local_tmpl_context()
# TODO(marcink): remove repo_info and use c.rhodecode_db_repo instead
c.repo_info = self.db_repo
self._register_global_c(c)
return c
@LoginRequired()
@HasRepoPermissionAnyDecorator('repository.admin')
@view_config(
route_name='edit_repo_fields', request_method='GET',
renderer='rhodecode:templates/admin/repos/repo_edit.mako')
def repo_field_edit(self):
c = self.load_default_context()
c.active = 'fields'
c.repo_fields = RepositoryField.query() \
.filter(RepositoryField.repository == self.db_repo).all()
return self._get_template_context(c)
@LoginRequired()
@HasRepoPermissionAnyDecorator('repository.admin')
@CSRFRequired()
@view_config(
route_name='edit_repo_fields_create', request_method='POST',
renderer='rhodecode:templates/admin/repos/repo_edit.mako')
def repo_field_create(self):
_ = self.request.translate
try:
form_result = RepoFieldForm()().to_python(dict(self.request.POST))
RepoModel().add_repo_field(
self.db_repo_name,
form_result['new_field_key'],
field_type=form_result['new_field_type'],
field_value=form_result['new_field_value'],
field_label=form_result['new_field_label'],
field_desc=form_result['new_field_desc'])
Session().commit()
except Exception as e:
log.exception("Exception creating field")
msg = _('An error occurred during creation of field')
if isinstance(e, formencode.Invalid):
msg += ". " + e.msg
h.flash(msg, category='error')
raise HTTPFound(
h.route_path('edit_repo_fields', repo_name=self.db_repo_name))
@LoginRequired()
@HasRepoPermissionAnyDecorator('repository.admin')
@CSRFRequired()
@view_config(
route_name='edit_repo_fields_delete', request_method='POST',
renderer='rhodecode:templates/admin/repos/repo_edit.mako')
def repo_field_delete(self):
_ = self.request.translate
field = RepositoryField.get_or_404(self.request.matchdict['field_id'])
try:
RepoModel().delete_repo_field(self.db_repo_name, field.field_key)
Session().commit()
except Exception:
log.exception('Exception during removal of field')
msg = _('An error occurred during removal of field')
h.flash(msg, category='error')
raise HTTPFound(
h.route_path('edit_repo_fields', repo_name=self.db_repo_name))

View file

@ -0,0 +1,129 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2017-2017 RhodeCode GmbH
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License, version 3
# (only), as published by the Free Software Foundation.
#
# 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 Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# This program is dual-licensed. If you wish to learn more about the
# RhodeCode Enterprise Edition, including its added features, Support services,
# and proprietary license terms, please see https://rhodecode.com/licenses/
import logging
from pyramid.httpexceptions import HTTPFound
from pyramid.view import view_config
from rhodecode.apps._base import RepoAppView
from rhodecode.lib import audit_logger
from rhodecode.lib import helpers as h
from rhodecode.lib.auth import (
LoginRequired, HasRepoPermissionAnyDecorator, CSRFRequired)
from rhodecode.model.forms import IssueTrackerPatternsForm
from rhodecode.model.meta import Session
from rhodecode.model.settings import IssueTrackerSettingsModel
log = logging.getLogger(__name__)
class RepoSettingsIssueTrackersView(RepoAppView):
def load_default_context(self):
c = self._get_local_tmpl_context()
# TODO(marcink): remove repo_info and use c.rhodecode_db_repo instead
c.repo_info = self.db_repo
self._register_global_c(c)
return c
@LoginRequired()
@HasRepoPermissionAnyDecorator('repository.admin')
@view_config(
route_name='edit_repo_issuetracker', request_method='GET',
renderer='rhodecode:templates/admin/repos/repo_edit.mako')
def repo_issuetracker(self):
c = self.load_default_context()
c.active = 'issuetracker'
c.data = 'data'
c.settings_model = IssueTrackerSettingsModel(repo=self.db_repo)
c.global_patterns = c.settings_model.get_global_settings()
c.repo_patterns = c.settings_model.get_repo_settings()
return self._get_template_context(c)
@LoginRequired()
@HasRepoPermissionAnyDecorator('repository.admin')
@CSRFRequired()
@view_config(
route_name='edit_repo_issuetracker_test', request_method='POST',
xhr=True, renderer='string')
def repo_issuetracker_test(self):
return h.urlify_commit_message(
self.request.POST.get('test_text', ''),
self.db_repo_name)
@LoginRequired()
@HasRepoPermissionAnyDecorator('repository.admin')
@CSRFRequired()
@view_config(
route_name='edit_repo_issuetracker_delete', request_method='POST',
renderer='rhodecode:templates/admin/repos/repo_edit.mako')
def repo_issuetracker_delete(self):
_ = self.request.translate
uid = self.request.POST.get('uid')
repo_settings = IssueTrackerSettingsModel(repo=self.db_repo_name)
try:
repo_settings.delete_entries(uid)
except Exception:
h.flash(_('Error occurred during deleting issue tracker entry'),
category='error')
else:
h.flash(_('Removed issue tracker entry'), category='success')
raise HTTPFound(
h.route_path('edit_repo_issuetracker', repo_name=self.db_repo_name))
def _update_patterns(self, form, repo_settings):
for uid in form['delete_patterns']:
repo_settings.delete_entries(uid)
for pattern_data in form['patterns']:
for setting_key, pattern, type_ in pattern_data:
sett = repo_settings.create_or_update_setting(
setting_key, pattern.strip(), type_)
Session().add(sett)
Session().commit()
@LoginRequired()
@HasRepoPermissionAnyDecorator('repository.admin')
@CSRFRequired()
@view_config(
route_name='edit_repo_issuetracker_update', request_method='POST',
renderer='rhodecode:templates/admin/repos/repo_edit.mako')
def repo_issuetracker_update(self):
_ = self.request.translate
# Save inheritance
repo_settings = IssueTrackerSettingsModel(repo=self.db_repo_name)
inherited = (
self.request.POST.get('inherit_global_issuetracker') == "inherited")
repo_settings.inherit_global_settings = inherited
Session().commit()
form = IssueTrackerPatternsForm()().to_python(self.request.POST)
if form:
self._update_patterns(form, repo_settings)
h.flash(_('Updated issue tracker entries'), category='success')
raise HTTPFound(
h.route_path('edit_repo_issuetracker', repo_name=self.db_repo_name))

View file

@ -0,0 +1,75 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2017-2017 RhodeCode GmbH
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License, version 3
# (only), as published by the Free Software Foundation.
#
# 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 Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# This program is dual-licensed. If you wish to learn more about the
# RhodeCode Enterprise Edition, including its added features, Support services,
# and proprietary license terms, please see https://rhodecode.com/licenses/
import logging
from pyramid.httpexceptions import HTTPFound
from pyramid.view import view_config
from rhodecode.apps._base import RepoAppView
from rhodecode.lib import helpers as h
from rhodecode.lib.auth import (
LoginRequired, CSRFRequired, HasRepoPermissionAnyDecorator)
from rhodecode.model.scm import ScmModel
log = logging.getLogger(__name__)
class RepoSettingsRemoteView(RepoAppView):
def load_default_context(self):
c = self._get_local_tmpl_context()
# TODO(marcink): remove repo_info and use c.rhodecode_db_repo instead
c.repo_info = self.db_repo
self._register_global_c(c)
return c
@LoginRequired()
@HasRepoPermissionAnyDecorator('repository.admin')
@view_config(
route_name='edit_repo_remote', request_method='GET',
renderer='rhodecode:templates/admin/repos/repo_edit.mako')
def repo_remote_edit_form(self):
c = self.load_default_context()
c.active = 'remote'
return self._get_template_context(c)
@LoginRequired()
@HasRepoPermissionAnyDecorator('repository.admin')
@CSRFRequired()
@view_config(
route_name='edit_repo_remote_pull', request_method='POST',
renderer=None)
def repo_remote_pull_changes(self):
_ = self.request.translate
self.load_default_context()
try:
ScmModel().pull_changes(
self.db_repo_name, self._rhodecode_user.username)
h.flash(_('Pulled from remote location'), category='success')
except Exception:
log.exception("Exception during pull from remote")
h.flash(_('An error occurred during pull from remote location'),
category='error')
raise HTTPFound(
h.route_path('edit_repo_remote', repo_name=self.db_repo_name))

View file

@ -0,0 +1,172 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2017-2017 RhodeCode GmbH
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License, version 3
# (only), as published by the Free Software Foundation.
#
# 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 Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# This program is dual-licensed. If you wish to learn more about the
# RhodeCode Enterprise Edition, including its added features, Support services,
# and proprietary license terms, please see https://rhodecode.com/licenses/
import logging
import formencode
from pyramid.httpexceptions import HTTPFound, HTTPBadRequest
from pyramid.response import Response
from pyramid.renderers import render
from pyramid.view import view_config
from rhodecode.apps._base import RepoAppView
from rhodecode.lib import audit_logger
from rhodecode.lib import helpers as h
from rhodecode.lib.auth import (
LoginRequired, HasRepoPermissionAnyDecorator, CSRFRequired)
from rhodecode.model.forms import RepoVcsSettingsForm
from rhodecode.model.meta import Session
from rhodecode.model.settings import VcsSettingsModel, SettingNotFound
log = logging.getLogger(__name__)
class RepoSettingsVcsView(RepoAppView):
def load_default_context(self):
c = self._get_local_tmpl_context()
# TODO(marcink): remove repo_info and use c.rhodecode_db_repo instead
c.repo_info = self.db_repo
self._register_global_c(c)
return c
def _vcs_form_defaults(self, repo_name):
model = VcsSettingsModel(repo=repo_name)
global_defaults = model.get_global_settings()
repo_defaults = {}
repo_defaults.update(global_defaults)
repo_defaults.update(model.get_repo_settings())
global_defaults = {
'{}_inherited'.format(k): global_defaults[k]
for k in global_defaults}
defaults = {
'inherit_global_settings': model.inherit_global_settings
}
defaults.update(global_defaults)
defaults.update(repo_defaults)
defaults.update({
'new_svn_branch': '',
'new_svn_tag': '',
})
return defaults
@LoginRequired()
@HasRepoPermissionAnyDecorator('repository.admin')
@view_config(
route_name='edit_repo_vcs', request_method='GET',
renderer='rhodecode:templates/admin/repos/repo_edit.mako')
def repo_vcs_settings(self):
c = self.load_default_context()
model = VcsSettingsModel(repo=self.db_repo_name)
c.active = 'vcs'
c.global_svn_branch_patterns = model.get_global_svn_branch_patterns()
c.global_svn_tag_patterns = model.get_global_svn_tag_patterns()
c.svn_branch_patterns = model.get_repo_svn_branch_patterns()
c.svn_tag_patterns = model.get_repo_svn_tag_patterns()
defaults = self._vcs_form_defaults(self.db_repo_name)
c.inherit_global_settings = defaults['inherit_global_settings']
data = render('rhodecode:templates/admin/repos/repo_edit.mako',
self._get_template_context(c), self.request)
html = formencode.htmlfill.render(
data,
defaults=defaults,
encoding="UTF-8",
force_defaults=False
)
return Response(html)
@LoginRequired()
@HasRepoPermissionAnyDecorator('repository.admin')
@CSRFRequired()
@view_config(
route_name='edit_repo_vcs_update', request_method='POST',
renderer='rhodecode:templates/admin/repos/repo_edit.mako')
def repo_settings_vcs_update(self):
_ = self.request.translate
c = self.load_default_context()
c.active = 'vcs'
model = VcsSettingsModel(repo=self.db_repo_name)
c.global_svn_branch_patterns = model.get_global_svn_branch_patterns()
c.global_svn_tag_patterns = model.get_global_svn_tag_patterns()
c.svn_branch_patterns = model.get_repo_svn_branch_patterns()
c.svn_tag_patterns = model.get_repo_svn_tag_patterns()
defaults = self._vcs_form_defaults(self.db_repo_name)
c.inherit_global_settings = defaults['inherit_global_settings']
application_form = RepoVcsSettingsForm(self.db_repo_name)()
try:
form_result = application_form.to_python(dict(self.request.POST))
except formencode.Invalid as errors:
h.flash(_("Some form inputs contain invalid data."),
category='error')
data = render('rhodecode:templates/admin/repos/repo_edit.mako',
self._get_template_context(c), self.request)
html = formencode.htmlfill.render(
data,
defaults=errors.value,
errors=errors.error_dict or {},
encoding="UTF-8",
force_defaults=False
)
return Response(html)
try:
inherit_global_settings = form_result['inherit_global_settings']
model.create_or_update_repo_settings(
form_result, inherit_global_settings=inherit_global_settings)
Session().commit()
h.flash(_('Updated VCS settings'), category='success')
except Exception:
log.exception("Exception while updating settings")
h.flash(
_('Error occurred during updating repository VCS settings'),
category='error')
raise HTTPFound(
h.route_path('edit_repo_vcs', repo_name=self.db_repo_name))
@LoginRequired()
@HasRepoPermissionAnyDecorator('repository.admin')
@CSRFRequired()
@view_config(
route_name='edit_repo_vcs_svn_pattern_delete', request_method='POST',
renderer='json_ext', xhr=True)
def repo_settings_delete_svn_pattern(self):
self.load_default_context()
delete_pattern_id = self.request.POST.get('delete_svn_pattern')
model = VcsSettingsModel(repo=self.db_repo_name)
try:
model.delete_repo_svn_pattern(delete_pattern_id)
except SettingNotFound:
log.exception('Failed to delete SVN pattern')
raise HTTPBadRequest()
Session().commit()
return True

View file

@ -24,8 +24,8 @@ from pyramid.view import view_config
from rhodecode.apps._base import RepoAppView
from rhodecode.lib import audit_logger
from rhodecode.lib import helpers as h
from rhodecode.lib.auth import (LoginRequired, HasRepoPermissionAnyDecorator,
NotAnonymous, CSRFRequired)
from rhodecode.lib.auth import (
LoginRequired, HasRepoPermissionAnyDecorator, CSRFRequired)
from rhodecode.lib.ext_json import json
log = logging.getLogger(__name__)
@ -44,7 +44,7 @@ class StripView(RepoAppView):
@LoginRequired()
@HasRepoPermissionAnyDecorator('repository.admin')
@view_config(
route_name='strip', request_method='GET',
route_name='edit_repo_strip', request_method='GET',
renderer='rhodecode:templates/admin/repos/repo_edit.mako')
def strip(self):
c = self.load_default_context()
@ -99,10 +99,10 @@ class StripView(RepoAppView):
continue
try:
ScmModel().strip(
repo=c.repo_info,
repo=self.db_repo,
commit_id=commit['rev'], branch=commit['branch'])
log.info('Stripped commit %s from repo `%s` by %s' % (
commit['rev'], c.repo_info.repo_name, user))
commit['rev'], self.db_repo_name, user))
data[commit['rev']] = True
audit_logger.store_web(

View file

@ -22,12 +22,9 @@ import logging
import string
from pyramid.view import view_config
from beaker.cache import cache_region
from rhodecode.controllers import utils
from rhodecode.apps._base import RepoAppView
from rhodecode.config.conf import (LANGUAGES_EXTENSIONS_MAP)
from rhodecode.lib import caches, helpers as h
@ -74,11 +71,16 @@ class RepoSummaryView(RepoAppView):
log.debug("Searching for a README file.")
readme_node = ReadmeFinder(default_renderer).search(commit)
if readme_node:
relative_url = h.route_path(
'repo_file_raw', repo_name=repo_name,
commit_id=commit.raw_id, f_path=readme_node.path)
relative_urls = {
'raw': h.route_path(
'repo_file_raw', repo_name=repo_name,
commit_id=commit.raw_id, f_path=readme_node.path),
'standard': h.route_path(
'repo_files', repo_name=repo_name,
commit_id=commit.raw_id, f_path=readme_node.path),
}
readme_data = self._render_readme_or_none(
commit, readme_node, relative_url)
commit, readme_node, relative_urls)
readme_filename = readme_node.path
return readme_data, readme_filename
@ -103,15 +105,15 @@ class RepoSummaryView(RepoAppView):
log.exception(
"Problem getting commit when trying to render the README.")
def _render_readme_or_none(self, commit, readme_node, relative_url):
def _render_readme_or_none(self, commit, readme_node, relative_urls):
log.debug(
'Found README file `%s` rendering...', readme_node.path)
renderer = MarkupRenderer()
try:
html_source = renderer.render(
readme_node.content, filename=readme_node.path)
if relative_url:
return relative_links(html_source, relative_url)
if relative_urls:
return relative_links(html_source, relative_urls)
return html_source
except Exception:
log.exception(

View file

@ -143,6 +143,8 @@ def load_pyramid_environment(global_config, settings):
# Store the settings to make them available to other modules.
rhodecode.PYRAMID_SETTINGS = settings_merged
# NOTE(marcink): needs to be enabled after full port to pyramid
# rhodecode.CONFIG = config
# If this is a test run we prepare the test environment like
# creating a test database, test search index and test repositories.

View file

@ -176,23 +176,6 @@ def make_map(config):
rmap.connect('ping', '%s/ping' % (ADMIN_PREFIX,), controller='home', action='ping')
rmap.connect('error_test', '%s/error_test' % (ADMIN_PREFIX,), controller='home', action='error_test')
# ADMIN REPOSITORY ROUTES
with rmap.submapper(path_prefix=ADMIN_PREFIX,
controller='admin/repos') as m:
m.connect('repos', '/repos',
action='create', conditions={'method': ['POST']})
m.connect('repos', '/repos',
action='index', conditions={'method': ['GET']})
m.connect('new_repo', '/create_repository', jsroute=True,
action='create_repository', conditions={'method': ['GET']})
m.connect('delete_repo', '/repos/{repo_name}',
action='delete', conditions={'method': ['DELETE']},
requirements=URL_NAME_REQUIREMENTS)
m.connect('repo', '/repos/{repo_name}',
action='show', conditions={'method': ['GET'],
'function': check_repo},
requirements=URL_NAME_REQUIREMENTS)
# ADMIN REPOSITORY GROUPS ROUTES
with rmap.submapper(path_prefix=ADMIN_PREFIX,
controller='admin/repo_groups') as m:
@ -406,81 +389,5 @@ def make_map(config):
m.connect('my_account_password', '/my_account/password',
action='my_account_password', conditions={'method': ['GET']})
#==========================================================================
# REPOSITORY ROUTES
#==========================================================================
# repo edit options
rmap.connect('edit_repo_fields', '/{repo_name}/settings/fields',
controller='admin/repos', action='edit_fields',
conditions={'method': ['GET'], 'function': check_repo},
requirements=URL_NAME_REQUIREMENTS)
rmap.connect('create_repo_fields', '/{repo_name}/settings/fields/new',
controller='admin/repos', action='create_repo_field',
conditions={'method': ['PUT'], 'function': check_repo},
requirements=URL_NAME_REQUIREMENTS)
rmap.connect('delete_repo_fields', '/{repo_name}/settings/fields/{field_id}',
controller='admin/repos', action='delete_repo_field',
conditions={'method': ['DELETE'], 'function': check_repo},
requirements=URL_NAME_REQUIREMENTS)
rmap.connect('toggle_locking', '/{repo_name}/settings/advanced/locking_toggle',
controller='admin/repos', action='toggle_locking',
conditions={'method': ['GET'], 'function': check_repo},
requirements=URL_NAME_REQUIREMENTS)
rmap.connect('edit_repo_remote', '/{repo_name}/settings/remote',
controller='admin/repos', action='edit_remote_form',
conditions={'method': ['GET'], 'function': check_repo},
requirements=URL_NAME_REQUIREMENTS)
rmap.connect('edit_repo_remote', '/{repo_name}/settings/remote',
controller='admin/repos', action='edit_remote',
conditions={'method': ['PUT'], 'function': check_repo},
requirements=URL_NAME_REQUIREMENTS)
rmap.connect('edit_repo_statistics', '/{repo_name}/settings/statistics',
controller='admin/repos', action='edit_statistics_form',
conditions={'method': ['GET'], 'function': check_repo},
requirements=URL_NAME_REQUIREMENTS)
rmap.connect('edit_repo_statistics', '/{repo_name}/settings/statistics',
controller='admin/repos', action='edit_statistics',
conditions={'method': ['PUT'], 'function': check_repo},
requirements=URL_NAME_REQUIREMENTS)
rmap.connect('repo_settings_issuetracker',
'/{repo_name}/settings/issue-tracker',
controller='admin/repos', action='repo_issuetracker',
conditions={'method': ['GET'], 'function': check_repo},
requirements=URL_NAME_REQUIREMENTS)
rmap.connect('repo_issuetracker_test',
'/{repo_name}/settings/issue-tracker/test',
controller='admin/repos', action='repo_issuetracker_test',
conditions={'method': ['POST'], 'function': check_repo},
requirements=URL_NAME_REQUIREMENTS)
rmap.connect('repo_issuetracker_delete',
'/{repo_name}/settings/issue-tracker/delete',
controller='admin/repos', action='repo_issuetracker_delete',
conditions={'method': ['DELETE'], 'function': check_repo},
requirements=URL_NAME_REQUIREMENTS)
rmap.connect('repo_issuetracker_save',
'/{repo_name}/settings/issue-tracker/save',
controller='admin/repos', action='repo_issuetracker_save',
conditions={'method': ['POST'], 'function': check_repo},
requirements=URL_NAME_REQUIREMENTS)
rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
controller='admin/repos', action='repo_settings_vcs_update',
conditions={'method': ['POST'], 'function': check_repo},
requirements=URL_NAME_REQUIREMENTS)
rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
controller='admin/repos', action='repo_settings_vcs',
conditions={'method': ['GET'], 'function': check_repo},
requirements=URL_NAME_REQUIREMENTS)
rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
controller='admin/repos', action='repo_delete_svn_pattern',
conditions={'method': ['DELETE'], 'function': check_repo},
requirements=URL_NAME_REQUIREMENTS)
rmap.connect('repo_pullrequest_settings', '/{repo_name}/settings/pullrequest',
controller='admin/repos', action='repo_settings_pullrequest',
conditions={'method': ['GET', 'POST'], 'function': check_repo},
requirements=URL_NAME_REQUIREMENTS)
return rmap

View file

@ -1,563 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2013-2017 RhodeCode GmbH
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License, version 3
# (only), as published by the Free Software Foundation.
#
# 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 Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# This program is dual-licensed. If you wish to learn more about the
# RhodeCode Enterprise Edition, including its added features, Support services,
# and proprietary license terms, please see https://rhodecode.com/licenses/
"""
Repositories controller for RhodeCode
"""
import logging
import traceback
import formencode
from formencode import htmlfill
from pylons import request, tmpl_context as c, url
from pylons.controllers.util import redirect
from pylons.i18n.translation import _
from webob.exc import HTTPForbidden, HTTPBadRequest
from pyramid.httpexceptions import HTTPFound
import rhodecode
from rhodecode.lib import auth, helpers as h
from rhodecode.lib.auth import (
LoginRequired, HasPermissionAllDecorator,
HasRepoPermissionAllDecorator, NotAnonymous, HasPermissionAny,
HasRepoGroupPermissionAny, HasRepoPermissionAnyDecorator)
from rhodecode.lib.base import BaseRepoController, render
from rhodecode.lib.ext_json import json
from rhodecode.lib.utils import repo_name_slug, jsonify
from rhodecode.lib.utils2 import safe_int, str2bool
from rhodecode.model.db import (Repository, RepoGroup, RepositoryField)
from rhodecode.model.forms import (
RepoForm, RepoFieldForm, RepoVcsSettingsForm, IssueTrackerPatternsForm)
from rhodecode.model.meta import Session
from rhodecode.model.repo import RepoModel
from rhodecode.model.scm import ScmModel, RepoGroupList, RepoList
from rhodecode.model.settings import (
SettingsModel, IssueTrackerSettingsModel, VcsSettingsModel,
SettingNotFound)
log = logging.getLogger(__name__)
class ReposController(BaseRepoController):
"""
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()
def __before__(self):
super(ReposController, self).__before__()
def _load_repo(self, repo_name):
repo_obj = Repository.get_by_repo_name(repo_name)
if repo_obj is None:
h.not_mapped_error(repo_name)
return redirect(url('repos'))
return repo_obj
def __load_defaults(self, repo=None):
acl_groups = RepoGroupList(RepoGroup.query().all(),
perm_set=['group.write', 'group.admin'])
c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
# in case someone no longer have a group.write access to a repository
# pre fill the list with this entry, we don't care if this is the same
# but it will allow saving repo data properly.
repo_group = None
if repo:
repo_group = repo.group
if repo_group and unicode(repo_group.group_id) not in c.repo_groups_choices:
c.repo_groups_choices.append(unicode(repo_group.group_id))
c.repo_groups.append(RepoGroup._generate_choice(repo_group))
choices, c.landing_revs = ScmModel().get_repo_landing_revs()
c.landing_revs_choices = choices
def __load_data(self, repo_name=None):
"""
Load defaults settings for edit, and update
:param repo_name:
"""
c.repo_info = self._load_repo(repo_name)
self.__load_defaults(c.repo_info)
# override defaults for exact repo info here git/hg etc
if not c.repository_requirements_missing:
choices, c.landing_revs = ScmModel().get_repo_landing_revs(
c.repo_info)
c.landing_revs_choices = choices
defaults = RepoModel()._get_defaults(repo_name)
return defaults
def _log_creation_exception(self, e, repo_name):
reason = None
if len(e.args) == 2:
reason = e.args[1]
if reason == 'INVALID_CERTIFICATE':
log.exception(
'Exception creating a repository: invalid certificate')
msg = (_('Error creating repository %s: invalid certificate')
% repo_name)
else:
log.exception("Exception creating a repository")
msg = (_('Error creating repository %s')
% repo_name)
return msg
@NotAnonymous()
def index(self, format='html'):
"""GET /repos: All items in the collection"""
# url('repos')
repo_list = Repository.get_all_repos()
c.repo_list = RepoList(repo_list, perm_set=['repository.admin'])
repos_data = RepoModel().get_repos_as_dict(
repo_list=c.repo_list, admin=True, super_user_actions=True)
# json used to render the grid
c.data = json.dumps(repos_data)
return render('admin/repos/repos.mako')
# perms check inside
@NotAnonymous()
@auth.CSRFRequired()
def create(self):
"""
POST /repos: Create a new item"""
# url('repos')
self.__load_defaults()
form_result = {}
task_id = None
c.personal_repo_group = c.rhodecode_user.personal_repo_group
try:
# CanWriteToGroup validators checks permissions of this POST
form_result = RepoForm(repo_groups=c.repo_groups_choices,
landing_revs=c.landing_revs_choices)()\
.to_python(dict(request.POST))
# create is done sometimes async on celery, db transaction
# management is handled there.
task = RepoModel().create(form_result, c.rhodecode_user.user_id)
from celery.result import BaseAsyncResult
if isinstance(task, BaseAsyncResult):
task_id = task.task_id
except formencode.Invalid as errors:
return htmlfill.render(
render('admin/repos/repo_add.mako'),
defaults=errors.value,
errors=errors.error_dict or {},
prefix_error=False,
encoding="UTF-8",
force_defaults=False)
except Exception as e:
msg = self._log_creation_exception(e, form_result.get('repo_name'))
h.flash(msg, category='error')
return redirect(h.route_path('home'))
raise HTTPFound(
h.route_path('repo_creating',
repo_name=form_result['repo_name_full'],
_query=dict(task_id=task_id)))
# perms check inside
@NotAnonymous()
def create_repository(self):
"""GET /_admin/create_repository: Form to create a new item"""
new_repo = request.GET.get('repo', '')
parent_group = safe_int(request.GET.get('parent_group'))
_gr = RepoGroup.get(parent_group)
if not HasPermissionAny('hg.admin', 'hg.create.repository')():
# you're not super admin nor have global create permissions,
# but maybe you have at least write permission to a parent group ?
gr_name = _gr.group_name if _gr else None
# create repositories with write permission on group is set to true
create_on_write = HasPermissionAny('hg.create.write_on_repogroup.true')()
group_admin = HasRepoGroupPermissionAny('group.admin')(group_name=gr_name)
group_write = HasRepoGroupPermissionAny('group.write')(group_name=gr_name)
if not (group_admin or (group_write and create_on_write)):
raise HTTPForbidden
acl_groups = RepoGroupList(RepoGroup.query().all(),
perm_set=['group.write', 'group.admin'])
c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
choices, c.landing_revs = ScmModel().get_repo_landing_revs()
c.personal_repo_group = c.rhodecode_user.personal_repo_group
c.new_repo = repo_name_slug(new_repo)
# apply the defaults from defaults page
defaults = SettingsModel().get_default_repo_settings(strip_prefix=True)
# set checkbox to autochecked
defaults['repo_copy_permissions'] = True
parent_group_choice = '-1'
if not c.rhodecode_user.is_admin and c.rhodecode_user.personal_repo_group:
parent_group_choice = c.rhodecode_user.personal_repo_group
if parent_group and _gr:
if parent_group in [x[0] for x in c.repo_groups]:
parent_group_choice = unicode(parent_group)
defaults.update({'repo_group': parent_group_choice})
return htmlfill.render(
render('admin/repos/repo_add.mako'),
defaults=defaults,
errors={},
prefix_error=False,
encoding="UTF-8",
force_defaults=False
)
@HasPermissionAllDecorator('hg.admin')
def show(self, repo_name, format='html'):
"""GET /repos/repo_name: Show a specific item"""
# url('repo', repo_name=ID)
@HasRepoPermissionAllDecorator('repository.admin')
def edit_fields(self, repo_name):
"""GET /repo_name/settings: Form to edit an existing item"""
c.repo_info = self._load_repo(repo_name)
c.repo_fields = RepositoryField.query()\
.filter(RepositoryField.repository == c.repo_info).all()
c.active = 'fields'
if request.POST:
return redirect(url('repo_edit_fields'))
return render('admin/repos/repo_edit.mako')
@HasRepoPermissionAllDecorator('repository.admin')
@auth.CSRFRequired()
def create_repo_field(self, repo_name):
try:
form_result = RepoFieldForm()().to_python(dict(request.POST))
RepoModel().add_repo_field(
repo_name, form_result['new_field_key'],
field_type=form_result['new_field_type'],
field_value=form_result['new_field_value'],
field_label=form_result['new_field_label'],
field_desc=form_result['new_field_desc'])
Session().commit()
except Exception as e:
log.exception("Exception creating field")
msg = _('An error occurred during creation of field')
if isinstance(e, formencode.Invalid):
msg += ". " + e.msg
h.flash(msg, category='error')
return redirect(url('edit_repo_fields', repo_name=repo_name))
@HasRepoPermissionAllDecorator('repository.admin')
@auth.CSRFRequired()
def delete_repo_field(self, repo_name, field_id):
field = RepositoryField.get_or_404(field_id)
try:
RepoModel().delete_repo_field(repo_name, field.field_key)
Session().commit()
except Exception as e:
log.exception("Exception during removal of field")
msg = _('An error occurred during removal of field')
h.flash(msg, category='error')
return redirect(url('edit_repo_fields', repo_name=repo_name))
@HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
@auth.CSRFRequired()
def toggle_locking(self, repo_name):
"""
Toggle locking of repository by simple GET call to url
:param repo_name:
"""
try:
repo = Repository.get_by_repo_name(repo_name)
if repo.enable_locking:
if repo.locked[0]:
Repository.unlock(repo)
action = _('Unlocked')
else:
Repository.lock(repo, c.rhodecode_user.user_id,
lock_reason=Repository.LOCK_WEB)
action = _('Locked')
h.flash(_('Repository has been %s') % action,
category='success')
except Exception:
log.exception("Exception during unlocking")
h.flash(_('An error occurred during unlocking'),
category='error')
return redirect(h.route_path('repo_summary', repo_name=repo_name))
@HasRepoPermissionAllDecorator('repository.admin')
@auth.CSRFRequired()
def edit_remote(self, repo_name):
"""PUT /{repo_name}/settings/remote: edit the repo remote."""
try:
ScmModel().pull_changes(repo_name, c.rhodecode_user.username)
h.flash(_('Pulled from remote location'), category='success')
except Exception:
log.exception("Exception during pull from remote")
h.flash(_('An error occurred during pull from remote location'),
category='error')
return redirect(url('edit_repo_remote', repo_name=c.repo_name))
@HasRepoPermissionAllDecorator('repository.admin')
def edit_remote_form(self, repo_name):
"""GET /repo_name/settings: Form to edit an existing item"""
c.repo_info = self._load_repo(repo_name)
c.active = 'remote'
return render('admin/repos/repo_edit.mako')
@HasRepoPermissionAllDecorator('repository.admin')
@auth.CSRFRequired()
def edit_statistics(self, repo_name):
"""PUT /{repo_name}/settings/statistics: reset the repo statistics."""
try:
RepoModel().delete_stats(repo_name)
Session().commit()
except Exception as e:
log.error(traceback.format_exc())
h.flash(_('An error occurred during deletion of repository stats'),
category='error')
return redirect(url('edit_repo_statistics', repo_name=c.repo_name))
@HasRepoPermissionAllDecorator('repository.admin')
def edit_statistics_form(self, repo_name):
"""GET /repo_name/settings: Form to edit an existing item"""
c.repo_info = self._load_repo(repo_name)
repo = c.repo_info.scm_instance()
if c.repo_info.stats:
# this is on what revision we ended up so we add +1 for count
last_rev = c.repo_info.stats.stat_on_revision + 1
else:
last_rev = 0
c.stats_revision = last_rev
c.repo_last_rev = repo.count()
if last_rev == 0 or c.repo_last_rev == 0:
c.stats_percentage = 0
else:
c.stats_percentage = '%.2f' % ((float((last_rev)) / c.repo_last_rev) * 100)
c.active = 'statistics'
return render('admin/repos/repo_edit.mako')
@HasRepoPermissionAllDecorator('repository.admin')
@auth.CSRFRequired()
def repo_issuetracker_test(self, repo_name):
if request.is_xhr:
return h.urlify_commit_message(
request.POST.get('test_text', ''),
repo_name)
else:
raise HTTPBadRequest()
@HasRepoPermissionAllDecorator('repository.admin')
@auth.CSRFRequired()
def repo_issuetracker_delete(self, repo_name):
uid = request.POST.get('uid')
repo_settings = IssueTrackerSettingsModel(repo=repo_name)
try:
repo_settings.delete_entries(uid)
except Exception:
h.flash(_('Error occurred during deleting issue tracker entry'),
category='error')
else:
h.flash(_('Removed issue tracker entry'), category='success')
return redirect(url('repo_settings_issuetracker',
repo_name=repo_name))
def _update_patterns(self, form, repo_settings):
for uid in form['delete_patterns']:
repo_settings.delete_entries(uid)
for pattern in form['patterns']:
for setting, value, type_ in pattern:
sett = repo_settings.create_or_update_setting(
setting, value, type_)
Session().add(sett)
Session().commit()
@HasRepoPermissionAllDecorator('repository.admin')
@auth.CSRFRequired()
def repo_issuetracker_save(self, repo_name):
# Save inheritance
repo_settings = IssueTrackerSettingsModel(repo=repo_name)
inherited = (request.POST.get('inherit_global_issuetracker')
== "inherited")
repo_settings.inherit_global_settings = inherited
Session().commit()
form = IssueTrackerPatternsForm()().to_python(request.POST)
if form:
self._update_patterns(form, repo_settings)
h.flash(_('Updated issue tracker entries'), category='success')
return redirect(url('repo_settings_issuetracker',
repo_name=repo_name))
@HasRepoPermissionAllDecorator('repository.admin')
def repo_issuetracker(self, repo_name):
"""GET /admin/settings/issue-tracker: All items in the collection"""
c.active = 'issuetracker'
c.data = 'data'
c.repo_info = self._load_repo(repo_name)
repo = Repository.get_by_repo_name(repo_name)
c.settings_model = IssueTrackerSettingsModel(repo=repo)
c.global_patterns = c.settings_model.get_global_settings()
c.repo_patterns = c.settings_model.get_repo_settings()
return render('admin/repos/repo_edit.mako')
@HasRepoPermissionAllDecorator('repository.admin')
def repo_settings_vcs(self, repo_name):
"""GET /{repo_name}/settings/vcs/: All items in the collection"""
model = VcsSettingsModel(repo=repo_name)
c.active = 'vcs'
c.global_svn_branch_patterns = model.get_global_svn_branch_patterns()
c.global_svn_tag_patterns = model.get_global_svn_tag_patterns()
c.svn_branch_patterns = model.get_repo_svn_branch_patterns()
c.svn_tag_patterns = model.get_repo_svn_tag_patterns()
c.repo_info = self._load_repo(repo_name)
defaults = self._vcs_form_defaults(repo_name)
c.inherit_global_settings = defaults['inherit_global_settings']
c.labs_active = str2bool(
rhodecode.CONFIG.get('labs_settings_active', 'true'))
return htmlfill.render(
render('admin/repos/repo_edit.mako'),
defaults=defaults,
encoding="UTF-8",
force_defaults=False)
@HasRepoPermissionAllDecorator('repository.admin')
@auth.CSRFRequired()
def repo_settings_vcs_update(self, repo_name):
"""POST /{repo_name}/settings/vcs/: All items in the collection"""
c.active = 'vcs'
model = VcsSettingsModel(repo=repo_name)
c.global_svn_branch_patterns = model.get_global_svn_branch_patterns()
c.global_svn_tag_patterns = model.get_global_svn_tag_patterns()
c.svn_branch_patterns = model.get_repo_svn_branch_patterns()
c.svn_tag_patterns = model.get_repo_svn_tag_patterns()
c.repo_info = self._load_repo(repo_name)
defaults = self._vcs_form_defaults(repo_name)
c.inherit_global_settings = defaults['inherit_global_settings']
application_form = RepoVcsSettingsForm(repo_name)()
try:
form_result = application_form.to_python(dict(request.POST))
except formencode.Invalid as errors:
h.flash(
_("Some form inputs contain invalid data."),
category='error')
return htmlfill.render(
render('admin/repos/repo_edit.mako'),
defaults=errors.value,
errors=errors.error_dict or {},
prefix_error=False,
encoding="UTF-8",
force_defaults=False
)
try:
inherit_global_settings = form_result['inherit_global_settings']
model.create_or_update_repo_settings(
form_result, inherit_global_settings=inherit_global_settings)
except Exception:
log.exception("Exception while updating settings")
h.flash(
_('Error occurred during updating repository VCS settings'),
category='error')
else:
Session().commit()
h.flash(_('Updated VCS settings'), category='success')
return redirect(url('repo_vcs_settings', repo_name=repo_name))
return htmlfill.render(
render('admin/repos/repo_edit.mako'),
defaults=self._vcs_form_defaults(repo_name),
encoding="UTF-8",
force_defaults=False)
@HasRepoPermissionAllDecorator('repository.admin')
@auth.CSRFRequired()
@jsonify
def repo_delete_svn_pattern(self, repo_name):
if not request.is_xhr:
return False
delete_pattern_id = request.POST.get('delete_svn_pattern')
model = VcsSettingsModel(repo=repo_name)
try:
model.delete_repo_svn_pattern(delete_pattern_id)
except SettingNotFound:
raise HTTPBadRequest()
Session().commit()
return True
def _vcs_form_defaults(self, repo_name):
model = VcsSettingsModel(repo=repo_name)
global_defaults = model.get_global_settings()
repo_defaults = {}
repo_defaults.update(global_defaults)
repo_defaults.update(model.get_repo_settings())
global_defaults = {
'{}_inherited'.format(k): global_defaults[k]
for k in global_defaults}
defaults = {
'inherit_global_settings': model.inherit_global_settings
}
defaults.update(global_defaults)
defaults.update(repo_defaults)
defaults.update({
'new_svn_branch': '',
'new_svn_tag': '',
})
return defaults

View file

@ -32,6 +32,7 @@ from formencode import htmlfill
from pylons import request, tmpl_context as c, url, config
from pylons.controllers.util import redirect
from pylons.i18n.translation import _
from pylons.decorators import jsonify
from pyramid.threadlocal import get_current_registry
from webob.exc import HTTPBadRequest
@ -47,7 +48,6 @@ from rhodecode.lib.utils import repo2db_mapper
from rhodecode.lib.utils2 import (
str2bool, safe_unicode, AttributeDict, safe_int)
from rhodecode.lib.compat import OrderedDict
from rhodecode.lib.utils import jsonify
from rhodecode.model.db import RhodeCodeUi, Repository
from rhodecode.model.forms import ApplicationSettingsForm, \

View file

@ -31,25 +31,19 @@ from pylons import request, tmpl_context as c, url, config
from pylons.controllers.util import redirect
from pylons.i18n.translation import _
from sqlalchemy.orm import joinedload
from rhodecode.lib import auth
from rhodecode.lib import helpers as h
from rhodecode.lib import audit_logger
from rhodecode.lib.ext_json import json
from rhodecode.lib.exceptions import UserGroupAssignedException,\
RepoGroupAssignmentError
from rhodecode.lib.utils import jsonify
from rhodecode.lib.utils2 import safe_unicode, str2bool, safe_int
from rhodecode.lib.auth import (
LoginRequired, NotAnonymous, HasUserGroupPermissionAnyDecorator,
HasPermissionAnyDecorator)
from rhodecode.lib.base import BaseController, render
from rhodecode.model.permission import PermissionModel
from rhodecode.model.scm import UserGroupList
from rhodecode.model.user_group import UserGroupModel
from rhodecode.model.db import (
User, UserGroup, UserGroupRepoToPerm, UserGroupRepoGroupToPerm)
from rhodecode.model.db import User, UserGroup
from rhodecode.model.forms import (
UserGroupForm, UserGroupPermsForm, UserIndividualPermissionsForm,
UserPermissionsForm)

View file

@ -34,15 +34,6 @@ import pyramid.threadlocal
from paste.auth.basic import AuthBasicAuthenticator
from paste.httpexceptions import HTTPUnauthorized, HTTPForbidden, get_exception
from paste.httpheaders import WWW_AUTHENTICATE, AUTHORIZATION
from pylons import tmpl_context as c, request, url
from pylons.controllers import WSGIController
from pylons.controllers.util import redirect
from pylons.i18n import translation
# marcink: don't remove this import
from pylons.templating import render_mako, pylons_globals, literal, cached_template
from pylons.i18n.translation import _
from webob.exc import HTTPFound
import rhodecode
from rhodecode.authentication.base import VCS_TYPE
@ -55,13 +46,15 @@ from rhodecode.lib.utils import (
get_enabled_hook_classes)
from rhodecode.lib.utils2 import (
str2bool, safe_unicode, AttributeDict, safe_int, md5, aslist)
from rhodecode.lib.vcs.exceptions import RepositoryRequirementError
from rhodecode.model import meta
from rhodecode.model.db import Repository, User, ChangesetComment
from rhodecode.model.notification import NotificationModel
from rhodecode.model.scm import ScmModel
from rhodecode.model.settings import VcsSettingsModel, SettingsModel
# NOTE(marcink): remove after base controller is no longer required
from pylons.controllers import WSGIController
from pylons.i18n import translation
log = logging.getLogger(__name__)
@ -75,6 +68,9 @@ def render(template_name, extra_vars=None, cache_key=None,
``cache_expire``.
"""
from pylons.templating import literal
from pylons.templating import cached_template, pylons_globals
# Create a render callable for the cache function
def render_template():
# Pull in extra vars if needed
@ -411,10 +407,6 @@ def attach_context_attributes(context, request, user_id):
'refresh_time': 120 * 1000,
'cutoff_limit': 1000 * 60 * 60 * 24 * 7
},
'pylons_dispatch': {
# 'controller': request.environ['pylons.routes_dict']['controller'],
# 'action': request.environ['pylons.routes_dict']['action'],
},
'pyramid_dispatch': {
},
@ -512,6 +504,7 @@ class BaseController(WSGIController):
"""
# on each call propagate settings calls into global settings.
from pylons import config
from pylons import tmpl_context as c, request, url
set_rhodecode_config(config)
attach_context_attributes(c, request, self._rhodecode_user.user_id)
@ -531,6 +524,7 @@ class BaseController(WSGIController):
user_lang, self._rhodecode_user)
def _dispatch_redirect(self, with_url, environ, start_response):
from webob.exc import HTTPFound
resp = HTTPFound(with_url)
environ['SCRIPT_NAME'] = '' # handle prefix middleware
environ['PATH_INFO'] = with_url
@ -542,6 +536,7 @@ class BaseController(WSGIController):
# the request is routed to. This routing information is
# available in environ['pylons.routes_dict']
from rhodecode.lib import helpers as h
from pylons import tmpl_context as c, request, url
# Provide the Pylons context to Pyramid's debugtoolbar if it asks
if environ.get('debugtoolbar.wants_pylons_context', False):
@ -620,93 +615,3 @@ def bootstrap_request(**kwargs):
config = pyramid.testing.setUp(request=request)
add_events_routes(config)
class BaseRepoController(BaseController):
"""
Base class for controllers responsible for loading all needed data for
repository loaded items are
c.rhodecode_repo: instance of scm repository
c.rhodecode_db_repo: instance of db
c.repository_requirements_missing: shows that repository specific data
could not be displayed due to the missing requirements
c.repository_pull_requests: show number of open pull requests
"""
def __before__(self):
super(BaseRepoController, self).__before__()
if c.repo_name: # extracted from routes
db_repo = Repository.get_by_repo_name(c.repo_name)
if not db_repo:
return
log.debug(
'Found repository in database %s with state `%s`',
safe_unicode(db_repo), safe_unicode(db_repo.repo_state))
route = getattr(request.environ.get('routes.route'), 'name', '')
# allow to delete repos that are somehow damages in filesystem
if route in ['delete_repo']:
return
if db_repo.repo_state in [Repository.STATE_PENDING]:
if route in ['repo_creating_home']:
return
check_url = url('repo_creating_home', repo_name=c.repo_name)
return redirect(check_url)
self.rhodecode_db_repo = db_repo
missing_requirements = False
try:
self.rhodecode_repo = self.rhodecode_db_repo.scm_instance()
except RepositoryRequirementError as e:
missing_requirements = True
self._handle_missing_requirements(e)
if self.rhodecode_repo is None and not missing_requirements:
log.error('%s this repository is present in database but it '
'cannot be created as an scm instance', c.repo_name)
h.flash(_(
"The repository at %(repo_name)s cannot be located.") %
{'repo_name': c.repo_name},
category='error', ignore_duplicate=True)
redirect(h.route_path('home'))
# update last change according to VCS data
if not missing_requirements:
commit = db_repo.get_commit(
pre_load=["author", "date", "message", "parents"])
db_repo.update_commit_cache(commit)
# Prepare context
c.rhodecode_db_repo = db_repo
c.rhodecode_repo = self.rhodecode_repo
c.repository_requirements_missing = missing_requirements
self._update_global_counters(self.scm_model, db_repo)
def _update_global_counters(self, scm_model, db_repo):
"""
Base variables that are exposed to every page of repository
"""
c.repository_pull_requests = scm_model.get_pull_requests(db_repo)
def _handle_missing_requirements(self, error):
self.rhodecode_repo = None
log.error(
'Requirements are missing for repository %s: %s',
c.repo_name, error.message)
summary_url = h.route_path('repo_summary', repo_name=c.repo_name)
statistics_url = url('edit_repo_statistics', repo_name=c.repo_name)
settings_update_url = url('repo', repo_name=c.repo_name)
path = request.path
should_redirect = (
path not in (summary_url, settings_update_url)
and '/settings' not in path or path == statistics_url
)
if should_redirect:
redirect(summary_url)

View file

View file

@ -852,30 +852,6 @@ class BasePasterCommand(Command):
initialize_database(config)
@decorator.decorator
def jsonify(func, *args, **kwargs):
"""Action decorator that formats output for JSON
Given a function that will return content, this decorator will turn
the result into JSON, with a content-type of 'application/json' and
output it.
"""
from pylons.decorators.util import get_pylons
from rhodecode.lib.ext_json import json
pylons = get_pylons(args)
pylons.response.headers['Content-Type'] = 'application/json; charset=utf-8'
data = func(*args, **kwargs)
if isinstance(data, (list, tuple)):
msg = "JSON responses with Array envelopes are susceptible to " \
"cross-site data leak attacks, see " \
"http://wiki.pylonshq.com/display/pylonsfaq/Warnings"
warnings.warn(msg, Warning, 2)
log.warning(msg)
log.debug("Returning JSON wrapped action output")
return json.dumps(data, encoding='utf-8')
class PartialRenderer(object):
"""
Partial renderer used to render chunks of html used in datagrids

View file

@ -18,10 +18,6 @@
# RhodeCode Enterprise Edition, including its added features, Support services,
# and proprietary license terms, please see https://rhodecode.com/licenses/
"""
Repository model for rhodecode
"""
import logging
import os
import re

View file

@ -55,7 +55,7 @@ function setRCMouseBindings(repoName, repoLandingRev) {
window.location = pyroutes.url('gists_new');
});
Mousetrap.bind(['n r'], function(e) {
window.location = pyroutes.url('new_repo');
window.location = pyroutes.url('repo_new');
});
if (repoName && repoName != '') {

View file

@ -12,7 +12,6 @@
******************************************************************************/
function registerRCRoutes() {
// routes registration
pyroutes.register('new_repo', '/_admin/create_repository', []);
pyroutes.register('edit_user', '/_admin/users/%(user_id)s/edit', ['user_id']);
pyroutes.register('favicon', '/favicon.ico', []);
pyroutes.register('robots', '/robots.txt', []);
@ -82,6 +81,9 @@ function registerRCRoutes() {
pyroutes.register('user_group_members_data', '/_admin/user_groups/%(user_group_id)s/members', ['user_group_id']);
pyroutes.register('edit_user_group_perms_summary', '/_admin/user_groups/%(user_group_id)s/edit/permissions_summary', ['user_group_id']);
pyroutes.register('edit_user_group_perms_summary_json', '/_admin/user_groups/%(user_group_id)s/edit/permissions_summary/json', ['user_group_id']);
pyroutes.register('repos', '/_admin/repos', []);
pyroutes.register('repo_new', '/_admin/repos/new', []);
pyroutes.register('repo_create', '/_admin/repos/create', []);
pyroutes.register('channelstream_connect', '/_admin/channelstream/connect', []);
pyroutes.register('channelstream_subscribe', '/_admin/channelstream/subscribe', []);
pyroutes.register('channelstream_proxy', '/_channelstream', []);
@ -177,11 +179,26 @@ function registerRCRoutes() {
pyroutes.register('edit_repo_advanced_fork', '/%(repo_name)s/settings/advanced/fork', ['repo_name']);
pyroutes.register('edit_repo_caches', '/%(repo_name)s/settings/caches', ['repo_name']);
pyroutes.register('edit_repo_perms', '/%(repo_name)s/settings/permissions', ['repo_name']);
pyroutes.register('edit_repo_maintenance', '/%(repo_name)s/settings/maintenance', ['repo_name']);
pyroutes.register('edit_repo_maintenance_execute', '/%(repo_name)s/settings/maintenance/execute', ['repo_name']);
pyroutes.register('edit_repo_fields', '/%(repo_name)s/settings/fields', ['repo_name']);
pyroutes.register('edit_repo_fields_create', '/%(repo_name)s/settings/fields/create', ['repo_name']);
pyroutes.register('edit_repo_fields_delete', '/%(repo_name)s/settings/fields/%(field_id)s/delete', ['repo_name', 'field_id']);
pyroutes.register('repo_edit_toggle_locking', '/%(repo_name)s/settings/toggle_locking', ['repo_name']);
pyroutes.register('edit_repo_remote', '/%(repo_name)s/settings/remote', ['repo_name']);
pyroutes.register('edit_repo_remote_pull', '/%(repo_name)s/settings/remote/pull', ['repo_name']);
pyroutes.register('edit_repo_statistics', '/%(repo_name)s/settings/statistics', ['repo_name']);
pyroutes.register('edit_repo_statistics_reset', '/%(repo_name)s/settings/statistics/update', ['repo_name']);
pyroutes.register('edit_repo_issuetracker', '/%(repo_name)s/settings/issue_trackers', ['repo_name']);
pyroutes.register('edit_repo_issuetracker_test', '/%(repo_name)s/settings/issue_trackers/test', ['repo_name']);
pyroutes.register('edit_repo_issuetracker_delete', '/%(repo_name)s/settings/issue_trackers/delete', ['repo_name']);
pyroutes.register('edit_repo_issuetracker_update', '/%(repo_name)s/settings/issue_trackers/update', ['repo_name']);
pyroutes.register('edit_repo_vcs', '/%(repo_name)s/settings/vcs', ['repo_name']);
pyroutes.register('edit_repo_vcs_update', '/%(repo_name)s/settings/vcs/update', ['repo_name']);
pyroutes.register('edit_repo_vcs_svn_pattern_delete', '/%(repo_name)s/settings/vcs/svn_pattern/delete', ['repo_name']);
pyroutes.register('repo_reviewers', '/%(repo_name)s/settings/review/rules', ['repo_name']);
pyroutes.register('repo_default_reviewers_data', '/%(repo_name)s/settings/review/default-reviewers', ['repo_name']);
pyroutes.register('repo_maintenance', '/%(repo_name)s/settings/maintenance', ['repo_name']);
pyroutes.register('repo_maintenance_execute', '/%(repo_name)s/settings/maintenance/execute', ['repo_name']);
pyroutes.register('strip', '/%(repo_name)s/settings/strip', ['repo_name']);
pyroutes.register('edit_repo_strip', '/%(repo_name)s/settings/strip', ['repo_name']);
pyroutes.register('strip_check', '/%(repo_name)s/settings/strip_check', ['repo_name']);
pyroutes.register('strip_execute', '/%(repo_name)s/settings/strip_execute', ['repo_name']);
pyroutes.register('rss_feed_home', '/%(repo_name)s/feed/rss', ['repo_name']);

View file

@ -10,9 +10,9 @@
<%def name="breadcrumbs_links()">
%if c.rhodecode_user.is_admin:
${h.link_to(_('Admin'),h.route_path('admin_home'))}
${h.link_to(_('Admin'), h.route_path('admin_home'))}
&raquo;
${h.link_to(_('Repositories'),h.url('repos'))}
${h.link_to(_('Repositories'), h.route_path('repos'))}
%else:
${_('Admin')}
&raquo;

View file

@ -1,6 +1,6 @@
## -*- coding: utf-8 -*-
${h.secure_form(h.url('repos'))}
${h.secure_form(h.route_path('repo_create'), method='POST', request=request)}
<div class="form">
<!-- fields -->
<div class="fields">

View file

@ -52,24 +52,24 @@
<a href="${h.route_path('edit_repo_advanced', repo_name=c.repo_name)}">${_('Advanced')}</a>
</li>
<li class="${'active' if c.active=='vcs' else ''}">
<a href="${h.url('repo_vcs_settings', repo_name=c.repo_name)}">${_('VCS')}</a>
<a href="${h.route_path('edit_repo_vcs', repo_name=c.repo_name)}">${_('VCS')}</a>
</li>
<li class="${'active' if c.active=='fields' else ''}">
<a href="${h.url('edit_repo_fields', repo_name=c.repo_name)}">${_('Extra Fields')}</a>
<a href="${h.route_path('edit_repo_fields', repo_name=c.repo_name)}">${_('Extra Fields')}</a>
</li>
<li class="${'active' if c.active=='issuetracker' else ''}">
<a href="${h.url('repo_settings_issuetracker', repo_name=c.repo_name)}">${_('Issue Tracker')}</a>
<a href="${h.route_path('edit_repo_issuetracker', repo_name=c.repo_name)}">${_('Issue Tracker')}</a>
</li>
<li class="${'active' if c.active=='caches' else ''}">
<a href="${h.route_path('edit_repo_caches', repo_name=c.repo_name)}">${_('Caches')}</a>
</li>
%if c.repo_info.repo_type != 'svn':
<li class="${'active' if c.active=='remote' else ''}">
<a href="${h.url('edit_repo_remote', repo_name=c.repo_name)}">${_('Remote')}</a>
<a href="${h.route_path('edit_repo_remote', repo_name=c.repo_name)}">${_('Remote')}</a>
</li>
%endif
<li class="${'active' if c.active=='statistics' else ''}">
<a href="${h.url('edit_repo_statistics', repo_name=c.repo_name)}">${_('Statistics')}</a>
<a href="${h.route_path('edit_repo_statistics', repo_name=c.repo_name)}">${_('Statistics')}</a>
</li>
<li class="${'active' if c.active=='integrations' else ''}">
<a href="${h.route_path('repo_integrations_home', repo_name=c.repo_name)}">${_('Integrations')}</a>
@ -80,10 +80,10 @@
</li>
%endif
<li class="${'active' if c.active=='maintenance' else ''}">
<a href="${h.route_path('repo_maintenance', repo_name=c.repo_name)}">${_('Maintenance')}</a>
<a href="${h.route_path('edit_repo_maintenance', repo_name=c.repo_name)}">${_('Maintenance')}</a>
</li>
<li class="${'active' if c.active=='strip' else ''}">
<a href="${h.route_path('strip', repo_name=c.repo_name)}">${_('Strip')}</a>
<a href="${h.route_path('edit_repo_strip', repo_name=c.repo_name)}">${_('Strip')}</a>
</li>
</ul>

View file

@ -18,7 +18,7 @@
<td class="td-hash">${field.field_key}</td>
<td class="td-type">${field.field_type}</td>
<td class="td-action">
${h.secure_form(h.url('delete_repo_fields', repo_name=c.repo_info.repo_name, field_id=field.repo_field_id),method='delete')}
${h.secure_form(h.route_path('edit_repo_fields_delete', repo_name=c.repo_info.repo_name, field_id=field.repo_field_id), method='POST', request=request)}
${h.hidden('del_repo_field',field.repo_field_id)}
<button class="btn btn-link btn-danger" type="submit"
onclick="return confirm('${_('Confirm to delete this field: %s') % field.field_key}');">
@ -31,7 +31,7 @@
</table>
</div>
%endif
${h.secure_form(h.url('create_repo_fields', repo_name=c.repo_name),method='put')}
${h.secure_form(h.route_path('edit_repo_fields_create', repo_name=c.repo_name), method='POST', request=request)}
<div class="form">
<!-- fields -->
<div class="fields">

View file

@ -1,7 +1,7 @@
<%namespace name="its" file="/base/issue_tracker_settings.mako"/>
<div id="repo_issue_tracker" class="${'inherited' if c.settings_model.inherit_global_settings else ''}">
${h.secure_form(h.url('repo_issuetracker_save', repo_name=c.repo_name), method='post', id="inherit-form")}
${h.secure_form(h.route_path('edit_repo_issuetracker_update', repo_name=c.repo_name), id="inherit-form", method='POST', request=request)}
<div class="panel panel-default panel-body">
<div class="fields">
<div class="field">
@ -72,8 +72,8 @@
<div class="panel-body">
${its.issue_tracker_settings_table(
patterns=c.repo_patterns.items(),
form_url=h.url('repo_settings_issuetracker', repo_name=c.repo_info.repo_name),
delete_url=h.url('repo_issuetracker_delete', repo_name=c.repo_info.repo_name)
form_url=h.route_path('edit_repo_issuetracker', repo_name=c.repo_info.repo_name),
delete_url=h.route_path('edit_repo_issuetracker_delete', repo_name=c.repo_info.repo_name)
)}
<div class="buttons">
<button type="submit" class="btn btn-primary save-inheritance" id="save">${_('Save')}</button>
@ -92,7 +92,7 @@
</div>
<div class="panel-body">
${its.issue_tracker_new_row()}
${its.issue_tracker_settings_test(test_url=h.url('repo_issuetracker_test', repo_name=c.repo_info.repo_name))}
${its.issue_tracker_settings_test(test_url=h.route_path('edit_repo_issuetracker_test', repo_name=c.repo_info.repo_name))}
</div>
</div>

View file

@ -19,7 +19,7 @@
</code>
</p>
${h.secure_form(h.url('edit_repo_remote', repo_name=c.repo_name), method='put')}
${h.secure_form(h.route_path('edit_repo_remote_pull', repo_name=c.repo_name), method='POST', request=request)}
<div class="form">
<div class="fields">
${h.submit('remote_pull_%s' % c.repo_info.repo_name,_('Pull changes from remote location'),class_="btn btn-small",onclick="return confirm('"+_('Confirm to pull changes from remote side')+"');")}

View file

@ -83,7 +83,7 @@
${h.hidden('repo_clone_uri_change', 'NEW')}
%endif
<p id="alter_clone_uri_help_block" class="help-block">
<% pull_link = h.literal(h.link_to('remote sync', h.url('edit_repo_remote', repo_name=c.repo_name))) %>
<% pull_link = h.literal(h.link_to('remote sync', h.route_path('edit_repo_remote', repo_name=c.repo_name))) %>
${_('http[s] url where from repository was imported, this field can used for doing {pull_link}.').format(pull_link=pull_link)|n} <br/>
${_('This field is stored encrypted inside Database, a format of http://user:password@server.com/repo_name can be used and will be hidden from display.')}
</p>

View file

@ -3,7 +3,7 @@
<h3 class="panel-title">${_('Repository statistics')}</h3>
</div>
<div class="panel-body">
${h.secure_form(h.url('edit_repo_statistics', repo_name=c.repo_info.repo_name), method='put')}
${h.secure_form(h.route_path('edit_repo_statistics_reset', repo_name=c.repo_info.repo_name), method='POST', request=request)}
<div class="form">
<div class="fields">
<div class="field" >

View file

@ -1,7 +1,7 @@
<%namespace name="vcss" file="/base/vcs_settings.mako"/>
<div id="repo_vcs_settings" class="${'inherited' if c.inherit_global_settings else ''}">
${h.secure_form(h.url('repo_vcs_settings', repo_name=c.repo_info.repo_name), method='post')}
${h.secure_form(h.route_path('edit_repo_vcs_update', repo_name=c.repo_info.repo_name), method='POST', request=request)}
<div class="form panel panel-default">
<div class="fields panel-body">
<div class="field">
@ -50,13 +50,12 @@
<script type="text/javascript">
function ajaxDeletePattern(pattern_id, field_id) {
var sUrl = "${h.url('repo_vcs_settings', repo_name=c.repo_info.repo_name)}";
var sUrl = "${h.route_path('edit_repo_vcs_svn_pattern_delete', repo_name=c.repo_info.repo_name)}";
var callback = function (o) {
var elem = $("#"+field_id);
elem.remove();
};
var postData = {
'_method': 'delete',
'delete_svn_pattern': pattern_id,
'csrf_token': CSRF_TOKEN
};

View file

@ -24,7 +24,7 @@
<ul class="links">
%if h.HasPermissionAny('hg.admin','hg.create.repository')():
<li>
<a href="${h.url('new_repo')}" class="btn btn-small btn-success">${_(u'Add Repository')}</a>
<a href="${h.route_path('repo_new')}" class="btn btn-small btn-success">${_(u'Add Repository')}</a>
</li>
%endif
</ul>

View file

@ -73,7 +73,7 @@
<%def name="admin_menu()">
<ul class="admin_menu submenu">
<li><a href="${h.route_path('admin_audit_logs')}">${_('Admin audit logs')}</a></li>
<li><a href="${h.url('repos')}">${_('Repositories')}</a></li>
<li><a href="${h.route_path('repos')}">${_('Repositories')}</a></li>
<li><a href="${h.url('repo_groups')}">${_('Repository groups')}</a></li>
<li><a href="${h.route_path('users')}">${_('Users')}</a></li>
<li><a href="${h.url('users_groups')}">${_('User groups')}</a></li>
@ -145,7 +145,7 @@
<%def name="admin_menu_simple(repositories=None, repository_groups=None, user_groups=None)">
<ul class="submenu">
%if repositories:
<li class="local-admin-repos"><a href="${h.url('repos')}">${_('Repositories')}</a></li>
<li class="local-admin-repos"><a href="${h.route_path('repos')}">${_('Repositories')}</a></li>
%endif
%if repository_groups:
<li class="local-admin-repo-groups"><a href="${h.url('repo_groups')}">${_('Repository groups')}</a></li>
@ -268,9 +268,9 @@
%if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name) and c.rhodecode_db_repo.enable_locking:
%if c.rhodecode_db_repo.locked[0]:
<li><a class="locking_del" href="${h.url('toggle_locking',repo_name=c.repo_name)}">${_('Unlock')}</a></li>
<li><a class="locking_del" href="${h.route_path('repo_edit_toggle_locking',repo_name=c.repo_name)}">${_('Unlock')}</a></li>
%else:
<li><a class="locking_add" href="${h.url('toggle_locking',repo_name=c.repo_name)}">${_('Lock')}</a></li>
<li><a class="locking_add" href="${h.route_path('repo_edit_toggle_locking',repo_name=c.repo_name)}">${_('Lock')}</a></li>
%endif
%endif
%if c.rhodecode_user.username != h.DEFAULT_USER:

View file

@ -108,8 +108,8 @@
alert("Error while deleting entry.\nError code {0} ({1}). URL: {2}".format(data.status,data.statusText,$(entry)[0].url));
}
});
};
}
}
};
$('.delete_issuetracker_entry').on('click', function(e){
e.preventDefault();

View file

@ -74,6 +74,8 @@
%else:
% if c.file.size < c.visual.cut_off_limit_file:
%if c.renderer and not c.annotate:
## pick relative url based on renderer
${c.renderer} XXXX
<%
relative_urls = {
'raw': h.route_path('repo_file_raw',repo_name=c.repo_name,commit_id=c.commit.raw_id,f_path=c.f_path),

View file

@ -27,7 +27,7 @@
%if not c.repo_group:
## no repository group context here
%if is_admin or create_repo:
<a href="${h.url('new_repo')}" class="btn btn-small btn-success btn-primary">${_('Add Repository')}</a>
<a href="${h.route_path('repo_new')}" class="btn btn-small btn-success btn-primary">${_('Add Repository')}</a>
%endif
%if is_admin or create_repo_group:
@ -36,7 +36,7 @@
%else:
##we're inside other repository group other terms apply
%if is_admin or group_admin or (group_write and create_on_write):
<a href="${h.url('new_repo',parent_group=c.repo_group.group_id)}" class="btn btn-small btn-success btn-primary">${_('Add Repository')}</a>
<a href="${h.route_path('repo_new',parent_group=c.repo_group.group_id)}" class="btn btn-small btn-success btn-primary">${_('Add Repository')}</a>
%endif
%if is_admin or group_admin:
<a href="${h.url('new_repo_group', parent_group=c.repo_group.group_id)}" class="btn btn-small btn-default">${_(u'Add Repository Group')}</a>

View file

@ -15,7 +15,7 @@
<div class="alert alert-dismissable alert-warning">
<strong>Missing requirements</strong>
Commits cannot be displayed, because this repository uses one or more extensions, which was not enabled. <br/>
Please <a href="${h.url('repo_vcs_settings', repo_name=c.repo_name)}">enable extension in settings</a>, or contact the repository owner for help.
Please <a href="${h.route_path('edit_repo_vcs', repo_name=c.repo_name)}">enable extension in settings</a>, or contact the repository owner for help.
Missing extensions could be:
<pre>

View file

@ -20,10 +20,7 @@
import pytest
from rhodecode.tests import (
TestController, url, assert_session_flash, link_to)
from rhodecode.model.db import User, UserGroup
from rhodecode.model.meta import Session
from rhodecode.tests import TestController, url
from rhodecode.tests.fixture import Fixture
@ -33,6 +30,8 @@ def route_path(name, params=None, **kwargs):
base_url = {
'home': '/',
'repos':
ADMIN_PREFIX + '/repos',
'user_groups':
ADMIN_PREFIX + '/user_groups',
'user_groups_data':
@ -63,7 +62,7 @@ class TestAdminDelegatedUser(TestController):
assert_response.no_element_exists('li.local-admin-repo-groups')
assert_response.no_element_exists('li.local-admin-user-groups')
response = self.app.get(url('repos'), status=200)
response = self.app.get(route_path('repos'), status=200)
response.mustcontain('data: []')
response = self.app.get(url('repo_groups'), status=200)
@ -98,7 +97,7 @@ class TestAdminDelegatedUser(TestController):
assert_response.one_element_exists('li.local-admin-user-groups')
# admin interfaces have visible elements
response = self.app.get(url('repos'), status=200)
response = self.app.get(route_path('repos'), status=200)
response.mustcontain('"name_raw": "{}"'.format(repo_name))
response = self.app.get(url('repo_groups'), status=200)
@ -140,7 +139,7 @@ class TestAdminDelegatedUser(TestController):
assert_response.one_element_exists('li.local-admin-user-groups')
# admin interfaces have visible elements
response = self.app.get(url('repos'), status=200)
response = self.app.get(route_path('repos'), status=200)
response.mustcontain('"name_raw": "{}"'.format(repo_name))
response = self.app.get(url('repo_groups'), status=200)

View file

@ -22,8 +22,6 @@ import pytest
from mock import Mock, patch
from rhodecode.lib import base
from rhodecode.lib.vcs.exceptions import RepositoryRequirementError
from rhodecode.lib import helpers as h
from rhodecode.model import db
@ -94,162 +92,3 @@ def call_vcs_operation_context(**kwargs_override):
return result
class TestBaseRepoController(object):
def test_context_is_updated_when_update_global_counters_is_called(self):
followers = 1
forks = 2
pull_requests = 3
is_following = True
scm_model = Mock(name="scm_model")
db_repo = Mock(name="db_repo")
scm_model.get_followers.return_value = followers
scm_model.get_forks.return_value = forks
scm_model.get_pull_requests.return_value = pull_requests
scm_model.is_following_repo.return_value = is_following
controller = base.BaseRepoController()
with patch.object(base, 'c') as context_mock:
controller._update_global_counters(scm_model, db_repo)
scm_model.get_pull_requests.assert_called_once_with(db_repo)
assert context_mock.repository_pull_requests == pull_requests
class TestBaseRepoControllerHandleMissingRequirements(object):
def test_logs_error_and_sets_repo_to_none(self, app):
controller = base.BaseRepoController()
error_message = 'Some message'
error = RepositoryRequirementError(error_message)
context_patcher = patch.object(base, 'c')
log_patcher = patch.object(base, 'log')
request_patcher = patch.object(base, 'request')
redirect_patcher = patch.object(base, 'redirect')
controller.rhodecode_repo = 'something'
with context_patcher as context_mock, log_patcher as log_mock, \
request_patcher, redirect_patcher:
context_mock.repo_name = 'abcde'
controller._handle_missing_requirements(error)
expected_log_message = (
'Requirements are missing for repository %s: %s', 'abcde',
error_message)
log_mock.error.assert_called_once_with(*expected_log_message)
assert controller.rhodecode_repo is None
@pytest.mark.parametrize('path, should_redirect', [
('/abcde', False),
('/abcde/settings', False),
('/abcde/settings/vcs', False),
('/_admin/repos/abcde', False), # Settings update
('/abcde/changelog', True),
('/abcde/files/tip', True),
('/abcde/settings/statistics', True),
])
def test_redirects_if_not_summary_or_settings_page(
self, app, path, should_redirect):
repo_name = 'abcde'
controller = base.BaseRepoController()
error = RepositoryRequirementError('Some message')
context_patcher = patch.object(base, 'c')
controller.rhodecode_repo = repo_name
request_patcher = patch.object(base, 'request')
redirect_patcher = patch.object(base, 'redirect')
with context_patcher as context_mock, \
request_patcher as request_mock, \
redirect_patcher as redirect_mock:
request_mock.path = path
context_mock.repo_name = repo_name
controller._handle_missing_requirements(error)
expected_url = h.route_path('repo_summary', repo_name=repo_name)
if should_redirect:
redirect_mock.assert_called_once_with(expected_url)
else:
redirect_mock.call_count == 0
class TestBaseRepoControllerBefore(object):
def test_flag_is_true_when_requirements_are_missing(self, before_mocks):
controller = self._get_controller()
handle_patcher = patch.object(
controller, '_handle_missing_requirements')
error = RepositoryRequirementError()
before_mocks.repository.scm_instance.side_effect = error
with handle_patcher as handle_mock:
controller.__before__()
handle_mock.assert_called_once_with(error)
assert before_mocks['context'].repository_requirements_missing is True
def test_flag_is_false_when_no_requirements_are_missing(
self, before_mocks):
controller = self._get_controller()
handle_patcher = patch.object(
controller, '_handle_missing_requirements')
with handle_patcher as handle_mock:
controller.__before__()
handle_mock.call_count == 0
assert before_mocks['context'].repository_requirements_missing is False
def test_update_global_counters_is_called(self, before_mocks):
controller = self._get_controller()
update_counters_patcher = patch.object(
controller, '_update_global_counters')
with update_counters_patcher as update_counters_mock:
controller.__before__()
update_counters_mock.assert_called_once_with(
controller.scm_model, before_mocks.repository)
def _get_controller(self):
controller = base.BaseRepoController()
controller.scm_model = Mock()
controller.rhodecode_repo = Mock()
return controller
@pytest.fixture
def before_mocks(request):
patcher = BeforePatcher()
patcher.start()
request.addfinalizer(patcher.stop)
return patcher
class BeforePatcher(object):
patchers = {}
mocks = {}
repository = None
def __init__(self):
self.repository = Mock()
def start(self):
self.patchers = {
'request': patch.object(base, 'request'),
'before': patch.object(base.BaseController, '__before__'),
'context': patch.object(base, 'c'),
'repo': patch.object(
base.Repository, 'get_by_repo_name',
return_value=self.repository)
}
self.mocks = {
p: self.patchers[p].start() for p in self.patchers
}
def stop(self):
for patcher in self.patchers.values():
patcher.stop()
def __getitem__(self, key):
return self.mocks[key]

View file

@ -1045,7 +1045,7 @@ class TestCreateOrUpdateRepoSettings(object):
def test_cache_is_marked_for_invalidation(self, repo_stub):
model = VcsSettingsModel(repo=repo_stub)
invalidation_patcher = mock.patch(
'rhodecode.controllers.admin.repos.ScmModel.mark_for_invalidation')
'rhodecode.model.scm.ScmModel.mark_for_invalidation')
with invalidation_patcher as invalidation_mock:
model.create_or_update_repo_settings(
data=self.FORM_DATA, inherit_global_settings=True)