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 @@ -21,6 +21,11 @@ def includeme(config): + # Summary + config.add_route( + name='repo_summary_explicit', + pattern='/{repo_name:.*?[^/]}/summary', repo_route=True) + # Tags config.add_route( name='tags_home', @@ -41,6 +46,23 @@ def includeme(config): name='edit_repo', pattern='/{repo_name:.*?[^/]}/settings', repo_route=True) + # Settings advanced + config.add_route( + name='edit_repo_advanced', + pattern='/{repo_name:.*?[^/]}/settings/advanced', repo_route=True) + config.add_route( + name='edit_repo_advanced_delete', + pattern='/{repo_name:.*?[^/]}/settings/advanced/delete', repo_route=True) + config.add_route( + name='edit_repo_advanced_locking', + pattern='/{repo_name:.*?[^/]}/settings/advanced/locking', repo_route=True) + config.add_route( + name='edit_repo_advanced_journal', + pattern='/{repo_name:.*?[^/]}/settings/advanced/journal', repo_route=True) + config.add_route( + name='edit_repo_advanced_fork', + pattern='/{repo_name:.*?[^/]}/settings/advanced/fork', repo_route=True) + # Caches config.add_route( name='edit_repo_caches', @@ -77,5 +99,11 @@ def includeme(config): config.add_route( name='strip_execute', pattern='/{repo_name:.*?[^/]}/settings/strip_execute', repo_route=True) + + # NOTE(marcink): needs to be at the end for catch-all + # config.add_route( + # name='repo_summary', + # pattern='/{repo_name:.*?[^/]}', repo_route=True) + # Scan module for configuration decorators. config.scan() 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 @@ -38,6 +38,7 @@ def route_path(name, params=None, **kwar base_url = { 'edit_repo': '/{repo_name}/settings', + 'edit_repo_advanced': '/{repo_name}/settings/advanced', 'edit_repo_caches': '/{repo_name}/settings/caches', 'edit_repo_perms': '/{repo_name}/settings/permissions', }[name].format(**kwargs) @@ -62,6 +63,7 @@ class TestAdminRepoSettings(object): 'edit_repo', 'edit_repo_caches', 'edit_repo_perms', + 'edit_repo_advanced', ]) def test_show_page(self, urlname, app, backend): app.get(route_path(urlname, repo_name=backend.repo_name), status=200) @@ -74,10 +76,9 @@ class TestAdminRepoSettings(object): self.app.get(route_path('edit_repo', repo_name=backend_hg.repo_name)) @pytest.mark.parametrize('urlname', [ - 'edit_repo_advanced', 'repo_vcs_settings', + 'repo_settings_issuetracker', 'edit_repo_fields', - 'repo_settings_issuetracker', 'edit_repo_remote', 'edit_repo_statistics', ]) diff --git a/rhodecode/apps/repository/tests/test_repo_settings_advanced.py b/rhodecode/apps/repository/tests/test_repo_settings_advanced.py new file mode 100644 --- /dev/null +++ b/rhodecode/apps/repository/tests/test_repo_settings_advanced.py @@ -0,0 +1,150 @@ +# -*- 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 pytest + +from rhodecode.lib.utils2 import safe_unicode, safe_str +from rhodecode.model.db import Repository +from rhodecode.model.repo import RepoModel +from rhodecode.tests import ( + HG_REPO, GIT_REPO, assert_session_flash, no_newline_id_generator) +from rhodecode.tests.fixture import Fixture +from rhodecode.tests.utils import repo_on_filesystem + +fixture = Fixture() + + +def route_path(name, params=None, **kwargs): + import urllib + + base_url = { + 'repo_summary_explicit': '/{repo_name}/summary', + 'repo_summary': '/{repo_name}', + 'edit_repo_advanced': '/{repo_name}/settings/advanced', + 'edit_repo_advanced_delete': '/{repo_name}/settings/advanced/delete', + 'edit_repo_advanced_fork': '/{repo_name}/settings/advanced/fork', + 'edit_repo_advanced_locking': '/{repo_name}/settings/advanced/locking', + 'edit_repo_advanced_journal': '/{repo_name}/settings/advanced/journal', + + }[name].format(**kwargs) + + if params: + base_url = '{}?{}'.format(base_url, urllib.urlencode(params)) + return base_url + + +@pytest.mark.usefixtures('autologin_user', 'app') +class TestAdminRepoSettingsAdvanced(object): + + def test_set_repo_fork_has_no_self_id(self, autologin_user, backend): + repo = backend.repo + response = self.app.get( + route_path('edit_repo_advanced', repo_name=backend.repo_name)) + opt = """""" % repo.repo_id + response.mustcontain(no=[opt]) + + def test_set_fork_of_target_repo( + self, autologin_user, backend, csrf_token): + target_repo = 'target_%s' % backend.alias + fixture.create_repo(target_repo, repo_type=backend.alias) + repo2 = Repository.get_by_repo_name(target_repo) + response = self.app.post( + route_path('edit_repo_advanced_fork', repo_name=backend.repo_name), + params={'id_fork_of': repo2.repo_id, + 'csrf_token': csrf_token}) + repo = Repository.get_by_repo_name(backend.repo_name) + repo2 = Repository.get_by_repo_name(target_repo) + assert_session_flash( + response, + 'Marked repo %s as fork of %s' % (repo.repo_name, repo2.repo_name)) + + assert repo.fork == repo2 + response = response.follow() + # check if given repo is selected + + opt = 'This repository is a fork of %s' % ( + route_path('repo_summary', repo_name=repo2.repo_name), + repo2.repo_name) + + response.mustcontain(opt) + + fixture.destroy_repo(target_repo, forks='detach') + + @pytest.mark.backends("hg", "git") + def test_set_fork_of_other_type_repo( + self, autologin_user, backend, csrf_token): + TARGET_REPO_MAP = { + 'git': { + 'type': 'hg', + 'repo_name': HG_REPO}, + 'hg': { + 'type': 'git', + 'repo_name': GIT_REPO}, + } + target_repo = TARGET_REPO_MAP[backend.alias] + + repo2 = Repository.get_by_repo_name(target_repo['repo_name']) + response = self.app.post( + route_path('edit_repo_advanced_fork', repo_name=backend.repo_name), + params={'id_fork_of': repo2.repo_id, + 'csrf_token': csrf_token}) + assert_session_flash( + response, + 'Cannot set repository as fork of repository with other type') + + def test_set_fork_of_none(self, autologin_user, backend, csrf_token): + # mark it as None + response = self.app.post( + route_path('edit_repo_advanced_fork', repo_name=backend.repo_name), + params={'id_fork_of': None, '_method': 'put', + 'csrf_token': csrf_token}) + assert_session_flash( + response, + 'Marked repo %s as fork of %s' + % (backend.repo_name, "Nothing")) + assert backend.repo.fork is None + + def test_set_fork_of_same_repo(self, autologin_user, backend, csrf_token): + repo = Repository.get_by_repo_name(backend.repo_name) + response = self.app.post( + route_path('edit_repo_advanced_fork', repo_name=backend.repo_name), + params={'id_fork_of': repo.repo_id, 'csrf_token': csrf_token}) + assert_session_flash( + response, 'An error occurred during this operation') + + @pytest.mark.parametrize( + "suffix", + ['', u'ąęł' , '123'], + ids=no_newline_id_generator) + def test_advanced_delete(self, autologin_user, backend, suffix, csrf_token): + repo = backend.create_repo(name_suffix=suffix) + repo_name = repo.repo_name + repo_name_str = safe_str(repo.repo_name) + + response = self.app.post( + route_path('edit_repo_advanced_delete', repo_name=repo_name_str), + params={'csrf_token': csrf_token}) + assert_session_flash(response, + u'Deleted repository `{}`'.format(repo_name)) + response.follow() + + # check if repo was deleted from db + assert RepoModel().get_by_repo_name(repo_name) is None + assert not repo_on_filesystem(repo_name_str) diff --git a/rhodecode/apps/repository/views/repo_settings_advanced.py b/rhodecode/apps/repository/views/repo_settings_advanced.py new file mode 100644 --- /dev/null +++ b/rhodecode/apps/repository/views/repo_settings_advanced.py @@ -0,0 +1,225 @@ +# -*- coding: utf-8 -*- + +# Copyright (C) 2011-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.view import view_config +from pyramid.httpexceptions import HTTPFound + +from rhodecode.apps._base import RepoAppView +from rhodecode.lib import helpers as h +from rhodecode.lib import audit_logger +from rhodecode.lib.auth import ( + LoginRequired, HasRepoPermissionAnyDecorator, CSRFRequired) +from rhodecode.lib.exceptions import AttachedForksError +from rhodecode.lib.utils2 import safe_int +from rhodecode.lib.vcs import RepositoryError +from rhodecode.model.db import Session, UserFollowing, User, Repository +from rhodecode.model.repo import RepoModel +from rhodecode.model.scm import ScmModel + +log = logging.getLogger(__name__) + + +class RepoSettingsView(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_advanced', request_method='GET', + renderer='rhodecode:templates/admin/repos/repo_edit.mako') + def edit_advanced(self): + c = self.load_default_context() + c.active = 'advanced' + + 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() + + c.has_origin_repo_read_perm = False + if self.db_repo.fork: + c.has_origin_repo_read_perm = h.HasRepoPermissionAny( + 'repository.write', 'repository.read', 'repository.admin')( + self.db_repo.fork.repo_name, 'repo set as fork page') + + return self._get_template_context(c) + + @LoginRequired() + @HasRepoPermissionAnyDecorator('repository.admin') + @view_config( + route_name='edit_repo_advanced_delete', request_method='POST', + renderer='rhodecode:templates/admin/repos/repo_edit.mako') + def edit_advanced_delete(self): + """ + Deletes the repository, or shows warnings if deletion is not possible + because of attached forks or other errors. + """ + _ = self.request.translate + handle_forks = self.request.POST.get('forks', None) + + try: + _forks = self.db_repo.forks.count() + if _forks and handle_forks: + if handle_forks == 'detach_forks': + handle_forks = 'detach' + h.flash(_('Detached %s forks') % _forks, category='success') + elif handle_forks == 'delete_forks': + handle_forks = 'delete' + h.flash(_('Deleted %s forks') % _forks, category='success') + + repo_data = self.db_repo.get_api_data() + RepoModel().delete(self.db_repo, forks=handle_forks) + + repo = audit_logger.RepoWrap(repo_id=self.db_repo.repo_id, + repo_name=self.db_repo.repo_name) + audit_logger.store( + action='repo.delete', action_data={'repo_data': repo_data}, + user=self._rhodecode_user, repo=repo, commit=False) + + ScmModel().mark_for_invalidation(self.db_repo_name) + h.flash( + _('Deleted repository `%s`') % self.db_repo_name, + category='success') + Session().commit() + except AttachedForksError: + repo_advanced_url = h.route_path( + 'edit_repo_advanced', repo_name=self.db_repo_name, + _anchor='advanced-delete') + delete_anchor = h.link_to(_('detach or delete'), repo_advanced_url) + h.flash(_('Cannot delete `{repo}` it still contains attached forks. ' + 'Try using {delete_or_detach} option.') + .format(repo=self.db_repo_name, delete_or_detach=delete_anchor), + category='warning') + + # redirect to advanced for forks handle action ? + raise HTTPFound(repo_advanced_url) + + except Exception: + log.exception("Exception during deletion of repository") + h.flash(_('An error occurred during deletion of `%s`') + % self.db_repo_name, category='error') + # redirect to advanced for more deletion options + raise HTTPFound( + h.route_path('edit_repo_advanced', repo_name=self.db_repo_name), + _anchor='advanced-delete') + + raise HTTPFound(h.route_path('home')) + + @LoginRequired() + @HasRepoPermissionAnyDecorator('repository.admin') + @CSRFRequired() + @view_config( + route_name='edit_repo_advanced_journal', request_method='POST', + renderer='rhodecode:templates/admin/repos/repo_edit.mako') + def edit_advanced_journal(self): + """ + Set's this repository to be visible in public journal, + in other words making default user to follow this repo + """ + _ = self.request.translate + + try: + user_id = User.get_default_user().user_id + ScmModel().toggle_following_repo(self.db_repo.repo_id, user_id) + h.flash(_('Updated repository visibility in public journal'), + category='success') + Session().commit() + except Exception: + h.flash(_('An error occurred during setting this ' + 'repository in public journal'), + category='error') + + raise HTTPFound( + h.route_path('edit_repo_advanced', repo_name=self.db_repo_name)) + + @LoginRequired() + @HasRepoPermissionAnyDecorator('repository.admin') + @CSRFRequired() + @view_config( + route_name='edit_repo_advanced_fork', request_method='POST', + renderer='rhodecode:templates/admin/repos/repo_edit.mako') + def edit_advanced_fork(self): + """ + Mark given repository as a fork of another + """ + _ = self.request.translate + + new_fork_id = self.request.POST.get('id_fork_of') + try: + + if new_fork_id and not new_fork_id.isdigit(): + log.error('Given fork id %s is not an INT', new_fork_id) + + fork_id = safe_int(new_fork_id) + repo = ScmModel().mark_as_fork( + self.db_repo_name, fork_id, self._rhodecode_user.user_id) + fork = repo.fork.repo_name if repo.fork else _('Nothing') + Session().commit() + h.flash(_('Marked repo %s as fork of %s') % (self.db_repo_name, fork), + category='success') + except RepositoryError as e: + log.exception("Repository Error occurred") + h.flash(str(e), category='error') + except Exception as e: + log.exception("Exception while editing fork") + h.flash(_('An error occurred during this operation'), + category='error') + + raise HTTPFound( + h.route_path('edit_repo_advanced', repo_name=self.db_repo_name)) + + @LoginRequired() + @HasRepoPermissionAnyDecorator('repository.admin') + @CSRFRequired() + @view_config( + route_name='edit_repo_advanced_locking', request_method='POST', + renderer='rhodecode:templates/admin/repos/repo_edit.mako') + def edit_advanced_locking(self): + """ + Toggle locking of repository + """ + _ = self.request.translate + set_lock = self.request.POST.get('set_lock') + set_unlock = self.request.POST.get('set_unlock') + + try: + if set_lock: + Repository.lock(self.db_repo, self._rhodecode_user.user_id, + lock_reason=Repository.LOCK_WEB) + h.flash(_('Locked repository'), category='success') + elif set_unlock: + Repository.unlock(self.db_repo) + h.flash(_('Unlocked repository'), category='success') + except Exception as e: + log.exception("Exception during unlocking") + h.flash(_('An error occurred during unlocking'), category='error') + + raise HTTPFound( + h.route_path('edit_repo_advanced', repo_name=self.db_repo_name)) diff --git a/rhodecode/config/routing.py b/rhodecode/config/routing.py --- a/rhodecode/config/routing.py +++ b/rhodecode/config/routing.py @@ -185,7 +185,7 @@ def make_map(config): #========================================================================== # MAIN PAGE - rmap.connect('home', '/', controller='home', action='index', jsroute=True) + rmap.connect('home', '/', controller='home', action='index') # ping and pylons error test rmap.connect('ping', '%s/ping' % (ADMIN_PREFIX,), controller='home', action='ping') @@ -674,30 +674,11 @@ def make_map(config): conditions={'method': ['DELETE'], 'function': check_repo}, requirements=URL_NAME_REQUIREMENTS) - rmap.connect('edit_repo_advanced', '/{repo_name}/settings/advanced', - controller='admin/repos', action='edit_advanced', - conditions={'method': ['GET'], 'function': check_repo}, - requirements=URL_NAME_REQUIREMENTS) - - rmap.connect('edit_repo_advanced_locking', '/{repo_name}/settings/advanced/locking', - controller='admin/repos', action='edit_advanced_locking', - conditions={'method': ['PUT'], '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_advanced_journal', '/{repo_name}/settings/advanced/journal', - controller='admin/repos', action='edit_advanced_journal', - conditions={'method': ['PUT'], 'function': check_repo}, - requirements=URL_NAME_REQUIREMENTS) - - rmap.connect('edit_repo_advanced_fork', '/{repo_name}/settings/advanced/fork', - controller='admin/repos', action='edit_advanced_fork', - conditions={'method': ['PUT'], '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}, diff --git a/rhodecode/controllers/admin/repos.py b/rhodecode/controllers/admin/repos.py --- a/rhodecode/controllers/admin/repos.py +++ b/rhodecode/controllers/admin/repos.py @@ -288,51 +288,6 @@ class ReposController(BaseRepoController return {'result': True} return {'result': False} - @HasRepoPermissionAllDecorator('repository.admin') - @auth.CSRFRequired() - def delete(self, repo_name): - """ - DELETE /repos/repo_name: Delete an existing item""" - # Forms posted to this method should contain a hidden field: - # - # Or using helpers: - # h.form(url('repo', repo_name=ID), - # method='delete') - # url('repo', repo_name=ID) - - repo_model = RepoModel() - repo = repo_model.get_by_repo_name(repo_name) - if not repo: - h.not_mapped_error(repo_name) - return redirect(url('repos')) - try: - _forks = repo.forks.count() - handle_forks = None - if _forks and request.POST.get('forks'): - do = request.POST['forks'] - if do == 'detach_forks': - handle_forks = 'detach' - h.flash(_('Detached %s forks') % _forks, category='success') - elif do == 'delete_forks': - handle_forks = 'delete' - h.flash(_('Deleted %s forks') % _forks, category='success') - repo_model.delete(repo, forks=handle_forks) - action_logger(c.rhodecode_user, 'admin_deleted_repo', - repo_name, self.ip_addr, self.sa) - ScmModel().mark_for_invalidation(repo_name) - h.flash(_('Deleted repository %s') % repo_name, category='success') - Session().commit() - except AttachedForksError: - h.flash(_('Cannot delete %s it still contains attached forks') - % repo_name, category='warning') - - except Exception: - log.exception("Exception during deletion of repository") - h.flash(_('An error occurred during deletion of %s') % repo_name, - category='error') - - return redirect(url('repos')) - @HasPermissionAllDecorator('hg.admin') def show(self, repo_name, format='html'): """GET /repos/repo_name: Show a specific item""" @@ -384,105 +339,6 @@ class ReposController(BaseRepoController h.flash(msg, category='error') return redirect(url('edit_repo_fields', repo_name=repo_name)) - @HasRepoPermissionAllDecorator('repository.admin') - def edit_advanced(self, repo_name): - """GET /repo_name/settings: Form to edit an existing item""" - c.repo_info = self._load_repo(repo_name) - 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() - - c.active = 'advanced' - c.has_origin_repo_read_perm = False - if c.repo_info.fork: - c.has_origin_repo_read_perm = h.HasRepoPermissionAny( - 'repository.write', 'repository.read', 'repository.admin')( - c.repo_info.fork.repo_name, 'repo set as fork page') - - if request.POST: - return redirect(url('repo_edit_advanced')) - return render('admin/repos/repo_edit.mako') - - @HasRepoPermissionAllDecorator('repository.admin') - @auth.CSRFRequired() - def edit_advanced_journal(self, repo_name): - """ - Set's this repository to be visible in public journal, - in other words assing default user to follow this repo - - :param repo_name: - """ - - try: - repo_id = Repository.get_by_repo_name(repo_name).repo_id - user_id = User.get_default_user().user_id - self.scm_model.toggle_following_repo(repo_id, user_id) - h.flash(_('Updated repository visibility in public journal'), - category='success') - Session().commit() - except Exception: - h.flash(_('An error occurred during setting this' - ' repository in public journal'), - category='error') - - return redirect(url('edit_repo_advanced', repo_name=repo_name)) - - @HasRepoPermissionAllDecorator('repository.admin') - @auth.CSRFRequired() - def edit_advanced_fork(self, repo_name): - """ - Mark given repository as a fork of another - - :param repo_name: - """ - - new_fork_id = request.POST.get('id_fork_of') - try: - - if new_fork_id and not new_fork_id.isdigit(): - log.error('Given fork id %s is not an INT', new_fork_id) - - fork_id = safe_int(new_fork_id) - repo = ScmModel().mark_as_fork(repo_name, fork_id, - c.rhodecode_user.username) - fork = repo.fork.repo_name if repo.fork else _('Nothing') - Session().commit() - h.flash(_('Marked repo %s as fork of %s') % (repo_name, fork), - category='success') - except RepositoryError as e: - log.exception("Repository Error occurred") - h.flash(str(e), category='error') - except Exception as e: - log.exception("Exception while editing fork") - h.flash(_('An error occurred during this operation'), - category='error') - - return redirect(url('edit_repo_advanced', repo_name=repo_name)) - - @HasRepoPermissionAllDecorator('repository.admin') - @auth.CSRFRequired() - def edit_advanced_locking(self, repo_name): - """ - Unlock repository when it is locked ! - - :param repo_name: - """ - try: - repo = Repository.get_by_repo_name(repo_name) - if request.POST.get('set_lock'): - Repository.lock(repo, c.rhodecode_user.user_id, - lock_reason=Repository.LOCK_WEB) - h.flash(_('Locked repository'), category='success') - elif request.POST.get('set_unlock'): - Repository.unlock(repo) - h.flash(_('Unlocked repository'), category='success') - except Exception as e: - log.exception("Exception during unlocking") - h.flash(_('An error occurred during unlocking'), - category='error') - return redirect(url('edit_repo_advanced', repo_name=repo_name)) - @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin') @auth.CSRFRequired() def toggle_locking(self, repo_name): diff --git a/rhodecode/lib/audit_logger.py b/rhodecode/lib/audit_logger.py --- a/rhodecode/lib/audit_logger.py +++ b/rhodecode/lib/audit_logger.py @@ -36,9 +36,10 @@ ACTIONS = { 'user.push': {}, 'user.pull': {}, - 'repo.add': {}, + 'repo.create': {}, 'repo.edit': {}, 'repo.edit.permissions': {}, + 'repo.delete': {}, 'repo.commit.strip': {}, 'repo.archive.download': {}, } @@ -101,7 +102,7 @@ def store( audit_logger.store( action='repo.edit', user=self._rhodecode_user) audit_logger.store( - action='repo.delete', + action='repo.delete', action_data={'repo_data': repo_data}, user=audit_logger.UserWrap(username='itried-login', ip_addr='8.8.8.8')) # repo action 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 @@ -96,10 +96,16 @@ function registerRCRoutes() { pyroutes.register('user_group_autocomplete_data', '/_user_groups', []); pyroutes.register('repo_list_data', '/_repos', []); pyroutes.register('goto_switcher_data', '/_goto_data', []); + pyroutes.register('repo_summary_explicit', '/%(repo_name)s/summary', ['repo_name']); pyroutes.register('tags_home', '/%(repo_name)s/tags', ['repo_name']); pyroutes.register('branches_home', '/%(repo_name)s/branches', ['repo_name']); pyroutes.register('bookmarks_home', '/%(repo_name)s/bookmarks', ['repo_name']); pyroutes.register('edit_repo', '/%(repo_name)s/settings', ['repo_name']); + pyroutes.register('edit_repo_advanced', '/%(repo_name)s/settings/advanced', ['repo_name']); + pyroutes.register('edit_repo_advanced_delete', '/%(repo_name)s/settings/advanced/delete', ['repo_name']); + pyroutes.register('edit_repo_advanced_locking', '/%(repo_name)s/settings/advanced/locking', ['repo_name']); + pyroutes.register('edit_repo_advanced_journal', '/%(repo_name)s/settings/advanced/journal', ['repo_name']); + 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('repo_reviewers', '/%(repo_name)s/settings/review/rules', ['repo_name']); diff --git a/rhodecode/templates/admin/repos/repo_edit.mako b/rhodecode/templates/admin/repos/repo_edit.mako --- a/rhodecode/templates/admin/repos/repo_edit.mako +++ b/rhodecode/templates/admin/repos/repo_edit.mako @@ -45,7 +45,7 @@ ${_('Permissions')}
  • - ${_('Advanced')} + ${_('Advanced')}
  • ${_('VCS')} diff --git a/rhodecode/templates/admin/repos/repo_edit_advanced.mako b/rhodecode/templates/admin/repos/repo_edit_advanced.mako --- a/rhodecode/templates/admin/repos/repo_edit_advanced.mako +++ b/rhodecode/templates/admin/repos/repo_edit_advanced.mako @@ -10,8 +10,8 @@ %>
    -
    -

    ${_('Repository: %s') % c.repo_info.repo_name}

    +
    +

    ${_('Repository: %s') % c.repo_info.repo_name}

    ${base.dt_info_panel(elems)} @@ -20,11 +20,11 @@
    -
    -

    ${_('Fork Reference')}

    +
    +

    ${_('Fork Reference')}

    - ${h.secure_form(url('edit_repo_advanced_fork', repo_name=c.repo_info.repo_name), method='put')} + ${h.secure_form(h.route_path('edit_repo_advanced_fork', repo_name=c.repo_info.repo_name), method='POST')} % if c.repo_info.fork:
    ${h.literal(_('This repository is a fork of %(repo_link)s') % {'repo_link': h.link_to_if(c.has_origin_repo_read_perm,c.repo_info.fork.repo_name, h.url('summary_home', repo_name=c.repo_info.fork.repo_name))})} @@ -44,15 +44,14 @@
    -
    -

    ${_('Public Journal Visibility')}

    +
    +

    ${_('Public Journal Visibility')}

    - ${h.secure_form(url('edit_repo_advanced_journal', repo_name=c.repo_info.repo_name), method='put')} + ${h.secure_form(h.route_path('edit_repo_advanced_journal', repo_name=c.repo_info.repo_name), method='POST')}
    %if c.in_public_journal: %else: @@ -70,11 +69,11 @@
    -
    -

    ${_('Locking state')}

    +
    +

    ${_('Locking state')}

    - ${h.secure_form(url('edit_repo_advanced_locking', repo_name=c.repo_info.repo_name), method='put')} + ${h.secure_form(h.route_path('edit_repo_advanced_locking', repo_name=c.repo_info.repo_name), method='POST')} %if c.repo_info.locked[0]:
    ${'Locked by %s on %s. Lock reason: %s' % (h.person_by_id(c.repo_info.locked[0]), @@ -110,15 +109,15 @@
    -
    -

    ${_('Delete repository')}

    +
    +

    ${_('Delete repository')}

    - ${h.secure_form(url('repo', repo_name=c.repo_name),method='delete')} + ${h.secure_form(h.route_path('edit_repo_advanced_delete', repo_name=c.repo_name), method='POST')}
    - ${ungettext('This repository has %s fork.', 'This repository has %s forks.', c.repo_info. forks.count()) % c.repo_info.forks.count()} + ${_ungettext('This repository has %s fork.', 'This repository has %s forks.', c.repo_info.forks.count()) % c.repo_info.forks.count()} %if c.repo_info.forks.count(): @@ -143,7 +142,7 @@
    - ${_('This repository will be renamed in a special way in order to make it inaccessible to RhodeCode Enterprise and its VCS systems. If you need to fully delete it from the file system, please do it manually, or with rhodecode-cleanup-repos command.')} + ${_('This repository will be renamed in a special way in order to make it inaccessible to RhodeCode Enterprise and its VCS systems. If you need to fully delete it from the file system, please do it manually, or with rhodecode-cleanup-repos command available in rhodecode-tools.')}
    diff --git a/rhodecode/templates/data_table/_dt_elements.mako b/rhodecode/templates/data_table/_dt_elements.mako --- a/rhodecode/templates/data_table/_dt_elements.mako +++ b/rhodecode/templates/data_table/_dt_elements.mako @@ -119,7 +119,7 @@ Edit
    - ${h.secure_form(h.url('repo', repo_name=repo_name),method='delete')} + ${h.secure_form(h.route_path('edit_repo_advanced_delete', repo_name=repo_name), method='POST')} ${h.submit('remove_%s' % repo_name,_('Delete'),class_="btn btn-link btn-danger", onclick="return confirm('"+_('Confirm to delete this repository: %s') % repo_name+"');")} ${h.end_form()} diff --git a/rhodecode/tests/functional/test_admin_repos.py b/rhodecode/tests/functional/test_admin_repos.py --- a/rhodecode/tests/functional/test_admin_repos.py +++ b/rhodecode/tests/functional/test_admin_repos.py @@ -382,21 +382,6 @@ class TestAdminRepos(object): csrf_token=csrf_token)) response.mustcontain('Repository name cannot end with .git') - @pytest.mark.parametrize("suffix", [u'', u'ąęł'], ids=['', 'non-ascii']) - def test_delete(self, autologin_user, backend, suffix, csrf_token): - repo = backend.create_repo(name_suffix=suffix) - repo_name = repo.repo_name - - response = self.app.post(url('repo', repo_name=repo_name), - params={'_method': 'delete', - 'csrf_token': csrf_token}) - assert_session_flash(response, 'Deleted repository %s' % (repo_name)) - response.follow() - - # check if repo was deleted from db - assert RepoModel().get_by_repo_name(repo_name) is None - assert not repo_on_filesystem(repo_name) - def test_show(self, autologin_user, backend): self.app.get(url('repo', repo_name=backend.repo_name)) @@ -414,82 +399,6 @@ class TestAdminRepos(object): assert permissions[0].permission.permission_name == 'repository.none' assert permissions[0].repository.private is True - def test_set_repo_fork_has_no_self_id(self, autologin_user, backend): - repo = backend.repo - response = self.app.get( - url('edit_repo_advanced', repo_name=backend.repo_name)) - opt = """""" % repo.repo_id - response.mustcontain(no=[opt]) - - def test_set_fork_of_target_repo( - self, autologin_user, backend, csrf_token): - target_repo = 'target_%s' % backend.alias - fixture.create_repo(target_repo, repo_type=backend.alias) - repo2 = Repository.get_by_repo_name(target_repo) - response = self.app.post( - url('edit_repo_advanced_fork', repo_name=backend.repo_name), - params={'id_fork_of': repo2.repo_id, '_method': 'put', - 'csrf_token': csrf_token}) - repo = Repository.get_by_repo_name(backend.repo_name) - repo2 = Repository.get_by_repo_name(target_repo) - assert_session_flash( - response, - 'Marked repo %s as fork of %s' % (repo.repo_name, repo2.repo_name)) - - assert repo.fork == repo2 - response = response.follow() - # check if given repo is selected - - opt = 'This repository is a fork of %s' % ( - url('summary_home', repo_name=repo2.repo_name), repo2.repo_name) - - response.mustcontain(opt) - - fixture.destroy_repo(target_repo, forks='detach') - - @pytest.mark.backends("hg", "git") - def test_set_fork_of_other_type_repo(self, autologin_user, backend, - csrf_token): - TARGET_REPO_MAP = { - 'git': { - 'type': 'hg', - 'repo_name': HG_REPO}, - 'hg': { - 'type': 'git', - 'repo_name': GIT_REPO}, - } - target_repo = TARGET_REPO_MAP[backend.alias] - - repo2 = Repository.get_by_repo_name(target_repo['repo_name']) - response = self.app.post( - url('edit_repo_advanced_fork', repo_name=backend.repo_name), - params={'id_fork_of': repo2.repo_id, '_method': 'put', - 'csrf_token': csrf_token}) - assert_session_flash( - response, - 'Cannot set repository as fork of repository with other type') - - def test_set_fork_of_none(self, autologin_user, backend, csrf_token): - # mark it as None - response = self.app.post( - url('edit_repo_advanced_fork', repo_name=backend.repo_name), - params={'id_fork_of': None, '_method': 'put', - 'csrf_token': csrf_token}) - assert_session_flash( - response, - 'Marked repo %s as fork of %s' - % (backend.repo_name, "Nothing")) - assert backend.repo.fork is None - - def test_set_fork_of_same_repo(self, autologin_user, backend, csrf_token): - repo = Repository.get_by_repo_name(backend.repo_name) - response = self.app.post( - url('edit_repo_advanced_fork', repo_name=backend.repo_name), - params={'id_fork_of': repo.repo_id, '_method': 'put', - 'csrf_token': csrf_token}) - assert_session_flash( - response, 'An error occurred during this operation') - def test_create_on_top_level_without_permissions(self, backend): session = login_user_session( self.app, TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS) diff --git a/rhodecode/tests/functional/test_forks.py b/rhodecode/tests/functional/test_forks.py --- a/rhodecode/tests/functional/test_forks.py +++ b/rhodecode/tests/functional/test_forks.py @@ -106,9 +106,7 @@ class _BaseTest(TestController): ) # remove this fork - response = self.app.post( - url('repo', repo_name=fork_name), - params={'_method': 'delete', 'csrf_token': self.csrf_token}) + fixture.destroy_repo(fork_name) def test_fork_create_into_group(self): self.log_user()