Show More
@@ -0,0 +1,110 b'' | |||
|
1 | # -*- coding: utf-8 -*- | |
|
2 | ||
|
3 | # Copyright (C) 2011-2017 RhodeCode GmbH | |
|
4 | # | |
|
5 | # This program is free software: you can redistribute it and/or modify | |
|
6 | # it under the terms of the GNU Affero General Public License, version 3 | |
|
7 | # (only), as published by the Free Software Foundation. | |
|
8 | # | |
|
9 | # This program is distributed in the hope that it will be useful, | |
|
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
|
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
|
12 | # GNU General Public License for more details. | |
|
13 | # | |
|
14 | # You should have received a copy of the GNU Affero General Public License | |
|
15 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
|
16 | # | |
|
17 | # This program is dual-licensed. If you wish to learn more about the | |
|
18 | # RhodeCode Enterprise Edition, including its added features, Support services, | |
|
19 | # and proprietary license terms, please see https://rhodecode.com/licenses/ | |
|
20 | ||
|
21 | import logging | |
|
22 | ||
|
23 | from pyramid.view import view_config | |
|
24 | from pyramid.httpexceptions import HTTPFound, HTTPNotFound | |
|
25 | ||
|
26 | from rhodecode.apps._base import BaseAppView | |
|
27 | from rhodecode.lib import helpers as h | |
|
28 | from rhodecode.lib.auth import (NotAnonymous, HasRepoPermissionAny) | |
|
29 | from rhodecode.model.db import Repository | |
|
30 | ||
|
31 | log = logging.getLogger(__name__) | |
|
32 | ||
|
33 | ||
|
34 | class RepoChecksView(BaseAppView): | |
|
35 | def load_default_context(self): | |
|
36 | c = self._get_local_tmpl_context() | |
|
37 | self._register_global_c(c) | |
|
38 | return c | |
|
39 | ||
|
40 | @NotAnonymous() | |
|
41 | @view_config( | |
|
42 | route_name='repo_creating', request_method='GET', | |
|
43 | renderer='rhodecode:templates/admin/repos/repo_creating.mako') | |
|
44 | def repo_creating(self): | |
|
45 | c = self.load_default_context() | |
|
46 | ||
|
47 | repo_name = self.request.matchdict['repo_name'] | |
|
48 | db_repo = Repository.get_by_repo_name(repo_name) | |
|
49 | if not db_repo: | |
|
50 | raise HTTPNotFound() | |
|
51 | ||
|
52 | # check if maybe repo is already created | |
|
53 | if db_repo.repo_state in [Repository.STATE_CREATED]: | |
|
54 | # re-check permissions before redirecting to prevent resource | |
|
55 | # discovery by checking the 302 code | |
|
56 | perm_set = ['repository.read', 'repository.write', 'repository.admin'] | |
|
57 | has_perm = HasRepoPermissionAny(*perm_set)( | |
|
58 | db_repo.repo_name, 'Repo Creating check') | |
|
59 | if not has_perm: | |
|
60 | raise HTTPNotFound() | |
|
61 | ||
|
62 | raise HTTPFound(h.route_path( | |
|
63 | 'repo_summary', repo_name=db_repo.repo_name)) | |
|
64 | ||
|
65 | c.task_id = self.request.GET.get('task_id') | |
|
66 | c.repo_name = repo_name | |
|
67 | ||
|
68 | return self._get_template_context(c) | |
|
69 | ||
|
70 | @NotAnonymous() | |
|
71 | @view_config( | |
|
72 | route_name='repo_creating_check', request_method='GET', | |
|
73 | renderer='json_ext') | |
|
74 | def repo_creating_check(self): | |
|
75 | _ = self.request.translate | |
|
76 | task_id = self.request.GET.get('task_id') | |
|
77 | self.load_default_context() | |
|
78 | ||
|
79 | repo_name = self.request.matchdict['repo_name'] | |
|
80 | ||
|
81 | if task_id and task_id not in ['None']: | |
|
82 | import rhodecode | |
|
83 | from celery.result import AsyncResult | |
|
84 | if rhodecode.CELERY_ENABLED: | |
|
85 | task = AsyncResult(task_id) | |
|
86 | if task.failed(): | |
|
87 | msg = self._log_creation_exception(task.result, repo_name) | |
|
88 | h.flash(msg, category='error') | |
|
89 | raise HTTPFound(h.route_path('home'), code=501) | |
|
90 | ||
|
91 | db_repo = Repository.get_by_repo_name(repo_name) | |
|
92 | if db_repo and db_repo.repo_state == Repository.STATE_CREATED: | |
|
93 | if db_repo.clone_uri: | |
|
94 | clone_uri = db_repo.clone_uri_hidden | |
|
95 | h.flash(_('Created repository %s from %s') | |
|
96 | % (db_repo.repo_name, clone_uri), category='success') | |
|
97 | else: | |
|
98 | repo_url = h.link_to( | |
|
99 | db_repo.repo_name, | |
|
100 | h.route_path('repo_summary', repo_name=db_repo.repo_name)) | |
|
101 | fork = db_repo.fork | |
|
102 | if fork: | |
|
103 | fork_name = fork.repo_name | |
|
104 | h.flash(h.literal(_('Forked repository %s as %s') | |
|
105 | % (fork_name, repo_url)), category='success') | |
|
106 | else: | |
|
107 | h.flash(h.literal(_('Created repository %s') % repo_url), | |
|
108 | category='success') | |
|
109 | return {'result': True} | |
|
110 | return {'result': False} |
@@ -362,14 +362,22 b' class RepoRoutePredicate(object):' | |||
|
362 | 362 | repo_model = repo.RepoModel() |
|
363 | 363 | by_name_match = repo_model.get_by_repo_name(repo_name, cache=True) |
|
364 | 364 | |
|
365 | def redirect_if_creating(db_repo): | |
|
366 | if db_repo.repo_state in [repo.Repository.STATE_PENDING]: | |
|
367 | raise HTTPFound( | |
|
368 | request.route_path('repo_creating', | |
|
369 | repo_name=db_repo.repo_name)) | |
|
370 | ||
|
365 | 371 | if by_name_match: |
|
366 | 372 | # register this as request object we can re-use later |
|
367 | 373 | request.db_repo = by_name_match |
|
374 | redirect_if_creating(by_name_match) | |
|
368 | 375 | return True |
|
369 | 376 | |
|
370 | 377 | by_id_match = repo_model.get_repo_by_id(repo_name) |
|
371 | 378 | if by_id_match: |
|
372 | 379 | request.db_repo = by_id_match |
|
380 | redirect_if_creating(by_id_match) | |
|
373 | 381 | return True |
|
374 | 382 | |
|
375 | 383 | return False |
@@ -22,6 +22,15 b' from rhodecode.apps._base import add_rou' | |||
|
22 | 22 | |
|
23 | 23 | def includeme(config): |
|
24 | 24 | |
|
25 | # repo creating checks, special cases that aren't repo routes | |
|
26 | config.add_route( | |
|
27 | name='repo_creating', | |
|
28 | pattern='/{repo_name:.*?[^/]}/repo_creating') | |
|
29 | ||
|
30 | config.add_route( | |
|
31 | name='repo_creating_check', | |
|
32 | pattern='/{repo_name:.*?[^/]}/repo_creating_check') | |
|
33 | ||
|
25 | 34 | # Summary |
|
26 | 35 | # NOTE(marcink): one additional route is defined in very bottom, catch |
|
27 | 36 | # all pattern |
@@ -47,8 +47,8 b' def route_path(name, params=None, **kwar' | |||
|
47 | 47 | 'repo_summary': '/{repo_name}', |
|
48 | 48 | 'repo_stats': '/{repo_name}/repo_stats/{commit_id}', |
|
49 | 49 | 'repo_refs_data': '/{repo_name}/refs-data', |
|
50 | 'repo_refs_changelog_data': '/{repo_name}/refs-data-changelog' | |
|
51 | ||
|
50 | 'repo_refs_changelog_data': '/{repo_name}/refs-data-changelog', | |
|
51 | 'repo_creating_check': '/{repo_name}/repo_creating_check', | |
|
52 | 52 | }[name].format(**kwargs) |
|
53 | 53 | |
|
54 | 54 | if params: |
@@ -416,13 +416,6 b' def make_map(config):' | |||
|
416 | 416 | # REPOSITORY ROUTES |
|
417 | 417 | #========================================================================== |
|
418 | 418 | |
|
419 | rmap.connect('repo_creating_home', '/{repo_name}/repo_creating', | |
|
420 | controller='admin/repos', action='repo_creating', | |
|
421 | requirements=URL_NAME_REQUIREMENTS) | |
|
422 | rmap.connect('repo_check_home', '/{repo_name}/crepo_check', | |
|
423 | controller='admin/repos', action='repo_check', | |
|
424 | requirements=URL_NAME_REQUIREMENTS) | |
|
425 | ||
|
426 | 419 | # repo edit options |
|
427 | 420 | rmap.connect('edit_repo_fields', '/{repo_name}/settings/fields', |
|
428 | 421 | controller='admin/repos', action='edit_fields', |
@@ -31,8 +31,9 b' from formencode import htmlfill' | |||
|
31 | 31 | from pylons import request, tmpl_context as c, url |
|
32 | 32 | from pylons.controllers.util import redirect |
|
33 | 33 | from pylons.i18n.translation import _ |
|
34 |
from webob.exc import HTTPForbidden, |
|
|
34 | from webob.exc import HTTPForbidden, HTTPBadRequest | |
|
35 | 35 | |
|
36 | from pyramid.httpexceptions import HTTPFound | |
|
36 | 37 | import rhodecode |
|
37 | 38 | from rhodecode.lib import auth, helpers as h |
|
38 | 39 | from rhodecode.lib.auth import ( |
@@ -183,9 +184,10 b' class ReposController(BaseRepoController' | |||
|
183 | 184 | h.flash(msg, category='error') |
|
184 | 185 | return redirect(h.route_path('home')) |
|
185 | 186 | |
|
186 | return redirect(h.url('repo_creating_home', | |
|
187 | raise HTTPFound( | |
|
188 | h.route_path('repo_creating', | |
|
187 | 189 |
|
|
188 |
|
|
|
190 | _query=dict(task_id=task_id))) | |
|
189 | 191 | |
|
190 | 192 | # perms check inside |
|
191 | 193 | @NotAnonymous() |
@@ -239,51 +241,6 b' class ReposController(BaseRepoController' | |||
|
239 | 241 | force_defaults=False |
|
240 | 242 | ) |
|
241 | 243 | |
|
242 | @NotAnonymous() | |
|
243 | def repo_creating(self, repo_name): | |
|
244 | c.repo = repo_name | |
|
245 | c.task_id = request.GET.get('task_id') | |
|
246 | if not c.repo: | |
|
247 | raise HTTPNotFound() | |
|
248 | return render('admin/repos/repo_creating.mako') | |
|
249 | ||
|
250 | @NotAnonymous() | |
|
251 | @jsonify | |
|
252 | def repo_check(self, repo_name): | |
|
253 | c.repo = repo_name | |
|
254 | task_id = request.GET.get('task_id') | |
|
255 | ||
|
256 | if task_id and task_id not in ['None']: | |
|
257 | import rhodecode | |
|
258 | from celery.result import AsyncResult | |
|
259 | if rhodecode.CELERY_ENABLED: | |
|
260 | task = AsyncResult(task_id) | |
|
261 | if task.failed(): | |
|
262 | msg = self._log_creation_exception(task.result, c.repo) | |
|
263 | h.flash(msg, category='error') | |
|
264 | return redirect(h.route_path('home'), code=501) | |
|
265 | ||
|
266 | repo = Repository.get_by_repo_name(repo_name) | |
|
267 | if repo and repo.repo_state == Repository.STATE_CREATED: | |
|
268 | if repo.clone_uri: | |
|
269 | clone_uri = repo.clone_uri_hidden | |
|
270 | h.flash(_('Created repository %s from %s') | |
|
271 | % (repo.repo_name, clone_uri), category='success') | |
|
272 | else: | |
|
273 | repo_url = h.link_to( | |
|
274 | repo.repo_name, | |
|
275 | h.route_path('repo_summary',repo_name=repo.repo_name)) | |
|
276 | fork = repo.fork | |
|
277 | if fork: | |
|
278 | fork_name = fork.repo_name | |
|
279 | h.flash(h.literal(_('Forked repository %s as %s') | |
|
280 | % (fork_name, repo_url)), category='success') | |
|
281 | else: | |
|
282 | h.flash(h.literal(_('Created repository %s') % repo_url), | |
|
283 | category='success') | |
|
284 | return {'result': True} | |
|
285 | return {'result': False} | |
|
286 | ||
|
287 | 244 | @HasPermissionAllDecorator('hg.admin') |
|
288 | 245 | def show(self, repo_name, format='html'): |
|
289 | 246 | """GET /repos/repo_name: Show a specific item""" |
@@ -30,6 +30,7 b' from pylons import tmpl_context as c, re' | |||
|
30 | 30 | from pylons.controllers.util import redirect |
|
31 | 31 | from pylons.i18n.translation import _ |
|
32 | 32 | |
|
33 | from pyramid.httpexceptions import HTTPFound | |
|
33 | 34 | import rhodecode.lib.helpers as h |
|
34 | 35 | |
|
35 | 36 | from rhodecode.lib import auth |
@@ -191,6 +192,7 b' class ForksController(BaseRepoController' | |||
|
191 | 192 | (repo_name, )) |
|
192 | 193 | h.flash(msg, category='error') |
|
193 | 194 | |
|
194 | return redirect(h.url('repo_creating_home', | |
|
195 | raise HTTPFound( | |
|
196 | h.route_path('repo_creating', | |
|
195 | 197 |
|
|
196 |
|
|
|
198 | _query=dict(task_id=task_id))) |
@@ -1833,6 +1833,11 b' BIN_FILENODE = 7' | |||
|
1833 | 1833 | } |
|
1834 | 1834 | } |
|
1835 | 1835 | |
|
1836 | ||
|
1837 | .creation_in_progress { | |
|
1838 | color: @grey4 | |
|
1839 | } | |
|
1840 | ||
|
1836 | 1841 | .status_box_menu { |
|
1837 | 1842 | margin: 0; |
|
1838 | 1843 | } |
@@ -96,6 +96,8 b' function registerRCRoutes() {' | |||
|
96 | 96 | pyroutes.register('journal_public_rss', '/_admin/public_journal/rss', []); |
|
97 | 97 | pyroutes.register('journal_public_rss_old', '/_admin/public_journal_rss', []); |
|
98 | 98 | pyroutes.register('toggle_following', '/_admin/toggle_following', []); |
|
99 | pyroutes.register('repo_creating', '/%(repo_name)s/repo_creating', ['repo_name']); | |
|
100 | pyroutes.register('repo_creating_check', '/%(repo_name)s/repo_creating_check', ['repo_name']); | |
|
99 | 101 | pyroutes.register('repo_summary_explicit', '/%(repo_name)s/summary', ['repo_name']); |
|
100 | 102 | pyroutes.register('repo_summary_commits', '/%(repo_name)s/summary-commits', ['repo_name']); |
|
101 | 103 | pyroutes.register('repo_commit', '/%(repo_name)s/changeset/%(commit_id)s', ['repo_name', 'commit_id']); |
@@ -2,14 +2,14 b'' | |||
|
2 | 2 | <%inherit file="/base/base.mako"/> |
|
3 | 3 | |
|
4 | 4 | <%def name="title()"> |
|
5 |
${_(' |
|
|
5 | ${_('{} Creating repository').format(c.repo_name)} | |
|
6 | 6 | %if c.rhodecode_name: |
|
7 | 7 | · ${h.branding(c.rhodecode_name)} |
|
8 | 8 | %endif |
|
9 | 9 | </%def> |
|
10 | 10 | |
|
11 | 11 | <%def name="breadcrumbs_links()"> |
|
12 | ${_('Creating repository')} ${c.repo} | |
|
12 | ${_('Creating repository')} ${c.repo_name} | |
|
13 | 13 | </%def> |
|
14 | 14 | |
|
15 | 15 | <%def name="menu_bar_nav()"> |
@@ -38,7 +38,7 b'' | |||
|
38 | 38 | <script> |
|
39 | 39 | (function worker() { |
|
40 | 40 | var skipCheck = false; |
|
41 |
var url = "${h. |
|
|
41 | var url = "${h.route_path('repo_creating_check', repo_name=c.repo_name, _query=dict(task_id=c.task_id))}"; | |
|
42 | 42 | $.ajax({ |
|
43 | 43 | url: url, |
|
44 | 44 | complete: function(resp) { |
@@ -48,12 +48,12 b'' | |||
|
48 | 48 | if (jsonResponse === undefined) { |
|
49 | 49 | setTimeout(function () { |
|
50 | 50 | // we might have a backend problem, try dashboard again |
|
51 | window.location = "${h.route_path('repo_summary', repo_name = c.repo)}"; | |
|
51 | window.location = "${h.route_path('repo_summary', repo_name = c.repo_name)}"; | |
|
52 | 52 | }, 3000); |
|
53 | 53 | } else { |
|
54 | 54 | if (skipCheck || jsonResponse.result === true) { |
|
55 | 55 | // success, means go to dashboard |
|
56 | window.location = "${h.route_path('repo_summary', repo_name = c.repo)}"; | |
|
56 | window.location = "${h.route_path('repo_summary', repo_name = c.repo_name)}"; | |
|
57 | 57 | } else { |
|
58 | 58 | // Schedule the next request when the current one's complete |
|
59 | 59 | setTimeout(worker, 1000); |
@@ -61,10 +61,17 b'' | |||
|
61 | 61 | } |
|
62 | 62 | } |
|
63 | 63 | else { |
|
64 | window.location = "${h.route_path('home')}"; | |
|
64 | var payload = { | |
|
65 | message: { | |
|
66 | message: _gettext('Fetching repository state failed. Error code: {0} {1}. Try refreshing this page.').format(resp.status, resp.statusText), | |
|
67 | level: 'error', | |
|
68 | force: true | |
|
69 | } | |
|
70 | }; | |
|
71 | $.Topic('/notifications').publish(payload); | |
|
65 | 72 | } |
|
66 | 73 | } |
|
67 | 74 | }); |
|
68 | 75 | })(); |
|
69 | 76 | </script> |
|
70 | </%def> No newline at end of file | |
|
77 | </%def> |
@@ -67,7 +67,9 b'' | |||
|
67 | 67 | <a href="${h.route_path('repo_summary',repo_name=fork_of.repo_name)}"><i class="icon-code-fork"></i></a> |
|
68 | 68 | %endif |
|
69 | 69 | %if rstate == 'repo_state_pending': |
|
70 |
< |
|
|
70 | <span class="creation_in_progress tooltip" title="${_('This repository is being created in a background task')}"> | |
|
71 | (${_('creating...')}) | |
|
72 | </span> | |
|
71 | 73 | %endif |
|
72 | 74 | </div> |
|
73 | 75 | </%def> |
@@ -122,8 +122,6 b' class _BaseTest(TestController):' | |||
|
122 | 122 | csrf_token=self.csrf_token)) |
|
123 | 123 | |
|
124 | 124 | # run the check page that triggers the flash message |
|
125 | # response = self.app.get(url('repo_check_home', repo_name=repo_name)) | |
|
126 | # assert response.json == {u'result': True} | |
|
127 | 125 | repo_gr_url = h.route_path( |
|
128 | 126 | 'repo_group_home', repo_group_name=repo_group_name) |
|
129 | 127 |
@@ -35,14 +35,26 b' from rhodecode.model.settings import Set' | |||
|
35 | 35 | from rhodecode.model.user import UserModel |
|
36 | 36 | from rhodecode.tests import ( |
|
37 | 37 | login_user_session, url, assert_session_flash, TEST_USER_ADMIN_LOGIN, |
|
38 |
TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS, |
|
|
39 | logout_user_session) | |
|
38 | TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS, logout_user_session) | |
|
40 | 39 | from rhodecode.tests.fixture import Fixture, error_function |
|
41 | 40 | from rhodecode.tests.utils import AssertResponse, repo_on_filesystem |
|
42 | 41 | |
|
43 | 42 | fixture = Fixture() |
|
44 | 43 | |
|
45 | 44 | |
|
45 | def route_path(name, params=None, **kwargs): | |
|
46 | import urllib | |
|
47 | ||
|
48 | base_url = { | |
|
49 | 'repo_summary': '/{repo_name}', | |
|
50 | 'repo_creating_check': '/{repo_name}/repo_creating_check', | |
|
51 | }[name].format(**kwargs) | |
|
52 | ||
|
53 | if params: | |
|
54 | base_url = '{}?{}'.format(base_url, urllib.urlencode(params)) | |
|
55 | return base_url | |
|
56 | ||
|
57 | ||
|
46 | 58 | @pytest.mark.usefixtures("app") |
|
47 | 59 | class TestAdminRepos(object): |
|
48 | 60 | |
@@ -461,7 +473,8 b' class TestAdminRepos(object):' | |||
|
461 | 473 | repo_name_utf8 = safe_str(repo_name) |
|
462 | 474 | |
|
463 | 475 | # run the check page that triggers the flash message |
|
464 | response = self.app.get(url('repo_check_home', repo_name=repo_name)) | |
|
476 | response = self.app.get( | |
|
477 | route_path('repo_creating_check', repo_name=safe_str(repo_name))) | |
|
465 | 478 | assert response.json == {u'result': True} |
|
466 | 479 | |
|
467 | 480 | flash_msg = u'Created repository <a href="/{}">{}</a>'.format( |
@@ -475,7 +488,8 b' class TestAdminRepos(object):' | |||
|
475 | 488 | assert new_repo.description == description |
|
476 | 489 | |
|
477 | 490 | # test if the repository is visible in the list ? |
|
478 | response = self.app.get(h.route_path('repo_summary', repo_name=repo_name)) | |
|
491 | response = self.app.get( | |
|
492 | h.route_path('repo_summary', repo_name=safe_str(repo_name))) | |
|
479 | 493 | response.mustcontain(repo_name) |
|
480 | 494 | response.mustcontain(backend.alias) |
|
481 | 495 |
@@ -32,6 +32,19 b' from rhodecode.model.meta import Session' | |||
|
32 | 32 | fixture = Fixture() |
|
33 | 33 | |
|
34 | 34 | |
|
35 | def route_path(name, params=None, **kwargs): | |
|
36 | import urllib | |
|
37 | ||
|
38 | base_url = { | |
|
39 | 'repo_summary': '/{repo_name}', | |
|
40 | 'repo_creating_check': '/{repo_name}/repo_creating_check', | |
|
41 | }[name].format(**kwargs) | |
|
42 | ||
|
43 | if params: | |
|
44 | base_url = '{}?{}'.format(base_url, urllib.urlencode(params)) | |
|
45 | return base_url | |
|
46 | ||
|
47 | ||
|
35 | 48 | class _BaseTest(TestController): |
|
36 | 49 | |
|
37 | 50 | REPO = None |
@@ -134,7 +147,8 b' class _BaseTest(TestController):' | |||
|
134 | 147 | assert repo.fork.repo_name == self.REPO |
|
135 | 148 | |
|
136 | 149 | # run the check page that triggers the flash message |
|
137 | response = self.app.get(url('repo_check_home', repo_name=fork_name_full)) | |
|
150 | response = self.app.get( | |
|
151 | route_path('repo_creating_check', repo_name=fork_name_full)) | |
|
138 | 152 | # test if we have a message that fork is ok |
|
139 | 153 | assert_session_flash(response, |
|
140 | 154 | 'Forked repository %s as <a href="/%s">%s</a>' |
@@ -180,7 +194,8 b' class _BaseTest(TestController):' | |||
|
180 | 194 | assert repo.fork.repo_name == self.REPO |
|
181 | 195 | |
|
182 | 196 | # run the check page that triggers the flash message |
|
183 | response = self.app.get(url('repo_check_home', repo_name=fork_name)) | |
|
197 | response = self.app.get( | |
|
198 | route_path('repo_creating_check', repo_name=fork_name)) | |
|
184 | 199 | # test if we have a message that fork is ok |
|
185 | 200 | assert_session_flash(response, |
|
186 | 201 | 'Forked repository %s as <a href="/%s">%s</a>' |
General Comments 0
You need to be logged in to leave comments.
Login now