##// END OF EJS Templates
forks: moved pylons code into pyramid.
marcink -
r1988:f7d6f6e1 default
parent child Browse files
Show More
@@ -0,0 +1,315 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2010-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 pytest
22
23 from rhodecode.tests import TestController, assert_session_flash, HG_FORK, GIT_FORK
24
25 from rhodecode.tests.fixture import Fixture
26 from rhodecode.lib import helpers as h
27
28 from rhodecode.model.db import Repository
29 from rhodecode.model.repo import RepoModel
30 from rhodecode.model.user import UserModel
31 from rhodecode.model.meta import Session
32
33 fixture = Fixture()
34
35
36 def route_path(name, params=None, **kwargs):
37 import urllib
38
39 base_url = {
40 'repo_summary': '/{repo_name}',
41 'repo_creating_check': '/{repo_name}/repo_creating_check',
42 'repo_fork_new': '/{repo_name}/fork',
43 'repo_fork_create': '/{repo_name}/fork/create',
44 'repo_forks_show_all': '/{repo_name}/forks',
45 'repo_forks_data': '/{repo_name}/forks/data',
46 }[name].format(**kwargs)
47
48 if params:
49 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
50 return base_url
51
52
53 FORK_NAME = {
54 'hg': HG_FORK,
55 'git': GIT_FORK
56 }
57
58
59 @pytest.mark.skip_backends('svn')
60 class TestRepoForkViewTests(TestController):
61
62 def test_show_forks(self, backend, xhr_header):
63 self.log_user()
64 response = self.app.get(
65 route_path('repo_forks_data', repo_name=backend.repo_name),
66 extra_environ=xhr_header)
67
68 assert response.json == {u'data': [], u'draw': None,
69 u'recordsFiltered': 0, u'recordsTotal': 0}
70
71 def test_no_permissions_to_fork_page(self, backend, user_util):
72 user = user_util.create_user(password='qweqwe')
73 user_id = user.user_id
74 self.log_user(user.username, 'qweqwe')
75
76 user_model = UserModel()
77 user_model.revoke_perm(user_id, 'hg.fork.repository')
78 user_model.grant_perm(user_id, 'hg.fork.none')
79 u = UserModel().get(user_id)
80 u.inherit_default_permissions = False
81 Session().commit()
82 # try create a fork
83 self.app.get(
84 route_path('repo_fork_new', repo_name=backend.repo_name),
85 status=404)
86
87 def test_no_permissions_to_fork_submit(self, backend, csrf_token, user_util):
88 user = user_util.create_user(password='qweqwe')
89 user_id = user.user_id
90 self.log_user(user.username, 'qweqwe')
91
92 user_model = UserModel()
93 user_model.revoke_perm(user_id, 'hg.fork.repository')
94 user_model.grant_perm(user_id, 'hg.fork.none')
95 u = UserModel().get(user_id)
96 u.inherit_default_permissions = False
97 Session().commit()
98 # try create a fork
99 self.app.post(
100 route_path('repo_fork_create', repo_name=backend.repo_name),
101 {'csrf_token': csrf_token},
102 status=404)
103
104 def test_fork_missing_data(self, autologin_user, backend, csrf_token):
105 # try create a fork
106 response = self.app.post(
107 route_path('repo_fork_create', repo_name=backend.repo_name),
108 {'csrf_token': csrf_token},
109 status=200)
110 # test if html fill works fine
111 response.mustcontain('Missing value')
112
113 def test_create_fork_page(self, autologin_user, backend):
114 self.app.get(
115 route_path('repo_fork_new', repo_name=backend.repo_name),
116 status=200)
117
118 def test_create_and_show_fork(
119 self, autologin_user, backend, csrf_token, xhr_header):
120
121 # create a fork
122 fork_name = FORK_NAME[backend.alias]
123 description = 'fork of vcs test'
124 repo_name = backend.repo_name
125 source_repo = Repository.get_by_repo_name(repo_name)
126 creation_args = {
127 'repo_name': fork_name,
128 'repo_group': '',
129 'fork_parent_id': source_repo.repo_id,
130 'repo_type': backend.alias,
131 'description': description,
132 'private': 'False',
133 'landing_rev': 'rev:tip',
134 'csrf_token': csrf_token,
135 }
136
137 self.app.post(
138 route_path('repo_fork_create', repo_name=repo_name), creation_args)
139
140 response = self.app.get(
141 route_path('repo_forks_data', repo_name=repo_name),
142 extra_environ=xhr_header)
143
144 assert response.json['data'][0]['fork_name'] == \
145 """<a href="/%s">%s</a>""" % (fork_name, fork_name)
146
147 # remove this fork
148 fixture.destroy_repo(fork_name)
149
150 def test_fork_create(self, autologin_user, backend, csrf_token):
151 fork_name = FORK_NAME[backend.alias]
152 description = 'fork of vcs test'
153 repo_name = backend.repo_name
154 source_repo = Repository.get_by_repo_name(repo_name)
155 creation_args = {
156 'repo_name': fork_name,
157 'repo_group': '',
158 'fork_parent_id': source_repo.repo_id,
159 'repo_type': backend.alias,
160 'description': description,
161 'private': 'False',
162 'landing_rev': 'rev:tip',
163 'csrf_token': csrf_token,
164 }
165 self.app.post(
166 route_path('repo_fork_create', repo_name=repo_name), creation_args)
167 repo = Repository.get_by_repo_name(FORK_NAME[backend.alias])
168 assert repo.fork.repo_name == backend.repo_name
169
170 # run the check page that triggers the flash message
171 response = self.app.get(
172 route_path('repo_creating_check', repo_name=fork_name))
173 # test if we have a message that fork is ok
174 assert_session_flash(response,
175 'Forked repository %s as <a href="/%s">%s</a>'
176 % (repo_name, fork_name, fork_name))
177
178 # test if the fork was created in the database
179 fork_repo = Session().query(Repository)\
180 .filter(Repository.repo_name == fork_name).one()
181
182 assert fork_repo.repo_name == fork_name
183 assert fork_repo.fork.repo_name == repo_name
184
185 # test if the repository is visible in the list ?
186 response = self.app.get(
187 h.route_path('repo_summary', repo_name=fork_name))
188 response.mustcontain(fork_name)
189 response.mustcontain(backend.alias)
190 response.mustcontain('Fork of')
191 response.mustcontain('<a href="/%s">%s</a>' % (repo_name, repo_name))
192
193 def test_fork_create_into_group(self, autologin_user, backend, csrf_token):
194 group = fixture.create_repo_group('vc')
195 group_id = group.group_id
196 fork_name = FORK_NAME[backend.alias]
197 fork_name_full = 'vc/%s' % fork_name
198 description = 'fork of vcs test'
199 repo_name = backend.repo_name
200 source_repo = Repository.get_by_repo_name(repo_name)
201 creation_args = {
202 'repo_name': fork_name,
203 'repo_group': group_id,
204 'fork_parent_id': source_repo.repo_id,
205 'repo_type': backend.alias,
206 'description': description,
207 'private': 'False',
208 'landing_rev': 'rev:tip',
209 'csrf_token': csrf_token,
210 }
211 self.app.post(
212 route_path('repo_fork_create', repo_name=repo_name), creation_args)
213 repo = Repository.get_by_repo_name(fork_name_full)
214 assert repo.fork.repo_name == backend.repo_name
215
216 # run the check page that triggers the flash message
217 response = self.app.get(
218 route_path('repo_creating_check', repo_name=fork_name_full))
219 # test if we have a message that fork is ok
220 assert_session_flash(response,
221 'Forked repository %s as <a href="/%s">%s</a>'
222 % (repo_name, fork_name_full, fork_name_full))
223
224 # test if the fork was created in the database
225 fork_repo = Session().query(Repository)\
226 .filter(Repository.repo_name == fork_name_full).one()
227
228 assert fork_repo.repo_name == fork_name_full
229 assert fork_repo.fork.repo_name == repo_name
230
231 # test if the repository is visible in the list ?
232 response = self.app.get(
233 h.route_path('repo_summary', repo_name=fork_name_full))
234 response.mustcontain(fork_name_full)
235 response.mustcontain(backend.alias)
236
237 response.mustcontain('Fork of')
238 response.mustcontain('<a href="/%s">%s</a>' % (repo_name, repo_name))
239
240 fixture.destroy_repo(fork_name_full)
241 fixture.destroy_repo_group(group_id)
242
243 def test_fork_read_permission(self, backend, xhr_header, user_util):
244 user = user_util.create_user(password='qweqwe')
245 user_id = user.user_id
246 self.log_user(user.username, 'qweqwe')
247
248 # create a fake fork
249 fork = user_util.create_repo(repo_type=backend.alias)
250 source = user_util.create_repo(repo_type=backend.alias)
251 repo_name = source.repo_name
252
253 fork.fork_id = source.repo_id
254 fork_name = fork.repo_name
255 Session().commit()
256
257 forks = Repository.query()\
258 .filter(Repository.repo_type == backend.alias)\
259 .filter(Repository.fork_id == source.repo_id).all()
260 assert 1 == len(forks)
261
262 # set read permissions for this
263 RepoModel().grant_user_permission(
264 repo=forks[0], user=user_id, perm='repository.read')
265 Session().commit()
266
267 response = self.app.get(
268 route_path('repo_forks_data', repo_name=repo_name),
269 extra_environ=xhr_header)
270
271 assert response.json['data'][0]['fork_name'] == \
272 """<a href="/%s">%s</a>""" % (fork_name, fork_name)
273
274 def test_fork_none_permission(self, backend, xhr_header, user_util):
275 user = user_util.create_user(password='qweqwe')
276 user_id = user.user_id
277 self.log_user(user.username, 'qweqwe')
278
279 # create a fake fork
280 fork = user_util.create_repo(repo_type=backend.alias)
281 source = user_util.create_repo(repo_type=backend.alias)
282 repo_name = source.repo_name
283
284 fork.fork_id = source.repo_id
285
286 Session().commit()
287
288 forks = Repository.query()\
289 .filter(Repository.repo_type == backend.alias)\
290 .filter(Repository.fork_id == source.repo_id).all()
291 assert 1 == len(forks)
292
293 # set none
294 RepoModel().grant_user_permission(
295 repo=forks[0], user=user_id, perm='repository.none')
296 Session().commit()
297
298 # fork shouldn't be there
299 response = self.app.get(
300 route_path('repo_forks_data', repo_name=repo_name),
301 extra_environ=xhr_header)
302
303 assert response.json == {u'data': [], u'draw': None,
304 u'recordsFiltered': 0, u'recordsTotal': 0}
305
306
307 class TestSVNFork(TestController):
308 @pytest.mark.parametrize('route_name', [
309 'repo_fork_create', 'repo_fork_new'
310 ])
311 def test_fork_redirects(self, autologin_user, backend_svn, route_name):
312
313 self.app.get(route_path(
314 route_name, repo_name=backend_svn.repo_name),
315 status=404)
@@ -0,0 +1,256 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 import datetime
23 import formencode
24 from pyramid.httpexceptions import HTTPNotFound, HTTPBadRequest, HTTPFound
25 from pyramid.view import view_config
26 from pyramid.renderers import render
27 from pyramid.response import Response
28
29 from rhodecode.apps._base import RepoAppView, DataGridAppView
30
31 from rhodecode.lib.auth import (
32 LoginRequired, HasRepoPermissionAnyDecorator, NotAnonymous,
33 HasRepoPermissionAny, HasPermissionAnyDecorator, CSRFRequired)
34 import rhodecode.lib.helpers as h
35 from rhodecode.model.db import (
36 coalesce, or_, Repository, RepoGroup, UserFollowing, User)
37 from rhodecode.model.repo import RepoModel
38 from rhodecode.model.forms import RepoForkForm
39 from rhodecode.model.scm import ScmModel, RepoGroupList
40 from rhodecode.lib.utils2 import safe_int, safe_unicode
41
42 log = logging.getLogger(__name__)
43
44
45 class RepoForksView(RepoAppView, DataGridAppView):
46
47 def load_default_context(self):
48 c = self._get_local_tmpl_context(include_app_defaults=True)
49
50 # TODO(marcink): remove repo_info and use c.rhodecode_db_repo instead
51 c.repo_info = self.db_repo
52 c.rhodecode_repo = self.rhodecode_vcs_repo
53
54 acl_groups = RepoGroupList(
55 RepoGroup.query().all(),
56 perm_set=['group.write', 'group.admin'])
57 c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
58 c.repo_groups_choices = map(lambda k: safe_unicode(k[0]), c.repo_groups)
59 choices, c.landing_revs = ScmModel().get_repo_landing_revs()
60 c.landing_revs_choices = choices
61 c.personal_repo_group = c.rhodecode_user.personal_repo_group
62
63 self._register_global_c(c)
64 return c
65
66 @LoginRequired()
67 @HasRepoPermissionAnyDecorator(
68 'repository.read', 'repository.write', 'repository.admin')
69 @view_config(
70 route_name='repo_forks_show_all', request_method='GET',
71 renderer='rhodecode:templates/forks/forks.mako')
72 def repo_forks_show_all(self):
73 c = self.load_default_context()
74 return self._get_template_context(c)
75
76 @LoginRequired()
77 @HasRepoPermissionAnyDecorator(
78 'repository.read', 'repository.write', 'repository.admin')
79 @view_config(
80 route_name='repo_forks_data', request_method='GET',
81 renderer='json_ext', xhr=True)
82 def repo_forks_data(self):
83 _ = self.request.translate
84 column_map = {
85 'fork_name': 'repo_name',
86 'fork_date': 'created_on',
87 'last_activity': 'updated_on'
88 }
89 draw, start, limit = self._extract_chunk(self.request)
90 search_q, order_by, order_dir = self._extract_ordering(
91 self.request, column_map=column_map)
92
93 acl_check = HasRepoPermissionAny(
94 'repository.read', 'repository.write', 'repository.admin')
95 repo_id = self.db_repo.repo_id
96 allowed_ids = []
97 for f in Repository.query().filter(Repository.fork_id == repo_id):
98 if acl_check(f.repo_name, 'get forks check'):
99 allowed_ids.append(f.repo_id)
100
101 forks_data_total_count = Repository.query()\
102 .filter(Repository.fork_id == repo_id)\
103 .filter(Repository.repo_id.in_(allowed_ids))\
104 .count()
105
106 # json generate
107 base_q = Repository.query()\
108 .filter(Repository.fork_id == repo_id)\
109 .filter(Repository.repo_id.in_(allowed_ids))\
110
111 if search_q:
112 like_expression = u'%{}%'.format(safe_unicode(search_q))
113 base_q = base_q.filter(or_(
114 Repository.repo_name.ilike(like_expression),
115 Repository.description.ilike(like_expression),
116 ))
117
118 forks_data_total_filtered_count = base_q.count()
119
120 sort_col = getattr(Repository, order_by, None)
121 if sort_col:
122 if order_dir == 'asc':
123 # handle null values properly to order by NULL last
124 if order_by in ['last_activity']:
125 sort_col = coalesce(sort_col, datetime.date.max)
126 sort_col = sort_col.asc()
127 else:
128 # handle null values properly to order by NULL last
129 if order_by in ['last_activity']:
130 sort_col = coalesce(sort_col, datetime.date.min)
131 sort_col = sort_col.desc()
132
133 base_q = base_q.order_by(sort_col)
134 base_q = base_q.offset(start).limit(limit)
135
136 fork_list = base_q.all()
137
138 def fork_actions(fork):
139 url_link = h.route_path(
140 'repo_compare',
141 repo_name=fork.repo_name,
142 source_ref_type=self.db_repo.landing_rev[0],
143 source_ref=self.db_repo.landing_rev[1],
144 target_ref_type=self.db_repo.landing_rev[0],
145 target_ref=self.db_repo.landing_rev[1],
146 _query=dict(merge=1, target_repo=f.repo_name))
147 return h.link_to(_('Compare fork'), url_link, class_='btn-link')
148
149 def fork_name(fork):
150 return h.link_to(fork.repo_name,
151 h.route_path('repo_summary', repo_name=fork.repo_name))
152
153 forks_data = []
154 for fork in fork_list:
155 forks_data.append({
156 "username": h.gravatar_with_user(self.request, fork.user.username),
157 "fork_name": fork_name(fork),
158 "description": fork.description,
159 "fork_date": h.age_component(fork.created_on, time_is_local=True),
160 "last_activity": h.format_date(fork.updated_on),
161 "action": fork_actions(fork),
162 })
163
164 data = ({
165 'draw': draw,
166 'data': forks_data,
167 'recordsTotal': forks_data_total_count,
168 'recordsFiltered': forks_data_total_filtered_count,
169 })
170
171 return data
172
173 @LoginRequired()
174 @NotAnonymous()
175 @HasPermissionAnyDecorator('hg.admin', 'hg.fork.repository')
176 @HasRepoPermissionAnyDecorator(
177 'repository.read', 'repository.write', 'repository.admin')
178 @view_config(
179 route_name='repo_fork_new', request_method='GET',
180 renderer='rhodecode:templates/forks/forks.mako')
181 def repo_fork_new(self):
182 c = self.load_default_context()
183
184 defaults = RepoModel()._get_defaults(self.db_repo_name)
185 # alter the description to indicate a fork
186 defaults['description'] = (
187 'fork of repository: %s \n%s' % (
188 defaults['repo_name'], defaults['description']))
189 # add suffix to fork
190 defaults['repo_name'] = '%s-fork' % defaults['repo_name']
191
192 data = render('rhodecode:templates/forks/fork.mako',
193 self._get_template_context(c), self.request)
194 html = formencode.htmlfill.render(
195 data,
196 defaults=defaults,
197 encoding="UTF-8",
198 force_defaults=False
199 )
200 return Response(html)
201
202 @LoginRequired()
203 @NotAnonymous()
204 @HasPermissionAnyDecorator('hg.admin', 'hg.fork.repository')
205 @HasRepoPermissionAnyDecorator(
206 'repository.read', 'repository.write', 'repository.admin')
207 @CSRFRequired()
208 @view_config(
209 route_name='repo_fork_create', request_method='POST',
210 renderer='rhodecode:templates/forks/fork.mako')
211 def repo_fork_create(self):
212 _ = self.request.translate
213 c = self.load_default_context()
214
215 _form = RepoForkForm(old_data={'repo_type': self.db_repo.repo_type},
216 repo_groups=c.repo_groups_choices,
217 landing_revs=c.landing_revs_choices)()
218 form_result = {}
219 task_id = None
220 try:
221 form_result = _form.to_python(dict(self.request.POST))
222 # create fork is done sometimes async on celery, db transaction
223 # management is handled there.
224 task = RepoModel().create_fork(
225 form_result, c.rhodecode_user.user_id)
226 from celery.result import BaseAsyncResult
227 if isinstance(task, BaseAsyncResult):
228 task_id = task.task_id
229 except formencode.Invalid as errors:
230 c.repo_info = self.db_repo
231
232 data = render('rhodecode:templates/forks/fork.mako',
233 self._get_template_context(c), self.request)
234 html = formencode.htmlfill.render(
235 data,
236 defaults=errors.value,
237 errors=errors.error_dict or {},
238 prefix_error=False,
239 encoding="UTF-8",
240 force_defaults=False
241 )
242 return Response(html)
243 except Exception:
244 log.exception(
245 u'Exception while trying to fork the repository %s',
246 self.db_repo_name)
247 msg = (
248 _('An error occurred during repository forking %s') % (
249 self.db_repo_name, ))
250 h.flash(msg, category='error')
251
252 repo_name = form_result.get('repo_name_full', self.db_repo_name)
253 raise HTTPFound(
254 h.route_path('repo_creating',
255 repo_name=repo_name,
256 _query=dict(task_id=task_id)))
@@ -219,10 +219,31 b' def includeme(config):'
219 219 name='branches_home',
220 220 pattern='/{repo_name:.*?[^/]}/branches', repo_route=True)
221 221
222 # Bookmarks
222 223 config.add_route(
223 224 name='bookmarks_home',
224 225 pattern='/{repo_name:.*?[^/]}/bookmarks', repo_route=True)
225 226
227 # Forks
228 config.add_route(
229 name='repo_fork_new',
230 pattern='/{repo_name:.*?[^/]}/fork', repo_route=True,
231 repo_accepted_types=['hg', 'git'])
232
233 config.add_route(
234 name='repo_fork_create',
235 pattern='/{repo_name:.*?[^/]}/fork/create', repo_route=True,
236 repo_accepted_types=['hg', 'git'])
237
238 config.add_route(
239 name='repo_forks_show_all',
240 pattern='/{repo_name:.*?[^/]}/forks', repo_route=True,
241 repo_accepted_types=['hg', 'git'])
242 config.add_route(
243 name='repo_forks_data',
244 pattern='/{repo_name:.*?[^/]}/forks/data', repo_route=True,
245 repo_accepted_types=['hg', 'git'])
246
226 247 # Pull Requests
227 248 config.add_route(
228 249 name='pullrequest_show',
@@ -489,20 +489,4 b' def make_map(config):'
489 489 conditions={'method': ['GET', 'POST'], 'function': check_repo},
490 490 requirements=URL_NAME_REQUIREMENTS)
491 491
492
493 rmap.connect('repo_fork_create_home', '/{repo_name}/fork',
494 controller='forks', action='fork_create',
495 conditions={'function': check_repo, 'method': ['POST']},
496 requirements=URL_NAME_REQUIREMENTS)
497
498 rmap.connect('repo_fork_home', '/{repo_name}/fork',
499 controller='forks', action='fork',
500 conditions={'function': check_repo},
501 requirements=URL_NAME_REQUIREMENTS)
502
503 rmap.connect('repo_forks_home', '/{repo_name}/forks',
504 controller='forks', action='forks',
505 conditions={'function': check_repo},
506 requirements=URL_NAME_REQUIREMENTS)
507
508 492 return rmap
@@ -1340,39 +1340,6 b' class XHRRequired(object):'
1340 1340 return func(*fargs, **fkwargs)
1341 1341
1342 1342
1343 class HasAcceptedRepoType(object):
1344 """
1345 Check if requested repo is within given repo type aliases
1346 """
1347
1348 # TODO(marcink): remove this in favor of the predicates in pyramid routes
1349
1350 def __init__(self, *repo_type_list):
1351 self.repo_type_list = set(repo_type_list)
1352
1353 def __call__(self, func):
1354 return get_cython_compat_decorator(self.__wrapper, func)
1355
1356 def __wrapper(self, func, *fargs, **fkwargs):
1357 import rhodecode.lib.helpers as h
1358 cls = fargs[0]
1359 rhodecode_repo = cls.rhodecode_repo
1360
1361 log.debug('%s checking repo type for %s in %s',
1362 self.__class__.__name__,
1363 rhodecode_repo.alias, self.repo_type_list)
1364
1365 if rhodecode_repo.alias in self.repo_type_list:
1366 return func(*fargs, **fkwargs)
1367 else:
1368 h.flash(h.literal(
1369 _('Action not supported for %s.' % rhodecode_repo.alias)),
1370 category='warning')
1371 raise HTTPFound(
1372 h.route_path('repo_summary',
1373 repo_name=cls.rhodecode_db_repo.repo_name))
1374
1375
1376 1343 class PermsDecorator(object):
1377 1344 """
1378 1345 Base class for controller decorators, we extract the current user from
@@ -145,6 +145,10 b' function registerRCRoutes() {'
145 145 pyroutes.register('tags_home', '/%(repo_name)s/tags', ['repo_name']);
146 146 pyroutes.register('branches_home', '/%(repo_name)s/branches', ['repo_name']);
147 147 pyroutes.register('bookmarks_home', '/%(repo_name)s/bookmarks', ['repo_name']);
148 pyroutes.register('repo_fork_new', '/%(repo_name)s/fork', ['repo_name']);
149 pyroutes.register('repo_fork_create', '/%(repo_name)s/fork/create', ['repo_name']);
150 pyroutes.register('repo_forks_show_all', '/%(repo_name)s/forks', ['repo_name']);
151 pyroutes.register('repo_forks_data', '/%(repo_name)s/forks/data', ['repo_name']);
148 152 pyroutes.register('pullrequest_show', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
149 153 pyroutes.register('pullrequest_show_all', '/%(repo_name)s/pull-request', ['repo_name']);
150 154 pyroutes.register('pullrequest_show_all_data', '/%(repo_name)s/pull-request-data', ['repo_name']);
@@ -275,7 +275,7 b''
275 275 %endif
276 276 %if c.rhodecode_user.username != h.DEFAULT_USER:
277 277 %if c.rhodecode_db_repo.repo_type in ['git','hg']:
278 <li><a href="${h.url('repo_fork_home',repo_name=c.repo_name)}">${_('Fork')}</a></li>
278 <li><a href="${h.route_path('repo_fork_new',repo_name=c.repo_name)}">${_('Fork')}</a></li>
279 279 <li><a href="${h.route_path('pullrequest_new',repo_name=c.repo_name)}">${_('Create Pull Request')}</a></li>
280 280 %endif
281 281 %endif
@@ -24,7 +24,7 b''
24 24 </a>
25 25 </li>
26 26 <li>
27 <a title="${_('Fork')}" href="${h.url('repo_fork_home',repo_name=repo_name)}">
27 <a title="${_('Fork')}" href="${h.route_path('repo_fork_new',repo_name=repo_name)}">
28 28 <span>${_('Fork')}</span>
29 29 </a>
30 30 </li>
@@ -27,7 +27,7 b''
27 27 ${self.breadcrumbs()}
28 28 </div>
29 29
30 ${h.secure_form(h.url('repo_fork_create_home',repo_name=c.repo_info.repo_name))}
30 ${h.secure_form(h.route_path('repo_fork_create',repo_name=c.repo_info.repo_name), method='POST', request=request)}
31 31 <div class="form">
32 32 <!-- fields -->
33 33 <div class="fields">
@@ -26,20 +26,94 b''
26 26 ${self.repo_page_title(c.rhodecode_db_repo)}
27 27 <ul class="links">
28 28 <li>
29 ## fork open link
30 <span>
31 <a class="btn btn-small btn-success" href="${h.url('repo_fork_home',repo_name=c.repo_name)}">
29 <a class="btn btn-small btn-success" href="${h.route_path('repo_fork_new',repo_name=c.repo_name)}">
32 30 ${_('Create new fork')}
33 31 </a>
34 </span>
35
36 32 </li>
37 33 </ul>
38 34 </div>
39 <div class="table">
40 <div id="forks">
41 ${c.forks_data}
35
36 <div id="fork_list_wrap">
37 <table id="fork_list_table" class="display"></table>
42 38 </div>
43 39 </div>
44 </div>
40
41
42
43 <script type="text/javascript">
44
45 $(document).ready(function() {
46 var $userListTable = $('#fork_list_table');
47
48 var getDatatableCount = function(){
49 var table = $userListTable.dataTable();
50 var page = table.api().page.info();
51 var active = page.recordsDisplay;
52 var total = page.recordsTotal;
53
54 var _text = _gettext("{0} out of {1} users").format(active, total);
55 $('#user_count').text(_text);
56 };
57
58 // user list
59 $userListTable.DataTable({
60 processing: true,
61 serverSide: true,
62 ajax: "${h.route_path('repo_forks_data', repo_name=c.repo_name)}",
63 dom: 'rtp',
64 pageLength: ${c.visual.dashboard_items},
65 order: [[ 0, "asc" ]],
66 columns: [
67 { data: {"_": "username",
68 "sort": "username"}, title: "${_('Owner')}", className: "td-user" },
69 { data: {"_": "fork_name",
70 "sort": "fork_name"}, title: "${_('Fork name')}", className: "td-email" },
71 { data: {"_": "description",
72 "sort": "description"}, title: "${_('Description')}", className: "td-user" },
73 { data: {"_": "fork_date",
74 "sort": "fork_date"}, title: "${_('Forked')}", className: "td-user" },
75 { data: {"_": "last_activity",
76 "sort": "last_activity",
77 "type": Number}, title: "${_('Last activity')}", className: "td-time" },
78 { data: {"_": "action",
79 "sort": "action"}, title: "${_('Action')}", className: "td-action", orderable: false }
80 ],
81
82 language: {
83 paginate: DEFAULT_GRID_PAGINATION,
84 sProcessing: _gettext('loading...'),
85 emptyTable: _gettext("No forks available yet.")
86 },
87
88 "createdRow": function ( row, data, index ) {
89 if (!data['active_raw']){
90 $(row).addClass('closed')
91 }
92 }
93 });
94
95 $userListTable.on('xhr.dt', function(e, settings, json, xhr){
96 $userListTable.css('opacity', 1);
97 });
98
99 $userListTable.on('preXhr.dt', function(e, settings, data){
100 $userListTable.css('opacity', 0.3);
101 });
102
103 // refresh counters on draw
104 $userListTable.on('draw.dt', function(){
105 getDatatableCount();
106 });
107
108 // filter
109 $('#q_filter').on('keyup',
110 $.debounce(250, function() {
111 $userListTable.DataTable().search(
112 $('#q_filter').val()
113 ).draw();
114 })
115 );
116
117 });
118 </script>
45 119 </%def>
@@ -108,7 +108,7 b''
108 108 % endif
109 109
110 110 ## forks
111 <a title="${_('Number of Repository Forks')}" href="${h.url('repo_forks_home', repo_name=c.repo_name)}">
111 <a title="${_('Number of Repository Forks')}" href="${h.route_path('repo_forks_show_all', repo_name=c.repo_name)}">
112 112 ${c.repository_forks} ${_ungettext('Fork', 'Forks', c.repository_forks)}</a>,
113 113
114 114 ## repo size
@@ -18,15 +18,10 b''
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 import mock
22 21 import pytest
23 from webob.exc import HTTPNotFound
24 22
25 import rhodecode
26 23 from rhodecode.model.db import Integration
27 24 from rhodecode.model.meta import Session
28 from rhodecode.tests import assert_session_flash, url, TEST_USER_ADMIN_LOGIN
29 from rhodecode.tests.utils import AssertResponse
30 25 from rhodecode.integrations import integration_type_registry
31 26 from rhodecode.config.routing import ADMIN_PREFIX
32 27
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now