##// END OF EJS Templates
repositories: added option to archive repositories instead of deleting them....
marcink -
r3090:bdd9dc16 default
parent child Browse files
Show More
@@ -0,0 +1,36 b''
1 import logging
2
3 from sqlalchemy import *
4
5 from rhodecode.model import meta
6 from rhodecode.lib.dbmigrate.versions import _reset_base, notify
7
8 log = logging.getLogger(__name__)
9
10
11 def upgrade(migrate_engine):
12 """
13 Upgrade operations go here.
14 Don't create your own engine; bind migrate_engine to your metadata
15 """
16 _reset_base(migrate_engine)
17 from rhodecode.lib.dbmigrate.schema import db_4_13_0_0 as db
18
19 repository_table = db.Repository.__table__
20
21 archived = Column('archived', Boolean(), nullable=True)
22 archived.create(table=repository_table)
23
24 # issue fixups
25 fixups(db, meta.Session)
26
27
28 def downgrade(migrate_engine):
29 meta = MetaData()
30 meta.bind = migrate_engine
31
32
33 def fixups(models, _SESSION):
34 pass
35
36
@@ -51,7 +51,7 b' PYRAMID_SETTINGS = {}'
51 51 EXTENSIONS = {}
52 52
53 53 __version__ = ('.'.join((str(each) for each in VERSION[:3])))
54 __dbversion__ = 90 # defines current db version for migrations
54 __dbversion__ = 91 # defines current db version for migrations
55 55 __platform__ = platform.system()
56 56 __license__ = 'AGPLv3, and Commercial License'
57 57 __author__ = 'RhodeCode GmbH'
@@ -505,6 +505,36 b' class RepoRoutePredicate(object):'
505 505 return False
506 506
507 507
508 class RepoForbidArchivedRoutePredicate(object):
509 def __init__(self, val, config):
510 self.val = val
511
512 def text(self):
513 return 'repo_forbid_archived = %s' % self.val
514
515 phash = text
516
517 def __call__(self, info, request):
518 _ = request.translate
519 rhodecode_db_repo = request.db_repo
520
521 log.debug(
522 '%s checking if archived flag for repo for %s',
523 self.__class__.__name__, rhodecode_db_repo.repo_name)
524
525 if rhodecode_db_repo.archived:
526 log.warning('Current view is not supported for archived repo:%s',
527 rhodecode_db_repo.repo_name)
528
529 h.flash(
530 h.literal(_('Action not supported for archived repository.')),
531 category='warning')
532 summary_url = request.route_path(
533 'repo_summary', repo_name=rhodecode_db_repo.repo_name)
534 raise HTTPFound(summary_url)
535 return True
536
537
508 538 class RepoTypeRoutePredicate(object):
509 539 def __init__(self, val, config):
510 540 self.val = val or ['hg', 'git', 'svn']
@@ -530,13 +560,6 b' class RepoTypeRoutePredicate(object):'
530 560 else:
531 561 log.warning('Current view is not supported for repo type:%s',
532 562 rhodecode_db_repo.repo_type)
533
534 # h.flash(h.literal(
535 # _('Action not supported for %s.' % rhodecode_repo.alias)),
536 # category='warning')
537 # return redirect(
538 # route_path('repo_summary', repo_name=cls.rhodecode_db_repo.repo_name))
539
540 563 return False
541 564
542 565
@@ -643,10 +666,12 b' def includeme(config):'
643 666 config.add_route_predicate(
644 667 'repo_accepted_types', RepoTypeRoutePredicate)
645 668 config.add_route_predicate(
669 'repo_forbid_when_archived', RepoForbidArchivedRoutePredicate)
670 config.add_route_predicate(
646 671 'repo_group_route', RepoGroupRoutePredicate)
647 672 config.add_route_predicate(
648 673 'user_group_route', UserGroupRoutePredicate)
649 674 config.add_route_predicate(
650 675 'user_route_with_default', UserRouteWithDefaultPredicate)
651 676 config.add_route_predicate(
652 'user_route', UserRoutePredicate) No newline at end of file
677 'user_route', UserRoutePredicate)
@@ -33,7 +33,7 b' from rhodecode.lib.index import searcher'
33 33 from rhodecode.lib.utils2 import safe_unicode, str2bool, safe_int
34 34 from rhodecode.lib.ext_json import json
35 35 from rhodecode.model.db import (
36 func, or_, in_filter_generator, Repository, RepoGroup, User, UserGroup)
36 func, true, or_, in_filter_generator, Repository, RepoGroup, User, UserGroup)
37 37 from rhodecode.model.repo import RepoModel
38 38 from rhodecode.model.repo_group import RepoGroupModel
39 39 from rhodecode.model.scm import RepoGroupList, RepoList
@@ -114,6 +114,7 b' class HomeView(BaseAppView):'
114 114 query = Repository.query()\
115 115 .order_by(func.length(Repository.repo_name))\
116 116 .order_by(Repository.repo_name)\
117 .filter(Repository.archived.isnot(true()))\
117 118 .filter(or_(
118 119 # generate multiple IN to fix limitation problems
119 120 *in_filter_generator(Repository.repo_id, allowed_ids)
@@ -231,11 +231,13 b' def includeme(config):'
231 231 config.add_route(
232 232 name='repo_fork_new',
233 233 pattern='/{repo_name:.*?[^/]}/fork', repo_route=True,
234 repo_forbid_when_archived=True,
234 235 repo_accepted_types=['hg', 'git'])
235 236
236 237 config.add_route(
237 238 name='repo_fork_create',
238 239 pattern='/{repo_name:.*?[^/]}/fork/create', repo_route=True,
240 repo_forbid_when_archived=True,
239 241 repo_accepted_types=['hg', 'git'])
240 242
241 243 config.add_route(
@@ -276,27 +278,29 b' def includeme(config):'
276 278 config.add_route(
277 279 name='pullrequest_new',
278 280 pattern='/{repo_name:.*?[^/]}/pull-request/new',
279 repo_route=True, repo_accepted_types=['hg', 'git'])
281 repo_route=True, repo_accepted_types=['hg', 'git'],
282 repo_forbid_when_archived=True)
280 283
281 284 config.add_route(
282 285 name='pullrequest_create',
283 286 pattern='/{repo_name:.*?[^/]}/pull-request/create',
284 repo_route=True, repo_accepted_types=['hg', 'git'])
287 repo_route=True, repo_accepted_types=['hg', 'git'],
288 repo_forbid_when_archived=True)
285 289
286 290 config.add_route(
287 291 name='pullrequest_update',
288 292 pattern='/{repo_name:.*?[^/]}/pull-request/{pull_request_id:\d+}/update',
289 repo_route=True)
293 repo_route=True, repo_forbid_when_archived=True)
290 294
291 295 config.add_route(
292 296 name='pullrequest_merge',
293 297 pattern='/{repo_name:.*?[^/]}/pull-request/{pull_request_id:\d+}/merge',
294 repo_route=True)
298 repo_route=True, repo_forbid_when_archived=True)
295 299
296 300 config.add_route(
297 301 name='pullrequest_delete',
298 302 pattern='/{repo_name:.*?[^/]}/pull-request/{pull_request_id:\d+}/delete',
299 repo_route=True)
303 repo_route=True, repo_forbid_when_archived=True)
300 304
301 305 config.add_route(
302 306 name='pullrequest_comment_create',
@@ -319,6 +323,9 b' def includeme(config):'
319 323 name='edit_repo_advanced',
320 324 pattern='/{repo_name:.*?[^/]}/settings/advanced', repo_route=True)
321 325 config.add_route(
326 name='edit_repo_advanced_archive',
327 pattern='/{repo_name:.*?[^/]}/settings/advanced/archive', repo_route=True)
328 config.add_route(
322 329 name='edit_repo_advanced_delete',
323 330 pattern='/{repo_name:.*?[^/]}/settings/advanced/delete', repo_route=True)
324 331 config.add_route(
@@ -303,6 +303,27 b' class TestRepoForkViewTests(TestControll'
303 303 assert response.json == {u'data': [], u'draw': None,
304 304 u'recordsFiltered': 0, u'recordsTotal': 0}
305 305
306 @pytest.mark.parametrize('url_type', [
307 'repo_fork_new',
308 'repo_fork_create'
309 ])
310 def test_fork_is_forbidden_on_archived_repo(self, backend, xhr_header, user_util, url_type):
311 user = user_util.create_user(password='qweqwe')
312 self.log_user(user.username, 'qweqwe')
313
314 # create a temporary repo
315 source = user_util.create_repo(repo_type=backend.alias)
316 repo_name = source.repo_name
317 repo = Repository.get_by_repo_name(repo_name)
318 repo.archived = True
319 Session().commit()
320
321 response = self.app.get(
322 route_path(url_type, repo_name=repo_name), status=302)
323
324 msg = 'Action not supported for archived repository.'
325 assert_session_flash(response, msg)
326
306 327
307 328 class TestSVNFork(TestController):
308 329 @pytest.mark.parametrize('route_name', [
@@ -26,7 +26,7 b' from rhodecode.lib.vcs.nodes import File'
26 26 from rhodecode.lib import helpers as h
27 27 from rhodecode.model.changeset_status import ChangesetStatusModel
28 28 from rhodecode.model.db import (
29 PullRequest, ChangesetStatus, UserLog, Notification, ChangesetComment)
29 PullRequest, ChangesetStatus, UserLog, Notification, ChangesetComment, Repository)
30 30 from rhodecode.model.meta import Session
31 31 from rhodecode.model.pull_request import PullRequestModel
32 32 from rhodecode.model.user import UserModel
@@ -1191,6 +1191,28 b' class TestPullrequestsControllerDelete(o'
1191 1191 )
1192 1192 assert response.body == 'true'
1193 1193
1194 @pytest.mark.parametrize('url_type', [
1195 'pullrequest_new',
1196 'pullrequest_create',
1197 'pullrequest_update',
1198 'pullrequest_merge',
1199 ])
1200 def test_pull_request_is_forbidden_on_archived_repo(
1201 self, autologin_user, backend, xhr_header, user_util, url_type):
1202
1203 # create a temporary repo
1204 source = user_util.create_repo(repo_type=backend.alias)
1205 repo_name = source.repo_name
1206 repo = Repository.get_by_repo_name(repo_name)
1207 repo.archived = True
1208 Session().commit()
1209
1210 response = self.app.get(
1211 route_path(url_type, repo_name=repo_name, pull_request_id=1), status=302)
1212
1213 msg = 'Action not supported for archived repository.'
1214 assert_session_flash(response, msg)
1215
1194 1216
1195 1217 def assert_pull_request_status(pull_request, expected_status):
1196 1218 status = ChangesetStatusModel().calculated_review_status(
@@ -39,6 +39,7 b' def route_path(name, params=None, **kwar'
39 39 'repo_summary': '/{repo_name}',
40 40 'edit_repo_advanced': '/{repo_name}/settings/advanced',
41 41 'edit_repo_advanced_delete': '/{repo_name}/settings/advanced/delete',
42 'edit_repo_advanced_archive': '/{repo_name}/settings/advanced/archive',
42 43 'edit_repo_advanced_fork': '/{repo_name}/settings/advanced/fork',
43 44 'edit_repo_advanced_locking': '/{repo_name}/settings/advanced/locking',
44 45 'edit_repo_advanced_journal': '/{repo_name}/settings/advanced/journal',
@@ -133,7 +134,7 b' class TestAdminRepoSettingsAdvanced(obje'
133 134 "suffix",
134 135 ['', u'Δ…Δ™Ε‚' , '123'],
135 136 ids=no_newline_id_generator)
136 def test_advanced_delete(self, autologin_user, backend, suffix, csrf_token):
137 def test_advanced_repo_delete(self, autologin_user, backend, suffix, csrf_token):
137 138 repo = backend.create_repo(name_suffix=suffix)
138 139 repo_name = repo.repo_name
139 140 repo_name_str = safe_str(repo.repo_name)
@@ -148,3 +149,25 b' class TestAdminRepoSettingsAdvanced(obje'
148 149 # check if repo was deleted from db
149 150 assert RepoModel().get_by_repo_name(repo_name) is None
150 151 assert not repo_on_filesystem(repo_name_str)
152
153 @pytest.mark.parametrize(
154 "suffix",
155 ['', u'Δ…Δ™Ε‚' , '123'],
156 ids=no_newline_id_generator)
157 def test_advanced_repo_archive(self, autologin_user, backend, suffix, csrf_token):
158 repo = backend.create_repo(name_suffix=suffix)
159 repo_name = repo.repo_name
160 repo_name_str = safe_str(repo.repo_name)
161
162 response = self.app.post(
163 route_path('edit_repo_advanced_archive', repo_name=repo_name_str),
164 params={'csrf_token': csrf_token})
165
166 assert_session_flash(response,
167 u'Archived repository `{}`'.format(repo_name))
168
169 response = self.app.get(route_path('repo_summary', repo_name=repo_name_str))
170 response.mustcontain('This repository has been archived. It is now read-only.')
171
172 # check if repo was deleted from db
173 assert RepoModel().get_by_repo_name(repo_name).archived is True
@@ -23,6 +23,7 b' import logging'
23 23 from pyramid.view import view_config
24 24 from pyramid.httpexceptions import HTTPFound
25 25
26 from rhodecode import events
26 27 from rhodecode.apps._base import RepoAppView
27 28 from rhodecode.lib import helpers as h
28 29 from rhodecode.lib import audit_logger
@@ -45,6 +46,13 b' class RepoSettingsView(RepoAppView):'
45 46 c = self._get_local_tmpl_context()
46 47 return c
47 48
49 def _get_users_with_permissions(self):
50 user_permissions = {}
51 for perm in self.db_repo.permissions():
52 user_permissions[perm.user_id] = perm
53
54 return user_permissions
55
48 56 @LoginRequired()
49 57 @HasRepoPermissionAnyDecorator('repository.admin')
50 58 @view_config(
@@ -71,6 +79,49 b' class RepoSettingsView(RepoAppView):'
71 79 @HasRepoPermissionAnyDecorator('repository.admin')
72 80 @CSRFRequired()
73 81 @view_config(
82 route_name='edit_repo_advanced_archive', request_method='POST',
83 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
84 def edit_advanced_archive(self):
85 """
86 Archives the repository. It will become read-only, and not visible in search
87 or other queries. But still visible for super-admins.
88 """
89
90 _ = self.request.translate
91
92 try:
93 old_data = self.db_repo.get_api_data()
94 RepoModel().archive(self.db_repo)
95
96 repo = audit_logger.RepoWrap(repo_id=None, repo_name=self.db_repo.repo_name)
97 audit_logger.store_web(
98 'repo.archive', action_data={'old_data': old_data},
99 user=self._rhodecode_user, repo=repo)
100
101 ScmModel().mark_for_invalidation(self.db_repo_name, delete=True)
102 h.flash(
103 _('Archived repository `%s`') % self.db_repo_name,
104 category='success')
105 Session().commit()
106 except Exception:
107 log.exception("Exception during archiving of repository")
108 h.flash(_('An error occurred during archiving of `%s`')
109 % self.db_repo_name, category='error')
110 # redirect to advanced for more deletion options
111 raise HTTPFound(
112 h.route_path('edit_repo_advanced', repo_name=self.db_repo_name,
113 _anchor='advanced-archive'))
114
115 # flush permissions for all users defined in permissions
116 affected_user_ids = self._get_users_with_permissions().keys()
117 events.trigger(events.UserPermissionsChange(affected_user_ids))
118
119 raise HTTPFound(h.route_path('home'))
120
121 @LoginRequired()
122 @HasRepoPermissionAnyDecorator('repository.admin')
123 @CSRFRequired()
124 @view_config(
74 125 route_name='edit_repo_advanced_delete', request_method='POST',
75 126 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
76 127 def edit_advanced_delete(self):
@@ -65,6 +65,7 b' ACTIONS_V1 = {'
65 65 'repo.edit': {'old_data': {}},
66 66 'repo.edit.permissions': {},
67 67 'repo.edit.permissions.branch': {},
68 'repo.archive': {'old_data': {}},
68 69 'repo.delete': {'old_data': {}},
69 70
70 71 'repo.archive.download': {'user_agent': '', 'archive_name': '',
@@ -322,6 +322,7 b' def _cached_perms_data(user_id, scope, u'
322 322
323 323 class PermOrigin(object):
324 324 SUPER_ADMIN = 'superadmin'
325 ARCHIVED = 'archived'
325 326
326 327 REPO_USER = 'user:%s'
327 328 REPO_USERGROUP = 'usergroup:%s'
@@ -463,8 +464,14 b' class PermissionCalculator(object):'
463 464 # repositories
464 465 for perm in self.default_repo_perms:
465 466 r_k = perm.UserRepoToPerm.repository.repo_name
467 archived = perm.UserRepoToPerm.repository.archived
466 468 p = 'repository.admin'
467 469 self.permissions_repositories[r_k] = p, PermOrigin.SUPER_ADMIN
470 # special case for archived repositories, which we block still even for
471 # super admins
472 if archived:
473 p = 'repository.read'
474 self.permissions_repositories[r_k] = p, PermOrigin.ARCHIVED
468 475
469 476 # repository groups
470 477 for perm in self.default_repo_groups_perms:
@@ -572,6 +579,7 b' class PermissionCalculator(object):'
572 579 def _calculate_default_permissions_repositories(self, user_inherit_object_permissions):
573 580 for perm in self.default_repo_perms:
574 581 r_k = perm.UserRepoToPerm.repository.repo_name
582 archived = perm.UserRepoToPerm.repository.archived
575 583 p = perm.Permission.permission_name
576 584 o = PermOrigin.REPO_DEFAULT
577 585 self.permissions_repositories[r_k] = p, o
@@ -602,6 +610,15 b' class PermissionCalculator(object):'
602 610 o = PermOrigin.SUPER_ADMIN
603 611 self.permissions_repositories[r_k] = p, o
604 612
613 # finally in case of archived repositories, we downgrade higher
614 # permissions to read
615 if archived:
616 current_perm = self.permissions_repositories[r_k]
617 if current_perm in ['repository.write', 'repository.admin']:
618 p = 'repository.read'
619 o = PermOrigin.ARCHIVED
620 self.permissions_repositories[r_k] = p, o
621
605 622 def _calculate_default_permissions_repository_branches(self, user_inherit_object_permissions):
606 623 for perm in self.default_branch_repo_perms:
607 624
@@ -1585,6 +1585,8 b' class Repository(Base, BaseModel):'
1585 1585 unique=False, default=None)
1586 1586 private = Column(
1587 1587 "private", Boolean(), nullable=True, unique=None, default=None)
1588 archived = Column(
1589 "archived", Boolean(), nullable=True, unique=None, default=None)
1588 1590 enable_statistics = Column(
1589 1591 "statistics", Boolean(), nullable=True, unique=None, default=True)
1590 1592 enable_downloads = Column(
@@ -1783,9 +1785,12 b' class Repository(Base, BaseModel):'
1783 1785
1784 1786 @classmethod
1785 1787 def get_all_repos(cls, user_id=Optional(None), group_id=Optional(None),
1786 case_insensitive=True):
1788 case_insensitive=True, archived=False):
1787 1789 q = Repository.query()
1788 1790
1791 if not archived:
1792 q = q.filter(Repository.archived.isnot(true()))
1793
1789 1794 if not isinstance(user_id, Optional):
1790 1795 q = q.filter(Repository.user_id == user_id)
1791 1796
@@ -1796,6 +1801,7 b' class Repository(Base, BaseModel):'
1796 1801 q = q.order_by(func.lower(Repository.repo_name))
1797 1802 else:
1798 1803 q = q.order_by(Repository.repo_name)
1804
1799 1805 return q.all()
1800 1806
1801 1807 @property
@@ -207,8 +207,8 b' class RepoModel(BaseModel):'
207 207 def quick_menu(repo_name):
208 208 return _render('quick_menu', repo_name)
209 209
210 def repo_lnk(name, rtype, rstate, private, fork_of):
211 return _render('repo_name', name, rtype, rstate, private, fork_of,
210 def repo_lnk(name, rtype, rstate, private, archived, fork_of):
211 return _render('repo_name', name, rtype, rstate, private, archived, fork_of,
212 212 short_name=not admin, admin=False)
213 213
214 214 def last_change(last_change):
@@ -246,8 +246,8 b' class RepoModel(BaseModel):'
246 246 row = {
247 247 "menu": quick_menu(repo.repo_name),
248 248
249 "name": repo_lnk(repo.repo_name, repo.repo_type,
250 repo.repo_state, repo.private, repo.fork),
249 "name": repo_lnk(repo.repo_name, repo.repo_type, repo.repo_state,
250 repo.private, repo.archived, repo.fork),
251 251 "name_raw": repo.repo_name.lower(),
252 252
253 253 "last_change": last_change(repo.last_db_change),
@@ -427,6 +427,7 b' class RepoModel(BaseModel):'
427 427 new_repo.group = repo_group
428 428 new_repo.description = description or repo_name
429 429 new_repo.private = private
430 new_repo.archived = False
430 431 new_repo.clone_uri = clone_uri
431 432 new_repo.landing_rev = landing_rev
432 433
@@ -608,6 +609,23 b' class RepoModel(BaseModel):'
608 609 from rhodecode.lib.celerylib import tasks, run_task
609 610 return run_task(tasks.create_repo_fork, form_data, cur_user)
610 611
612 def archive(self, repo):
613 """
614 Archive given repository. Set archive flag.
615
616 :param repo:
617 """
618 repo = self._get_repo(repo)
619 if repo:
620
621 try:
622 repo.archived = True
623 self.sa.add(repo)
624 self.sa.commit()
625 except Exception:
626 log.error(traceback.format_exc())
627 raise
628
611 629 def delete(self, repo, forks=None, pull_requests=None, fs_remove=True, cur_user=None):
612 630 """
613 631 Delete given repository, forks parameter defines what do do with
@@ -616,6 +634,7 b' class RepoModel(BaseModel):'
616 634
617 635 :param repo:
618 636 :param forks: str 'delete' or 'detach'
637 :param pull_requests: str 'delete' or None
619 638 :param fs_remove: remove(archive) repo from filesystem
620 639 """
621 640 if not cur_user:
@@ -228,6 +228,7 b' function registerRCRoutes() {'
228 228 pyroutes.register('pullrequest_comment_delete', '/%(repo_name)s/pull-request/%(pull_request_id)s/comment/%(comment_id)s/delete', ['repo_name', 'pull_request_id', 'comment_id']);
229 229 pyroutes.register('edit_repo', '/%(repo_name)s/settings', ['repo_name']);
230 230 pyroutes.register('edit_repo_advanced', '/%(repo_name)s/settings/advanced', ['repo_name']);
231 pyroutes.register('edit_repo_advanced_archive', '/%(repo_name)s/settings/advanced/archive', ['repo_name']);
231 232 pyroutes.register('edit_repo_advanced_delete', '/%(repo_name)s/settings/advanced/delete', ['repo_name']);
232 233 pyroutes.register('edit_repo_advanced_locking', '/%(repo_name)s/settings/advanced/locking', ['repo_name']);
233 234 pyroutes.register('edit_repo_advanced_journal', '/%(repo_name)s/settings/advanced/journal', ['repo_name']);
@@ -98,7 +98,7 b''
98 98 <button class="btn btn-small" type="submit"
99 99 onclick="return confirm('${_('Confirm to lock repository.')}');">
100 100 <i class="icon-lock"></i>
101 ${_('Lock Repository')}
101 ${_('Lock repository')}
102 102 </button>
103 103 %endif
104 104 </div>
@@ -111,6 +111,35 b''
111 111 </div>
112 112 </div>
113 113
114
115 <div class="panel panel-warning">
116 <div class="panel-heading" id="advanced-archive">
117 <h3 class="panel-title">${_('Archive repository')} <a class="permalink" href="#advanced-archive"> ΒΆ</a></h3>
118 </div>
119 <div class="panel-body">
120 ${h.secure_form(h.route_path('edit_repo_advanced_archive', repo_name=c.repo_name), request=request)}
121
122 <div style="margin: 0 0 20px 0" class="fake-space"></div>
123
124 <div class="field">
125 <button class="btn btn-small btn-danger" type="submit"
126 onclick="return confirm('${_('Confirm to archive this repository: %s') % c.repo_name}');">
127 <i class="icon-remove-sign"></i>
128 ${_('Archive this repository')}
129 </button>
130 </div>
131 <div class="field">
132 <span class="help-block">
133 ${_('Archiving the repository will make it entirely read-only. The repository cannot be committed to.'
134 'It is hidden from the search results and dashboard. ')}
135 </span>
136 </div>
137
138 ${h.end_form()}
139 </div>
140 </div>
141
142
114 143 <div class="panel panel-danger">
115 144 <div class="panel-heading" id="advanced-delete">
116 145 <h3 class="panel-title">${_('Delete repository')} <a class="permalink" href="#advanced-delete"> ΒΆ</a></h3>
@@ -152,7 +181,7 b''
152 181 <button class="btn btn-small btn-danger" type="submit"
153 182 onclick="return confirm('${_('Confirm to delete this repository: %s') % c.repo_name}');">
154 183 <i class="icon-remove-sign"></i>
155 ${_('Delete This Repository')}
184 ${_('Delete this repository')}
156 185 </button>
157 186 </div>
158 187 <div class="field">
@@ -287,6 +287,11 b''
287 287 </div>
288 288 <div class="clear"></div>
289 289 </div>
290 % if c.rhodecode_db_repo.archived:
291 <div class="alert alert-warning text-center">
292 <strong>${_('This repository has been archived. It is now read-only.')}</strong>
293 </div>
294 % endif
290 295 <!--- END CONTEXT BAR -->
291 296
292 297 </%def>
@@ -66,7 +66,7 b''
66 66 </div>
67 67 </%def>
68 68
69 <%def name="repo_name(name,rtype,rstate,private,fork_of,short_name=False,admin=False)">
69 <%def name="repo_name(name,rtype,rstate,private,archived,fork_of,short_name=False,admin=False)">
70 70 <%
71 71 def get_name(name,short_name=short_name):
72 72 if short_name:
@@ -105,6 +105,7 b''
105 105 (${_('creating...')})
106 106 </span>
107 107 %endif
108
108 109 </div>
109 110 </%def>
110 111
@@ -561,7 +561,7 b' def test_get_repo_by_id(test, expected):'
561 561
562 562
563 563 def test_invalidation_context(baseapp):
564 repo_id = 999
564 repo_id = 9999
565 565
566 566 cache_namespace_uid = 'cache_repo_instance.{}_{}'.format(
567 567 repo_id, CacheKey.CACHE_TYPE_README)
@@ -25,10 +25,13 b' import pytest'
25 25 from rhodecode.lib.vcs.backends.git.repository import GitRepository
26 26 from rhodecode.lib.vcs.backends.hg.repository import MercurialRepository
27 27 from rhodecode.lib.vcs.nodes import FileNode
28 from rhodecode.model.db import Repository
28 29 from rhodecode.model.meta import Session
30 from rhodecode.tests import GIT_REPO, HG_REPO
29 31
30 32 from rhodecode.tests.vcs_operations import (
31 Command, _check_proper_clone, _check_proper_git_push, _check_proper_hg_push)
33 Command, _check_proper_clone, _check_proper_git_push, _check_proper_hg_push,
34 _add_files_and_push)
32 35
33 36
34 37 @pytest.mark.usefixtures("disable_locking")
@@ -242,3 +245,37 b' class TestVCSOperationsSpecial(object):'
242 245 assert 'remote: RhodeCode: open pull request link: {}'.format(ref) in stdout
243 246 assert 'remote: RhodeCode: push completed' in stdout
244 247 assert 'exporting bookmark feature2' in stdout
248
249 def test_push_is_forbidden_on_archived_repo_hg(self, backend_hg, rc_web_server, tmpdir):
250 empty_repo = backend_hg.create_repo()
251 repo_name = empty_repo.repo_name
252
253 repo = Repository.get_by_repo_name(repo_name)
254 repo.archived = True
255 Session().commit()
256
257 clone_url = rc_web_server.repo_clone_url(repo_name)
258 stdout, stderr = Command('/tmp').execute(
259 'hg clone', clone_url, tmpdir.strpath)
260
261 stdout, stderr = _add_files_and_push(
262 'hg', tmpdir.strpath, clone_url=clone_url)
263
264 assert 'abort: HTTP Error 403: Forbidden' in stderr
265
266 def test_push_is_forbidden_on_archived_repo_git(self, backend_git, rc_web_server, tmpdir):
267 empty_repo = backend_git.create_repo()
268 repo_name = empty_repo.repo_name
269
270 repo = Repository.get_by_repo_name(repo_name)
271 repo.archived = True
272 Session().commit()
273
274 clone_url = rc_web_server.repo_clone_url(repo_name)
275 stdout, stderr = Command('/tmp').execute(
276 'git clone', clone_url, tmpdir.strpath)
277
278 stdout, stderr = _add_files_and_push(
279 'git', tmpdir.strpath, clone_url=clone_url)
280
281 assert "The requested URL returned error: 403" in stderr
General Comments 0
You need to be logged in to leave comments. Login now