diff --git a/rhodecode/apps/admin/__init__.py b/rhodecode/apps/admin/__init__.py
--- a/rhodecode/apps/admin/__init__.py
+++ b/rhodecode/apps/admin/__init__.py
@@ -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()
diff --git a/rhodecode/tests/functional/test_admin_repos.py b/rhodecode/apps/admin/tests/test_admin_repos.py
rename from rhodecode/tests/functional/test_admin_repos.py
rename to rhodecode/apps/admin/tests/test_admin_repos.py
--- a/rhodecode/tests/functional/test_admin_repos.py
+++ b/rhodecode/apps/admin/tests/test_admin_repos.py
@@ -23,19 +23,19 @@ 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, str2bool
+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.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)
+ 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
@@ -46,7 +46,10 @@ def route_path(name, params=None, **kwar
import urllib
base_url = {
- 'repo_summary': '/{repo_name}',
+ '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)
@@ -55,35 +58,48 @@ def route_path(name, params=None, **kwar
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_index(self):
- self.app.get(url('repos'))
+ def test_repo_list(self, autologin_user, user_util):
+ repo = user_util.create_repo()
+ response = self.app.get(
+ route_path('repos'), status=200)
- def test_create_page_restricted(self, autologin_user, backend):
+ 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(url('new_repo'), status=200)
+ 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(self, autologin_user, backend):
- response = self.app.get(url('new_repo'), status=200)
+ 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'])
+ @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'),
+ route_path('repo_create'),
fixture._get_repo_create_params(
repo_private=False,
repo_name=repo_name,
@@ -95,12 +111,12 @@ class TestAdminRepos(object):
self.assert_repository_is_created_correctly(
repo_name, description, backend)
- def test_create_numeric(self, autologin_user, backend, csrf_token):
+ 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(
- url('repos'),
+ route_path('repo_create'),
fixture._get_repo_create_params(
repo_private=False,
repo_name=repo_name,
@@ -126,7 +142,7 @@ class TestAdminRepos(object):
[group_name, repo_name])
description = u'description for newly created repo'
self.app.post(
- url('repos'),
+ route_path('repo_create'),
fixture._get_repo_create_params(
repo_private=False,
repo_name=safe_str(repo_name),
@@ -149,7 +165,7 @@ class TestAdminRepos(object):
RepoGroupModel().delete(group_name)
Session().commit()
- def test_create_in_group_numeric(
+ def test_create_in_group_numeric_name(
self, autologin_user, backend, csrf_token):
# create GROUP
group_name = 'sometest_%s' % backend.alias
@@ -162,7 +178,7 @@ class TestAdminRepos(object):
repo_name_full = RepoGroup.url_sep().join([group_name, repo_name])
description = 'description for newly created repo'
self.app.post(
- url('repos'),
+ route_path('repo_create'),
fixture._get_repo_create_params(
repo_private=False,
repo_name=repo_name,
@@ -221,7 +237,7 @@ class TestAdminRepos(object):
repo_name = 'ingroup'
description = 'description for newly created repo'
response = self.app.post(
- url('repos'),
+ route_path('repo_create'),
fixture._get_repo_create_params(
repo_private=False,
repo_name=repo_name,
@@ -238,7 +254,7 @@ class TestAdminRepos(object):
[group_name_allowed, repo_name])
description = 'description for newly created repo'
response = self.app.post(
- url('repos'),
+ route_path('repo_create'),
fixture._get_repo_create_params(
repo_private=False,
repo_name=repo_name,
@@ -282,7 +298,7 @@ class TestAdminRepos(object):
repo_name_full = RepoGroup.url_sep().join([group_name, repo_name])
description = 'description for newly created repo'
self.app.post(
- url('repos'),
+ route_path('repo_create'),
fixture._get_repo_create_params(
repo_private=False,
repo_name=repo_name,
@@ -326,7 +342,7 @@ class TestAdminRepos(object):
repo_name = backend.new_repo_name()
response = self.app.post(
- url('repos'),
+ route_path('repo_create'),
fixture._get_repo_create_params(
repo_private=False,
repo_name=repo_name,
@@ -354,7 +370,7 @@ class TestAdminRepos(object):
repo_name = backend.new_repo_name()
description = 'description for newly created repo'
response = self.app.post(
- url('repos'),
+ route_path('repo_create'),
fixture._get_repo_create_params(
repo_private=False,
repo_name=repo_name,
@@ -370,7 +386,7 @@ class TestAdminRepos(object):
repo_name = backend.new_repo_name()
description = 'description for newly created repo'
response = self.app.post(
- url('repos'),
+ route_path('repo_create'),
fixture._get_repo_create_params(
repo_private=False,
repo_name=repo_name,
@@ -385,7 +401,7 @@ class TestAdminRepos(object):
repo_name = backend.new_repo_name() + ".git"
description = 'description for newly created repo'
response = self.app.post(
- url('repos'),
+ route_path('repo_create'),
fixture._get_repo_create_params(
repo_private=False,
repo_name=repo_name,
@@ -394,11 +410,8 @@ class TestAdminRepos(object):
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):
+ self, autologin_user, user_util, backend):
group = user_util.create_repo_group()
@@ -434,7 +447,7 @@ class TestAdminRepos(object):
repo_name = backend.new_repo_name()
description = 'description for newly created repo'
response = self.app.post(
- url('repos'),
+ route_path('repo_create'),
fixture._get_repo_create_params(
repo_private=False,
repo_name=repo_name,
@@ -453,7 +466,7 @@ class TestAdminRepos(object):
description = 'description for newly created repo'
response = self.app.post(
- url('repos'),
+ route_path('repo_create'),
fixture._get_repo_create_params(
repo_private=False,
repo_name=repo_name,
@@ -494,638 +507,3 @@ class TestAdminRepos(object):
response.mustcontain(backend.alias)
assert repo_on_filesystem(repo_name)
-
-
-@pytest.mark.usefixtures("app")
-class TestVcsSettings(object):
- FORM_DATA = {
- 'inherit_global_settings': False,
- 'hooks_changegroup_repo_size': False,
- 'hooks_changegroup_push_logger': False,
- 'hooks_outgoing_pull_logger': False,
- 'extensions_largefiles': False,
- 'extensions_evolve': False,
- 'phases_publish': 'False',
- 'rhodecode_pr_merge_enabled': False,
- 'rhodecode_use_outdated_comments': False,
- 'new_svn_branch': '',
- 'new_svn_tag': ''
- }
-
- @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))
-
- expected_settings = (
- 'rhodecode_use_outdated_comments', 'rhodecode_pr_merge_enabled',
- 'hooks_changegroup_repo_size', 'hooks_changegroup_push_logger',
- 'hooks_outgoing_pull_logger'
- )
- for setting in expected_settings:
- self.assert_repo_value_equals_global_value(response, setting)
-
- def test_show_settings_requires_repo_admin_permission(
- self, backend, user_util, settings_util):
- repo = backend.create_repo()
- repo_name = repo.repo_name
- user = UserModel().get_by_username(TEST_USER_REGULAR_LOGIN)
- 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)
-
- 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))
-
- assert_response = AssertResponse(response)
- element = assert_response.get_element('#inherit_global_settings')
- assert element.checked
-
- @pytest.mark.parametrize('checked_value', [True, False])
- def test_inherit_global_settings_value(
- self, autologin_user, backend, checked_value, settings_util):
- repo = backend.create_repo()
- 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))
-
- assert_response = AssertResponse(response)
- element = assert_response.get_element('#inherit_global_settings')
- assert element.checked == checked_value
-
- @pytest.mark.skip_backends('svn')
- def test_hooks_settings_are_created(
- self, autologin_user, backend, csrf_token):
- repo_name = backend.repo_name
- data = self.FORM_DATA.copy()
- data['csrf_token'] = csrf_token
- self.app.post(
- url('repo_vcs_settings', repo_name=repo_name), data, status=302)
- settings = SettingsModel(repo=repo_name)
- try:
- for section, key in VcsSettingsModel.HOOKS_SETTINGS:
- ui = settings.get_ui_by_section_and_key(section, key)
- assert ui.ui_active is False
- finally:
- self._cleanup_repo_settings(settings)
-
- def test_hooks_settings_are_not_created_for_svn(
- self, autologin_user, backend_svn, csrf_token):
- repo_name = backend_svn.repo_name
- data = self.FORM_DATA.copy()
- data['csrf_token'] = csrf_token
- self.app.post(
- url('repo_vcs_settings', repo_name=repo_name), data, status=302)
- settings = SettingsModel(repo=repo_name)
- try:
- for section, key in VcsSettingsModel.HOOKS_SETTINGS:
- ui = settings.get_ui_by_section_and_key(section, key)
- assert ui is None
- finally:
- self._cleanup_repo_settings(settings)
-
- @pytest.mark.skip_backends('svn')
- def test_hooks_settings_are_updated(
- self, autologin_user, backend, csrf_token):
- repo_name = backend.repo_name
- settings = SettingsModel(repo=repo_name)
- for section, key in VcsSettingsModel.HOOKS_SETTINGS:
- settings.create_ui_section_value(section, '', key=key, active=True)
-
- data = self.FORM_DATA.copy()
- data['csrf_token'] = csrf_token
- self.app.post(
- url('repo_vcs_settings', 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)
- assert ui.ui_active is False
- finally:
- self._cleanup_repo_settings(settings)
-
- def test_hooks_settings_are_not_updated_for_svn(
- self, autologin_user, backend_svn, csrf_token):
- repo_name = backend_svn.repo_name
- settings = SettingsModel(repo=repo_name)
- for section, key in VcsSettingsModel.HOOKS_SETTINGS:
- settings.create_ui_section_value(section, '', key=key, active=True)
-
- data = self.FORM_DATA.copy()
- data['csrf_token'] = csrf_token
- self.app.post(
- url('repo_vcs_settings', 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)
- assert ui.ui_active is True
- finally:
- self._cleanup_repo_settings(settings)
-
- @pytest.mark.skip_backends('svn')
- def test_pr_settings_are_created(
- self, autologin_user, backend, csrf_token):
- repo_name = backend.repo_name
- data = self.FORM_DATA.copy()
- data['csrf_token'] = csrf_token
- self.app.post(
- url('repo_vcs_settings', repo_name=repo_name), data, status=302)
- settings = SettingsModel(repo=repo_name)
- try:
- for name in VcsSettingsModel.GENERAL_SETTINGS:
- setting = settings.get_setting_by_name(name)
- assert setting.app_settings_value is False
- finally:
- self._cleanup_repo_settings(settings)
-
- def test_pr_settings_are_not_created_for_svn(
- self, autologin_user, backend_svn, csrf_token):
- repo_name = backend_svn.repo_name
- data = self.FORM_DATA.copy()
- data['csrf_token'] = csrf_token
- self.app.post(
- url('repo_vcs_settings', repo_name=repo_name), data, status=302)
- settings = SettingsModel(repo=repo_name)
- try:
- for name in VcsSettingsModel.GENERAL_SETTINGS:
- setting = settings.get_setting_by_name(name)
- assert setting is None
- finally:
- self._cleanup_repo_settings(settings)
-
- def test_pr_settings_creation_requires_repo_admin_permission(
- self, backend, user_util, settings_util, csrf_token):
- repo = backend.create_repo()
- repo_name = repo.repo_name
-
- logout_user_session(self.app, csrf_token)
- session = login_user_session(
- self.app, TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
- new_csrf_token = auth.get_csrf_token(session)
-
- user = UserModel().get_by_username(TEST_USER_REGULAR_LOGIN)
- repo = Repository.get_by_repo_name(repo_name)
- user_util.grant_user_permission_to_repo(repo, user, 'repository.admin')
- data = self.FORM_DATA.copy()
- data['csrf_token'] = new_csrf_token
- settings = SettingsModel(repo=repo_name)
-
- try:
- self.app.post(
- url('repo_vcs_settings', repo_name=repo_name), data,
- status=302)
- finally:
- self._cleanup_repo_settings(settings)
-
- @pytest.mark.skip_backends('svn')
- def test_pr_settings_are_updated(
- self, autologin_user, backend, csrf_token):
- repo_name = backend.repo_name
- settings = SettingsModel(repo=repo_name)
- for name in VcsSettingsModel.GENERAL_SETTINGS:
- settings.create_or_update_setting(name, True, 'bool')
-
- data = self.FORM_DATA.copy()
- data['csrf_token'] = csrf_token
- self.app.post(
- url('repo_vcs_settings', repo_name=repo_name), data, status=302)
- try:
- for name in VcsSettingsModel.GENERAL_SETTINGS:
- setting = settings.get_setting_by_name(name)
- assert setting.app_settings_value is False
- finally:
- self._cleanup_repo_settings(settings)
-
- def test_pr_settings_are_not_updated_for_svn(
- self, autologin_user, backend_svn, csrf_token):
- repo_name = backend_svn.repo_name
- settings = SettingsModel(repo=repo_name)
- for name in VcsSettingsModel.GENERAL_SETTINGS:
- settings.create_or_update_setting(name, True, 'bool')
-
- data = self.FORM_DATA.copy()
- data['csrf_token'] = csrf_token
- self.app.post(
- url('repo_vcs_settings', repo_name=repo_name), data, status=302)
- try:
- for name in VcsSettingsModel.GENERAL_SETTINGS:
- setting = settings.get_setting_by_name(name)
- assert setting.app_settings_value is True
- finally:
- self._cleanup_repo_settings(settings)
-
- def test_svn_settings_are_created(
- self, autologin_user, backend_svn, csrf_token, settings_util):
- repo_name = backend_svn.repo_name
- data = self.FORM_DATA.copy()
- data['new_svn_tag'] = 'svn-tag'
- data['new_svn_branch'] = 'svn-branch'
- data['csrf_token'] = csrf_token
-
- # Create few global settings to make sure that uniqueness validators
- # are not triggered
- settings_util.create_rhodecode_ui(
- VcsSettingsModel.SVN_BRANCH_SECTION, 'svn-branch')
- settings_util.create_rhodecode_ui(
- VcsSettingsModel.SVN_TAG_SECTION, 'svn-tag')
-
- self.app.post(
- url('repo_vcs_settings', repo_name=repo_name), data, status=302)
- settings = SettingsModel(repo=repo_name)
- try:
- svn_branches = settings.get_ui_by_section(
- VcsSettingsModel.SVN_BRANCH_SECTION)
- svn_branch_names = [b.ui_value for b in svn_branches]
- svn_tags = settings.get_ui_by_section(
- VcsSettingsModel.SVN_TAG_SECTION)
- svn_tag_names = [b.ui_value for b in svn_tags]
- assert 'svn-branch' in svn_branch_names
- assert 'svn-tag' in svn_tag_names
- finally:
- self._cleanup_repo_settings(settings)
-
- def test_svn_settings_are_unique(
- self, autologin_user, backend_svn, csrf_token, settings_util):
- repo = backend_svn.repo
- repo_name = repo.repo_name
- data = self.FORM_DATA.copy()
- data['new_svn_tag'] = 'test_tag'
- data['new_svn_branch'] = 'test_branch'
- data['csrf_token'] = csrf_token
- settings_util.create_repo_rhodecode_ui(
- repo, VcsSettingsModel.SVN_BRANCH_SECTION, 'test_branch')
- settings_util.create_repo_rhodecode_ui(
- repo, VcsSettingsModel.SVN_TAG_SECTION, 'test_tag')
-
- response = self.app.post(
- url('repo_vcs_settings', repo_name=repo_name), data, status=200)
- response.mustcontain('Pattern already exists')
-
- def test_svn_settings_with_empty_values_are_not_created(
- self, autologin_user, backend_svn, csrf_token):
- repo_name = backend_svn.repo_name
- data = self.FORM_DATA.copy()
- data['csrf_token'] = csrf_token
- self.app.post(
- url('repo_vcs_settings', repo_name=repo_name), data, status=302)
- settings = SettingsModel(repo=repo_name)
- try:
- svn_branches = settings.get_ui_by_section(
- VcsSettingsModel.SVN_BRANCH_SECTION)
- svn_tags = settings.get_ui_by_section(
- VcsSettingsModel.SVN_TAG_SECTION)
- assert len(svn_branches) == 0
- assert len(svn_tags) == 0
- finally:
- self._cleanup_repo_settings(settings)
-
- def test_svn_settings_are_shown_for_svn_repository(
- 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)
- response.mustcontain('Subversion Settings')
-
- @pytest.mark.skip_backends('svn')
- def test_svn_settings_are_not_created_for_not_svn_repository(
- self, autologin_user, backend, csrf_token):
- repo_name = backend.repo_name
- data = self.FORM_DATA.copy()
- data['csrf_token'] = csrf_token
- self.app.post(
- url('repo_vcs_settings', repo_name=repo_name), data, status=302)
- settings = SettingsModel(repo=repo_name)
- try:
- svn_branches = settings.get_ui_by_section(
- VcsSettingsModel.SVN_BRANCH_SECTION)
- svn_tags = settings.get_ui_by_section(
- VcsSettingsModel.SVN_TAG_SECTION)
- assert len(svn_branches) == 0
- assert len(svn_tags) == 0
- finally:
- self._cleanup_repo_settings(settings)
-
- @pytest.mark.skip_backends('svn')
- def test_svn_settings_are_shown_only_for_svn_repository(
- 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)
- response.mustcontain(no='Subversion Settings')
-
- def test_hg_settings_are_created(
- self, autologin_user, backend_hg, csrf_token):
- repo_name = backend_hg.repo_name
- data = self.FORM_DATA.copy()
- data['new_svn_tag'] = 'svn-tag'
- 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)
- settings = SettingsModel(repo=repo_name)
- try:
- largefiles_ui = settings.get_ui_by_section_and_key(
- 'extensions', 'largefiles')
- assert largefiles_ui.ui_active is False
- phases_ui = settings.get_ui_by_section_and_key(
- 'phases', 'publish')
- assert str2bool(phases_ui.ui_value) is False
- finally:
- self._cleanup_repo_settings(settings)
-
- def test_hg_settings_are_updated(
- self, autologin_user, backend_hg, csrf_token):
- repo_name = backend_hg.repo_name
- settings = SettingsModel(repo=repo_name)
- settings.create_ui_section_value(
- 'extensions', '', key='largefiles', active=True)
- settings.create_ui_section_value(
- 'phases', '1', key='publish', active=True)
-
- data = self.FORM_DATA.copy()
- data['csrf_token'] = csrf_token
- self.app.post(
- url('repo_vcs_settings', repo_name=repo_name), data, status=302)
- try:
- largefiles_ui = settings.get_ui_by_section_and_key(
- 'extensions', 'largefiles')
- assert largefiles_ui.ui_active is False
- phases_ui = settings.get_ui_by_section_and_key(
- 'phases', 'publish')
- assert str2bool(phases_ui.ui_value) is False
- finally:
- self._cleanup_repo_settings(settings)
-
- def test_hg_settings_are_shown_for_hg_repository(
- 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)
- response.mustcontain('Mercurial Settings')
-
- @pytest.mark.skip_backends('hg')
- def test_hg_settings_are_created_only_for_hg_repository(
- self, autologin_user, backend, csrf_token):
- repo_name = backend.repo_name
- data = self.FORM_DATA.copy()
- data['csrf_token'] = csrf_token
- self.app.post(
- url('repo_vcs_settings', repo_name=repo_name), data, status=302)
- settings = SettingsModel(repo=repo_name)
- try:
- largefiles_ui = settings.get_ui_by_section_and_key(
- 'extensions', 'largefiles')
- assert largefiles_ui is None
- phases_ui = settings.get_ui_by_section_and_key(
- 'phases', 'publish')
- assert phases_ui is None
- finally:
- self._cleanup_repo_settings(settings)
-
- @pytest.mark.skip_backends('hg')
- def test_hg_settings_are_shown_only_for_hg_repository(
- 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)
- response.mustcontain(no='Mercurial Settings')
-
- @pytest.mark.skip_backends('hg')
- def test_hg_settings_are_updated_only_for_hg_repository(
- self, autologin_user, backend, csrf_token):
- repo_name = backend.repo_name
- settings = SettingsModel(repo=repo_name)
- settings.create_ui_section_value(
- 'extensions', '', key='largefiles', active=True)
- settings.create_ui_section_value(
- 'phases', '1', key='publish', active=True)
-
- data = self.FORM_DATA.copy()
- data['csrf_token'] = csrf_token
- self.app.post(
- url('repo_vcs_settings', repo_name=repo_name), data, status=302)
- try:
- largefiles_ui = settings.get_ui_by_section_and_key(
- 'extensions', 'largefiles')
- assert largefiles_ui.ui_active is True
- phases_ui = settings.get_ui_by_section_and_key(
- 'phases', 'publish')
- assert phases_ui.ui_value == '1'
- finally:
- self._cleanup_repo_settings(settings)
-
- def test_per_repo_svn_settings_are_displayed(
- self, autologin_user, backend_svn, settings_util):
- repo = backend_svn.create_repo()
- repo_name = repo.repo_name
- branches = [
- settings_util.create_repo_rhodecode_ui(
- repo, VcsSettingsModel.SVN_BRANCH_SECTION,
- 'branch_{}'.format(i))
- for i in range(10)]
- tags = [
- settings_util.create_repo_rhodecode_ui(
- repo, VcsSettingsModel.SVN_TAG_SECTION, 'tag_{}'.format(i))
- for i in range(10)]
-
- response = self.app.get(
- url('repo_vcs_settings', repo_name=repo_name), status=200)
- assert_response = AssertResponse(response)
- for branch in branches:
- css_selector = '[name=branch_value_{}]'.format(branch.ui_id)
- element = assert_response.get_element(css_selector)
- assert element.value == branch.ui_value
- for tag in tags:
- css_selector = '[name=tag_ui_value_new_{}]'.format(tag.ui_id)
- element = assert_response.get_element(css_selector)
- assert element.value == tag.ui_value
-
- def test_per_repo_hg_and_pr_settings_are_not_displayed_for_svn(
- self, autologin_user, backend_svn, settings_util):
- repo = backend_svn.create_repo()
- repo_name = repo.repo_name
- response = self.app.get(
- url('repo_vcs_settings', repo_name=repo_name), status=200)
- response.mustcontain(no='')
- response.mustcontain(no='')
-
- def test_inherit_global_settings_value_is_saved(
- self, autologin_user, backend, csrf_token):
- repo_name = backend.repo_name
- data = self.FORM_DATA.copy()
- data['csrf_token'] = csrf_token
- data['inherit_global_settings'] = True
- self.app.post(
- url('repo_vcs_settings', repo_name=repo_name), data, status=302)
-
- settings = SettingsModel(repo=repo_name)
- vcs_settings = VcsSettingsModel(repo=repo_name)
- try:
- assert vcs_settings.inherit_global_settings is True
- finally:
- self._cleanup_repo_settings(settings)
-
- def test_repo_cache_is_invalidated_when_settings_are_updated(
- self, autologin_user, backend, csrf_token):
- repo_name = backend.repo_name
- data = self.FORM_DATA.copy()
- data['csrf_token'] = csrf_token
- data['inherit_global_settings'] = True
- settings = SettingsModel(repo=repo_name)
-
- invalidation_patcher = mock.patch(
- 'rhodecode.controllers.admin.repos.ScmModel.mark_for_invalidation')
- with invalidation_patcher as invalidation_mock:
- self.app.post(
- url('repo_vcs_settings', repo_name=repo_name), data,
- status=302)
- try:
- invalidation_mock.assert_called_once_with(repo_name, delete=True)
- finally:
- self._cleanup_repo_settings(settings)
-
- def test_other_settings_not_saved_inherit_global_settings_is_true(
- self, autologin_user, backend, csrf_token):
- repo_name = backend.repo_name
- data = self.FORM_DATA.copy()
- data['csrf_token'] = csrf_token
- data['inherit_global_settings'] = True
- self.app.post(
- url('repo_vcs_settings', repo_name=repo_name), data, status=302)
-
- settings = SettingsModel(repo=repo_name)
- ui_settings = (
- VcsSettingsModel.HOOKS_SETTINGS + VcsSettingsModel.HG_SETTINGS)
-
- vcs_settings = []
- try:
- for section, key in ui_settings:
- ui = settings.get_ui_by_section_and_key(section, key)
- if ui:
- vcs_settings.append(ui)
- vcs_settings.extend(settings.get_ui_by_section(
- VcsSettingsModel.SVN_BRANCH_SECTION))
- vcs_settings.extend(settings.get_ui_by_section(
- VcsSettingsModel.SVN_TAG_SECTION))
- for name in VcsSettingsModel.GENERAL_SETTINGS:
- setting = settings.get_setting_by_name(name)
- if setting:
- vcs_settings.append(setting)
- assert vcs_settings == []
- finally:
- self._cleanup_repo_settings(settings)
-
- def test_delete_svn_branch_and_tag_patterns(
- self, autologin_user, backend_svn, settings_util, csrf_token):
- repo = backend_svn.create_repo()
- repo_name = repo.repo_name
- branch = settings_util.create_repo_rhodecode_ui(
- repo, VcsSettingsModel.SVN_BRANCH_SECTION, 'test_branch',
- cleanup=False)
- 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)
- 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):
- repo = backend_svn.create_repo()
- repo_name = repo.repo_name
-
- logout_user_session(self.app, csrf_token)
- session = login_user_session(
- self.app, TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
- csrf_token = auth.get_csrf_token(session)
-
- repo = Repository.get_by_repo_name(repo_name)
- user = UserModel().get_by_username(TEST_USER_REGULAR_LOGIN)
- user_util.grant_user_permission_to_repo(repo, user, 'repository.admin')
- branch = settings_util.create_repo_rhodecode_ui(
- 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)
-
- def test_delete_svn_branch_raises_400_when_not_found(
- self, autologin_user, backend_svn, settings_util, csrf_token):
- 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)
-
- def test_delete_svn_branch_raises_400_when_no_id_specified(
- self, autologin_user, backend_svn, settings_util, csrf_token):
- 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)
-
- def _cleanup_repo_settings(self, settings_model):
- cleanup = []
- ui_settings = (
- VcsSettingsModel.HOOKS_SETTINGS + VcsSettingsModel.HG_SETTINGS)
-
- for section, key in ui_settings:
- ui = settings_model.get_ui_by_section_and_key(section, key)
- if ui:
- cleanup.append(ui)
-
- cleanup.extend(settings_model.get_ui_by_section(
- VcsSettingsModel.INHERIT_SETTINGS))
- cleanup.extend(settings_model.get_ui_by_section(
- VcsSettingsModel.SVN_BRANCH_SECTION))
- cleanup.extend(settings_model.get_ui_by_section(
- VcsSettingsModel.SVN_TAG_SECTION))
-
- for name in VcsSettingsModel.GENERAL_SETTINGS:
- setting = settings_model.get_setting_by_name(name)
- if setting:
- cleanup.append(setting)
-
- for object_ in cleanup:
- Session().delete(object_)
- Session().commit()
-
- def assert_repo_value_equals_global_value(self, response, setting):
- assert_response = AssertResponse(response)
- global_css_selector = '[name={}_inherited]'.format(setting)
- repo_css_selector = '[name={}]'.format(setting)
- repo_element = assert_response.get_element(repo_css_selector)
- global_element = assert_response.get_element(global_css_selector)
- assert repo_element.value == global_element.value
-
-
-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
diff --git a/rhodecode/apps/admin/views/open_source_licenses.py b/rhodecode/apps/admin/views/open_source_licenses.py
--- a/rhodecode/apps/admin/views/open_source_licenses.py
+++ b/rhodecode/apps/admin/views/open_source_licenses.py
@@ -48,7 +48,7 @@ class OpenSourceLicensesAdminSettingsVie
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)
diff --git a/rhodecode/apps/admin/views/repositories.py b/rhodecode/apps/admin/views/repositories.py
new file mode 100644
--- /dev/null
+++ b/rhodecode/apps/admin/views/repositories.py
@@ -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 .
+#
+# 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)))
diff --git a/rhodecode/apps/admin/views/user_groups.py b/rhodecode/apps/admin/views/user_groups.py
--- a/rhodecode/apps/admin/views/user_groups.py
+++ b/rhodecode/apps/admin/views/user_groups.py
@@ -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 UserGrou
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)
diff --git a/rhodecode/apps/ops/tests/__init__.py b/rhodecode/apps/ops/tests/__init__.py
new file mode 100644
diff --git a/rhodecode/apps/repo_group/tests/__init__.py b/rhodecode/apps/repo_group/tests/__init__.py
new file mode 100644
diff --git a/rhodecode/apps/repository/__init__.py b/rhodecode/apps/repository/__init__.py
--- a/rhodecode/apps/repository/__init__.py
+++ b/rhodecode/apps/repository/__init__.py
@@ -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(
diff --git a/rhodecode/tests/functional/test_admin_repos_issuetracker.py b/rhodecode/apps/repository/tests/test_repo_issue_tracker.py
rename from rhodecode/tests/functional/test_admin_repos_issuetracker.py
rename to rhodecode/apps/repository/tests/test_repo_issue_tracker.py
--- a/rhodecode/tests/functional/test_admin_repos_issuetracker.py
+++ b/rhodecode/apps/repository/tests/test_repo_issue_tracker.py
@@ -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 prefix 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)
diff --git a/rhodecode/apps/repository/tests/test_repo_settings.py b/rhodecode/apps/repository/tests/test_repo_settings.py
--- a/rhodecode/apps/repository/tests/test_repo_settings.py
+++ b/rhodecode/apps/repository/tests/test_repo_settings.py
@@ -26,8 +26,7 @@ from rhodecode.lib.vcs.exceptions import
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, **kwar
'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},
diff --git a/rhodecode/apps/repository/tests/test_repo_vcs_settings.py b/rhodecode/apps/repository/tests/test_repo_vcs_settings.py
new file mode 100644
--- /dev/null
+++ b/rhodecode/apps/repository/tests/test_repo_vcs_settings.py
@@ -0,0 +1,685 @@
+# -*- 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 .
+#
+# 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 mock
+import pytest
+
+from rhodecode.lib import auth
+from rhodecode.lib.utils2 import str2bool
+from rhodecode.model.db import (
+ Repository, UserRepoToPerm, User)
+from rhodecode.model.meta import Session
+from rhodecode.model.settings import SettingsModel, VcsSettingsModel
+from rhodecode.model.user import UserModel
+from rhodecode.tests import (
+ 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()
+
+
+def route_path(name, params=None, **kwargs):
+ import urllib
+
+ 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:
+ base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
+ return base_url
+
+
+@pytest.mark.usefixtures("app")
+class TestVcsSettings(object):
+ FORM_DATA = {
+ 'inherit_global_settings': False,
+ 'hooks_changegroup_repo_size': False,
+ 'hooks_changegroup_push_logger': False,
+ 'hooks_outgoing_pull_logger': False,
+ 'extensions_largefiles': False,
+ 'extensions_evolve': False,
+ 'phases_publish': 'False',
+ 'rhodecode_pr_merge_enabled': False,
+ 'rhodecode_use_outdated_comments': False,
+ 'new_svn_branch': '',
+ 'new_svn_tag': ''
+ }
+
+ @pytest.mark.skip_backends('svn')
+ def test_global_settings_initial_values(self, autologin_user, backend):
+ repo_name = backend.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',
+ 'hooks_changegroup_repo_size', 'hooks_changegroup_push_logger',
+ 'hooks_outgoing_pull_logger'
+ )
+ for setting in expected_settings:
+ self.assert_repo_value_equals_global_value(response, setting)
+
+ def test_show_settings_requires_repo_admin_permission(
+ self, backend, user_util, settings_util):
+ repo = backend.create_repo()
+ repo_name = repo.repo_name
+ user = UserModel().get_by_username(TEST_USER_REGULAR_LOGIN)
+ 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(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(route_path('edit_repo_vcs', repo_name=repo_name))
+
+ assert_response = AssertResponse(response)
+ element = assert_response.get_element('#inherit_global_settings')
+ assert element.checked
+
+ @pytest.mark.parametrize('checked_value', [True, False])
+ def test_inherit_global_settings_value(
+ self, autologin_user, backend, checked_value, settings_util):
+ repo = backend.create_repo()
+ repo_name = repo.repo_name
+ settings_util.create_repo_rhodecode_setting(
+ repo, 'inherit_vcs_settings', checked_value, 'bool')
+ 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')
+ assert element.checked == checked_value
+
+ @pytest.mark.skip_backends('svn')
+ def test_hooks_settings_are_created(
+ self, autologin_user, backend, csrf_token):
+ repo_name = backend.repo_name
+ data = self.FORM_DATA.copy()
+ data['csrf_token'] = csrf_token
+ self.app.post(
+ 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:
+ ui = settings.get_ui_by_section_and_key(section, key)
+ assert ui.ui_active is False
+ finally:
+ self._cleanup_repo_settings(settings)
+
+ def test_hooks_settings_are_not_created_for_svn(
+ self, autologin_user, backend_svn, csrf_token):
+ repo_name = backend_svn.repo_name
+ data = self.FORM_DATA.copy()
+ data['csrf_token'] = csrf_token
+ self.app.post(
+ 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:
+ ui = settings.get_ui_by_section_and_key(section, key)
+ assert ui is None
+ finally:
+ self._cleanup_repo_settings(settings)
+
+ @pytest.mark.skip_backends('svn')
+ def test_hooks_settings_are_updated(
+ self, autologin_user, backend, csrf_token):
+ repo_name = backend.repo_name
+ settings = SettingsModel(repo=repo_name)
+ for section, key in VcsSettingsModel.HOOKS_SETTINGS:
+ settings.create_ui_section_value(section, '', key=key, active=True)
+
+ data = self.FORM_DATA.copy()
+ data['csrf_token'] = csrf_token
+ self.app.post(
+ 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)
+ assert ui.ui_active is False
+ finally:
+ self._cleanup_repo_settings(settings)
+
+ def test_hooks_settings_are_not_updated_for_svn(
+ self, autologin_user, backend_svn, csrf_token):
+ repo_name = backend_svn.repo_name
+ settings = SettingsModel(repo=repo_name)
+ for section, key in VcsSettingsModel.HOOKS_SETTINGS:
+ settings.create_ui_section_value(section, '', key=key, active=True)
+
+ data = self.FORM_DATA.copy()
+ data['csrf_token'] = csrf_token
+ self.app.post(
+ 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)
+ assert ui.ui_active is True
+ finally:
+ self._cleanup_repo_settings(settings)
+
+ @pytest.mark.skip_backends('svn')
+ def test_pr_settings_are_created(
+ self, autologin_user, backend, csrf_token):
+ repo_name = backend.repo_name
+ data = self.FORM_DATA.copy()
+ data['csrf_token'] = csrf_token
+ self.app.post(
+ 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:
+ setting = settings.get_setting_by_name(name)
+ assert setting.app_settings_value is False
+ finally:
+ self._cleanup_repo_settings(settings)
+
+ def test_pr_settings_are_not_created_for_svn(
+ self, autologin_user, backend_svn, csrf_token):
+ repo_name = backend_svn.repo_name
+ data = self.FORM_DATA.copy()
+ data['csrf_token'] = csrf_token
+ self.app.post(
+ 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:
+ setting = settings.get_setting_by_name(name)
+ assert setting is None
+ finally:
+ self._cleanup_repo_settings(settings)
+
+ def test_pr_settings_creation_requires_repo_admin_permission(
+ self, backend, user_util, settings_util, csrf_token):
+ repo = backend.create_repo()
+ repo_name = repo.repo_name
+
+ logout_user_session(self.app, csrf_token)
+ session = login_user_session(
+ self.app, TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
+ new_csrf_token = auth.get_csrf_token(session)
+
+ user = UserModel().get_by_username(TEST_USER_REGULAR_LOGIN)
+ repo = Repository.get_by_repo_name(repo_name)
+ user_util.grant_user_permission_to_repo(repo, user, 'repository.admin')
+ data = self.FORM_DATA.copy()
+ data['csrf_token'] = new_csrf_token
+ settings = SettingsModel(repo=repo_name)
+
+ try:
+ self.app.post(
+ route_path('edit_repo_vcs_update', repo_name=repo_name), data,
+ status=302)
+ finally:
+ self._cleanup_repo_settings(settings)
+
+ @pytest.mark.skip_backends('svn')
+ def test_pr_settings_are_updated(
+ self, autologin_user, backend, csrf_token):
+ repo_name = backend.repo_name
+ settings = SettingsModel(repo=repo_name)
+ for name in VcsSettingsModel.GENERAL_SETTINGS:
+ settings.create_or_update_setting(name, True, 'bool')
+
+ data = self.FORM_DATA.copy()
+ data['csrf_token'] = csrf_token
+ self.app.post(
+ 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)
+ assert setting.app_settings_value is False
+ finally:
+ self._cleanup_repo_settings(settings)
+
+ def test_pr_settings_are_not_updated_for_svn(
+ self, autologin_user, backend_svn, csrf_token):
+ repo_name = backend_svn.repo_name
+ settings = SettingsModel(repo=repo_name)
+ for name in VcsSettingsModel.GENERAL_SETTINGS:
+ settings.create_or_update_setting(name, True, 'bool')
+
+ data = self.FORM_DATA.copy()
+ data['csrf_token'] = csrf_token
+ self.app.post(
+ 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)
+ assert setting.app_settings_value is True
+ finally:
+ self._cleanup_repo_settings(settings)
+
+ def test_svn_settings_are_created(
+ self, autologin_user, backend_svn, csrf_token, settings_util):
+ repo_name = backend_svn.repo_name
+ data = self.FORM_DATA.copy()
+ data['new_svn_tag'] = 'svn-tag'
+ data['new_svn_branch'] = 'svn-branch'
+ data['csrf_token'] = csrf_token
+
+ # Create few global settings to make sure that uniqueness validators
+ # are not triggered
+ settings_util.create_rhodecode_ui(
+ VcsSettingsModel.SVN_BRANCH_SECTION, 'svn-branch')
+ settings_util.create_rhodecode_ui(
+ VcsSettingsModel.SVN_TAG_SECTION, 'svn-tag')
+
+ self.app.post(
+ 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(
+ VcsSettingsModel.SVN_BRANCH_SECTION)
+ svn_branch_names = [b.ui_value for b in svn_branches]
+ svn_tags = settings.get_ui_by_section(
+ VcsSettingsModel.SVN_TAG_SECTION)
+ svn_tag_names = [b.ui_value for b in svn_tags]
+ assert 'svn-branch' in svn_branch_names
+ assert 'svn-tag' in svn_tag_names
+ finally:
+ self._cleanup_repo_settings(settings)
+
+ def test_svn_settings_are_unique(
+ self, autologin_user, backend_svn, csrf_token, settings_util):
+ repo = backend_svn.repo
+ repo_name = repo.repo_name
+ data = self.FORM_DATA.copy()
+ data['new_svn_tag'] = 'test_tag'
+ data['new_svn_branch'] = 'test_branch'
+ data['csrf_token'] = csrf_token
+ settings_util.create_repo_rhodecode_ui(
+ repo, VcsSettingsModel.SVN_BRANCH_SECTION, 'test_branch')
+ settings_util.create_repo_rhodecode_ui(
+ repo, VcsSettingsModel.SVN_TAG_SECTION, 'test_tag')
+
+ response = self.app.post(
+ 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(
+ self, autologin_user, backend_svn, csrf_token):
+ repo_name = backend_svn.repo_name
+ data = self.FORM_DATA.copy()
+ data['csrf_token'] = csrf_token
+ self.app.post(
+ 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(
+ VcsSettingsModel.SVN_BRANCH_SECTION)
+ svn_tags = settings.get_ui_by_section(
+ VcsSettingsModel.SVN_TAG_SECTION)
+ assert len(svn_branches) == 0
+ assert len(svn_tags) == 0
+ finally:
+ self._cleanup_repo_settings(settings)
+
+ def test_svn_settings_are_shown_for_svn_repository(
+ self, autologin_user, backend_svn, csrf_token):
+ repo_name = backend_svn.repo_name
+ response = self.app.get(
+ route_path('edit_repo_vcs', repo_name=repo_name), status=200)
+ response.mustcontain('Subversion Settings')
+
+ @pytest.mark.skip_backends('svn')
+ def test_svn_settings_are_not_created_for_not_svn_repository(
+ self, autologin_user, backend, csrf_token):
+ repo_name = backend.repo_name
+ data = self.FORM_DATA.copy()
+ data['csrf_token'] = csrf_token
+ self.app.post(
+ 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(
+ VcsSettingsModel.SVN_BRANCH_SECTION)
+ svn_tags = settings.get_ui_by_section(
+ VcsSettingsModel.SVN_TAG_SECTION)
+ assert len(svn_branches) == 0
+ assert len(svn_tags) == 0
+ finally:
+ self._cleanup_repo_settings(settings)
+
+ @pytest.mark.skip_backends('svn')
+ def test_svn_settings_are_shown_only_for_svn_repository(
+ self, autologin_user, backend, csrf_token):
+ repo_name = backend.repo_name
+ response = self.app.get(
+ route_path('edit_repo_vcs', repo_name=repo_name), status=200)
+ response.mustcontain(no='Subversion Settings')
+
+ def test_hg_settings_are_created(
+ self, autologin_user, backend_hg, csrf_token):
+ repo_name = backend_hg.repo_name
+ data = self.FORM_DATA.copy()
+ data['new_svn_tag'] = 'svn-tag'
+ data['new_svn_branch'] = 'svn-branch'
+ data['csrf_token'] = csrf_token
+ self.app.post(
+ 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(
+ 'extensions', 'largefiles')
+ assert largefiles_ui.ui_active is False
+ phases_ui = settings.get_ui_by_section_and_key(
+ 'phases', 'publish')
+ assert str2bool(phases_ui.ui_value) is False
+ finally:
+ self._cleanup_repo_settings(settings)
+
+ def test_hg_settings_are_updated(
+ self, autologin_user, backend_hg, csrf_token):
+ repo_name = backend_hg.repo_name
+ settings = SettingsModel(repo=repo_name)
+ settings.create_ui_section_value(
+ 'extensions', '', key='largefiles', active=True)
+ settings.create_ui_section_value(
+ 'phases', '1', key='publish', active=True)
+
+ data = self.FORM_DATA.copy()
+ data['csrf_token'] = csrf_token
+ self.app.post(
+ 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')
+ assert largefiles_ui.ui_active is False
+ phases_ui = settings.get_ui_by_section_and_key(
+ 'phases', 'publish')
+ assert str2bool(phases_ui.ui_value) is False
+ finally:
+ self._cleanup_repo_settings(settings)
+
+ def test_hg_settings_are_shown_for_hg_repository(
+ self, autologin_user, backend_hg, csrf_token):
+ repo_name = backend_hg.repo_name
+ response = self.app.get(
+ route_path('edit_repo_vcs', repo_name=repo_name), status=200)
+ response.mustcontain('Mercurial Settings')
+
+ @pytest.mark.skip_backends('hg')
+ def test_hg_settings_are_created_only_for_hg_repository(
+ self, autologin_user, backend, csrf_token):
+ repo_name = backend.repo_name
+ data = self.FORM_DATA.copy()
+ data['csrf_token'] = csrf_token
+ self.app.post(
+ 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(
+ 'extensions', 'largefiles')
+ assert largefiles_ui is None
+ phases_ui = settings.get_ui_by_section_and_key(
+ 'phases', 'publish')
+ assert phases_ui is None
+ finally:
+ self._cleanup_repo_settings(settings)
+
+ @pytest.mark.skip_backends('hg')
+ def test_hg_settings_are_shown_only_for_hg_repository(
+ self, autologin_user, backend, csrf_token):
+ repo_name = backend.repo_name
+ response = self.app.get(
+ route_path('edit_repo_vcs', repo_name=repo_name), status=200)
+ response.mustcontain(no='Mercurial Settings')
+
+ @pytest.mark.skip_backends('hg')
+ def test_hg_settings_are_updated_only_for_hg_repository(
+ self, autologin_user, backend, csrf_token):
+ repo_name = backend.repo_name
+ settings = SettingsModel(repo=repo_name)
+ settings.create_ui_section_value(
+ 'extensions', '', key='largefiles', active=True)
+ settings.create_ui_section_value(
+ 'phases', '1', key='publish', active=True)
+
+ data = self.FORM_DATA.copy()
+ data['csrf_token'] = csrf_token
+ self.app.post(
+ 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')
+ assert largefiles_ui.ui_active is True
+ phases_ui = settings.get_ui_by_section_and_key(
+ 'phases', 'publish')
+ assert phases_ui.ui_value == '1'
+ finally:
+ self._cleanup_repo_settings(settings)
+
+ def test_per_repo_svn_settings_are_displayed(
+ self, autologin_user, backend_svn, settings_util):
+ repo = backend_svn.create_repo()
+ repo_name = repo.repo_name
+ branches = [
+ settings_util.create_repo_rhodecode_ui(
+ repo, VcsSettingsModel.SVN_BRANCH_SECTION,
+ 'branch_{}'.format(i))
+ for i in range(10)]
+ tags = [
+ settings_util.create_repo_rhodecode_ui(
+ repo, VcsSettingsModel.SVN_TAG_SECTION, 'tag_{}'.format(i))
+ for i in range(10)]
+
+ response = self.app.get(
+ 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)
+ element = assert_response.get_element(css_selector)
+ assert element.value == branch.ui_value
+ for tag in tags:
+ css_selector = '[name=tag_ui_value_new_{}]'.format(tag.ui_id)
+ element = assert_response.get_element(css_selector)
+ assert element.value == tag.ui_value
+
+ def test_per_repo_hg_and_pr_settings_are_not_displayed_for_svn(
+ self, autologin_user, backend_svn, settings_util):
+ repo = backend_svn.create_repo()
+ repo_name = repo.repo_name
+ response = self.app.get(
+ route_path('edit_repo_vcs', repo_name=repo_name), status=200)
+ response.mustcontain(no='')
+ response.mustcontain(no='')
+
+ def test_inherit_global_settings_value_is_saved(
+ self, autologin_user, backend, csrf_token):
+ repo_name = backend.repo_name
+ data = self.FORM_DATA.copy()
+ data['csrf_token'] = csrf_token
+ data['inherit_global_settings'] = True
+ self.app.post(
+ route_path('edit_repo_vcs_update', repo_name=repo_name), data, status=302)
+
+ settings = SettingsModel(repo=repo_name)
+ vcs_settings = VcsSettingsModel(repo=repo_name)
+ try:
+ assert vcs_settings.inherit_global_settings is True
+ finally:
+ self._cleanup_repo_settings(settings)
+
+ def test_repo_cache_is_invalidated_when_settings_are_updated(
+ self, autologin_user, backend, csrf_token):
+ repo_name = backend.repo_name
+ data = self.FORM_DATA.copy()
+ data['csrf_token'] = csrf_token
+ data['inherit_global_settings'] = True
+ settings = SettingsModel(repo=repo_name)
+
+ invalidation_patcher = mock.patch(
+ 'rhodecode.model.scm.ScmModel.mark_for_invalidation')
+ with invalidation_patcher as invalidation_mock:
+ self.app.post(
+ route_path('edit_repo_vcs_update', repo_name=repo_name), data,
+ status=302)
+ try:
+ invalidation_mock.assert_called_once_with(repo_name, delete=True)
+ finally:
+ self._cleanup_repo_settings(settings)
+
+ def test_other_settings_not_saved_inherit_global_settings_is_true(
+ self, autologin_user, backend, csrf_token):
+ repo_name = backend.repo_name
+ data = self.FORM_DATA.copy()
+ data['csrf_token'] = csrf_token
+ data['inherit_global_settings'] = True
+ self.app.post(
+ route_path('edit_repo_vcs_update', repo_name=repo_name), data, status=302)
+
+ settings = SettingsModel(repo=repo_name)
+ ui_settings = (
+ VcsSettingsModel.HOOKS_SETTINGS + VcsSettingsModel.HG_SETTINGS)
+
+ vcs_settings = []
+ try:
+ for section, key in ui_settings:
+ ui = settings.get_ui_by_section_and_key(section, key)
+ if ui:
+ vcs_settings.append(ui)
+ vcs_settings.extend(settings.get_ui_by_section(
+ VcsSettingsModel.SVN_BRANCH_SECTION))
+ vcs_settings.extend(settings.get_ui_by_section(
+ VcsSettingsModel.SVN_TAG_SECTION))
+ for name in VcsSettingsModel.GENERAL_SETTINGS:
+ setting = settings.get_setting_by_name(name)
+ if setting:
+ vcs_settings.append(setting)
+ assert vcs_settings == []
+ finally:
+ self._cleanup_repo_settings(settings)
+
+ def test_delete_svn_branch_and_tag_patterns(
+ 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(
+ repo, VcsSettingsModel.SVN_BRANCH_SECTION, 'test_branch',
+ cleanup=False)
+ tag = settings_util.create_repo_rhodecode_ui(
+ repo, VcsSettingsModel.SVN_TAG_SECTION, 'test_tag', cleanup=False)
+ data = {
+ 'csrf_token': csrf_token
+ }
+ for id_ in (branch.ui_id, tag.ui_id):
+ data['delete_svn_pattern'] = id_,
+ self.app.post(
+ 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, xhr_header):
+ repo = backend_svn.create_repo()
+ repo_name = repo.repo_name
+
+ logout_user_session(self.app, csrf_token)
+ session = login_user_session(
+ self.app, TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
+ csrf_token = auth.get_csrf_token(session)
+
+ repo = Repository.get_by_repo_name(repo_name)
+ user = UserModel().get_by_username(TEST_USER_REGULAR_LOGIN)
+ user_util.grant_user_permission_to_repo(repo, user, 'repository.admin')
+ branch = settings_util.create_repo_rhodecode_ui(
+ repo, VcsSettingsModel.SVN_BRANCH_SECTION, 'test_branch',
+ cleanup=False)
+ data = {
+ 'csrf_token': csrf_token,
+ 'delete_svn_pattern': branch.ui_id
+ }
+ self.app.post(
+ 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, xhr_header):
+ repo_name = backend_svn.repo_name
+ data = {
+ 'delete_svn_pattern': 123,
+ 'csrf_token': csrf_token
+ }
+ self.app.post(
+ 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, xhr_header):
+ repo_name = backend_svn.repo_name
+ data = {
+ 'csrf_token': csrf_token
+ }
+ self.app.post(
+ 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 = []
+ ui_settings = (
+ VcsSettingsModel.HOOKS_SETTINGS + VcsSettingsModel.HG_SETTINGS)
+
+ for section, key in ui_settings:
+ ui = settings_model.get_ui_by_section_and_key(section, key)
+ if ui:
+ cleanup.append(ui)
+
+ cleanup.extend(settings_model.get_ui_by_section(
+ VcsSettingsModel.INHERIT_SETTINGS))
+ cleanup.extend(settings_model.get_ui_by_section(
+ VcsSettingsModel.SVN_BRANCH_SECTION))
+ cleanup.extend(settings_model.get_ui_by_section(
+ VcsSettingsModel.SVN_TAG_SECTION))
+
+ for name in VcsSettingsModel.GENERAL_SETTINGS:
+ setting = settings_model.get_setting_by_name(name)
+ if setting:
+ cleanup.append(setting)
+
+ for object_ in cleanup:
+ Session().delete(object_)
+ Session().commit()
+
+ def assert_repo_value_equals_global_value(self, response, setting):
+ assert_response = AssertResponse(response)
+ global_css_selector = '[name={}_inherited]'.format(setting)
+ repo_css_selector = '[name={}]'.format(setting)
+ repo_element = assert_response.get_element(repo_css_selector)
+ global_element = assert_response.get_element(global_css_selector)
+ assert repo_element.value == global_element.value
+
+
+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
diff --git a/rhodecode/apps/repository/tests/test_vcs_settings.py b/rhodecode/apps/repository/tests/test_vcs_settings.py
--- a/rhodecode/apps/repository/tests/test_vcs_settings.py
+++ b/rhodecode/apps/repository/tests/test_vcs_settings.py
@@ -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, **kwar
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'}):
@@ -64,24 +65,6 @@ class TestAdminRepoVcsSettings(object):
@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']),
- ])
def test_update_boolean_settings(
self, csrf_token, setting_name, setting_backends, backend):
if backend.alias not in setting_backends:
@@ -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,
diff --git a/rhodecode/apps/repository/views/repo_commits.py b/rhodecode/apps/repository/views/repo_commits.py
--- a/rhodecode/apps/repository/views/repo_commits.py
+++ b/rhodecode/apps/repository/views/repo_commits.py
@@ -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)
diff --git a/rhodecode/apps/repository/views/repo_feed.py b/rhodecode/apps/repository/views/repo_feed.py
--- a/rhodecode/apps/repository/views/repo_feed.py
+++ b/rhodecode/apps/repository/views/repo_feed.py
@@ -29,10 +29,9 @@ from webhelpers.feedgenerator import Rss
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
diff --git a/rhodecode/apps/repository/views/repo_maintainance.py b/rhodecode/apps/repository/views/repo_maintainance.py
--- a/rhodecode/apps/repository/views/repo_maintainance.py
+++ b/rhodecode/apps/repository/views/repo_maintainance.py
@@ -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()
diff --git a/rhodecode/apps/repository/views/repo_permissions.py b/rhodecode/apps/repository/views/repo_permissions.py
--- a/rhodecode/apps/repository/views/repo_permissions.py
+++ b/rhodecode/apps/repository/views/repo_permissions.py
@@ -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(RepoAp
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(RepoAp
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(RepoAp
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))
diff --git a/rhodecode/apps/repository/views/repo_settings.py b/rhodecode/apps/repository/views/repo_settings.py
--- a/rhodecode/apps/repository/views/repo_settings.py
+++ b/rhodecode/apps/repository/views/repo_settings.py
@@ -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))
diff --git a/rhodecode/apps/repository/views/repo_settings_advanced.py b/rhodecode/apps/repository/views/repo_settings_advanced.py
--- a/rhodecode/apps/repository/views/repo_settings_advanced.py
+++ b/rhodecode/apps/repository/views/repo_settings_advanced.py
@@ -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:
diff --git a/rhodecode/apps/repository/views/repo_settings_fields.py b/rhodecode/apps/repository/views/repo_settings_fields.py
new file mode 100644
--- /dev/null
+++ b/rhodecode/apps/repository/views/repo_settings_fields.py
@@ -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 .
+#
+# 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))
diff --git a/rhodecode/apps/repository/views/repo_settings_issue_trackers.py b/rhodecode/apps/repository/views/repo_settings_issue_trackers.py
new file mode 100644
--- /dev/null
+++ b/rhodecode/apps/repository/views/repo_settings_issue_trackers.py
@@ -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 .
+#
+# 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))
+
diff --git a/rhodecode/apps/repository/views/repo_settings_remote.py b/rhodecode/apps/repository/views/repo_settings_remote.py
new file mode 100644
--- /dev/null
+++ b/rhodecode/apps/repository/views/repo_settings_remote.py
@@ -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 .
+#
+# 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))
diff --git a/rhodecode/apps/repository/views/repo_settings_vcs.py b/rhodecode/apps/repository/views/repo_settings_vcs.py
new file mode 100644
--- /dev/null
+++ b/rhodecode/apps/repository/views/repo_settings_vcs.py
@@ -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 .
+#
+# 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
diff --git a/rhodecode/apps/repository/views/repo_strip.py b/rhodecode/apps/repository/views/repo_strip.py
--- a/rhodecode/apps/repository/views/repo_strip.py
+++ b/rhodecode/apps/repository/views/repo_strip.py
@@ -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(
diff --git a/rhodecode/apps/repository/views/repo_summary.py b/rhodecode/apps/repository/views/repo_summary.py
--- a/rhodecode/apps/repository/views/repo_summary.py
+++ b/rhodecode/apps/repository/views/repo_summary.py
@@ -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(
diff --git a/rhodecode/config/environment.py b/rhodecode/config/environment.py
--- a/rhodecode/config/environment.py
+++ b/rhodecode/config/environment.py
@@ -143,6 +143,8 @@ def load_pyramid_environment(global_conf
# 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.
diff --git a/rhodecode/config/routing.py b/rhodecode/config/routing.py
--- a/rhodecode/config/routing.py
+++ b/rhodecode/config/routing.py
@@ -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
diff --git a/rhodecode/controllers/admin/repos.py b/rhodecode/controllers/admin/repos.py
deleted file mode 100644
--- a/rhodecode/controllers/admin/repos.py
+++ /dev/null
@@ -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 .
-#
-# 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
diff --git a/rhodecode/controllers/admin/settings.py b/rhodecode/controllers/admin/settings.py
--- a/rhodecode/controllers/admin/settings.py
+++ b/rhodecode/controllers/admin/settings.py
@@ -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_
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, \
diff --git a/rhodecode/controllers/admin/user_groups.py b/rhodecode/controllers/admin/user_groups.py
--- a/rhodecode/controllers/admin/user_groups.py
+++ b/rhodecode/controllers/admin/user_groups.py
@@ -31,25 +31,19 @@ from pylons import request, tmpl_context
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)
diff --git a/rhodecode/lib/base.py b/rhodecode/lib/base.py
--- a/rhodecode/lib/base.py
+++ b/rhodecode/lib/base.py
@@ -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=Non
``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, r
'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)
diff --git a/rhodecode/lib/rc_commands/__init__.py b/rhodecode/lib/rc_commands/__init__.py
new file mode 100644
diff --git a/rhodecode/lib/utils.py b/rhodecode/lib/utils.py
--- a/rhodecode/lib/utils.py
+++ b/rhodecode/lib/utils.py
@@ -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
diff --git a/rhodecode/model/repo.py b/rhodecode/model/repo.py
--- a/rhodecode/model/repo.py
+++ b/rhodecode/model/repo.py
@@ -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
diff --git a/rhodecode/public/js/rhodecode/base/keyboard-bindings.js b/rhodecode/public/js/rhodecode/base/keyboard-bindings.js
--- a/rhodecode/public/js/rhodecode/base/keyboard-bindings.js
+++ b/rhodecode/public/js/rhodecode/base/keyboard-bindings.js
@@ -55,7 +55,7 @@ function setRCMouseBindings(repoName, re
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 != '') {
diff --git a/rhodecode/public/js/rhodecode/routes.js b/rhodecode/public/js/rhodecode/routes.js
--- a/rhodecode/public/js/rhodecode/routes.js
+++ b/rhodecode/public/js/rhodecode/routes.js
@@ -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']);
diff --git a/rhodecode/templates/admin/repos/repo_add.mako b/rhodecode/templates/admin/repos/repo_add.mako
--- a/rhodecode/templates/admin/repos/repo_add.mako
+++ b/rhodecode/templates/admin/repos/repo_add.mako
@@ -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'))}
»
- ${h.link_to(_('Repositories'),h.url('repos'))}
+ ${h.link_to(_('Repositories'), h.route_path('repos'))}
%else:
${_('Admin')}
»
diff --git a/rhodecode/templates/admin/repos/repo_add_base.mako b/rhodecode/templates/admin/repos/repo_add_base.mako
--- a/rhodecode/templates/admin/repos/repo_add_base.mako
+++ b/rhodecode/templates/admin/repos/repo_add_base.mako
@@ -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)}