##// END OF EJS Templates
audit-logs: introduced new view to replace admin journal....
marcink -
r1758:98d57cd2 default
parent child Browse files
Show More
@@ -0,0 +1,187 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 os
22 import csv
23 import datetime
24
25 import pytest
26
27 from rhodecode.tests import *
28 from rhodecode.tests.fixture import FIXTURES
29 from rhodecode.model.db import UserLog
30 from rhodecode.model.meta import Session
31 from rhodecode.lib.utils2 import safe_unicode
32
33
34 def route_path(name, params=None, **kwargs):
35 import urllib
36 from rhodecode.apps._base import ADMIN_PREFIX
37
38 base_url = {
39 'admin_home': ADMIN_PREFIX,
40 'admin_audit_logs': ADMIN_PREFIX + '/audit_logs',
41
42 }[name].format(**kwargs)
43
44 if params:
45 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
46 return base_url
47
48
49 class TestAdminController(TestController):
50
51 @pytest.fixture(scope='class', autouse=True)
52 def prepare(self, request, pylonsapp):
53 UserLog.query().delete()
54 Session().commit()
55
56 def strptime(val):
57 fmt = '%Y-%m-%d %H:%M:%S'
58 if '.' not in val:
59 return datetime.datetime.strptime(val, fmt)
60
61 nofrag, frag = val.split(".")
62 date = datetime.datetime.strptime(nofrag, fmt)
63
64 frag = frag[:6] # truncate to microseconds
65 frag += (6 - len(frag)) * '0' # add 0s
66 return date.replace(microsecond=int(frag))
67
68 with open(os.path.join(FIXTURES, 'journal_dump.csv')) as f:
69 for row in csv.DictReader(f):
70 ul = UserLog()
71 for k, v in row.iteritems():
72 v = safe_unicode(v)
73 if k == 'action_date':
74 v = strptime(v)
75 if k in ['user_id', 'repository_id']:
76 # nullable due to FK problems
77 v = None
78 setattr(ul, k, v)
79 Session().add(ul)
80 Session().commit()
81
82 @request.addfinalizer
83 def cleanup():
84 UserLog.query().delete()
85 Session().commit()
86
87 def test_index(self):
88 self.log_user()
89 response = self.app.get(route_path('admin_audit_logs'))
90 response.mustcontain('Admin audit logs')
91
92 def test_filter_all_entries(self):
93 self.log_user()
94 response = self.app.get(route_path('admin_audit_logs'))
95 all_count = UserLog.query().count()
96 response.mustcontain('%s entries' % all_count)
97
98 def test_filter_journal_filter_exact_match_on_repository(self):
99 self.log_user()
100 response = self.app.get(route_path('admin_audit_logs',
101 params=dict(filter='repository:rhodecode')))
102 response.mustcontain('3 entries')
103
104 def test_filter_journal_filter_exact_match_on_repository_CamelCase(self):
105 self.log_user()
106 response = self.app.get(route_path('admin_audit_logs',
107 params=dict(filter='repository:RhodeCode')))
108 response.mustcontain('3 entries')
109
110 def test_filter_journal_filter_wildcard_on_repository(self):
111 self.log_user()
112 response = self.app.get(route_path('admin_audit_logs',
113 params=dict(filter='repository:*test*')))
114 response.mustcontain('862 entries')
115
116 def test_filter_journal_filter_prefix_on_repository(self):
117 self.log_user()
118 response = self.app.get(route_path('admin_audit_logs',
119 params=dict(filter='repository:test*')))
120 response.mustcontain('257 entries')
121
122 def test_filter_journal_filter_prefix_on_repository_CamelCase(self):
123 self.log_user()
124 response = self.app.get(route_path('admin_audit_logs',
125 params=dict(filter='repository:Test*')))
126 response.mustcontain('257 entries')
127
128 def test_filter_journal_filter_prefix_on_repository_and_user(self):
129 self.log_user()
130 response = self.app.get(route_path('admin_audit_logs',
131 params=dict(filter='repository:test* AND username:demo')))
132 response.mustcontain('130 entries')
133
134 def test_filter_journal_filter_prefix_on_repository_or_target_repo(self):
135 self.log_user()
136 response = self.app.get(route_path('admin_audit_logs',
137 params=dict(filter='repository:test* OR repository:rhodecode')))
138 response.mustcontain('260 entries') # 257 + 3
139
140 def test_filter_journal_filter_exact_match_on_username(self):
141 self.log_user()
142 response = self.app.get(route_path('admin_audit_logs',
143 params=dict(filter='username:demo')))
144 response.mustcontain('1087 entries')
145
146 def test_filter_journal_filter_exact_match_on_username_camelCase(self):
147 self.log_user()
148 response = self.app.get(route_path('admin_audit_logs',
149 params=dict(filter='username:DemO')))
150 response.mustcontain('1087 entries')
151
152 def test_filter_journal_filter_wildcard_on_username(self):
153 self.log_user()
154 response = self.app.get(route_path('admin_audit_logs',
155 params=dict(filter='username:*test*')))
156 entries_count = UserLog.query().filter(UserLog.username.ilike('%test%')).count()
157 response.mustcontain('{} entries'.format(entries_count))
158
159 def test_filter_journal_filter_prefix_on_username(self):
160 self.log_user()
161 response = self.app.get(route_path('admin_audit_logs',
162 params=dict(filter='username:demo*')))
163 response.mustcontain('1101 entries')
164
165 def test_filter_journal_filter_prefix_on_user_or_other_user(self):
166 self.log_user()
167 response = self.app.get(route_path('admin_audit_logs',
168 params=dict(filter='username:demo OR username:volcan')))
169 response.mustcontain('1095 entries') # 1087 + 8
170
171 def test_filter_journal_filter_wildcard_on_action(self):
172 self.log_user()
173 response = self.app.get(route_path('admin_audit_logs',
174 params=dict(filter='action:*pull_request*')))
175 response.mustcontain('187 entries')
176
177 def test_filter_journal_filter_on_date(self):
178 self.log_user()
179 response = self.app.get(route_path('admin_audit_logs',
180 params=dict(filter='date:20121010')))
181 response.mustcontain('47 entries')
182
183 def test_filter_journal_filter_on_date_2(self):
184 self.log_user()
185 response = self.app.get(route_path('admin_audit_logs',
186 params=dict(filter='date:20121020')))
187 response.mustcontain('17 entries')
@@ -0,0 +1,82 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
24 from rhodecode.tests.fixture import Fixture
25
26 fixture = Fixture()
27
28
29 def route_path(name, params=None, **kwargs):
30 import urllib
31 from rhodecode.apps._base import ADMIN_PREFIX
32
33 base_url = {
34 'admin_home': ADMIN_PREFIX,
35 'pullrequest_show': '/{repo_name}/pull-request/{pull_request_id}',
36 'pull_requests_global': ADMIN_PREFIX + '/pull-request/{pull_request_id}',
37 'pull_requests_global_0': ADMIN_PREFIX + '/pull_requests/{pull_request_id}',
38 'pull_requests_global_1': ADMIN_PREFIX + '/pull-requests/{pull_request_id}',
39
40 }[name].format(**kwargs)
41
42 if params:
43 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
44 return base_url
45
46
47 class TestAdminMainView(TestController):
48
49 def test_redirect_admin_home(self):
50 self.log_user()
51 response = self.app.get(route_path('admin_home'), status=302)
52 assert response.location.endswith('/audit_logs')
53
54 def test_redirect_pull_request_view(self, view):
55 self.log_user()
56 self.app.get(
57 route_path(view, pull_request_id='xxxx'),
58 status=404)
59
60 @pytest.mark.backends("git", "hg")
61 @pytest.mark.parametrize('view', [
62 'pull_requests_global',
63 'pull_requests_global_0',
64 'pull_requests_global_1',
65 ])
66 def test_redirect_pull_request_view(self, view, pr_util):
67 self.log_user()
68 pull_request = pr_util.create_pull_request()
69 pull_request_id = pull_request.pull_request_id
70
71 response = self.app.get(
72 route_path(view, pull_request_id=pull_request_id),
73 status=302)
74 assert response.location.endswith(
75 'pull-request/{}'.format(pull_request_id))
76
77 repo_name = pull_request.target_repo.repo_name
78 redirect_url = route_path(
79 'pullrequest_show', repo_name=repo_name,
80 pull_request_id=pull_request.pull_request_id)
81
82 assert redirect_url in response.location
@@ -0,0 +1,73 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2016-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 sqlalchemy.orm import joinedload
25
26 from rhodecode.apps._base import BaseAppView
27 from rhodecode.model.db import UserLog
28 from rhodecode.lib.user_log_filter import user_log_filter
29 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
30 from rhodecode.lib.utils2 import safe_int
31 from rhodecode.lib.helpers import Page
32
33 log = logging.getLogger(__name__)
34
35
36 class AdminAuditLogsView(BaseAppView):
37 def load_default_context(self):
38 c = self._get_local_tmpl_context()
39 self._register_global_c(c)
40 return c
41
42 @LoginRequired()
43 @HasPermissionAllDecorator('hg.admin')
44 @view_config(
45 route_name='admin_audit_logs', request_method='GET',
46 renderer='rhodecode:templates/admin/admin_audit_logs.mako')
47 def admin_audit_logs(self):
48 c = self.load_default_context()
49
50 users_log = UserLog.query()\
51 .options(joinedload(UserLog.user))\
52 .options(joinedload(UserLog.repository))
53
54 # FILTERING
55 c.search_term = self.request.GET.get('filter')
56 try:
57 users_log = user_log_filter(users_log, c.search_term)
58 except Exception:
59 # we want this to crash for now
60 raise
61
62 users_log = users_log.order_by(UserLog.action_date.desc())
63
64 p = safe_int(self.request.GET.get('page', 1), 1)
65
66 def url_generator(**kw):
67 if c.search_term:
68 kw['filter'] = c.search_term
69 return self.request.current_route_path(_query=kw)
70
71 c.audit_logs = Page(users_log, page=p, items_per_page=10,
72 url=url_generator)
73 return self._get_template_context(c)
@@ -0,0 +1,63 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2016-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
24 from pyramid.httpexceptions import HTTPFound
25 from pyramid.view import view_config
26
27 from rhodecode.apps._base import BaseAppView
28 from rhodecode.lib import helpers as h
29 from rhodecode.lib.auth import (LoginRequired, HasPermissionAllDecorator)
30 from rhodecode.model.db import PullRequest
31
32
33 log = logging.getLogger(__name__)
34
35
36 class AdminMainView(BaseAppView):
37
38 @LoginRequired()
39 @HasPermissionAllDecorator('hg.admin')
40 @view_config(
41 route_name='admin_home', request_method='GET')
42 def admin_main(self):
43 # redirect _admin to audit logs...
44 raise HTTPFound(h.route_path('admin_audit_logs'))
45
46 @LoginRequired()
47 @view_config(route_name='pull_requests_global_0', request_method='GET')
48 @view_config(route_name='pull_requests_global_1', request_method='GET')
49 @view_config(route_name='pull_requests_global', request_method='GET')
50 def pull_requests(self):
51 """
52 Global redirect for Pull Requests
53
54 :param pull_request_id: id of pull requests in the system
55 """
56
57 pull_request_id = self.request.matchdict.get('pull_request_id')
58 pull_request = PullRequest.get_or_404(pull_request_id, pyramid_exc=True)
59 repo_name = pull_request.target_repo.repo_name
60
61 raise HTTPFound(
62 h.route_path('pullrequest_show', repo_name=repo_name,
63 pull_request_id=pull_request_id))
@@ -1,99 +1,115 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2017 RhodeCode GmbH
3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
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
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
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/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21
21
22 from rhodecode.apps.admin.navigation import NavigationRegistry
22 from rhodecode.apps.admin.navigation import NavigationRegistry
23 from rhodecode.config.routing import ADMIN_PREFIX
23 from rhodecode.config.routing import ADMIN_PREFIX
24 from rhodecode.lib.utils2 import str2bool
24 from rhodecode.lib.utils2 import str2bool
25
25
26
26
27 def admin_routes(config):
27 def admin_routes(config):
28 """
28 """
29 Admin prefixed routes
29 Admin prefixed routes
30 """
30 """
31
31
32 config.add_route(
32 config.add_route(
33 name='admin_audit_logs',
34 pattern='/audit_logs')
35
36 config.add_route(
37 name='pull_requests_global_0', # backward compat
38 pattern='/pull_requests/{pull_request_id:[0-9]+}')
39 config.add_route(
40 name='pull_requests_global_1', # backward compat
41 pattern='/pull-requests/{pull_request_id:[0-9]+}')
42 config.add_route(
43 name='pull_requests_global',
44 pattern='/pull-request/{pull_request_id:[0-9]+}')
45
46 config.add_route(
33 name='admin_settings_open_source',
47 name='admin_settings_open_source',
34 pattern='/settings/open_source')
48 pattern='/settings/open_source')
35 config.add_route(
49 config.add_route(
36 name='admin_settings_vcs_svn_generate_cfg',
50 name='admin_settings_vcs_svn_generate_cfg',
37 pattern='/settings/vcs/svn_generate_cfg')
51 pattern='/settings/vcs/svn_generate_cfg')
38
52
39 config.add_route(
53 config.add_route(
40 name='admin_settings_system',
54 name='admin_settings_system',
41 pattern='/settings/system')
55 pattern='/settings/system')
42 config.add_route(
56 config.add_route(
43 name='admin_settings_system_update',
57 name='admin_settings_system_update',
44 pattern='/settings/system/updates')
58 pattern='/settings/system/updates')
45
59
46 config.add_route(
60 config.add_route(
47 name='admin_settings_sessions',
61 name='admin_settings_sessions',
48 pattern='/settings/sessions')
62 pattern='/settings/sessions')
49 config.add_route(
63 config.add_route(
50 name='admin_settings_sessions_cleanup',
64 name='admin_settings_sessions_cleanup',
51 pattern='/settings/sessions/cleanup')
65 pattern='/settings/sessions/cleanup')
52
66
53 # users admin
67 # users admin
54 config.add_route(
68 config.add_route(
55 name='users',
69 name='users',
56 pattern='/users')
70 pattern='/users')
57
71
58 config.add_route(
72 config.add_route(
59 name='users_data',
73 name='users_data',
60 pattern='/users_data')
74 pattern='/users_data')
61
75
62 # user auth tokens
76 # user auth tokens
63 config.add_route(
77 config.add_route(
64 name='edit_user_auth_tokens',
78 name='edit_user_auth_tokens',
65 pattern='/users/{user_id:\d+}/edit/auth_tokens')
79 pattern='/users/{user_id:\d+}/edit/auth_tokens')
66 config.add_route(
80 config.add_route(
67 name='edit_user_auth_tokens_add',
81 name='edit_user_auth_tokens_add',
68 pattern='/users/{user_id:\d+}/edit/auth_tokens/new')
82 pattern='/users/{user_id:\d+}/edit/auth_tokens/new')
69 config.add_route(
83 config.add_route(
70 name='edit_user_auth_tokens_delete',
84 name='edit_user_auth_tokens_delete',
71 pattern='/users/{user_id:\d+}/edit/auth_tokens/delete')
85 pattern='/users/{user_id:\d+}/edit/auth_tokens/delete')
72
86
73 # user groups management
87 # user groups management
74 config.add_route(
88 config.add_route(
75 name='edit_user_groups_management',
89 name='edit_user_groups_management',
76 pattern='/users/{user_id:\d+}/edit/groups_management')
90 pattern='/users/{user_id:\d+}/edit/groups_management')
77
91
78 config.add_route(
92 config.add_route(
79 name='edit_user_groups_management_updates',
93 name='edit_user_groups_management_updates',
80 pattern='/users/{user_id:\d+}/edit/edit_user_groups_management/updates')
94 pattern='/users/{user_id:\d+}/edit/edit_user_groups_management/updates')
81
95
82 # user audit logs
96 # user audit logs
83 config.add_route(
97 config.add_route(
84 name='edit_user_audit_logs',
98 name='edit_user_audit_logs',
85 pattern='/users/{user_id:\d+}/edit/audit')
99 pattern='/users/{user_id:\d+}/edit/audit')
86
100
87
101
88 def includeme(config):
102 def includeme(config):
89 settings = config.get_settings()
103 settings = config.get_settings()
90
104
91 # Create admin navigation registry and add it to the pyramid registry.
105 # Create admin navigation registry and add it to the pyramid registry.
92 labs_active = str2bool(settings.get('labs_settings_active', False))
106 labs_active = str2bool(settings.get('labs_settings_active', False))
93 navigation_registry = NavigationRegistry(labs_active=labs_active)
107 navigation_registry = NavigationRegistry(labs_active=labs_active)
94 config.registry.registerUtility(navigation_registry)
108 config.registry.registerUtility(navigation_registry)
95
109
110 # main admin routes
111 config.add_route(name='admin_home', pattern=ADMIN_PREFIX)
96 config.include(admin_routes, route_prefix=ADMIN_PREFIX)
112 config.include(admin_routes, route_prefix=ADMIN_PREFIX)
97
113
98 # Scan module for configuration decorators.
114 # Scan module for configuration decorators.
99 config.scan()
115 config.scan()
@@ -1,109 +1,115 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2017 RhodeCode GmbH
3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
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
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
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/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21
21
22 def includeme(config):
22 def includeme(config):
23
23
24 # Summary
24 # Summary
25 config.add_route(
25 config.add_route(
26 name='repo_summary_explicit',
26 name='repo_summary_explicit',
27 pattern='/{repo_name:.*?[^/]}/summary', repo_route=True)
27 pattern='/{repo_name:.*?[^/]}/summary', repo_route=True)
28
28
29 # Tags
29 # Tags
30 config.add_route(
30 config.add_route(
31 name='tags_home',
31 name='tags_home',
32 pattern='/{repo_name:.*?[^/]}/tags', repo_route=True)
32 pattern='/{repo_name:.*?[^/]}/tags', repo_route=True)
33
33
34 # Branches
34 # Branches
35 config.add_route(
35 config.add_route(
36 name='branches_home',
36 name='branches_home',
37 pattern='/{repo_name:.*?[^/]}/branches', repo_route=True)
37 pattern='/{repo_name:.*?[^/]}/branches', repo_route=True)
38
38
39 # Bookmarks
39 # Bookmarks
40 config.add_route(
40 config.add_route(
41 name='bookmarks_home',
41 name='bookmarks_home',
42 pattern='/{repo_name:.*?[^/]}/bookmarks', repo_route=True)
42 pattern='/{repo_name:.*?[^/]}/bookmarks', repo_route=True)
43
43
44 # Pull Requests
45 config.add_route(
46 name='pullrequest_show',
47 pattern='/{repo_name:.*?[^/]}/pull-request/{pull_request_id}',
48 repo_route=True)
49
44 # Settings
50 # Settings
45 config.add_route(
51 config.add_route(
46 name='edit_repo',
52 name='edit_repo',
47 pattern='/{repo_name:.*?[^/]}/settings', repo_route=True)
53 pattern='/{repo_name:.*?[^/]}/settings', repo_route=True)
48
54
49 # Settings advanced
55 # Settings advanced
50 config.add_route(
56 config.add_route(
51 name='edit_repo_advanced',
57 name='edit_repo_advanced',
52 pattern='/{repo_name:.*?[^/]}/settings/advanced', repo_route=True)
58 pattern='/{repo_name:.*?[^/]}/settings/advanced', repo_route=True)
53 config.add_route(
59 config.add_route(
54 name='edit_repo_advanced_delete',
60 name='edit_repo_advanced_delete',
55 pattern='/{repo_name:.*?[^/]}/settings/advanced/delete', repo_route=True)
61 pattern='/{repo_name:.*?[^/]}/settings/advanced/delete', repo_route=True)
56 config.add_route(
62 config.add_route(
57 name='edit_repo_advanced_locking',
63 name='edit_repo_advanced_locking',
58 pattern='/{repo_name:.*?[^/]}/settings/advanced/locking', repo_route=True)
64 pattern='/{repo_name:.*?[^/]}/settings/advanced/locking', repo_route=True)
59 config.add_route(
65 config.add_route(
60 name='edit_repo_advanced_journal',
66 name='edit_repo_advanced_journal',
61 pattern='/{repo_name:.*?[^/]}/settings/advanced/journal', repo_route=True)
67 pattern='/{repo_name:.*?[^/]}/settings/advanced/journal', repo_route=True)
62 config.add_route(
68 config.add_route(
63 name='edit_repo_advanced_fork',
69 name='edit_repo_advanced_fork',
64 pattern='/{repo_name:.*?[^/]}/settings/advanced/fork', repo_route=True)
70 pattern='/{repo_name:.*?[^/]}/settings/advanced/fork', repo_route=True)
65
71
66 # Caches
72 # Caches
67 config.add_route(
73 config.add_route(
68 name='edit_repo_caches',
74 name='edit_repo_caches',
69 pattern='/{repo_name:.*?[^/]}/settings/caches', repo_route=True)
75 pattern='/{repo_name:.*?[^/]}/settings/caches', repo_route=True)
70
76
71 # Permissions
77 # Permissions
72 config.add_route(
78 config.add_route(
73 name='edit_repo_perms',
79 name='edit_repo_perms',
74 pattern='/{repo_name:.*?[^/]}/settings/permissions', repo_route=True)
80 pattern='/{repo_name:.*?[^/]}/settings/permissions', repo_route=True)
75
81
76 # Repo Review Rules
82 # Repo Review Rules
77 config.add_route(
83 config.add_route(
78 name='repo_reviewers',
84 name='repo_reviewers',
79 pattern='/{repo_name:.*?[^/]}/settings/review/rules', repo_route=True)
85 pattern='/{repo_name:.*?[^/]}/settings/review/rules', repo_route=True)
80
86
81 # Maintenance
87 # Maintenance
82 config.add_route(
88 config.add_route(
83 name='repo_maintenance',
89 name='repo_maintenance',
84 pattern='/{repo_name:.*?[^/]}/settings/maintenance', repo_route=True)
90 pattern='/{repo_name:.*?[^/]}/settings/maintenance', repo_route=True)
85
91
86 config.add_route(
92 config.add_route(
87 name='repo_maintenance_execute',
93 name='repo_maintenance_execute',
88 pattern='/{repo_name:.*?[^/]}/settings/maintenance/execute', repo_route=True)
94 pattern='/{repo_name:.*?[^/]}/settings/maintenance/execute', repo_route=True)
89
95
90 # Strip
96 # Strip
91 config.add_route(
97 config.add_route(
92 name='strip',
98 name='strip',
93 pattern='/{repo_name:.*?[^/]}/settings/strip', repo_route=True)
99 pattern='/{repo_name:.*?[^/]}/settings/strip', repo_route=True)
94
100
95 config.add_route(
101 config.add_route(
96 name='strip_check',
102 name='strip_check',
97 pattern='/{repo_name:.*?[^/]}/settings/strip_check', repo_route=True)
103 pattern='/{repo_name:.*?[^/]}/settings/strip_check', repo_route=True)
98
104
99 config.add_route(
105 config.add_route(
100 name='strip_execute',
106 name='strip_execute',
101 pattern='/{repo_name:.*?[^/]}/settings/strip_execute', repo_route=True)
107 pattern='/{repo_name:.*?[^/]}/settings/strip_execute', repo_route=True)
102
108
103 # NOTE(marcink): needs to be at the end for catch-all
109 # NOTE(marcink): needs to be at the end for catch-all
104 # config.add_route(
110 # config.add_route(
105 # name='repo_summary',
111 # name='repo_summary',
106 # pattern='/{repo_name:.*?[^/]}', repo_route=True)
112 # pattern='/{repo_name:.*?[^/]}', repo_route=True)
107
113
108 # Scan module for configuration decorators.
114 # Scan module for configuration decorators.
109 config.scan()
115 config.scan()
@@ -1,1053 +1,1037 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
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
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
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/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 """
21 """
22 Routes configuration
22 Routes configuration
23
23
24 The more specific and detailed routes should be defined first so they
24 The more specific and detailed routes should be defined first so they
25 may take precedent over the more generic routes. For more information
25 may take precedent over the more generic routes. For more information
26 refer to the routes manual at http://routes.groovie.org/docs/
26 refer to the routes manual at http://routes.groovie.org/docs/
27
27
28 IMPORTANT: if you change any routing here, make sure to take a look at lib/base.py
28 IMPORTANT: if you change any routing here, make sure to take a look at lib/base.py
29 and _route_name variable which uses some of stored naming here to do redirects.
29 and _route_name variable which uses some of stored naming here to do redirects.
30 """
30 """
31 import os
31 import os
32 import re
32 import re
33 from routes import Mapper
33 from routes import Mapper
34
34
35 # prefix for non repository related links needs to be prefixed with `/`
35 # prefix for non repository related links needs to be prefixed with `/`
36 ADMIN_PREFIX = '/_admin'
36 ADMIN_PREFIX = '/_admin'
37 STATIC_FILE_PREFIX = '/_static'
37 STATIC_FILE_PREFIX = '/_static'
38
38
39 # Default requirements for URL parts
39 # Default requirements for URL parts
40 URL_NAME_REQUIREMENTS = {
40 URL_NAME_REQUIREMENTS = {
41 # group name can have a slash in them, but they must not end with a slash
41 # group name can have a slash in them, but they must not end with a slash
42 'group_name': r'.*?[^/]',
42 'group_name': r'.*?[^/]',
43 'repo_group_name': r'.*?[^/]',
43 'repo_group_name': r'.*?[^/]',
44 # repo names can have a slash in them, but they must not end with a slash
44 # repo names can have a slash in them, but they must not end with a slash
45 'repo_name': r'.*?[^/]',
45 'repo_name': r'.*?[^/]',
46 # file path eats up everything at the end
46 # file path eats up everything at the end
47 'f_path': r'.*',
47 'f_path': r'.*',
48 # reference types
48 # reference types
49 'source_ref_type': '(branch|book|tag|rev|\%\(source_ref_type\)s)',
49 'source_ref_type': '(branch|book|tag|rev|\%\(source_ref_type\)s)',
50 'target_ref_type': '(branch|book|tag|rev|\%\(target_ref_type\)s)',
50 'target_ref_type': '(branch|book|tag|rev|\%\(target_ref_type\)s)',
51 }
51 }
52
52
53
53
54 def add_route_requirements(route_path, requirements):
54 def add_route_requirements(route_path, requirements):
55 """
55 """
56 Adds regex requirements to pyramid routes using a mapping dict
56 Adds regex requirements to pyramid routes using a mapping dict
57
57
58 >>> add_route_requirements('/{action}/{id}', {'id': r'\d+'})
58 >>> add_route_requirements('/{action}/{id}', {'id': r'\d+'})
59 '/{action}/{id:\d+}'
59 '/{action}/{id:\d+}'
60
60
61 """
61 """
62 for key, regex in requirements.items():
62 for key, regex in requirements.items():
63 route_path = route_path.replace('{%s}' % key, '{%s:%s}' % (key, regex))
63 route_path = route_path.replace('{%s}' % key, '{%s:%s}' % (key, regex))
64 return route_path
64 return route_path
65
65
66
66
67 class JSRoutesMapper(Mapper):
67 class JSRoutesMapper(Mapper):
68 """
68 """
69 Wrapper for routes.Mapper to make pyroutes compatible url definitions
69 Wrapper for routes.Mapper to make pyroutes compatible url definitions
70 """
70 """
71 _named_route_regex = re.compile(r'^[a-z-_0-9A-Z]+$')
71 _named_route_regex = re.compile(r'^[a-z-_0-9A-Z]+$')
72 _argument_prog = re.compile('\{(.*?)\}|:\((.*)\)')
72 _argument_prog = re.compile('\{(.*?)\}|:\((.*)\)')
73 def __init__(self, *args, **kw):
73 def __init__(self, *args, **kw):
74 super(JSRoutesMapper, self).__init__(*args, **kw)
74 super(JSRoutesMapper, self).__init__(*args, **kw)
75 self._jsroutes = []
75 self._jsroutes = []
76
76
77 def connect(self, *args, **kw):
77 def connect(self, *args, **kw):
78 """
78 """
79 Wrapper for connect to take an extra argument jsroute=True
79 Wrapper for connect to take an extra argument jsroute=True
80
80
81 :param jsroute: boolean, if True will add the route to the pyroutes list
81 :param jsroute: boolean, if True will add the route to the pyroutes list
82 """
82 """
83 if kw.pop('jsroute', False):
83 if kw.pop('jsroute', False):
84 if not self._named_route_regex.match(args[0]):
84 if not self._named_route_regex.match(args[0]):
85 raise Exception('only named routes can be added to pyroutes')
85 raise Exception('only named routes can be added to pyroutes')
86 self._jsroutes.append(args[0])
86 self._jsroutes.append(args[0])
87
87
88 super(JSRoutesMapper, self).connect(*args, **kw)
88 super(JSRoutesMapper, self).connect(*args, **kw)
89
89
90 def _extract_route_information(self, route):
90 def _extract_route_information(self, route):
91 """
91 """
92 Convert a route into tuple(name, path, args), eg:
92 Convert a route into tuple(name, path, args), eg:
93 ('show_user', '/profile/%(username)s', ['username'])
93 ('show_user', '/profile/%(username)s', ['username'])
94 """
94 """
95 routepath = route.routepath
95 routepath = route.routepath
96 def replace(matchobj):
96 def replace(matchobj):
97 if matchobj.group(1):
97 if matchobj.group(1):
98 return "%%(%s)s" % matchobj.group(1).split(':')[0]
98 return "%%(%s)s" % matchobj.group(1).split(':')[0]
99 else:
99 else:
100 return "%%(%s)s" % matchobj.group(2)
100 return "%%(%s)s" % matchobj.group(2)
101
101
102 routepath = self._argument_prog.sub(replace, routepath)
102 routepath = self._argument_prog.sub(replace, routepath)
103 return (
103 return (
104 route.name,
104 route.name,
105 routepath,
105 routepath,
106 [(arg[0].split(':')[0] if arg[0] != '' else arg[1])
106 [(arg[0].split(':')[0] if arg[0] != '' else arg[1])
107 for arg in self._argument_prog.findall(route.routepath)]
107 for arg in self._argument_prog.findall(route.routepath)]
108 )
108 )
109
109
110 def jsroutes(self):
110 def jsroutes(self):
111 """
111 """
112 Return a list of pyroutes.js compatible routes
112 Return a list of pyroutes.js compatible routes
113 """
113 """
114 for route_name in self._jsroutes:
114 for route_name in self._jsroutes:
115 yield self._extract_route_information(self._routenames[route_name])
115 yield self._extract_route_information(self._routenames[route_name])
116
116
117
117
118 def make_map(config):
118 def make_map(config):
119 """Create, configure and return the routes Mapper"""
119 """Create, configure and return the routes Mapper"""
120 rmap = JSRoutesMapper(directory=config['pylons.paths']['controllers'],
120 rmap = JSRoutesMapper(directory=config['pylons.paths']['controllers'],
121 always_scan=config['debug'])
121 always_scan=config['debug'])
122 rmap.minimization = False
122 rmap.minimization = False
123 rmap.explicit = False
123 rmap.explicit = False
124
124
125 from rhodecode.lib.utils2 import str2bool
125 from rhodecode.lib.utils2 import str2bool
126 from rhodecode.model import repo, repo_group
126 from rhodecode.model import repo, repo_group
127
127
128 def check_repo(environ, match_dict):
128 def check_repo(environ, match_dict):
129 """
129 """
130 check for valid repository for proper 404 handling
130 check for valid repository for proper 404 handling
131
131
132 :param environ:
132 :param environ:
133 :param match_dict:
133 :param match_dict:
134 """
134 """
135 repo_name = match_dict.get('repo_name')
135 repo_name = match_dict.get('repo_name')
136
136
137 if match_dict.get('f_path'):
137 if match_dict.get('f_path'):
138 # fix for multiple initial slashes that causes errors
138 # fix for multiple initial slashes that causes errors
139 match_dict['f_path'] = match_dict['f_path'].lstrip('/')
139 match_dict['f_path'] = match_dict['f_path'].lstrip('/')
140 repo_model = repo.RepoModel()
140 repo_model = repo.RepoModel()
141 by_name_match = repo_model.get_by_repo_name(repo_name)
141 by_name_match = repo_model.get_by_repo_name(repo_name)
142 # if we match quickly from database, short circuit the operation,
142 # if we match quickly from database, short circuit the operation,
143 # and validate repo based on the type.
143 # and validate repo based on the type.
144 if by_name_match:
144 if by_name_match:
145 return True
145 return True
146
146
147 by_id_match = repo_model.get_repo_by_id(repo_name)
147 by_id_match = repo_model.get_repo_by_id(repo_name)
148 if by_id_match:
148 if by_id_match:
149 repo_name = by_id_match.repo_name
149 repo_name = by_id_match.repo_name
150 match_dict['repo_name'] = repo_name
150 match_dict['repo_name'] = repo_name
151 return True
151 return True
152
152
153 return False
153 return False
154
154
155 def check_group(environ, match_dict):
155 def check_group(environ, match_dict):
156 """
156 """
157 check for valid repository group path for proper 404 handling
157 check for valid repository group path for proper 404 handling
158
158
159 :param environ:
159 :param environ:
160 :param match_dict:
160 :param match_dict:
161 """
161 """
162 repo_group_name = match_dict.get('group_name')
162 repo_group_name = match_dict.get('group_name')
163 repo_group_model = repo_group.RepoGroupModel()
163 repo_group_model = repo_group.RepoGroupModel()
164 by_name_match = repo_group_model.get_by_group_name(repo_group_name)
164 by_name_match = repo_group_model.get_by_group_name(repo_group_name)
165 if by_name_match:
165 if by_name_match:
166 return True
166 return True
167
167
168 return False
168 return False
169
169
170 def check_user_group(environ, match_dict):
170 def check_user_group(environ, match_dict):
171 """
171 """
172 check for valid user group for proper 404 handling
172 check for valid user group for proper 404 handling
173
173
174 :param environ:
174 :param environ:
175 :param match_dict:
175 :param match_dict:
176 """
176 """
177 return True
177 return True
178
178
179 def check_int(environ, match_dict):
179 def check_int(environ, match_dict):
180 return match_dict.get('id').isdigit()
180 return match_dict.get('id').isdigit()
181
181
182
182
183 #==========================================================================
183 #==========================================================================
184 # CUSTOM ROUTES HERE
184 # CUSTOM ROUTES HERE
185 #==========================================================================
185 #==========================================================================
186
186
187 # MAIN PAGE
187 # MAIN PAGE
188 rmap.connect('home', '/', controller='home', action='index')
188 rmap.connect('home', '/', controller='home', action='index')
189
189
190 # ping and pylons error test
190 # ping and pylons error test
191 rmap.connect('ping', '%s/ping' % (ADMIN_PREFIX,), controller='home', action='ping')
191 rmap.connect('ping', '%s/ping' % (ADMIN_PREFIX,), controller='home', action='ping')
192 rmap.connect('error_test', '%s/error_test' % (ADMIN_PREFIX,), controller='home', action='error_test')
192 rmap.connect('error_test', '%s/error_test' % (ADMIN_PREFIX,), controller='home', action='error_test')
193
193
194 # ADMIN REPOSITORY ROUTES
194 # ADMIN REPOSITORY ROUTES
195 with rmap.submapper(path_prefix=ADMIN_PREFIX,
195 with rmap.submapper(path_prefix=ADMIN_PREFIX,
196 controller='admin/repos') as m:
196 controller='admin/repos') as m:
197 m.connect('repos', '/repos',
197 m.connect('repos', '/repos',
198 action='create', conditions={'method': ['POST']})
198 action='create', conditions={'method': ['POST']})
199 m.connect('repos', '/repos',
199 m.connect('repos', '/repos',
200 action='index', conditions={'method': ['GET']})
200 action='index', conditions={'method': ['GET']})
201 m.connect('new_repo', '/create_repository', jsroute=True,
201 m.connect('new_repo', '/create_repository', jsroute=True,
202 action='create_repository', conditions={'method': ['GET']})
202 action='create_repository', conditions={'method': ['GET']})
203 m.connect('delete_repo', '/repos/{repo_name}',
203 m.connect('delete_repo', '/repos/{repo_name}',
204 action='delete', conditions={'method': ['DELETE']},
204 action='delete', conditions={'method': ['DELETE']},
205 requirements=URL_NAME_REQUIREMENTS)
205 requirements=URL_NAME_REQUIREMENTS)
206 m.connect('repo', '/repos/{repo_name}',
206 m.connect('repo', '/repos/{repo_name}',
207 action='show', conditions={'method': ['GET'],
207 action='show', conditions={'method': ['GET'],
208 'function': check_repo},
208 'function': check_repo},
209 requirements=URL_NAME_REQUIREMENTS)
209 requirements=URL_NAME_REQUIREMENTS)
210
210
211 # ADMIN REPOSITORY GROUPS ROUTES
211 # ADMIN REPOSITORY GROUPS ROUTES
212 with rmap.submapper(path_prefix=ADMIN_PREFIX,
212 with rmap.submapper(path_prefix=ADMIN_PREFIX,
213 controller='admin/repo_groups') as m:
213 controller='admin/repo_groups') as m:
214 m.connect('repo_groups', '/repo_groups',
214 m.connect('repo_groups', '/repo_groups',
215 action='create', conditions={'method': ['POST']})
215 action='create', conditions={'method': ['POST']})
216 m.connect('repo_groups', '/repo_groups',
216 m.connect('repo_groups', '/repo_groups',
217 action='index', conditions={'method': ['GET']})
217 action='index', conditions={'method': ['GET']})
218 m.connect('new_repo_group', '/repo_groups/new',
218 m.connect('new_repo_group', '/repo_groups/new',
219 action='new', conditions={'method': ['GET']})
219 action='new', conditions={'method': ['GET']})
220 m.connect('update_repo_group', '/repo_groups/{group_name}',
220 m.connect('update_repo_group', '/repo_groups/{group_name}',
221 action='update', conditions={'method': ['PUT'],
221 action='update', conditions={'method': ['PUT'],
222 'function': check_group},
222 'function': check_group},
223 requirements=URL_NAME_REQUIREMENTS)
223 requirements=URL_NAME_REQUIREMENTS)
224
224
225 # EXTRAS REPO GROUP ROUTES
225 # EXTRAS REPO GROUP ROUTES
226 m.connect('edit_repo_group', '/repo_groups/{group_name}/edit',
226 m.connect('edit_repo_group', '/repo_groups/{group_name}/edit',
227 action='edit',
227 action='edit',
228 conditions={'method': ['GET'], 'function': check_group},
228 conditions={'method': ['GET'], 'function': check_group},
229 requirements=URL_NAME_REQUIREMENTS)
229 requirements=URL_NAME_REQUIREMENTS)
230 m.connect('edit_repo_group', '/repo_groups/{group_name}/edit',
230 m.connect('edit_repo_group', '/repo_groups/{group_name}/edit',
231 action='edit',
231 action='edit',
232 conditions={'method': ['PUT'], 'function': check_group},
232 conditions={'method': ['PUT'], 'function': check_group},
233 requirements=URL_NAME_REQUIREMENTS)
233 requirements=URL_NAME_REQUIREMENTS)
234
234
235 m.connect('edit_repo_group_advanced', '/repo_groups/{group_name}/edit/advanced',
235 m.connect('edit_repo_group_advanced', '/repo_groups/{group_name}/edit/advanced',
236 action='edit_repo_group_advanced',
236 action='edit_repo_group_advanced',
237 conditions={'method': ['GET'], 'function': check_group},
237 conditions={'method': ['GET'], 'function': check_group},
238 requirements=URL_NAME_REQUIREMENTS)
238 requirements=URL_NAME_REQUIREMENTS)
239 m.connect('edit_repo_group_advanced', '/repo_groups/{group_name}/edit/advanced',
239 m.connect('edit_repo_group_advanced', '/repo_groups/{group_name}/edit/advanced',
240 action='edit_repo_group_advanced',
240 action='edit_repo_group_advanced',
241 conditions={'method': ['PUT'], 'function': check_group},
241 conditions={'method': ['PUT'], 'function': check_group},
242 requirements=URL_NAME_REQUIREMENTS)
242 requirements=URL_NAME_REQUIREMENTS)
243
243
244 m.connect('edit_repo_group_perms', '/repo_groups/{group_name}/edit/permissions',
244 m.connect('edit_repo_group_perms', '/repo_groups/{group_name}/edit/permissions',
245 action='edit_repo_group_perms',
245 action='edit_repo_group_perms',
246 conditions={'method': ['GET'], 'function': check_group},
246 conditions={'method': ['GET'], 'function': check_group},
247 requirements=URL_NAME_REQUIREMENTS)
247 requirements=URL_NAME_REQUIREMENTS)
248 m.connect('edit_repo_group_perms', '/repo_groups/{group_name}/edit/permissions',
248 m.connect('edit_repo_group_perms', '/repo_groups/{group_name}/edit/permissions',
249 action='update_perms',
249 action='update_perms',
250 conditions={'method': ['PUT'], 'function': check_group},
250 conditions={'method': ['PUT'], 'function': check_group},
251 requirements=URL_NAME_REQUIREMENTS)
251 requirements=URL_NAME_REQUIREMENTS)
252
252
253 m.connect('delete_repo_group', '/repo_groups/{group_name}',
253 m.connect('delete_repo_group', '/repo_groups/{group_name}',
254 action='delete', conditions={'method': ['DELETE'],
254 action='delete', conditions={'method': ['DELETE'],
255 'function': check_group},
255 'function': check_group},
256 requirements=URL_NAME_REQUIREMENTS)
256 requirements=URL_NAME_REQUIREMENTS)
257
257
258 # ADMIN USER ROUTES
258 # ADMIN USER ROUTES
259 with rmap.submapper(path_prefix=ADMIN_PREFIX,
259 with rmap.submapper(path_prefix=ADMIN_PREFIX,
260 controller='admin/users') as m:
260 controller='admin/users') as m:
261 m.connect('users', '/users',
261 m.connect('users', '/users',
262 action='create', conditions={'method': ['POST']})
262 action='create', conditions={'method': ['POST']})
263 m.connect('new_user', '/users/new',
263 m.connect('new_user', '/users/new',
264 action='new', conditions={'method': ['GET']})
264 action='new', conditions={'method': ['GET']})
265 m.connect('update_user', '/users/{user_id}',
265 m.connect('update_user', '/users/{user_id}',
266 action='update', conditions={'method': ['PUT']})
266 action='update', conditions={'method': ['PUT']})
267 m.connect('delete_user', '/users/{user_id}',
267 m.connect('delete_user', '/users/{user_id}',
268 action='delete', conditions={'method': ['DELETE']})
268 action='delete', conditions={'method': ['DELETE']})
269 m.connect('edit_user', '/users/{user_id}/edit',
269 m.connect('edit_user', '/users/{user_id}/edit',
270 action='edit', conditions={'method': ['GET']}, jsroute=True)
270 action='edit', conditions={'method': ['GET']}, jsroute=True)
271 m.connect('user', '/users/{user_id}',
271 m.connect('user', '/users/{user_id}',
272 action='show', conditions={'method': ['GET']})
272 action='show', conditions={'method': ['GET']})
273 m.connect('force_password_reset_user', '/users/{user_id}/password_reset',
273 m.connect('force_password_reset_user', '/users/{user_id}/password_reset',
274 action='reset_password', conditions={'method': ['POST']})
274 action='reset_password', conditions={'method': ['POST']})
275 m.connect('create_personal_repo_group', '/users/{user_id}/create_repo_group',
275 m.connect('create_personal_repo_group', '/users/{user_id}/create_repo_group',
276 action='create_personal_repo_group', conditions={'method': ['POST']})
276 action='create_personal_repo_group', conditions={'method': ['POST']})
277
277
278 # EXTRAS USER ROUTES
278 # EXTRAS USER ROUTES
279 m.connect('edit_user_advanced', '/users/{user_id}/edit/advanced',
279 m.connect('edit_user_advanced', '/users/{user_id}/edit/advanced',
280 action='edit_advanced', conditions={'method': ['GET']})
280 action='edit_advanced', conditions={'method': ['GET']})
281 m.connect('edit_user_advanced', '/users/{user_id}/edit/advanced',
281 m.connect('edit_user_advanced', '/users/{user_id}/edit/advanced',
282 action='update_advanced', conditions={'method': ['PUT']})
282 action='update_advanced', conditions={'method': ['PUT']})
283
283
284 m.connect('edit_user_global_perms', '/users/{user_id}/edit/global_permissions',
284 m.connect('edit_user_global_perms', '/users/{user_id}/edit/global_permissions',
285 action='edit_global_perms', conditions={'method': ['GET']})
285 action='edit_global_perms', conditions={'method': ['GET']})
286 m.connect('edit_user_global_perms', '/users/{user_id}/edit/global_permissions',
286 m.connect('edit_user_global_perms', '/users/{user_id}/edit/global_permissions',
287 action='update_global_perms', conditions={'method': ['PUT']})
287 action='update_global_perms', conditions={'method': ['PUT']})
288
288
289 m.connect('edit_user_perms_summary', '/users/{user_id}/edit/permissions_summary',
289 m.connect('edit_user_perms_summary', '/users/{user_id}/edit/permissions_summary',
290 action='edit_perms_summary', conditions={'method': ['GET']})
290 action='edit_perms_summary', conditions={'method': ['GET']})
291
291
292 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
292 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
293 action='edit_emails', conditions={'method': ['GET']})
293 action='edit_emails', conditions={'method': ['GET']})
294 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
294 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
295 action='add_email', conditions={'method': ['PUT']})
295 action='add_email', conditions={'method': ['PUT']})
296 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
296 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
297 action='delete_email', conditions={'method': ['DELETE']})
297 action='delete_email', conditions={'method': ['DELETE']})
298
298
299 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
299 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
300 action='edit_ips', conditions={'method': ['GET']})
300 action='edit_ips', conditions={'method': ['GET']})
301 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
301 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
302 action='add_ip', conditions={'method': ['PUT']})
302 action='add_ip', conditions={'method': ['PUT']})
303 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
303 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
304 action='delete_ip', conditions={'method': ['DELETE']})
304 action='delete_ip', conditions={'method': ['DELETE']})
305
305
306 # ADMIN USER GROUPS REST ROUTES
306 # ADMIN USER GROUPS REST ROUTES
307 with rmap.submapper(path_prefix=ADMIN_PREFIX,
307 with rmap.submapper(path_prefix=ADMIN_PREFIX,
308 controller='admin/user_groups') as m:
308 controller='admin/user_groups') as m:
309 m.connect('users_groups', '/user_groups',
309 m.connect('users_groups', '/user_groups',
310 action='create', conditions={'method': ['POST']})
310 action='create', conditions={'method': ['POST']})
311 m.connect('users_groups', '/user_groups',
311 m.connect('users_groups', '/user_groups',
312 action='index', conditions={'method': ['GET']})
312 action='index', conditions={'method': ['GET']})
313 m.connect('new_users_group', '/user_groups/new',
313 m.connect('new_users_group', '/user_groups/new',
314 action='new', conditions={'method': ['GET']})
314 action='new', conditions={'method': ['GET']})
315 m.connect('update_users_group', '/user_groups/{user_group_id}',
315 m.connect('update_users_group', '/user_groups/{user_group_id}',
316 action='update', conditions={'method': ['PUT']})
316 action='update', conditions={'method': ['PUT']})
317 m.connect('delete_users_group', '/user_groups/{user_group_id}',
317 m.connect('delete_users_group', '/user_groups/{user_group_id}',
318 action='delete', conditions={'method': ['DELETE']})
318 action='delete', conditions={'method': ['DELETE']})
319 m.connect('edit_users_group', '/user_groups/{user_group_id}/edit',
319 m.connect('edit_users_group', '/user_groups/{user_group_id}/edit',
320 action='edit', conditions={'method': ['GET']},
320 action='edit', conditions={'method': ['GET']},
321 function=check_user_group)
321 function=check_user_group)
322
322
323 # EXTRAS USER GROUP ROUTES
323 # EXTRAS USER GROUP ROUTES
324 m.connect('edit_user_group_global_perms',
324 m.connect('edit_user_group_global_perms',
325 '/user_groups/{user_group_id}/edit/global_permissions',
325 '/user_groups/{user_group_id}/edit/global_permissions',
326 action='edit_global_perms', conditions={'method': ['GET']})
326 action='edit_global_perms', conditions={'method': ['GET']})
327 m.connect('edit_user_group_global_perms',
327 m.connect('edit_user_group_global_perms',
328 '/user_groups/{user_group_id}/edit/global_permissions',
328 '/user_groups/{user_group_id}/edit/global_permissions',
329 action='update_global_perms', conditions={'method': ['PUT']})
329 action='update_global_perms', conditions={'method': ['PUT']})
330 m.connect('edit_user_group_perms_summary',
330 m.connect('edit_user_group_perms_summary',
331 '/user_groups/{user_group_id}/edit/permissions_summary',
331 '/user_groups/{user_group_id}/edit/permissions_summary',
332 action='edit_perms_summary', conditions={'method': ['GET']})
332 action='edit_perms_summary', conditions={'method': ['GET']})
333
333
334 m.connect('edit_user_group_perms',
334 m.connect('edit_user_group_perms',
335 '/user_groups/{user_group_id}/edit/permissions',
335 '/user_groups/{user_group_id}/edit/permissions',
336 action='edit_perms', conditions={'method': ['GET']})
336 action='edit_perms', conditions={'method': ['GET']})
337 m.connect('edit_user_group_perms',
337 m.connect('edit_user_group_perms',
338 '/user_groups/{user_group_id}/edit/permissions',
338 '/user_groups/{user_group_id}/edit/permissions',
339 action='update_perms', conditions={'method': ['PUT']})
339 action='update_perms', conditions={'method': ['PUT']})
340
340
341 m.connect('edit_user_group_advanced',
341 m.connect('edit_user_group_advanced',
342 '/user_groups/{user_group_id}/edit/advanced',
342 '/user_groups/{user_group_id}/edit/advanced',
343 action='edit_advanced', conditions={'method': ['GET']})
343 action='edit_advanced', conditions={'method': ['GET']})
344
344
345 m.connect('edit_user_group_advanced_sync',
345 m.connect('edit_user_group_advanced_sync',
346 '/user_groups/{user_group_id}/edit/advanced/sync',
346 '/user_groups/{user_group_id}/edit/advanced/sync',
347 action='edit_advanced_set_synchronization', conditions={'method': ['POST']})
347 action='edit_advanced_set_synchronization', conditions={'method': ['POST']})
348
348
349 m.connect('edit_user_group_members',
349 m.connect('edit_user_group_members',
350 '/user_groups/{user_group_id}/edit/members', jsroute=True,
350 '/user_groups/{user_group_id}/edit/members', jsroute=True,
351 action='user_group_members', conditions={'method': ['GET']})
351 action='user_group_members', conditions={'method': ['GET']})
352
352
353 # ADMIN PERMISSIONS ROUTES
353 # ADMIN PERMISSIONS ROUTES
354 with rmap.submapper(path_prefix=ADMIN_PREFIX,
354 with rmap.submapper(path_prefix=ADMIN_PREFIX,
355 controller='admin/permissions') as m:
355 controller='admin/permissions') as m:
356 m.connect('admin_permissions_application', '/permissions/application',
356 m.connect('admin_permissions_application', '/permissions/application',
357 action='permission_application_update', conditions={'method': ['POST']})
357 action='permission_application_update', conditions={'method': ['POST']})
358 m.connect('admin_permissions_application', '/permissions/application',
358 m.connect('admin_permissions_application', '/permissions/application',
359 action='permission_application', conditions={'method': ['GET']})
359 action='permission_application', conditions={'method': ['GET']})
360
360
361 m.connect('admin_permissions_global', '/permissions/global',
361 m.connect('admin_permissions_global', '/permissions/global',
362 action='permission_global_update', conditions={'method': ['POST']})
362 action='permission_global_update', conditions={'method': ['POST']})
363 m.connect('admin_permissions_global', '/permissions/global',
363 m.connect('admin_permissions_global', '/permissions/global',
364 action='permission_global', conditions={'method': ['GET']})
364 action='permission_global', conditions={'method': ['GET']})
365
365
366 m.connect('admin_permissions_object', '/permissions/object',
366 m.connect('admin_permissions_object', '/permissions/object',
367 action='permission_objects_update', conditions={'method': ['POST']})
367 action='permission_objects_update', conditions={'method': ['POST']})
368 m.connect('admin_permissions_object', '/permissions/object',
368 m.connect('admin_permissions_object', '/permissions/object',
369 action='permission_objects', conditions={'method': ['GET']})
369 action='permission_objects', conditions={'method': ['GET']})
370
370
371 m.connect('admin_permissions_ips', '/permissions/ips',
371 m.connect('admin_permissions_ips', '/permissions/ips',
372 action='permission_ips', conditions={'method': ['POST']})
372 action='permission_ips', conditions={'method': ['POST']})
373 m.connect('admin_permissions_ips', '/permissions/ips',
373 m.connect('admin_permissions_ips', '/permissions/ips',
374 action='permission_ips', conditions={'method': ['GET']})
374 action='permission_ips', conditions={'method': ['GET']})
375
375
376 m.connect('admin_permissions_overview', '/permissions/overview',
376 m.connect('admin_permissions_overview', '/permissions/overview',
377 action='permission_perms', conditions={'method': ['GET']})
377 action='permission_perms', conditions={'method': ['GET']})
378
378
379 # ADMIN DEFAULTS REST ROUTES
379 # ADMIN DEFAULTS REST ROUTES
380 with rmap.submapper(path_prefix=ADMIN_PREFIX,
380 with rmap.submapper(path_prefix=ADMIN_PREFIX,
381 controller='admin/defaults') as m:
381 controller='admin/defaults') as m:
382 m.connect('admin_defaults_repositories', '/defaults/repositories',
382 m.connect('admin_defaults_repositories', '/defaults/repositories',
383 action='update_repository_defaults', conditions={'method': ['POST']})
383 action='update_repository_defaults', conditions={'method': ['POST']})
384 m.connect('admin_defaults_repositories', '/defaults/repositories',
384 m.connect('admin_defaults_repositories', '/defaults/repositories',
385 action='index', conditions={'method': ['GET']})
385 action='index', conditions={'method': ['GET']})
386
386
387 # ADMIN DEBUG STYLE ROUTES
387 # ADMIN DEBUG STYLE ROUTES
388 if str2bool(config.get('debug_style')):
388 if str2bool(config.get('debug_style')):
389 with rmap.submapper(path_prefix=ADMIN_PREFIX + '/debug_style',
389 with rmap.submapper(path_prefix=ADMIN_PREFIX + '/debug_style',
390 controller='debug_style') as m:
390 controller='debug_style') as m:
391 m.connect('debug_style_home', '',
391 m.connect('debug_style_home', '',
392 action='index', conditions={'method': ['GET']})
392 action='index', conditions={'method': ['GET']})
393 m.connect('debug_style_template', '/t/{t_path}',
393 m.connect('debug_style_template', '/t/{t_path}',
394 action='template', conditions={'method': ['GET']})
394 action='template', conditions={'method': ['GET']})
395
395
396 # ADMIN SETTINGS ROUTES
396 # ADMIN SETTINGS ROUTES
397 with rmap.submapper(path_prefix=ADMIN_PREFIX,
397 with rmap.submapper(path_prefix=ADMIN_PREFIX,
398 controller='admin/settings') as m:
398 controller='admin/settings') as m:
399
399
400 # default
400 # default
401 m.connect('admin_settings', '/settings',
401 m.connect('admin_settings', '/settings',
402 action='settings_global_update',
402 action='settings_global_update',
403 conditions={'method': ['POST']})
403 conditions={'method': ['POST']})
404 m.connect('admin_settings', '/settings',
404 m.connect('admin_settings', '/settings',
405 action='settings_global', conditions={'method': ['GET']})
405 action='settings_global', conditions={'method': ['GET']})
406
406
407 m.connect('admin_settings_vcs', '/settings/vcs',
407 m.connect('admin_settings_vcs', '/settings/vcs',
408 action='settings_vcs_update',
408 action='settings_vcs_update',
409 conditions={'method': ['POST']})
409 conditions={'method': ['POST']})
410 m.connect('admin_settings_vcs', '/settings/vcs',
410 m.connect('admin_settings_vcs', '/settings/vcs',
411 action='settings_vcs',
411 action='settings_vcs',
412 conditions={'method': ['GET']})
412 conditions={'method': ['GET']})
413 m.connect('admin_settings_vcs', '/settings/vcs',
413 m.connect('admin_settings_vcs', '/settings/vcs',
414 action='delete_svn_pattern',
414 action='delete_svn_pattern',
415 conditions={'method': ['DELETE']})
415 conditions={'method': ['DELETE']})
416
416
417 m.connect('admin_settings_mapping', '/settings/mapping',
417 m.connect('admin_settings_mapping', '/settings/mapping',
418 action='settings_mapping_update',
418 action='settings_mapping_update',
419 conditions={'method': ['POST']})
419 conditions={'method': ['POST']})
420 m.connect('admin_settings_mapping', '/settings/mapping',
420 m.connect('admin_settings_mapping', '/settings/mapping',
421 action='settings_mapping', conditions={'method': ['GET']})
421 action='settings_mapping', conditions={'method': ['GET']})
422
422
423 m.connect('admin_settings_global', '/settings/global',
423 m.connect('admin_settings_global', '/settings/global',
424 action='settings_global_update',
424 action='settings_global_update',
425 conditions={'method': ['POST']})
425 conditions={'method': ['POST']})
426 m.connect('admin_settings_global', '/settings/global',
426 m.connect('admin_settings_global', '/settings/global',
427 action='settings_global', conditions={'method': ['GET']})
427 action='settings_global', conditions={'method': ['GET']})
428
428
429 m.connect('admin_settings_visual', '/settings/visual',
429 m.connect('admin_settings_visual', '/settings/visual',
430 action='settings_visual_update',
430 action='settings_visual_update',
431 conditions={'method': ['POST']})
431 conditions={'method': ['POST']})
432 m.connect('admin_settings_visual', '/settings/visual',
432 m.connect('admin_settings_visual', '/settings/visual',
433 action='settings_visual', conditions={'method': ['GET']})
433 action='settings_visual', conditions={'method': ['GET']})
434
434
435 m.connect('admin_settings_issuetracker',
435 m.connect('admin_settings_issuetracker',
436 '/settings/issue-tracker', action='settings_issuetracker',
436 '/settings/issue-tracker', action='settings_issuetracker',
437 conditions={'method': ['GET']})
437 conditions={'method': ['GET']})
438 m.connect('admin_settings_issuetracker_save',
438 m.connect('admin_settings_issuetracker_save',
439 '/settings/issue-tracker/save',
439 '/settings/issue-tracker/save',
440 action='settings_issuetracker_save',
440 action='settings_issuetracker_save',
441 conditions={'method': ['POST']})
441 conditions={'method': ['POST']})
442 m.connect('admin_issuetracker_test', '/settings/issue-tracker/test',
442 m.connect('admin_issuetracker_test', '/settings/issue-tracker/test',
443 action='settings_issuetracker_test',
443 action='settings_issuetracker_test',
444 conditions={'method': ['POST']})
444 conditions={'method': ['POST']})
445 m.connect('admin_issuetracker_delete',
445 m.connect('admin_issuetracker_delete',
446 '/settings/issue-tracker/delete',
446 '/settings/issue-tracker/delete',
447 action='settings_issuetracker_delete',
447 action='settings_issuetracker_delete',
448 conditions={'method': ['DELETE']})
448 conditions={'method': ['DELETE']})
449
449
450 m.connect('admin_settings_email', '/settings/email',
450 m.connect('admin_settings_email', '/settings/email',
451 action='settings_email_update',
451 action='settings_email_update',
452 conditions={'method': ['POST']})
452 conditions={'method': ['POST']})
453 m.connect('admin_settings_email', '/settings/email',
453 m.connect('admin_settings_email', '/settings/email',
454 action='settings_email', conditions={'method': ['GET']})
454 action='settings_email', conditions={'method': ['GET']})
455
455
456 m.connect('admin_settings_hooks', '/settings/hooks',
456 m.connect('admin_settings_hooks', '/settings/hooks',
457 action='settings_hooks_update',
457 action='settings_hooks_update',
458 conditions={'method': ['POST', 'DELETE']})
458 conditions={'method': ['POST', 'DELETE']})
459 m.connect('admin_settings_hooks', '/settings/hooks',
459 m.connect('admin_settings_hooks', '/settings/hooks',
460 action='settings_hooks', conditions={'method': ['GET']})
460 action='settings_hooks', conditions={'method': ['GET']})
461
461
462 m.connect('admin_settings_search', '/settings/search',
462 m.connect('admin_settings_search', '/settings/search',
463 action='settings_search', conditions={'method': ['GET']})
463 action='settings_search', conditions={'method': ['GET']})
464
464
465 m.connect('admin_settings_supervisor', '/settings/supervisor',
465 m.connect('admin_settings_supervisor', '/settings/supervisor',
466 action='settings_supervisor', conditions={'method': ['GET']})
466 action='settings_supervisor', conditions={'method': ['GET']})
467 m.connect('admin_settings_supervisor_log', '/settings/supervisor/{procid}/log',
467 m.connect('admin_settings_supervisor_log', '/settings/supervisor/{procid}/log',
468 action='settings_supervisor_log', conditions={'method': ['GET']})
468 action='settings_supervisor_log', conditions={'method': ['GET']})
469
469
470 m.connect('admin_settings_labs', '/settings/labs',
470 m.connect('admin_settings_labs', '/settings/labs',
471 action='settings_labs_update',
471 action='settings_labs_update',
472 conditions={'method': ['POST']})
472 conditions={'method': ['POST']})
473 m.connect('admin_settings_labs', '/settings/labs',
473 m.connect('admin_settings_labs', '/settings/labs',
474 action='settings_labs', conditions={'method': ['GET']})
474 action='settings_labs', conditions={'method': ['GET']})
475
475
476 # ADMIN MY ACCOUNT
476 # ADMIN MY ACCOUNT
477 with rmap.submapper(path_prefix=ADMIN_PREFIX,
477 with rmap.submapper(path_prefix=ADMIN_PREFIX,
478 controller='admin/my_account') as m:
478 controller='admin/my_account') as m:
479
479
480 m.connect('my_account_edit', '/my_account/edit',
480 m.connect('my_account_edit', '/my_account/edit',
481 action='my_account_edit', conditions={'method': ['GET']})
481 action='my_account_edit', conditions={'method': ['GET']})
482 m.connect('my_account', '/my_account/update',
482 m.connect('my_account', '/my_account/update',
483 action='my_account_update', conditions={'method': ['POST']})
483 action='my_account_update', conditions={'method': ['POST']})
484
484
485 # NOTE(marcink): this needs to be kept for password force flag to be
485 # NOTE(marcink): this needs to be kept for password force flag to be
486 # handler, remove after migration to pyramid
486 # handler, remove after migration to pyramid
487 m.connect('my_account_password', '/my_account/password',
487 m.connect('my_account_password', '/my_account/password',
488 action='my_account_password', conditions={'method': ['GET']})
488 action='my_account_password', conditions={'method': ['GET']})
489
489
490 m.connect('my_account_repos', '/my_account/repos',
490 m.connect('my_account_repos', '/my_account/repos',
491 action='my_account_repos', conditions={'method': ['GET']})
491 action='my_account_repos', conditions={'method': ['GET']})
492
492
493 m.connect('my_account_watched', '/my_account/watched',
493 m.connect('my_account_watched', '/my_account/watched',
494 action='my_account_watched', conditions={'method': ['GET']})
494 action='my_account_watched', conditions={'method': ['GET']})
495
495
496 m.connect('my_account_pullrequests', '/my_account/pull_requests',
496 m.connect('my_account_pullrequests', '/my_account/pull_requests',
497 action='my_account_pullrequests', conditions={'method': ['GET']})
497 action='my_account_pullrequests', conditions={'method': ['GET']})
498
498
499 m.connect('my_account_perms', '/my_account/perms',
499 m.connect('my_account_perms', '/my_account/perms',
500 action='my_account_perms', conditions={'method': ['GET']})
500 action='my_account_perms', conditions={'method': ['GET']})
501
501
502 m.connect('my_account_emails', '/my_account/emails',
502 m.connect('my_account_emails', '/my_account/emails',
503 action='my_account_emails', conditions={'method': ['GET']})
503 action='my_account_emails', conditions={'method': ['GET']})
504 m.connect('my_account_emails', '/my_account/emails',
504 m.connect('my_account_emails', '/my_account/emails',
505 action='my_account_emails_add', conditions={'method': ['POST']})
505 action='my_account_emails_add', conditions={'method': ['POST']})
506 m.connect('my_account_emails', '/my_account/emails',
506 m.connect('my_account_emails', '/my_account/emails',
507 action='my_account_emails_delete', conditions={'method': ['DELETE']})
507 action='my_account_emails_delete', conditions={'method': ['DELETE']})
508
508
509 m.connect('my_account_notifications', '/my_account/notifications',
509 m.connect('my_account_notifications', '/my_account/notifications',
510 action='my_notifications',
510 action='my_notifications',
511 conditions={'method': ['GET']})
511 conditions={'method': ['GET']})
512 m.connect('my_account_notifications_toggle_visibility',
512 m.connect('my_account_notifications_toggle_visibility',
513 '/my_account/toggle_visibility',
513 '/my_account/toggle_visibility',
514 action='my_notifications_toggle_visibility',
514 action='my_notifications_toggle_visibility',
515 conditions={'method': ['POST']})
515 conditions={'method': ['POST']})
516
516
517 # NOTIFICATION REST ROUTES
517 # NOTIFICATION REST ROUTES
518 with rmap.submapper(path_prefix=ADMIN_PREFIX,
518 with rmap.submapper(path_prefix=ADMIN_PREFIX,
519 controller='admin/notifications') as m:
519 controller='admin/notifications') as m:
520 m.connect('notifications', '/notifications',
520 m.connect('notifications', '/notifications',
521 action='index', conditions={'method': ['GET']})
521 action='index', conditions={'method': ['GET']})
522 m.connect('notifications_mark_all_read', '/notifications/mark_all_read',
522 m.connect('notifications_mark_all_read', '/notifications/mark_all_read',
523 action='mark_all_read', conditions={'method': ['POST']})
523 action='mark_all_read', conditions={'method': ['POST']})
524 m.connect('/notifications/{notification_id}',
524 m.connect('/notifications/{notification_id}',
525 action='update', conditions={'method': ['PUT']})
525 action='update', conditions={'method': ['PUT']})
526 m.connect('/notifications/{notification_id}',
526 m.connect('/notifications/{notification_id}',
527 action='delete', conditions={'method': ['DELETE']})
527 action='delete', conditions={'method': ['DELETE']})
528 m.connect('notification', '/notifications/{notification_id}',
528 m.connect('notification', '/notifications/{notification_id}',
529 action='show', conditions={'method': ['GET']})
529 action='show', conditions={'method': ['GET']})
530
530
531 # ADMIN GIST
531 # ADMIN GIST
532 with rmap.submapper(path_prefix=ADMIN_PREFIX,
532 with rmap.submapper(path_prefix=ADMIN_PREFIX,
533 controller='admin/gists') as m:
533 controller='admin/gists') as m:
534 m.connect('gists', '/gists',
534 m.connect('gists', '/gists',
535 action='create', conditions={'method': ['POST']})
535 action='create', conditions={'method': ['POST']})
536 m.connect('gists', '/gists', jsroute=True,
536 m.connect('gists', '/gists', jsroute=True,
537 action='index', conditions={'method': ['GET']})
537 action='index', conditions={'method': ['GET']})
538 m.connect('new_gist', '/gists/new', jsroute=True,
538 m.connect('new_gist', '/gists/new', jsroute=True,
539 action='new', conditions={'method': ['GET']})
539 action='new', conditions={'method': ['GET']})
540
540
541 m.connect('/gists/{gist_id}',
541 m.connect('/gists/{gist_id}',
542 action='delete', conditions={'method': ['DELETE']})
542 action='delete', conditions={'method': ['DELETE']})
543 m.connect('edit_gist', '/gists/{gist_id}/edit',
543 m.connect('edit_gist', '/gists/{gist_id}/edit',
544 action='edit_form', conditions={'method': ['GET']})
544 action='edit_form', conditions={'method': ['GET']})
545 m.connect('edit_gist', '/gists/{gist_id}/edit',
545 m.connect('edit_gist', '/gists/{gist_id}/edit',
546 action='edit', conditions={'method': ['POST']})
546 action='edit', conditions={'method': ['POST']})
547 m.connect(
547 m.connect(
548 'edit_gist_check_revision', '/gists/{gist_id}/edit/check_revision',
548 'edit_gist_check_revision', '/gists/{gist_id}/edit/check_revision',
549 action='check_revision', conditions={'method': ['GET']})
549 action='check_revision', conditions={'method': ['GET']})
550
550
551 m.connect('gist', '/gists/{gist_id}',
551 m.connect('gist', '/gists/{gist_id}',
552 action='show', conditions={'method': ['GET']})
552 action='show', conditions={'method': ['GET']})
553 m.connect('gist_rev', '/gists/{gist_id}/{revision}',
553 m.connect('gist_rev', '/gists/{gist_id}/{revision}',
554 revision='tip',
554 revision='tip',
555 action='show', conditions={'method': ['GET']})
555 action='show', conditions={'method': ['GET']})
556 m.connect('formatted_gist', '/gists/{gist_id}/{revision}/{format}',
556 m.connect('formatted_gist', '/gists/{gist_id}/{revision}/{format}',
557 revision='tip',
557 revision='tip',
558 action='show', conditions={'method': ['GET']})
558 action='show', conditions={'method': ['GET']})
559 m.connect('formatted_gist_file', '/gists/{gist_id}/{revision}/{format}/{f_path}',
559 m.connect('formatted_gist_file', '/gists/{gist_id}/{revision}/{format}/{f_path}',
560 revision='tip',
560 revision='tip',
561 action='show', conditions={'method': ['GET']},
561 action='show', conditions={'method': ['GET']},
562 requirements=URL_NAME_REQUIREMENTS)
562 requirements=URL_NAME_REQUIREMENTS)
563
563
564 # ADMIN MAIN PAGES
565 with rmap.submapper(path_prefix=ADMIN_PREFIX,
566 controller='admin/admin') as m:
567 m.connect('admin_home', '', action='index')
568 m.connect('admin_add_repo', '/add_repo/{new_repo:[a-z0-9\. _-]*}',
569 action='add_repo')
570 m.connect(
571 'pull_requests_global_0', '/pull_requests/{pull_request_id:[0-9]+}',
572 action='pull_requests')
573 m.connect(
574 'pull_requests_global_1', '/pull-requests/{pull_request_id:[0-9]+}',
575 action='pull_requests')
576 m.connect(
577 'pull_requests_global', '/pull-request/{pull_request_id:[0-9]+}',
578 action='pull_requests')
579
580 # USER JOURNAL
564 # USER JOURNAL
581 rmap.connect('journal', '%s/journal' % (ADMIN_PREFIX,),
565 rmap.connect('journal', '%s/journal' % (ADMIN_PREFIX,),
582 controller='journal', action='index')
566 controller='journal', action='index')
583 rmap.connect('journal_rss', '%s/journal/rss' % (ADMIN_PREFIX,),
567 rmap.connect('journal_rss', '%s/journal/rss' % (ADMIN_PREFIX,),
584 controller='journal', action='journal_rss')
568 controller='journal', action='journal_rss')
585 rmap.connect('journal_atom', '%s/journal/atom' % (ADMIN_PREFIX,),
569 rmap.connect('journal_atom', '%s/journal/atom' % (ADMIN_PREFIX,),
586 controller='journal', action='journal_atom')
570 controller='journal', action='journal_atom')
587
571
588 rmap.connect('public_journal', '%s/public_journal' % (ADMIN_PREFIX,),
572 rmap.connect('public_journal', '%s/public_journal' % (ADMIN_PREFIX,),
589 controller='journal', action='public_journal')
573 controller='journal', action='public_journal')
590
574
591 rmap.connect('public_journal_rss', '%s/public_journal/rss' % (ADMIN_PREFIX,),
575 rmap.connect('public_journal_rss', '%s/public_journal/rss' % (ADMIN_PREFIX,),
592 controller='journal', action='public_journal_rss')
576 controller='journal', action='public_journal_rss')
593
577
594 rmap.connect('public_journal_rss_old', '%s/public_journal_rss' % (ADMIN_PREFIX,),
578 rmap.connect('public_journal_rss_old', '%s/public_journal_rss' % (ADMIN_PREFIX,),
595 controller='journal', action='public_journal_rss')
579 controller='journal', action='public_journal_rss')
596
580
597 rmap.connect('public_journal_atom',
581 rmap.connect('public_journal_atom',
598 '%s/public_journal/atom' % (ADMIN_PREFIX,), controller='journal',
582 '%s/public_journal/atom' % (ADMIN_PREFIX,), controller='journal',
599 action='public_journal_atom')
583 action='public_journal_atom')
600
584
601 rmap.connect('public_journal_atom_old',
585 rmap.connect('public_journal_atom_old',
602 '%s/public_journal_atom' % (ADMIN_PREFIX,), controller='journal',
586 '%s/public_journal_atom' % (ADMIN_PREFIX,), controller='journal',
603 action='public_journal_atom')
587 action='public_journal_atom')
604
588
605 rmap.connect('toggle_following', '%s/toggle_following' % (ADMIN_PREFIX,),
589 rmap.connect('toggle_following', '%s/toggle_following' % (ADMIN_PREFIX,),
606 controller='journal', action='toggle_following', jsroute=True,
590 controller='journal', action='toggle_following', jsroute=True,
607 conditions={'method': ['POST']})
591 conditions={'method': ['POST']})
608
592
609 # FEEDS
593 # FEEDS
610 rmap.connect('rss_feed_home', '/{repo_name}/feed/rss',
594 rmap.connect('rss_feed_home', '/{repo_name}/feed/rss',
611 controller='feed', action='rss',
595 controller='feed', action='rss',
612 conditions={'function': check_repo},
596 conditions={'function': check_repo},
613 requirements=URL_NAME_REQUIREMENTS)
597 requirements=URL_NAME_REQUIREMENTS)
614
598
615 rmap.connect('atom_feed_home', '/{repo_name}/feed/atom',
599 rmap.connect('atom_feed_home', '/{repo_name}/feed/atom',
616 controller='feed', action='atom',
600 controller='feed', action='atom',
617 conditions={'function': check_repo},
601 conditions={'function': check_repo},
618 requirements=URL_NAME_REQUIREMENTS)
602 requirements=URL_NAME_REQUIREMENTS)
619
603
620 #==========================================================================
604 #==========================================================================
621 # REPOSITORY ROUTES
605 # REPOSITORY ROUTES
622 #==========================================================================
606 #==========================================================================
623
607
624 rmap.connect('repo_creating_home', '/{repo_name}/repo_creating',
608 rmap.connect('repo_creating_home', '/{repo_name}/repo_creating',
625 controller='admin/repos', action='repo_creating',
609 controller='admin/repos', action='repo_creating',
626 requirements=URL_NAME_REQUIREMENTS)
610 requirements=URL_NAME_REQUIREMENTS)
627 rmap.connect('repo_check_home', '/{repo_name}/crepo_check',
611 rmap.connect('repo_check_home', '/{repo_name}/crepo_check',
628 controller='admin/repos', action='repo_check',
612 controller='admin/repos', action='repo_check',
629 requirements=URL_NAME_REQUIREMENTS)
613 requirements=URL_NAME_REQUIREMENTS)
630
614
631 rmap.connect('repo_stats', '/{repo_name}/repo_stats/{commit_id}',
615 rmap.connect('repo_stats', '/{repo_name}/repo_stats/{commit_id}',
632 controller='summary', action='repo_stats',
616 controller='summary', action='repo_stats',
633 conditions={'function': check_repo},
617 conditions={'function': check_repo},
634 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
618 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
635
619
636 rmap.connect('repo_refs_data', '/{repo_name}/refs-data',
620 rmap.connect('repo_refs_data', '/{repo_name}/refs-data',
637 controller='summary', action='repo_refs_data',
621 controller='summary', action='repo_refs_data',
638 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
622 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
639 rmap.connect('repo_refs_changelog_data', '/{repo_name}/refs-data-changelog',
623 rmap.connect('repo_refs_changelog_data', '/{repo_name}/refs-data-changelog',
640 controller='summary', action='repo_refs_changelog_data',
624 controller='summary', action='repo_refs_changelog_data',
641 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
625 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
642 rmap.connect('repo_default_reviewers_data', '/{repo_name}/default-reviewers',
626 rmap.connect('repo_default_reviewers_data', '/{repo_name}/default-reviewers',
643 controller='summary', action='repo_default_reviewers_data',
627 controller='summary', action='repo_default_reviewers_data',
644 jsroute=True, requirements=URL_NAME_REQUIREMENTS)
628 jsroute=True, requirements=URL_NAME_REQUIREMENTS)
645
629
646 rmap.connect('changeset_home', '/{repo_name}/changeset/{revision}',
630 rmap.connect('changeset_home', '/{repo_name}/changeset/{revision}',
647 controller='changeset', revision='tip',
631 controller='changeset', revision='tip',
648 conditions={'function': check_repo},
632 conditions={'function': check_repo},
649 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
633 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
650 rmap.connect('changeset_children', '/{repo_name}/changeset_children/{revision}',
634 rmap.connect('changeset_children', '/{repo_name}/changeset_children/{revision}',
651 controller='changeset', revision='tip', action='changeset_children',
635 controller='changeset', revision='tip', action='changeset_children',
652 conditions={'function': check_repo},
636 conditions={'function': check_repo},
653 requirements=URL_NAME_REQUIREMENTS)
637 requirements=URL_NAME_REQUIREMENTS)
654 rmap.connect('changeset_parents', '/{repo_name}/changeset_parents/{revision}',
638 rmap.connect('changeset_parents', '/{repo_name}/changeset_parents/{revision}',
655 controller='changeset', revision='tip', action='changeset_parents',
639 controller='changeset', revision='tip', action='changeset_parents',
656 conditions={'function': check_repo},
640 conditions={'function': check_repo},
657 requirements=URL_NAME_REQUIREMENTS)
641 requirements=URL_NAME_REQUIREMENTS)
658
642
659 # repo edit options
643 # repo edit options
660 rmap.connect('edit_repo_fields', '/{repo_name}/settings/fields',
644 rmap.connect('edit_repo_fields', '/{repo_name}/settings/fields',
661 controller='admin/repos', action='edit_fields',
645 controller='admin/repos', action='edit_fields',
662 conditions={'method': ['GET'], 'function': check_repo},
646 conditions={'method': ['GET'], 'function': check_repo},
663 requirements=URL_NAME_REQUIREMENTS)
647 requirements=URL_NAME_REQUIREMENTS)
664 rmap.connect('create_repo_fields', '/{repo_name}/settings/fields/new',
648 rmap.connect('create_repo_fields', '/{repo_name}/settings/fields/new',
665 controller='admin/repos', action='create_repo_field',
649 controller='admin/repos', action='create_repo_field',
666 conditions={'method': ['PUT'], 'function': check_repo},
650 conditions={'method': ['PUT'], 'function': check_repo},
667 requirements=URL_NAME_REQUIREMENTS)
651 requirements=URL_NAME_REQUIREMENTS)
668 rmap.connect('delete_repo_fields', '/{repo_name}/settings/fields/{field_id}',
652 rmap.connect('delete_repo_fields', '/{repo_name}/settings/fields/{field_id}',
669 controller='admin/repos', action='delete_repo_field',
653 controller='admin/repos', action='delete_repo_field',
670 conditions={'method': ['DELETE'], 'function': check_repo},
654 conditions={'method': ['DELETE'], 'function': check_repo},
671 requirements=URL_NAME_REQUIREMENTS)
655 requirements=URL_NAME_REQUIREMENTS)
672
656
673 rmap.connect('toggle_locking', '/{repo_name}/settings/advanced/locking_toggle',
657 rmap.connect('toggle_locking', '/{repo_name}/settings/advanced/locking_toggle',
674 controller='admin/repos', action='toggle_locking',
658 controller='admin/repos', action='toggle_locking',
675 conditions={'method': ['GET'], 'function': check_repo},
659 conditions={'method': ['GET'], 'function': check_repo},
676 requirements=URL_NAME_REQUIREMENTS)
660 requirements=URL_NAME_REQUIREMENTS)
677
661
678 rmap.connect('edit_repo_remote', '/{repo_name}/settings/remote',
662 rmap.connect('edit_repo_remote', '/{repo_name}/settings/remote',
679 controller='admin/repos', action='edit_remote_form',
663 controller='admin/repos', action='edit_remote_form',
680 conditions={'method': ['GET'], 'function': check_repo},
664 conditions={'method': ['GET'], 'function': check_repo},
681 requirements=URL_NAME_REQUIREMENTS)
665 requirements=URL_NAME_REQUIREMENTS)
682 rmap.connect('edit_repo_remote', '/{repo_name}/settings/remote',
666 rmap.connect('edit_repo_remote', '/{repo_name}/settings/remote',
683 controller='admin/repos', action='edit_remote',
667 controller='admin/repos', action='edit_remote',
684 conditions={'method': ['PUT'], 'function': check_repo},
668 conditions={'method': ['PUT'], 'function': check_repo},
685 requirements=URL_NAME_REQUIREMENTS)
669 requirements=URL_NAME_REQUIREMENTS)
686
670
687 rmap.connect('edit_repo_statistics', '/{repo_name}/settings/statistics',
671 rmap.connect('edit_repo_statistics', '/{repo_name}/settings/statistics',
688 controller='admin/repos', action='edit_statistics_form',
672 controller='admin/repos', action='edit_statistics_form',
689 conditions={'method': ['GET'], 'function': check_repo},
673 conditions={'method': ['GET'], 'function': check_repo},
690 requirements=URL_NAME_REQUIREMENTS)
674 requirements=URL_NAME_REQUIREMENTS)
691 rmap.connect('edit_repo_statistics', '/{repo_name}/settings/statistics',
675 rmap.connect('edit_repo_statistics', '/{repo_name}/settings/statistics',
692 controller='admin/repos', action='edit_statistics',
676 controller='admin/repos', action='edit_statistics',
693 conditions={'method': ['PUT'], 'function': check_repo},
677 conditions={'method': ['PUT'], 'function': check_repo},
694 requirements=URL_NAME_REQUIREMENTS)
678 requirements=URL_NAME_REQUIREMENTS)
695 rmap.connect('repo_settings_issuetracker',
679 rmap.connect('repo_settings_issuetracker',
696 '/{repo_name}/settings/issue-tracker',
680 '/{repo_name}/settings/issue-tracker',
697 controller='admin/repos', action='repo_issuetracker',
681 controller='admin/repos', action='repo_issuetracker',
698 conditions={'method': ['GET'], 'function': check_repo},
682 conditions={'method': ['GET'], 'function': check_repo},
699 requirements=URL_NAME_REQUIREMENTS)
683 requirements=URL_NAME_REQUIREMENTS)
700 rmap.connect('repo_issuetracker_test',
684 rmap.connect('repo_issuetracker_test',
701 '/{repo_name}/settings/issue-tracker/test',
685 '/{repo_name}/settings/issue-tracker/test',
702 controller='admin/repos', action='repo_issuetracker_test',
686 controller='admin/repos', action='repo_issuetracker_test',
703 conditions={'method': ['POST'], 'function': check_repo},
687 conditions={'method': ['POST'], 'function': check_repo},
704 requirements=URL_NAME_REQUIREMENTS)
688 requirements=URL_NAME_REQUIREMENTS)
705 rmap.connect('repo_issuetracker_delete',
689 rmap.connect('repo_issuetracker_delete',
706 '/{repo_name}/settings/issue-tracker/delete',
690 '/{repo_name}/settings/issue-tracker/delete',
707 controller='admin/repos', action='repo_issuetracker_delete',
691 controller='admin/repos', action='repo_issuetracker_delete',
708 conditions={'method': ['DELETE'], 'function': check_repo},
692 conditions={'method': ['DELETE'], 'function': check_repo},
709 requirements=URL_NAME_REQUIREMENTS)
693 requirements=URL_NAME_REQUIREMENTS)
710 rmap.connect('repo_issuetracker_save',
694 rmap.connect('repo_issuetracker_save',
711 '/{repo_name}/settings/issue-tracker/save',
695 '/{repo_name}/settings/issue-tracker/save',
712 controller='admin/repos', action='repo_issuetracker_save',
696 controller='admin/repos', action='repo_issuetracker_save',
713 conditions={'method': ['POST'], 'function': check_repo},
697 conditions={'method': ['POST'], 'function': check_repo},
714 requirements=URL_NAME_REQUIREMENTS)
698 requirements=URL_NAME_REQUIREMENTS)
715 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
699 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
716 controller='admin/repos', action='repo_settings_vcs_update',
700 controller='admin/repos', action='repo_settings_vcs_update',
717 conditions={'method': ['POST'], 'function': check_repo},
701 conditions={'method': ['POST'], 'function': check_repo},
718 requirements=URL_NAME_REQUIREMENTS)
702 requirements=URL_NAME_REQUIREMENTS)
719 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
703 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
720 controller='admin/repos', action='repo_settings_vcs',
704 controller='admin/repos', action='repo_settings_vcs',
721 conditions={'method': ['GET'], 'function': check_repo},
705 conditions={'method': ['GET'], 'function': check_repo},
722 requirements=URL_NAME_REQUIREMENTS)
706 requirements=URL_NAME_REQUIREMENTS)
723 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
707 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
724 controller='admin/repos', action='repo_delete_svn_pattern',
708 controller='admin/repos', action='repo_delete_svn_pattern',
725 conditions={'method': ['DELETE'], 'function': check_repo},
709 conditions={'method': ['DELETE'], 'function': check_repo},
726 requirements=URL_NAME_REQUIREMENTS)
710 requirements=URL_NAME_REQUIREMENTS)
727 rmap.connect('repo_pullrequest_settings', '/{repo_name}/settings/pullrequest',
711 rmap.connect('repo_pullrequest_settings', '/{repo_name}/settings/pullrequest',
728 controller='admin/repos', action='repo_settings_pullrequest',
712 controller='admin/repos', action='repo_settings_pullrequest',
729 conditions={'method': ['GET', 'POST'], 'function': check_repo},
713 conditions={'method': ['GET', 'POST'], 'function': check_repo},
730 requirements=URL_NAME_REQUIREMENTS)
714 requirements=URL_NAME_REQUIREMENTS)
731
715
732 # still working url for backward compat.
716 # still working url for backward compat.
733 rmap.connect('raw_changeset_home_depraced',
717 rmap.connect('raw_changeset_home_depraced',
734 '/{repo_name}/raw-changeset/{revision}',
718 '/{repo_name}/raw-changeset/{revision}',
735 controller='changeset', action='changeset_raw',
719 controller='changeset', action='changeset_raw',
736 revision='tip', conditions={'function': check_repo},
720 revision='tip', conditions={'function': check_repo},
737 requirements=URL_NAME_REQUIREMENTS)
721 requirements=URL_NAME_REQUIREMENTS)
738
722
739 # new URLs
723 # new URLs
740 rmap.connect('changeset_raw_home',
724 rmap.connect('changeset_raw_home',
741 '/{repo_name}/changeset-diff/{revision}',
725 '/{repo_name}/changeset-diff/{revision}',
742 controller='changeset', action='changeset_raw',
726 controller='changeset', action='changeset_raw',
743 revision='tip', conditions={'function': check_repo},
727 revision='tip', conditions={'function': check_repo},
744 requirements=URL_NAME_REQUIREMENTS)
728 requirements=URL_NAME_REQUIREMENTS)
745
729
746 rmap.connect('changeset_patch_home',
730 rmap.connect('changeset_patch_home',
747 '/{repo_name}/changeset-patch/{revision}',
731 '/{repo_name}/changeset-patch/{revision}',
748 controller='changeset', action='changeset_patch',
732 controller='changeset', action='changeset_patch',
749 revision='tip', conditions={'function': check_repo},
733 revision='tip', conditions={'function': check_repo},
750 requirements=URL_NAME_REQUIREMENTS)
734 requirements=URL_NAME_REQUIREMENTS)
751
735
752 rmap.connect('changeset_download_home',
736 rmap.connect('changeset_download_home',
753 '/{repo_name}/changeset-download/{revision}',
737 '/{repo_name}/changeset-download/{revision}',
754 controller='changeset', action='changeset_download',
738 controller='changeset', action='changeset_download',
755 revision='tip', conditions={'function': check_repo},
739 revision='tip', conditions={'function': check_repo},
756 requirements=URL_NAME_REQUIREMENTS)
740 requirements=URL_NAME_REQUIREMENTS)
757
741
758 rmap.connect('changeset_comment',
742 rmap.connect('changeset_comment',
759 '/{repo_name}/changeset/{revision}/comment', jsroute=True,
743 '/{repo_name}/changeset/{revision}/comment', jsroute=True,
760 controller='changeset', revision='tip', action='comment',
744 controller='changeset', revision='tip', action='comment',
761 conditions={'function': check_repo},
745 conditions={'function': check_repo},
762 requirements=URL_NAME_REQUIREMENTS)
746 requirements=URL_NAME_REQUIREMENTS)
763
747
764 rmap.connect('changeset_comment_preview',
748 rmap.connect('changeset_comment_preview',
765 '/{repo_name}/changeset/comment/preview', jsroute=True,
749 '/{repo_name}/changeset/comment/preview', jsroute=True,
766 controller='changeset', action='preview_comment',
750 controller='changeset', action='preview_comment',
767 conditions={'function': check_repo, 'method': ['POST']},
751 conditions={'function': check_repo, 'method': ['POST']},
768 requirements=URL_NAME_REQUIREMENTS)
752 requirements=URL_NAME_REQUIREMENTS)
769
753
770 rmap.connect('changeset_comment_delete',
754 rmap.connect('changeset_comment_delete',
771 '/{repo_name}/changeset/comment/{comment_id}/delete',
755 '/{repo_name}/changeset/comment/{comment_id}/delete',
772 controller='changeset', action='delete_comment',
756 controller='changeset', action='delete_comment',
773 conditions={'function': check_repo, 'method': ['DELETE']},
757 conditions={'function': check_repo, 'method': ['DELETE']},
774 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
758 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
775
759
776 rmap.connect('changeset_info', '/{repo_name}/changeset_info/{revision}',
760 rmap.connect('changeset_info', '/{repo_name}/changeset_info/{revision}',
777 controller='changeset', action='changeset_info',
761 controller='changeset', action='changeset_info',
778 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
762 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
779
763
780 rmap.connect('compare_home',
764 rmap.connect('compare_home',
781 '/{repo_name}/compare',
765 '/{repo_name}/compare',
782 controller='compare', action='index',
766 controller='compare', action='index',
783 conditions={'function': check_repo},
767 conditions={'function': check_repo},
784 requirements=URL_NAME_REQUIREMENTS)
768 requirements=URL_NAME_REQUIREMENTS)
785
769
786 rmap.connect('compare_url',
770 rmap.connect('compare_url',
787 '/{repo_name}/compare/{source_ref_type}@{source_ref:.*?}...{target_ref_type}@{target_ref:.*?}',
771 '/{repo_name}/compare/{source_ref_type}@{source_ref:.*?}...{target_ref_type}@{target_ref:.*?}',
788 controller='compare', action='compare',
772 controller='compare', action='compare',
789 conditions={'function': check_repo},
773 conditions={'function': check_repo},
790 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
774 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
791
775
792 rmap.connect('pullrequest_home',
776 rmap.connect('pullrequest_home',
793 '/{repo_name}/pull-request/new', controller='pullrequests',
777 '/{repo_name}/pull-request/new', controller='pullrequests',
794 action='index', conditions={'function': check_repo,
778 action='index', conditions={'function': check_repo,
795 'method': ['GET']},
779 'method': ['GET']},
796 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
780 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
797
781
798 rmap.connect('pullrequest',
782 rmap.connect('pullrequest',
799 '/{repo_name}/pull-request/new', controller='pullrequests',
783 '/{repo_name}/pull-request/new', controller='pullrequests',
800 action='create', conditions={'function': check_repo,
784 action='create', conditions={'function': check_repo,
801 'method': ['POST']},
785 'method': ['POST']},
802 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
786 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
803
787
804 rmap.connect('pullrequest_repo_refs',
788 rmap.connect('pullrequest_repo_refs',
805 '/{repo_name}/pull-request/refs/{target_repo_name:.*?[^/]}',
789 '/{repo_name}/pull-request/refs/{target_repo_name:.*?[^/]}',
806 controller='pullrequests',
790 controller='pullrequests',
807 action='get_repo_refs',
791 action='get_repo_refs',
808 conditions={'function': check_repo, 'method': ['GET']},
792 conditions={'function': check_repo, 'method': ['GET']},
809 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
793 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
810
794
811 rmap.connect('pullrequest_repo_destinations',
795 rmap.connect('pullrequest_repo_destinations',
812 '/{repo_name}/pull-request/repo-destinations',
796 '/{repo_name}/pull-request/repo-destinations',
813 controller='pullrequests',
797 controller='pullrequests',
814 action='get_repo_destinations',
798 action='get_repo_destinations',
815 conditions={'function': check_repo, 'method': ['GET']},
799 conditions={'function': check_repo, 'method': ['GET']},
816 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
800 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
817
801
818 rmap.connect('pullrequest_show',
802 rmap.connect('pullrequest_show',
819 '/{repo_name}/pull-request/{pull_request_id}',
803 '/{repo_name}/pull-request/{pull_request_id}',
820 controller='pullrequests',
804 controller='pullrequests',
821 action='show', conditions={'function': check_repo,
805 action='show', conditions={'function': check_repo,
822 'method': ['GET']},
806 'method': ['GET']},
823 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
807 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
824
808
825 rmap.connect('pullrequest_update',
809 rmap.connect('pullrequest_update',
826 '/{repo_name}/pull-request/{pull_request_id}',
810 '/{repo_name}/pull-request/{pull_request_id}',
827 controller='pullrequests',
811 controller='pullrequests',
828 action='update', conditions={'function': check_repo,
812 action='update', conditions={'function': check_repo,
829 'method': ['PUT']},
813 'method': ['PUT']},
830 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
814 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
831
815
832 rmap.connect('pullrequest_merge',
816 rmap.connect('pullrequest_merge',
833 '/{repo_name}/pull-request/{pull_request_id}',
817 '/{repo_name}/pull-request/{pull_request_id}',
834 controller='pullrequests',
818 controller='pullrequests',
835 action='merge', conditions={'function': check_repo,
819 action='merge', conditions={'function': check_repo,
836 'method': ['POST']},
820 'method': ['POST']},
837 requirements=URL_NAME_REQUIREMENTS)
821 requirements=URL_NAME_REQUIREMENTS)
838
822
839 rmap.connect('pullrequest_delete',
823 rmap.connect('pullrequest_delete',
840 '/{repo_name}/pull-request/{pull_request_id}',
824 '/{repo_name}/pull-request/{pull_request_id}',
841 controller='pullrequests',
825 controller='pullrequests',
842 action='delete', conditions={'function': check_repo,
826 action='delete', conditions={'function': check_repo,
843 'method': ['DELETE']},
827 'method': ['DELETE']},
844 requirements=URL_NAME_REQUIREMENTS)
828 requirements=URL_NAME_REQUIREMENTS)
845
829
846 rmap.connect('pullrequest_show_all',
830 rmap.connect('pullrequest_show_all',
847 '/{repo_name}/pull-request',
831 '/{repo_name}/pull-request',
848 controller='pullrequests',
832 controller='pullrequests',
849 action='show_all', conditions={'function': check_repo,
833 action='show_all', conditions={'function': check_repo,
850 'method': ['GET']},
834 'method': ['GET']},
851 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
835 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
852
836
853 rmap.connect('pullrequest_comment',
837 rmap.connect('pullrequest_comment',
854 '/{repo_name}/pull-request-comment/{pull_request_id}',
838 '/{repo_name}/pull-request-comment/{pull_request_id}',
855 controller='pullrequests',
839 controller='pullrequests',
856 action='comment', conditions={'function': check_repo,
840 action='comment', conditions={'function': check_repo,
857 'method': ['POST']},
841 'method': ['POST']},
858 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
842 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
859
843
860 rmap.connect('pullrequest_comment_delete',
844 rmap.connect('pullrequest_comment_delete',
861 '/{repo_name}/pull-request-comment/{comment_id}/delete',
845 '/{repo_name}/pull-request-comment/{comment_id}/delete',
862 controller='pullrequests', action='delete_comment',
846 controller='pullrequests', action='delete_comment',
863 conditions={'function': check_repo, 'method': ['DELETE']},
847 conditions={'function': check_repo, 'method': ['DELETE']},
864 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
848 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
865
849
866 rmap.connect('summary_home_explicit', '/{repo_name}/summary',
850 rmap.connect('summary_home_explicit', '/{repo_name}/summary',
867 controller='summary', conditions={'function': check_repo},
851 controller='summary', conditions={'function': check_repo},
868 requirements=URL_NAME_REQUIREMENTS)
852 requirements=URL_NAME_REQUIREMENTS)
869
853
870 rmap.connect('changelog_home', '/{repo_name}/changelog', jsroute=True,
854 rmap.connect('changelog_home', '/{repo_name}/changelog', jsroute=True,
871 controller='changelog', conditions={'function': check_repo},
855 controller='changelog', conditions={'function': check_repo},
872 requirements=URL_NAME_REQUIREMENTS)
856 requirements=URL_NAME_REQUIREMENTS)
873
857
874 rmap.connect('changelog_summary_home', '/{repo_name}/changelog_summary',
858 rmap.connect('changelog_summary_home', '/{repo_name}/changelog_summary',
875 controller='changelog', action='changelog_summary',
859 controller='changelog', action='changelog_summary',
876 conditions={'function': check_repo},
860 conditions={'function': check_repo},
877 requirements=URL_NAME_REQUIREMENTS)
861 requirements=URL_NAME_REQUIREMENTS)
878
862
879 rmap.connect('changelog_file_home',
863 rmap.connect('changelog_file_home',
880 '/{repo_name}/changelog/{revision}/{f_path}',
864 '/{repo_name}/changelog/{revision}/{f_path}',
881 controller='changelog', f_path=None,
865 controller='changelog', f_path=None,
882 conditions={'function': check_repo},
866 conditions={'function': check_repo},
883 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
867 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
884
868
885 rmap.connect('changelog_elements', '/{repo_name}/changelog_details',
869 rmap.connect('changelog_elements', '/{repo_name}/changelog_details',
886 controller='changelog', action='changelog_elements',
870 controller='changelog', action='changelog_elements',
887 conditions={'function': check_repo},
871 conditions={'function': check_repo},
888 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
872 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
889
873
890 rmap.connect('files_home', '/{repo_name}/files/{revision}/{f_path}',
874 rmap.connect('files_home', '/{repo_name}/files/{revision}/{f_path}',
891 controller='files', revision='tip', f_path='',
875 controller='files', revision='tip', f_path='',
892 conditions={'function': check_repo},
876 conditions={'function': check_repo},
893 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
877 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
894
878
895 rmap.connect('files_home_simple_catchrev',
879 rmap.connect('files_home_simple_catchrev',
896 '/{repo_name}/files/{revision}',
880 '/{repo_name}/files/{revision}',
897 controller='files', revision='tip', f_path='',
881 controller='files', revision='tip', f_path='',
898 conditions={'function': check_repo},
882 conditions={'function': check_repo},
899 requirements=URL_NAME_REQUIREMENTS)
883 requirements=URL_NAME_REQUIREMENTS)
900
884
901 rmap.connect('files_home_simple_catchall',
885 rmap.connect('files_home_simple_catchall',
902 '/{repo_name}/files',
886 '/{repo_name}/files',
903 controller='files', revision='tip', f_path='',
887 controller='files', revision='tip', f_path='',
904 conditions={'function': check_repo},
888 conditions={'function': check_repo},
905 requirements=URL_NAME_REQUIREMENTS)
889 requirements=URL_NAME_REQUIREMENTS)
906
890
907 rmap.connect('files_history_home',
891 rmap.connect('files_history_home',
908 '/{repo_name}/history/{revision}/{f_path}',
892 '/{repo_name}/history/{revision}/{f_path}',
909 controller='files', action='history', revision='tip', f_path='',
893 controller='files', action='history', revision='tip', f_path='',
910 conditions={'function': check_repo},
894 conditions={'function': check_repo},
911 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
895 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
912
896
913 rmap.connect('files_authors_home',
897 rmap.connect('files_authors_home',
914 '/{repo_name}/authors/{revision}/{f_path}',
898 '/{repo_name}/authors/{revision}/{f_path}',
915 controller='files', action='authors', revision='tip', f_path='',
899 controller='files', action='authors', revision='tip', f_path='',
916 conditions={'function': check_repo},
900 conditions={'function': check_repo},
917 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
901 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
918
902
919 rmap.connect('files_diff_home', '/{repo_name}/diff/{f_path}',
903 rmap.connect('files_diff_home', '/{repo_name}/diff/{f_path}',
920 controller='files', action='diff', f_path='',
904 controller='files', action='diff', f_path='',
921 conditions={'function': check_repo},
905 conditions={'function': check_repo},
922 requirements=URL_NAME_REQUIREMENTS)
906 requirements=URL_NAME_REQUIREMENTS)
923
907
924 rmap.connect('files_diff_2way_home',
908 rmap.connect('files_diff_2way_home',
925 '/{repo_name}/diff-2way/{f_path}',
909 '/{repo_name}/diff-2way/{f_path}',
926 controller='files', action='diff_2way', f_path='',
910 controller='files', action='diff_2way', f_path='',
927 conditions={'function': check_repo},
911 conditions={'function': check_repo},
928 requirements=URL_NAME_REQUIREMENTS)
912 requirements=URL_NAME_REQUIREMENTS)
929
913
930 rmap.connect('files_rawfile_home',
914 rmap.connect('files_rawfile_home',
931 '/{repo_name}/rawfile/{revision}/{f_path}',
915 '/{repo_name}/rawfile/{revision}/{f_path}',
932 controller='files', action='rawfile', revision='tip',
916 controller='files', action='rawfile', revision='tip',
933 f_path='', conditions={'function': check_repo},
917 f_path='', conditions={'function': check_repo},
934 requirements=URL_NAME_REQUIREMENTS)
918 requirements=URL_NAME_REQUIREMENTS)
935
919
936 rmap.connect('files_raw_home',
920 rmap.connect('files_raw_home',
937 '/{repo_name}/raw/{revision}/{f_path}',
921 '/{repo_name}/raw/{revision}/{f_path}',
938 controller='files', action='raw', revision='tip', f_path='',
922 controller='files', action='raw', revision='tip', f_path='',
939 conditions={'function': check_repo},
923 conditions={'function': check_repo},
940 requirements=URL_NAME_REQUIREMENTS)
924 requirements=URL_NAME_REQUIREMENTS)
941
925
942 rmap.connect('files_render_home',
926 rmap.connect('files_render_home',
943 '/{repo_name}/render/{revision}/{f_path}',
927 '/{repo_name}/render/{revision}/{f_path}',
944 controller='files', action='index', revision='tip', f_path='',
928 controller='files', action='index', revision='tip', f_path='',
945 rendered=True, conditions={'function': check_repo},
929 rendered=True, conditions={'function': check_repo},
946 requirements=URL_NAME_REQUIREMENTS)
930 requirements=URL_NAME_REQUIREMENTS)
947
931
948 rmap.connect('files_annotate_home',
932 rmap.connect('files_annotate_home',
949 '/{repo_name}/annotate/{revision}/{f_path}',
933 '/{repo_name}/annotate/{revision}/{f_path}',
950 controller='files', action='index', revision='tip',
934 controller='files', action='index', revision='tip',
951 f_path='', annotate=True, conditions={'function': check_repo},
935 f_path='', annotate=True, conditions={'function': check_repo},
952 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
936 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
953
937
954 rmap.connect('files_annotate_previous',
938 rmap.connect('files_annotate_previous',
955 '/{repo_name}/annotate-previous/{revision}/{f_path}',
939 '/{repo_name}/annotate-previous/{revision}/{f_path}',
956 controller='files', action='annotate_previous', revision='tip',
940 controller='files', action='annotate_previous', revision='tip',
957 f_path='', annotate=True, conditions={'function': check_repo},
941 f_path='', annotate=True, conditions={'function': check_repo},
958 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
942 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
959
943
960 rmap.connect('files_edit',
944 rmap.connect('files_edit',
961 '/{repo_name}/edit/{revision}/{f_path}',
945 '/{repo_name}/edit/{revision}/{f_path}',
962 controller='files', action='edit', revision='tip',
946 controller='files', action='edit', revision='tip',
963 f_path='',
947 f_path='',
964 conditions={'function': check_repo, 'method': ['POST']},
948 conditions={'function': check_repo, 'method': ['POST']},
965 requirements=URL_NAME_REQUIREMENTS)
949 requirements=URL_NAME_REQUIREMENTS)
966
950
967 rmap.connect('files_edit_home',
951 rmap.connect('files_edit_home',
968 '/{repo_name}/edit/{revision}/{f_path}',
952 '/{repo_name}/edit/{revision}/{f_path}',
969 controller='files', action='edit_home', revision='tip',
953 controller='files', action='edit_home', revision='tip',
970 f_path='', conditions={'function': check_repo},
954 f_path='', conditions={'function': check_repo},
971 requirements=URL_NAME_REQUIREMENTS)
955 requirements=URL_NAME_REQUIREMENTS)
972
956
973 rmap.connect('files_add',
957 rmap.connect('files_add',
974 '/{repo_name}/add/{revision}/{f_path}',
958 '/{repo_name}/add/{revision}/{f_path}',
975 controller='files', action='add', revision='tip',
959 controller='files', action='add', revision='tip',
976 f_path='',
960 f_path='',
977 conditions={'function': check_repo, 'method': ['POST']},
961 conditions={'function': check_repo, 'method': ['POST']},
978 requirements=URL_NAME_REQUIREMENTS)
962 requirements=URL_NAME_REQUIREMENTS)
979
963
980 rmap.connect('files_add_home',
964 rmap.connect('files_add_home',
981 '/{repo_name}/add/{revision}/{f_path}',
965 '/{repo_name}/add/{revision}/{f_path}',
982 controller='files', action='add_home', revision='tip',
966 controller='files', action='add_home', revision='tip',
983 f_path='', conditions={'function': check_repo},
967 f_path='', conditions={'function': check_repo},
984 requirements=URL_NAME_REQUIREMENTS)
968 requirements=URL_NAME_REQUIREMENTS)
985
969
986 rmap.connect('files_delete',
970 rmap.connect('files_delete',
987 '/{repo_name}/delete/{revision}/{f_path}',
971 '/{repo_name}/delete/{revision}/{f_path}',
988 controller='files', action='delete', revision='tip',
972 controller='files', action='delete', revision='tip',
989 f_path='',
973 f_path='',
990 conditions={'function': check_repo, 'method': ['POST']},
974 conditions={'function': check_repo, 'method': ['POST']},
991 requirements=URL_NAME_REQUIREMENTS)
975 requirements=URL_NAME_REQUIREMENTS)
992
976
993 rmap.connect('files_delete_home',
977 rmap.connect('files_delete_home',
994 '/{repo_name}/delete/{revision}/{f_path}',
978 '/{repo_name}/delete/{revision}/{f_path}',
995 controller='files', action='delete_home', revision='tip',
979 controller='files', action='delete_home', revision='tip',
996 f_path='', conditions={'function': check_repo},
980 f_path='', conditions={'function': check_repo},
997 requirements=URL_NAME_REQUIREMENTS)
981 requirements=URL_NAME_REQUIREMENTS)
998
982
999 rmap.connect('files_archive_home', '/{repo_name}/archive/{fname}',
983 rmap.connect('files_archive_home', '/{repo_name}/archive/{fname}',
1000 controller='files', action='archivefile',
984 controller='files', action='archivefile',
1001 conditions={'function': check_repo},
985 conditions={'function': check_repo},
1002 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
986 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1003
987
1004 rmap.connect('files_nodelist_home',
988 rmap.connect('files_nodelist_home',
1005 '/{repo_name}/nodelist/{revision}/{f_path}',
989 '/{repo_name}/nodelist/{revision}/{f_path}',
1006 controller='files', action='nodelist',
990 controller='files', action='nodelist',
1007 conditions={'function': check_repo},
991 conditions={'function': check_repo},
1008 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
992 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1009
993
1010 rmap.connect('files_nodetree_full',
994 rmap.connect('files_nodetree_full',
1011 '/{repo_name}/nodetree_full/{commit_id}/{f_path}',
995 '/{repo_name}/nodetree_full/{commit_id}/{f_path}',
1012 controller='files', action='nodetree_full',
996 controller='files', action='nodetree_full',
1013 conditions={'function': check_repo},
997 conditions={'function': check_repo},
1014 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
998 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1015
999
1016 rmap.connect('repo_fork_create_home', '/{repo_name}/fork',
1000 rmap.connect('repo_fork_create_home', '/{repo_name}/fork',
1017 controller='forks', action='fork_create',
1001 controller='forks', action='fork_create',
1018 conditions={'function': check_repo, 'method': ['POST']},
1002 conditions={'function': check_repo, 'method': ['POST']},
1019 requirements=URL_NAME_REQUIREMENTS)
1003 requirements=URL_NAME_REQUIREMENTS)
1020
1004
1021 rmap.connect('repo_fork_home', '/{repo_name}/fork',
1005 rmap.connect('repo_fork_home', '/{repo_name}/fork',
1022 controller='forks', action='fork',
1006 controller='forks', action='fork',
1023 conditions={'function': check_repo},
1007 conditions={'function': check_repo},
1024 requirements=URL_NAME_REQUIREMENTS)
1008 requirements=URL_NAME_REQUIREMENTS)
1025
1009
1026 rmap.connect('repo_forks_home', '/{repo_name}/forks',
1010 rmap.connect('repo_forks_home', '/{repo_name}/forks',
1027 controller='forks', action='forks',
1011 controller='forks', action='forks',
1028 conditions={'function': check_repo},
1012 conditions={'function': check_repo},
1029 requirements=URL_NAME_REQUIREMENTS)
1013 requirements=URL_NAME_REQUIREMENTS)
1030
1014
1031 # must be here for proper group/repo catching pattern
1015 # must be here for proper group/repo catching pattern
1032 _connect_with_slash(
1016 _connect_with_slash(
1033 rmap, 'repo_group_home', '/{group_name}',
1017 rmap, 'repo_group_home', '/{group_name}',
1034 controller='home', action='index_repo_group',
1018 controller='home', action='index_repo_group',
1035 conditions={'function': check_group},
1019 conditions={'function': check_group},
1036 requirements=URL_NAME_REQUIREMENTS)
1020 requirements=URL_NAME_REQUIREMENTS)
1037
1021
1038 # catch all, at the end
1022 # catch all, at the end
1039 _connect_with_slash(
1023 _connect_with_slash(
1040 rmap, 'summary_home', '/{repo_name}', jsroute=True,
1024 rmap, 'summary_home', '/{repo_name}', jsroute=True,
1041 controller='summary', action='index',
1025 controller='summary', action='index',
1042 conditions={'function': check_repo},
1026 conditions={'function': check_repo},
1043 requirements=URL_NAME_REQUIREMENTS)
1027 requirements=URL_NAME_REQUIREMENTS)
1044
1028
1045 return rmap
1029 return rmap
1046
1030
1047
1031
1048 def _connect_with_slash(mapper, name, path, *args, **kwargs):
1032 def _connect_with_slash(mapper, name, path, *args, **kwargs):
1049 """
1033 """
1050 Connect a route with an optional trailing slash in `path`.
1034 Connect a route with an optional trailing slash in `path`.
1051 """
1035 """
1052 mapper.connect(name + '_slash', path + '/', *args, **kwargs)
1036 mapper.connect(name + '_slash', path + '/', *args, **kwargs)
1053 mapper.connect(name, path, *args, **kwargs)
1037 mapper.connect(name, path, *args, **kwargs)
@@ -1,306 +1,306 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
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
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
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/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 """
21 """
22 Journal / user event log controller for rhodecode
22 Journal / user event log controller for rhodecode
23 """
23 """
24
24
25 import logging
25 import logging
26 from itertools import groupby
26 from itertools import groupby
27
27
28 from sqlalchemy import or_
28 from sqlalchemy import or_
29 from sqlalchemy.orm import joinedload
29 from sqlalchemy.orm import joinedload
30
30
31 from webhelpers.feedgenerator import Atom1Feed, Rss201rev2Feed
31 from webhelpers.feedgenerator import Atom1Feed, Rss201rev2Feed
32
32
33 from webob.exc import HTTPBadRequest
33 from webob.exc import HTTPBadRequest
34 from pylons import request, tmpl_context as c, response, url
34 from pylons import request, tmpl_context as c, response, url
35 from pylons.i18n.translation import _
35 from pylons.i18n.translation import _
36
36
37 from rhodecode.controllers.admin.admin import user_log_filter
38 from rhodecode.model.db import UserLog, UserFollowing, User, UserApiKeys
37 from rhodecode.model.db import UserLog, UserFollowing, User, UserApiKeys
39 from rhodecode.model.meta import Session
38 from rhodecode.model.meta import Session
40 import rhodecode.lib.helpers as h
39 import rhodecode.lib.helpers as h
41 from rhodecode.lib.helpers import Page
40 from rhodecode.lib.helpers import Page
41 from rhodecode.lib.user_log_filter import user_log_filter
42 from rhodecode.lib.auth import LoginRequired, NotAnonymous, CSRFRequired
42 from rhodecode.lib.auth import LoginRequired, NotAnonymous, CSRFRequired
43 from rhodecode.lib.base import BaseController, render
43 from rhodecode.lib.base import BaseController, render
44 from rhodecode.lib.utils2 import safe_int, AttributeDict
44 from rhodecode.lib.utils2 import safe_int, AttributeDict
45
45
46 log = logging.getLogger(__name__)
46 log = logging.getLogger(__name__)
47
47
48
48
49 class JournalController(BaseController):
49 class JournalController(BaseController):
50
50
51 def __before__(self):
51 def __before__(self):
52 super(JournalController, self).__before__()
52 super(JournalController, self).__before__()
53 self.language = 'en-us'
53 self.language = 'en-us'
54 self.ttl = "5"
54 self.ttl = "5"
55 self.feed_nr = 20
55 self.feed_nr = 20
56 c.search_term = request.GET.get('filter')
56 c.search_term = request.GET.get('filter')
57
57
58 def _get_daily_aggregate(self, journal):
58 def _get_daily_aggregate(self, journal):
59 groups = []
59 groups = []
60 for k, g in groupby(journal, lambda x: x.action_as_day):
60 for k, g in groupby(journal, lambda x: x.action_as_day):
61 user_group = []
61 user_group = []
62 #groupby username if it's a present value, else fallback to journal username
62 #groupby username if it's a present value, else fallback to journal username
63 for _, g2 in groupby(list(g), lambda x: x.user.username if x.user else x.username):
63 for _, g2 in groupby(list(g), lambda x: x.user.username if x.user else x.username):
64 l = list(g2)
64 l = list(g2)
65 user_group.append((l[0].user, l))
65 user_group.append((l[0].user, l))
66
66
67 groups.append((k, user_group,))
67 groups.append((k, user_group,))
68
68
69 return groups
69 return groups
70
70
71 def _get_journal_data(self, following_repos):
71 def _get_journal_data(self, following_repos):
72 repo_ids = [x.follows_repository.repo_id for x in following_repos
72 repo_ids = [x.follows_repository.repo_id for x in following_repos
73 if x.follows_repository is not None]
73 if x.follows_repository is not None]
74 user_ids = [x.follows_user.user_id for x in following_repos
74 user_ids = [x.follows_user.user_id for x in following_repos
75 if x.follows_user is not None]
75 if x.follows_user is not None]
76
76
77 filtering_criterion = None
77 filtering_criterion = None
78
78
79 if repo_ids and user_ids:
79 if repo_ids and user_ids:
80 filtering_criterion = or_(UserLog.repository_id.in_(repo_ids),
80 filtering_criterion = or_(UserLog.repository_id.in_(repo_ids),
81 UserLog.user_id.in_(user_ids))
81 UserLog.user_id.in_(user_ids))
82 if repo_ids and not user_ids:
82 if repo_ids and not user_ids:
83 filtering_criterion = UserLog.repository_id.in_(repo_ids)
83 filtering_criterion = UserLog.repository_id.in_(repo_ids)
84 if not repo_ids and user_ids:
84 if not repo_ids and user_ids:
85 filtering_criterion = UserLog.user_id.in_(user_ids)
85 filtering_criterion = UserLog.user_id.in_(user_ids)
86 if filtering_criterion is not None:
86 if filtering_criterion is not None:
87 journal = self.sa.query(UserLog)\
87 journal = self.sa.query(UserLog)\
88 .options(joinedload(UserLog.user))\
88 .options(joinedload(UserLog.user))\
89 .options(joinedload(UserLog.repository))
89 .options(joinedload(UserLog.repository))
90 #filter
90 #filter
91 try:
91 try:
92 journal = user_log_filter(journal, c.search_term)
92 journal = user_log_filter(journal, c.search_term)
93 except Exception:
93 except Exception:
94 # we want this to crash for now
94 # we want this to crash for now
95 raise
95 raise
96 journal = journal.filter(filtering_criterion)\
96 journal = journal.filter(filtering_criterion)\
97 .order_by(UserLog.action_date.desc())
97 .order_by(UserLog.action_date.desc())
98 else:
98 else:
99 journal = []
99 journal = []
100
100
101 return journal
101 return journal
102
102
103 def _atom_feed(self, repos, public=True):
103 def _atom_feed(self, repos, public=True):
104 journal = self._get_journal_data(repos)
104 journal = self._get_journal_data(repos)
105 if public:
105 if public:
106 _link = url('public_journal_atom', qualified=True)
106 _link = url('public_journal_atom', qualified=True)
107 _desc = '%s %s %s' % (c.rhodecode_name, _('public journal'),
107 _desc = '%s %s %s' % (c.rhodecode_name, _('public journal'),
108 'atom feed')
108 'atom feed')
109 else:
109 else:
110 _link = url('journal_atom', qualified=True)
110 _link = url('journal_atom', qualified=True)
111 _desc = '%s %s %s' % (c.rhodecode_name, _('journal'), 'atom feed')
111 _desc = '%s %s %s' % (c.rhodecode_name, _('journal'), 'atom feed')
112
112
113 feed = Atom1Feed(title=_desc,
113 feed = Atom1Feed(title=_desc,
114 link=_link,
114 link=_link,
115 description=_desc,
115 description=_desc,
116 language=self.language,
116 language=self.language,
117 ttl=self.ttl)
117 ttl=self.ttl)
118
118
119 for entry in journal[:self.feed_nr]:
119 for entry in journal[:self.feed_nr]:
120 user = entry.user
120 user = entry.user
121 if user is None:
121 if user is None:
122 #fix deleted users
122 #fix deleted users
123 user = AttributeDict({'short_contact': entry.username,
123 user = AttributeDict({'short_contact': entry.username,
124 'email': '',
124 'email': '',
125 'full_contact': ''})
125 'full_contact': ''})
126 action, action_extra, ico = h.action_parser(entry, feed=True)
126 action, action_extra, ico = h.action_parser(entry, feed=True)
127 title = "%s - %s %s" % (user.short_contact, action(),
127 title = "%s - %s %s" % (user.short_contact, action(),
128 entry.repository.repo_name)
128 entry.repository.repo_name)
129 desc = action_extra()
129 desc = action_extra()
130 _url = None
130 _url = None
131 if entry.repository is not None:
131 if entry.repository is not None:
132 _url = url('changelog_home',
132 _url = url('changelog_home',
133 repo_name=entry.repository.repo_name,
133 repo_name=entry.repository.repo_name,
134 qualified=True)
134 qualified=True)
135
135
136 feed.add_item(title=title,
136 feed.add_item(title=title,
137 pubdate=entry.action_date,
137 pubdate=entry.action_date,
138 link=_url or url('', qualified=True),
138 link=_url or url('', qualified=True),
139 author_email=user.email,
139 author_email=user.email,
140 author_name=user.full_contact,
140 author_name=user.full_contact,
141 description=desc)
141 description=desc)
142
142
143 response.content_type = feed.mime_type
143 response.content_type = feed.mime_type
144 return feed.writeString('utf-8')
144 return feed.writeString('utf-8')
145
145
146 def _rss_feed(self, repos, public=True):
146 def _rss_feed(self, repos, public=True):
147 journal = self._get_journal_data(repos)
147 journal = self._get_journal_data(repos)
148 if public:
148 if public:
149 _link = url('public_journal_atom', qualified=True)
149 _link = url('public_journal_atom', qualified=True)
150 _desc = '%s %s %s' % (c.rhodecode_name, _('public journal'),
150 _desc = '%s %s %s' % (c.rhodecode_name, _('public journal'),
151 'rss feed')
151 'rss feed')
152 else:
152 else:
153 _link = url('journal_atom', qualified=True)
153 _link = url('journal_atom', qualified=True)
154 _desc = '%s %s %s' % (c.rhodecode_name, _('journal'), 'rss feed')
154 _desc = '%s %s %s' % (c.rhodecode_name, _('journal'), 'rss feed')
155
155
156 feed = Rss201rev2Feed(title=_desc,
156 feed = Rss201rev2Feed(title=_desc,
157 link=_link,
157 link=_link,
158 description=_desc,
158 description=_desc,
159 language=self.language,
159 language=self.language,
160 ttl=self.ttl)
160 ttl=self.ttl)
161
161
162 for entry in journal[:self.feed_nr]:
162 for entry in journal[:self.feed_nr]:
163 user = entry.user
163 user = entry.user
164 if user is None:
164 if user is None:
165 #fix deleted users
165 #fix deleted users
166 user = AttributeDict({'short_contact': entry.username,
166 user = AttributeDict({'short_contact': entry.username,
167 'email': '',
167 'email': '',
168 'full_contact': ''})
168 'full_contact': ''})
169 action, action_extra, ico = h.action_parser(entry, feed=True)
169 action, action_extra, ico = h.action_parser(entry, feed=True)
170 title = "%s - %s %s" % (user.short_contact, action(),
170 title = "%s - %s %s" % (user.short_contact, action(),
171 entry.repository.repo_name)
171 entry.repository.repo_name)
172 desc = action_extra()
172 desc = action_extra()
173 _url = None
173 _url = None
174 if entry.repository is not None:
174 if entry.repository is not None:
175 _url = url('changelog_home',
175 _url = url('changelog_home',
176 repo_name=entry.repository.repo_name,
176 repo_name=entry.repository.repo_name,
177 qualified=True)
177 qualified=True)
178
178
179 feed.add_item(title=title,
179 feed.add_item(title=title,
180 pubdate=entry.action_date,
180 pubdate=entry.action_date,
181 link=_url or url('', qualified=True),
181 link=_url or url('', qualified=True),
182 author_email=user.email,
182 author_email=user.email,
183 author_name=user.full_contact,
183 author_name=user.full_contact,
184 description=desc)
184 description=desc)
185
185
186 response.content_type = feed.mime_type
186 response.content_type = feed.mime_type
187 return feed.writeString('utf-8')
187 return feed.writeString('utf-8')
188
188
189 @LoginRequired()
189 @LoginRequired()
190 @NotAnonymous()
190 @NotAnonymous()
191 def index(self):
191 def index(self):
192 # Return a rendered template
192 # Return a rendered template
193 p = safe_int(request.GET.get('page', 1), 1)
193 p = safe_int(request.GET.get('page', 1), 1)
194 c.user = User.get(c.rhodecode_user.user_id)
194 c.user = User.get(c.rhodecode_user.user_id)
195 following = self.sa.query(UserFollowing)\
195 following = self.sa.query(UserFollowing)\
196 .filter(UserFollowing.user_id == c.rhodecode_user.user_id)\
196 .filter(UserFollowing.user_id == c.rhodecode_user.user_id)\
197 .options(joinedload(UserFollowing.follows_repository))\
197 .options(joinedload(UserFollowing.follows_repository))\
198 .all()
198 .all()
199
199
200 journal = self._get_journal_data(following)
200 journal = self._get_journal_data(following)
201
201
202 def url_generator(**kw):
202 def url_generator(**kw):
203 return url.current(filter=c.search_term, **kw)
203 return url.current(filter=c.search_term, **kw)
204
204
205 c.journal_pager = Page(journal, page=p, items_per_page=20, url=url_generator)
205 c.journal_pager = Page(journal, page=p, items_per_page=20, url=url_generator)
206 c.journal_day_aggreagate = self._get_daily_aggregate(c.journal_pager)
206 c.journal_day_aggreagate = self._get_daily_aggregate(c.journal_pager)
207
207
208 c.journal_data = render('journal/journal_data.mako')
208 c.journal_data = render('journal/journal_data.mako')
209 if request.is_xhr:
209 if request.is_xhr:
210 return c.journal_data
210 return c.journal_data
211
211
212 return render('journal/journal.mako')
212 return render('journal/journal.mako')
213
213
214 @LoginRequired(auth_token_access=[UserApiKeys.ROLE_FEED])
214 @LoginRequired(auth_token_access=[UserApiKeys.ROLE_FEED])
215 @NotAnonymous()
215 @NotAnonymous()
216 def journal_atom(self):
216 def journal_atom(self):
217 """
217 """
218 Produce an atom-1.0 feed via feedgenerator module
218 Produce an atom-1.0 feed via feedgenerator module
219 """
219 """
220 following = self.sa.query(UserFollowing)\
220 following = self.sa.query(UserFollowing)\
221 .filter(UserFollowing.user_id == c.rhodecode_user.user_id)\
221 .filter(UserFollowing.user_id == c.rhodecode_user.user_id)\
222 .options(joinedload(UserFollowing.follows_repository))\
222 .options(joinedload(UserFollowing.follows_repository))\
223 .all()
223 .all()
224 return self._atom_feed(following, public=False)
224 return self._atom_feed(following, public=False)
225
225
226 @LoginRequired(auth_token_access=[UserApiKeys.ROLE_FEED])
226 @LoginRequired(auth_token_access=[UserApiKeys.ROLE_FEED])
227 @NotAnonymous()
227 @NotAnonymous()
228 def journal_rss(self):
228 def journal_rss(self):
229 """
229 """
230 Produce an rss feed via feedgenerator module
230 Produce an rss feed via feedgenerator module
231 """
231 """
232 following = self.sa.query(UserFollowing)\
232 following = self.sa.query(UserFollowing)\
233 .filter(UserFollowing.user_id == c.rhodecode_user.user_id)\
233 .filter(UserFollowing.user_id == c.rhodecode_user.user_id)\
234 .options(joinedload(UserFollowing.follows_repository))\
234 .options(joinedload(UserFollowing.follows_repository))\
235 .all()
235 .all()
236 return self._rss_feed(following, public=False)
236 return self._rss_feed(following, public=False)
237
237
238 @CSRFRequired()
238 @CSRFRequired()
239 @LoginRequired()
239 @LoginRequired()
240 @NotAnonymous()
240 @NotAnonymous()
241 def toggle_following(self):
241 def toggle_following(self):
242 user_id = request.POST.get('follows_user_id')
242 user_id = request.POST.get('follows_user_id')
243 if user_id:
243 if user_id:
244 try:
244 try:
245 self.scm_model.toggle_following_user(
245 self.scm_model.toggle_following_user(
246 user_id, c.rhodecode_user.user_id)
246 user_id, c.rhodecode_user.user_id)
247 Session().commit()
247 Session().commit()
248 return 'ok'
248 return 'ok'
249 except Exception:
249 except Exception:
250 raise HTTPBadRequest()
250 raise HTTPBadRequest()
251
251
252 repo_id = request.POST.get('follows_repo_id')
252 repo_id = request.POST.get('follows_repo_id')
253 if repo_id:
253 if repo_id:
254 try:
254 try:
255 self.scm_model.toggle_following_repo(
255 self.scm_model.toggle_following_repo(
256 repo_id, c.rhodecode_user.user_id)
256 repo_id, c.rhodecode_user.user_id)
257 Session().commit()
257 Session().commit()
258 return 'ok'
258 return 'ok'
259 except Exception:
259 except Exception:
260 raise HTTPBadRequest()
260 raise HTTPBadRequest()
261
261
262
262
263 @LoginRequired()
263 @LoginRequired()
264 def public_journal(self):
264 def public_journal(self):
265 # Return a rendered template
265 # Return a rendered template
266 p = safe_int(request.GET.get('page', 1), 1)
266 p = safe_int(request.GET.get('page', 1), 1)
267
267
268 c.following = self.sa.query(UserFollowing)\
268 c.following = self.sa.query(UserFollowing)\
269 .filter(UserFollowing.user_id == c.rhodecode_user.user_id)\
269 .filter(UserFollowing.user_id == c.rhodecode_user.user_id)\
270 .options(joinedload(UserFollowing.follows_repository))\
270 .options(joinedload(UserFollowing.follows_repository))\
271 .all()
271 .all()
272
272
273 journal = self._get_journal_data(c.following)
273 journal = self._get_journal_data(c.following)
274
274
275 c.journal_pager = Page(journal, page=p, items_per_page=20)
275 c.journal_pager = Page(journal, page=p, items_per_page=20)
276
276
277 c.journal_day_aggreagate = self._get_daily_aggregate(c.journal_pager)
277 c.journal_day_aggreagate = self._get_daily_aggregate(c.journal_pager)
278
278
279 c.journal_data = render('journal/journal_data.mako')
279 c.journal_data = render('journal/journal_data.mako')
280 if request.is_xhr:
280 if request.is_xhr:
281 return c.journal_data
281 return c.journal_data
282 return render('journal/public_journal.mako')
282 return render('journal/public_journal.mako')
283
283
284 @LoginRequired(auth_token_access=[UserApiKeys.ROLE_FEED])
284 @LoginRequired(auth_token_access=[UserApiKeys.ROLE_FEED])
285 def public_journal_atom(self):
285 def public_journal_atom(self):
286 """
286 """
287 Produce an atom-1.0 feed via feedgenerator module
287 Produce an atom-1.0 feed via feedgenerator module
288 """
288 """
289 c.following = self.sa.query(UserFollowing)\
289 c.following = self.sa.query(UserFollowing)\
290 .filter(UserFollowing.user_id == c.rhodecode_user.user_id)\
290 .filter(UserFollowing.user_id == c.rhodecode_user.user_id)\
291 .options(joinedload(UserFollowing.follows_repository))\
291 .options(joinedload(UserFollowing.follows_repository))\
292 .all()
292 .all()
293
293
294 return self._atom_feed(c.following)
294 return self._atom_feed(c.following)
295
295
296 @LoginRequired(auth_token_access=[UserApiKeys.ROLE_FEED])
296 @LoginRequired(auth_token_access=[UserApiKeys.ROLE_FEED])
297 def public_journal_rss(self):
297 def public_journal_rss(self):
298 """
298 """
299 Produce an rss2 feed via feedgenerator module
299 Produce an rss2 feed via feedgenerator module
300 """
300 """
301 c.following = self.sa.query(UserFollowing)\
301 c.following = self.sa.query(UserFollowing)\
302 .filter(UserFollowing.user_id == c.rhodecode_user.user_id)\
302 .filter(UserFollowing.user_id == c.rhodecode_user.user_id)\
303 .options(joinedload(UserFollowing.follows_repository))\
303 .options(joinedload(UserFollowing.follows_repository))\
304 .all()
304 .all()
305
305
306 return self._rss_feed(c.following)
306 return self._rss_feed(c.following)
@@ -1,128 +1,134 b''
1
1
2 /******************************************************************************
2 /******************************************************************************
3 * *
3 * *
4 * DO NOT CHANGE THIS FILE MANUALLY *
4 * DO NOT CHANGE THIS FILE MANUALLY *
5 * *
5 * *
6 * *
6 * *
7 * This file is automatically generated when the app starts up with *
7 * This file is automatically generated when the app starts up with *
8 * generate_js_files = true *
8 * generate_js_files = true *
9 * *
9 * *
10 * To add a route here pass jsroute=True to the route definition in the app *
10 * To add a route here pass jsroute=True to the route definition in the app *
11 * *
11 * *
12 ******************************************************************************/
12 ******************************************************************************/
13 function registerRCRoutes() {
13 function registerRCRoutes() {
14 // routes registration
14 // routes registration
15 pyroutes.register('new_repo', '/_admin/create_repository', []);
15 pyroutes.register('new_repo', '/_admin/create_repository', []);
16 pyroutes.register('edit_user', '/_admin/users/%(user_id)s/edit', ['user_id']);
16 pyroutes.register('edit_user', '/_admin/users/%(user_id)s/edit', ['user_id']);
17 pyroutes.register('edit_user_group_members', '/_admin/user_groups/%(user_group_id)s/edit/members', ['user_group_id']);
17 pyroutes.register('edit_user_group_members', '/_admin/user_groups/%(user_group_id)s/edit/members', ['user_group_id']);
18 pyroutes.register('gists', '/_admin/gists', []);
18 pyroutes.register('gists', '/_admin/gists', []);
19 pyroutes.register('new_gist', '/_admin/gists/new', []);
19 pyroutes.register('new_gist', '/_admin/gists/new', []);
20 pyroutes.register('toggle_following', '/_admin/toggle_following', []);
20 pyroutes.register('toggle_following', '/_admin/toggle_following', []);
21 pyroutes.register('repo_stats', '/%(repo_name)s/repo_stats/%(commit_id)s', ['repo_name', 'commit_id']);
21 pyroutes.register('repo_stats', '/%(repo_name)s/repo_stats/%(commit_id)s', ['repo_name', 'commit_id']);
22 pyroutes.register('repo_refs_data', '/%(repo_name)s/refs-data', ['repo_name']);
22 pyroutes.register('repo_refs_data', '/%(repo_name)s/refs-data', ['repo_name']);
23 pyroutes.register('repo_refs_changelog_data', '/%(repo_name)s/refs-data-changelog', ['repo_name']);
23 pyroutes.register('repo_refs_changelog_data', '/%(repo_name)s/refs-data-changelog', ['repo_name']);
24 pyroutes.register('repo_default_reviewers_data', '/%(repo_name)s/default-reviewers', ['repo_name']);
24 pyroutes.register('repo_default_reviewers_data', '/%(repo_name)s/default-reviewers', ['repo_name']);
25 pyroutes.register('changeset_home', '/%(repo_name)s/changeset/%(revision)s', ['repo_name', 'revision']);
25 pyroutes.register('changeset_home', '/%(repo_name)s/changeset/%(revision)s', ['repo_name', 'revision']);
26 pyroutes.register('changeset_comment', '/%(repo_name)s/changeset/%(revision)s/comment', ['repo_name', 'revision']);
26 pyroutes.register('changeset_comment', '/%(repo_name)s/changeset/%(revision)s/comment', ['repo_name', 'revision']);
27 pyroutes.register('changeset_comment_preview', '/%(repo_name)s/changeset/comment/preview', ['repo_name']);
27 pyroutes.register('changeset_comment_preview', '/%(repo_name)s/changeset/comment/preview', ['repo_name']);
28 pyroutes.register('changeset_comment_delete', '/%(repo_name)s/changeset/comment/%(comment_id)s/delete', ['repo_name', 'comment_id']);
28 pyroutes.register('changeset_comment_delete', '/%(repo_name)s/changeset/comment/%(comment_id)s/delete', ['repo_name', 'comment_id']);
29 pyroutes.register('changeset_info', '/%(repo_name)s/changeset_info/%(revision)s', ['repo_name', 'revision']);
29 pyroutes.register('changeset_info', '/%(repo_name)s/changeset_info/%(revision)s', ['repo_name', 'revision']);
30 pyroutes.register('compare_url', '/%(repo_name)s/compare/%(source_ref_type)s@%(source_ref)s...%(target_ref_type)s@%(target_ref)s', ['repo_name', 'source_ref_type', 'source_ref', 'target_ref_type', 'target_ref']);
30 pyroutes.register('compare_url', '/%(repo_name)s/compare/%(source_ref_type)s@%(source_ref)s...%(target_ref_type)s@%(target_ref)s', ['repo_name', 'source_ref_type', 'source_ref', 'target_ref_type', 'target_ref']);
31 pyroutes.register('pullrequest_home', '/%(repo_name)s/pull-request/new', ['repo_name']);
31 pyroutes.register('pullrequest_home', '/%(repo_name)s/pull-request/new', ['repo_name']);
32 pyroutes.register('pullrequest', '/%(repo_name)s/pull-request/new', ['repo_name']);
32 pyroutes.register('pullrequest', '/%(repo_name)s/pull-request/new', ['repo_name']);
33 pyroutes.register('pullrequest_repo_refs', '/%(repo_name)s/pull-request/refs/%(target_repo_name)s', ['repo_name', 'target_repo_name']);
33 pyroutes.register('pullrequest_repo_refs', '/%(repo_name)s/pull-request/refs/%(target_repo_name)s', ['repo_name', 'target_repo_name']);
34 pyroutes.register('pullrequest_repo_destinations', '/%(repo_name)s/pull-request/repo-destinations', ['repo_name']);
34 pyroutes.register('pullrequest_repo_destinations', '/%(repo_name)s/pull-request/repo-destinations', ['repo_name']);
35 pyroutes.register('pullrequest_show', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
35 pyroutes.register('pullrequest_show', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
36 pyroutes.register('pullrequest_update', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
36 pyroutes.register('pullrequest_update', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
37 pyroutes.register('pullrequest_show_all', '/%(repo_name)s/pull-request', ['repo_name']);
37 pyroutes.register('pullrequest_show_all', '/%(repo_name)s/pull-request', ['repo_name']);
38 pyroutes.register('pullrequest_comment', '/%(repo_name)s/pull-request-comment/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
38 pyroutes.register('pullrequest_comment', '/%(repo_name)s/pull-request-comment/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
39 pyroutes.register('pullrequest_comment_delete', '/%(repo_name)s/pull-request-comment/%(comment_id)s/delete', ['repo_name', 'comment_id']);
39 pyroutes.register('pullrequest_comment_delete', '/%(repo_name)s/pull-request-comment/%(comment_id)s/delete', ['repo_name', 'comment_id']);
40 pyroutes.register('changelog_home', '/%(repo_name)s/changelog', ['repo_name']);
40 pyroutes.register('changelog_home', '/%(repo_name)s/changelog', ['repo_name']);
41 pyroutes.register('changelog_file_home', '/%(repo_name)s/changelog/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
41 pyroutes.register('changelog_file_home', '/%(repo_name)s/changelog/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
42 pyroutes.register('changelog_elements', '/%(repo_name)s/changelog_details', ['repo_name']);
42 pyroutes.register('changelog_elements', '/%(repo_name)s/changelog_details', ['repo_name']);
43 pyroutes.register('files_home', '/%(repo_name)s/files/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
43 pyroutes.register('files_home', '/%(repo_name)s/files/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
44 pyroutes.register('files_history_home', '/%(repo_name)s/history/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
44 pyroutes.register('files_history_home', '/%(repo_name)s/history/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
45 pyroutes.register('files_authors_home', '/%(repo_name)s/authors/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
45 pyroutes.register('files_authors_home', '/%(repo_name)s/authors/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
46 pyroutes.register('files_annotate_home', '/%(repo_name)s/annotate/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
46 pyroutes.register('files_annotate_home', '/%(repo_name)s/annotate/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
47 pyroutes.register('files_annotate_previous', '/%(repo_name)s/annotate-previous/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
47 pyroutes.register('files_annotate_previous', '/%(repo_name)s/annotate-previous/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
48 pyroutes.register('files_archive_home', '/%(repo_name)s/archive/%(fname)s', ['repo_name', 'fname']);
48 pyroutes.register('files_archive_home', '/%(repo_name)s/archive/%(fname)s', ['repo_name', 'fname']);
49 pyroutes.register('files_nodelist_home', '/%(repo_name)s/nodelist/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
49 pyroutes.register('files_nodelist_home', '/%(repo_name)s/nodelist/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
50 pyroutes.register('files_nodetree_full', '/%(repo_name)s/nodetree_full/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
50 pyroutes.register('files_nodetree_full', '/%(repo_name)s/nodetree_full/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
51 pyroutes.register('summary_home_slash', '/%(repo_name)s/', ['repo_name']);
51 pyroutes.register('summary_home_slash', '/%(repo_name)s/', ['repo_name']);
52 pyroutes.register('summary_home', '/%(repo_name)s', ['repo_name']);
52 pyroutes.register('summary_home', '/%(repo_name)s', ['repo_name']);
53 pyroutes.register('favicon', '/favicon.ico', []);
53 pyroutes.register('favicon', '/favicon.ico', []);
54 pyroutes.register('robots', '/robots.txt', []);
54 pyroutes.register('robots', '/robots.txt', []);
55 pyroutes.register('auth_home', '/_admin/auth*traverse', []);
55 pyroutes.register('auth_home', '/_admin/auth*traverse', []);
56 pyroutes.register('global_integrations_new', '/_admin/integrations/new', []);
56 pyroutes.register('global_integrations_new', '/_admin/integrations/new', []);
57 pyroutes.register('global_integrations_home', '/_admin/integrations', []);
57 pyroutes.register('global_integrations_home', '/_admin/integrations', []);
58 pyroutes.register('global_integrations_list', '/_admin/integrations/%(integration)s', ['integration']);
58 pyroutes.register('global_integrations_list', '/_admin/integrations/%(integration)s', ['integration']);
59 pyroutes.register('global_integrations_create', '/_admin/integrations/%(integration)s/new', ['integration']);
59 pyroutes.register('global_integrations_create', '/_admin/integrations/%(integration)s/new', ['integration']);
60 pyroutes.register('global_integrations_edit', '/_admin/integrations/%(integration)s/%(integration_id)s', ['integration', 'integration_id']);
60 pyroutes.register('global_integrations_edit', '/_admin/integrations/%(integration)s/%(integration_id)s', ['integration', 'integration_id']);
61 pyroutes.register('repo_group_integrations_home', '%(repo_group_name)s/settings/integrations', ['repo_group_name']);
61 pyroutes.register('repo_group_integrations_home', '%(repo_group_name)s/settings/integrations', ['repo_group_name']);
62 pyroutes.register('repo_group_integrations_list', '%(repo_group_name)s/settings/integrations/%(integration)s', ['repo_group_name', 'integration']);
62 pyroutes.register('repo_group_integrations_list', '%(repo_group_name)s/settings/integrations/%(integration)s', ['repo_group_name', 'integration']);
63 pyroutes.register('repo_group_integrations_new', '%(repo_group_name)s/settings/integrations/new', ['repo_group_name']);
63 pyroutes.register('repo_group_integrations_new', '%(repo_group_name)s/settings/integrations/new', ['repo_group_name']);
64 pyroutes.register('repo_group_integrations_create', '%(repo_group_name)s/settings/integrations/%(integration)s/new', ['repo_group_name', 'integration']);
64 pyroutes.register('repo_group_integrations_create', '%(repo_group_name)s/settings/integrations/%(integration)s/new', ['repo_group_name', 'integration']);
65 pyroutes.register('repo_group_integrations_edit', '%(repo_group_name)s/settings/integrations/%(integration)s/%(integration_id)s', ['repo_group_name', 'integration', 'integration_id']);
65 pyroutes.register('repo_group_integrations_edit', '%(repo_group_name)s/settings/integrations/%(integration)s/%(integration_id)s', ['repo_group_name', 'integration', 'integration_id']);
66 pyroutes.register('repo_integrations_home', '%(repo_name)s/settings/integrations', ['repo_name']);
66 pyroutes.register('repo_integrations_home', '%(repo_name)s/settings/integrations', ['repo_name']);
67 pyroutes.register('repo_integrations_list', '%(repo_name)s/settings/integrations/%(integration)s', ['repo_name', 'integration']);
67 pyroutes.register('repo_integrations_list', '%(repo_name)s/settings/integrations/%(integration)s', ['repo_name', 'integration']);
68 pyroutes.register('repo_integrations_new', '%(repo_name)s/settings/integrations/new', ['repo_name']);
68 pyroutes.register('repo_integrations_new', '%(repo_name)s/settings/integrations/new', ['repo_name']);
69 pyroutes.register('repo_integrations_create', '%(repo_name)s/settings/integrations/%(integration)s/new', ['repo_name', 'integration']);
69 pyroutes.register('repo_integrations_create', '%(repo_name)s/settings/integrations/%(integration)s/new', ['repo_name', 'integration']);
70 pyroutes.register('repo_integrations_edit', '%(repo_name)s/settings/integrations/%(integration)s/%(integration_id)s', ['repo_name', 'integration', 'integration_id']);
70 pyroutes.register('repo_integrations_edit', '%(repo_name)s/settings/integrations/%(integration)s/%(integration_id)s', ['repo_name', 'integration', 'integration_id']);
71 pyroutes.register('ops_ping', '_admin/ops/ping', []);
71 pyroutes.register('ops_ping', '_admin/ops/ping', []);
72 pyroutes.register('admin_home', '/_admin', []);
73 pyroutes.register('admin_audit_logs', '_admin/audit_logs', []);
74 pyroutes.register('pull_requests_global_0', '_admin/pull_requests/%(pull_request_id)s', ['pull_request_id']);
75 pyroutes.register('pull_requests_global_1', '_admin/pull-requests/%(pull_request_id)s', ['pull_request_id']);
76 pyroutes.register('pull_requests_global', '_admin/pull-request/%(pull_request_id)s', ['pull_request_id']);
72 pyroutes.register('admin_settings_open_source', '_admin/settings/open_source', []);
77 pyroutes.register('admin_settings_open_source', '_admin/settings/open_source', []);
73 pyroutes.register('admin_settings_vcs_svn_generate_cfg', '_admin/settings/vcs/svn_generate_cfg', []);
78 pyroutes.register('admin_settings_vcs_svn_generate_cfg', '_admin/settings/vcs/svn_generate_cfg', []);
74 pyroutes.register('admin_settings_system', '_admin/settings/system', []);
79 pyroutes.register('admin_settings_system', '_admin/settings/system', []);
75 pyroutes.register('admin_settings_system_update', '_admin/settings/system/updates', []);
80 pyroutes.register('admin_settings_system_update', '_admin/settings/system/updates', []);
76 pyroutes.register('admin_settings_sessions', '_admin/settings/sessions', []);
81 pyroutes.register('admin_settings_sessions', '_admin/settings/sessions', []);
77 pyroutes.register('admin_settings_sessions_cleanup', '_admin/settings/sessions/cleanup', []);
82 pyroutes.register('admin_settings_sessions_cleanup', '_admin/settings/sessions/cleanup', []);
78 pyroutes.register('users', '_admin/users', []);
83 pyroutes.register('users', '_admin/users', []);
79 pyroutes.register('users_data', '_admin/users_data', []);
84 pyroutes.register('users_data', '_admin/users_data', []);
80 pyroutes.register('edit_user_auth_tokens', '_admin/users/%(user_id)s/edit/auth_tokens', ['user_id']);
85 pyroutes.register('edit_user_auth_tokens', '_admin/users/%(user_id)s/edit/auth_tokens', ['user_id']);
81 pyroutes.register('edit_user_auth_tokens_add', '_admin/users/%(user_id)s/edit/auth_tokens/new', ['user_id']);
86 pyroutes.register('edit_user_auth_tokens_add', '_admin/users/%(user_id)s/edit/auth_tokens/new', ['user_id']);
82 pyroutes.register('edit_user_auth_tokens_delete', '_admin/users/%(user_id)s/edit/auth_tokens/delete', ['user_id']);
87 pyroutes.register('edit_user_auth_tokens_delete', '_admin/users/%(user_id)s/edit/auth_tokens/delete', ['user_id']);
83 pyroutes.register('edit_user_groups_management', '_admin/users/%(user_id)s/edit/groups_management', ['user_id']);
88 pyroutes.register('edit_user_groups_management', '_admin/users/%(user_id)s/edit/groups_management', ['user_id']);
84 pyroutes.register('edit_user_groups_management_updates', '_admin/users/%(user_id)s/edit/edit_user_groups_management/updates', ['user_id']);
89 pyroutes.register('edit_user_groups_management_updates', '_admin/users/%(user_id)s/edit/edit_user_groups_management/updates', ['user_id']);
85 pyroutes.register('edit_user_audit_logs', '_admin/users/%(user_id)s/edit/audit', ['user_id']);
90 pyroutes.register('edit_user_audit_logs', '_admin/users/%(user_id)s/edit/audit', ['user_id']);
86 pyroutes.register('channelstream_connect', '/_admin/channelstream/connect', []);
91 pyroutes.register('channelstream_connect', '/_admin/channelstream/connect', []);
87 pyroutes.register('channelstream_subscribe', '/_admin/channelstream/subscribe', []);
92 pyroutes.register('channelstream_subscribe', '/_admin/channelstream/subscribe', []);
88 pyroutes.register('channelstream_proxy', '/_channelstream', []);
93 pyroutes.register('channelstream_proxy', '/_channelstream', []);
89 pyroutes.register('login', '/_admin/login', []);
94 pyroutes.register('login', '/_admin/login', []);
90 pyroutes.register('logout', '/_admin/logout', []);
95 pyroutes.register('logout', '/_admin/logout', []);
91 pyroutes.register('register', '/_admin/register', []);
96 pyroutes.register('register', '/_admin/register', []);
92 pyroutes.register('reset_password', '/_admin/password_reset', []);
97 pyroutes.register('reset_password', '/_admin/password_reset', []);
93 pyroutes.register('reset_password_confirmation', '/_admin/password_reset_confirmation', []);
98 pyroutes.register('reset_password_confirmation', '/_admin/password_reset_confirmation', []);
94 pyroutes.register('home', '/', []);
99 pyroutes.register('home', '/', []);
95 pyroutes.register('user_autocomplete_data', '/_users', []);
100 pyroutes.register('user_autocomplete_data', '/_users', []);
96 pyroutes.register('user_group_autocomplete_data', '/_user_groups', []);
101 pyroutes.register('user_group_autocomplete_data', '/_user_groups', []);
97 pyroutes.register('repo_list_data', '/_repos', []);
102 pyroutes.register('repo_list_data', '/_repos', []);
98 pyroutes.register('goto_switcher_data', '/_goto_data', []);
103 pyroutes.register('goto_switcher_data', '/_goto_data', []);
99 pyroutes.register('repo_summary_explicit', '/%(repo_name)s/summary', ['repo_name']);
104 pyroutes.register('repo_summary_explicit', '/%(repo_name)s/summary', ['repo_name']);
100 pyroutes.register('tags_home', '/%(repo_name)s/tags', ['repo_name']);
105 pyroutes.register('tags_home', '/%(repo_name)s/tags', ['repo_name']);
101 pyroutes.register('branches_home', '/%(repo_name)s/branches', ['repo_name']);
106 pyroutes.register('branches_home', '/%(repo_name)s/branches', ['repo_name']);
102 pyroutes.register('bookmarks_home', '/%(repo_name)s/bookmarks', ['repo_name']);
107 pyroutes.register('bookmarks_home', '/%(repo_name)s/bookmarks', ['repo_name']);
108 pyroutes.register('pullrequest_show', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
103 pyroutes.register('edit_repo', '/%(repo_name)s/settings', ['repo_name']);
109 pyroutes.register('edit_repo', '/%(repo_name)s/settings', ['repo_name']);
104 pyroutes.register('edit_repo_advanced', '/%(repo_name)s/settings/advanced', ['repo_name']);
110 pyroutes.register('edit_repo_advanced', '/%(repo_name)s/settings/advanced', ['repo_name']);
105 pyroutes.register('edit_repo_advanced_delete', '/%(repo_name)s/settings/advanced/delete', ['repo_name']);
111 pyroutes.register('edit_repo_advanced_delete', '/%(repo_name)s/settings/advanced/delete', ['repo_name']);
106 pyroutes.register('edit_repo_advanced_locking', '/%(repo_name)s/settings/advanced/locking', ['repo_name']);
112 pyroutes.register('edit_repo_advanced_locking', '/%(repo_name)s/settings/advanced/locking', ['repo_name']);
107 pyroutes.register('edit_repo_advanced_journal', '/%(repo_name)s/settings/advanced/journal', ['repo_name']);
113 pyroutes.register('edit_repo_advanced_journal', '/%(repo_name)s/settings/advanced/journal', ['repo_name']);
108 pyroutes.register('edit_repo_advanced_fork', '/%(repo_name)s/settings/advanced/fork', ['repo_name']);
114 pyroutes.register('edit_repo_advanced_fork', '/%(repo_name)s/settings/advanced/fork', ['repo_name']);
109 pyroutes.register('edit_repo_caches', '/%(repo_name)s/settings/caches', ['repo_name']);
115 pyroutes.register('edit_repo_caches', '/%(repo_name)s/settings/caches', ['repo_name']);
110 pyroutes.register('edit_repo_perms', '/%(repo_name)s/settings/permissions', ['repo_name']);
116 pyroutes.register('edit_repo_perms', '/%(repo_name)s/settings/permissions', ['repo_name']);
111 pyroutes.register('repo_reviewers', '/%(repo_name)s/settings/review/rules', ['repo_name']);
117 pyroutes.register('repo_reviewers', '/%(repo_name)s/settings/review/rules', ['repo_name']);
112 pyroutes.register('repo_maintenance', '/%(repo_name)s/settings/maintenance', ['repo_name']);
118 pyroutes.register('repo_maintenance', '/%(repo_name)s/settings/maintenance', ['repo_name']);
113 pyroutes.register('repo_maintenance_execute', '/%(repo_name)s/settings/maintenance/execute', ['repo_name']);
119 pyroutes.register('repo_maintenance_execute', '/%(repo_name)s/settings/maintenance/execute', ['repo_name']);
114 pyroutes.register('strip', '/%(repo_name)s/settings/strip', ['repo_name']);
120 pyroutes.register('strip', '/%(repo_name)s/settings/strip', ['repo_name']);
115 pyroutes.register('strip_check', '/%(repo_name)s/settings/strip_check', ['repo_name']);
121 pyroutes.register('strip_check', '/%(repo_name)s/settings/strip_check', ['repo_name']);
116 pyroutes.register('strip_execute', '/%(repo_name)s/settings/strip_execute', ['repo_name']);
122 pyroutes.register('strip_execute', '/%(repo_name)s/settings/strip_execute', ['repo_name']);
117 pyroutes.register('search', '/_admin/search', []);
123 pyroutes.register('search', '/_admin/search', []);
118 pyroutes.register('search_repo', '/%(repo_name)s/search', ['repo_name']);
124 pyroutes.register('search_repo', '/%(repo_name)s/search', ['repo_name']);
119 pyroutes.register('user_profile', '/_profiles/%(username)s', ['username']);
125 pyroutes.register('user_profile', '/_profiles/%(username)s', ['username']);
120 pyroutes.register('my_account_profile', '/_admin/my_account/profile', []);
126 pyroutes.register('my_account_profile', '/_admin/my_account/profile', []);
121 pyroutes.register('my_account_password', '/_admin/my_account/password', []);
127 pyroutes.register('my_account_password', '/_admin/my_account/password', []);
122 pyroutes.register('my_account_password_update', '/_admin/my_account/password', []);
128 pyroutes.register('my_account_password_update', '/_admin/my_account/password', []);
123 pyroutes.register('my_account_auth_tokens', '/_admin/my_account/auth_tokens', []);
129 pyroutes.register('my_account_auth_tokens', '/_admin/my_account/auth_tokens', []);
124 pyroutes.register('my_account_auth_tokens_add', '/_admin/my_account/auth_tokens/new', []);
130 pyroutes.register('my_account_auth_tokens_add', '/_admin/my_account/auth_tokens/new', []);
125 pyroutes.register('my_account_auth_tokens_delete', '/_admin/my_account/auth_tokens/delete', []);
131 pyroutes.register('my_account_auth_tokens_delete', '/_admin/my_account/auth_tokens/delete', []);
126 pyroutes.register('my_account_notifications_test_channelstream', '/_admin/my_account/test_channelstream', []);
132 pyroutes.register('my_account_notifications_test_channelstream', '/_admin/my_account/test_channelstream', []);
127 pyroutes.register('apiv2', '/_admin/api', []);
133 pyroutes.register('apiv2', '/_admin/api', []);
128 }
134 }
@@ -1,40 +1,40 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.mako"/>
2 <%inherit file="/base/base.mako"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 ${_('Admin journal')}
5 ${_('Admin audit logs')}
6 %if c.rhodecode_name:
6 %if c.rhodecode_name:
7 &middot; ${h.branding(c.rhodecode_name)}
7 &middot; ${h.branding(c.rhodecode_name)}
8 %endif
8 %endif
9 </%def>
9 </%def>
10
10
11 <%def name="breadcrumbs_links()">
11 <%def name="breadcrumbs_links()">
12 ${h.form(None, id_="filter_form", method="get")}
12 ${h.form(None, id_="filter_form", method="get")}
13 <input class="q_filter_box ${'' if c.search_term else 'initial'}" id="j_filter" size="15" type="text" name="filter" value="${c.search_term or ''}" placeholder="${_('journal filter...')}"/>
13 <input class="q_filter_box ${'' if c.search_term else 'initial'}" id="j_filter" size="15" type="text" name="filter" value="${c.search_term or ''}" placeholder="${_('filter...')}"/>
14 <input type='submit' value="${_('filter')}" class="btn" />
14 <input type='submit' value="${_('filter')}" class="btn" />
15 ${_('Admin journal')} - ${ungettext('%s entry', '%s entries', c.audit_logs.item_count) % (c.audit_logs.item_count)}
15 ${_('Audit logs')} - ${_ungettext('%s entry', '%s entries', c.audit_logs.item_count) % (c.audit_logs.item_count)}
16 ${h.end_form()}
16 ${h.end_form()}
17 <p class="tooltip filterexample" title="${h.tooltip(h.journal_filter_help())}">${_('Example Queries')}</p>
17 <p class="tooltip filterexample" title="${h.tooltip(h.journal_filter_help())}">${_('Example Queries')}</p>
18 </%def>
18 </%def>
19
19
20 <%def name="menu_bar_nav()">
20 <%def name="menu_bar_nav()">
21 ${self.menu_items(active='admin')}
21 ${self.menu_items(active='admin')}
22 </%def>
22 </%def>
23 <%def name="main()">
23 <%def name="main()">
24 <div class="box">
24 <div class="box">
25 <!-- box / title -->
25 <!-- box / title -->
26 <div class="title">
26 <div class="title">
27 ${self.breadcrumbs()}
27 ${self.breadcrumbs()}
28 </div>
28 </div>
29 <!-- end box / title -->
29 <!-- end box / title -->
30 <div class="table">
30 <div class="table">
31 <div id="user_log">
31 <div id="user_log">
32 ${c.log_data}
32 <%include file="/admin/admin_log_base.mako" />
33 </div>
33 </div>
34 </div>
34 </div>
35 </div>
35 </div>
36
36
37 <script>
37 <script>
38 $('#j_filter').autoGrowInput();
38 $('#j_filter').autoGrowInput();
39 </script>
39 </script>
40 </%def>
40 </%def>
@@ -1,116 +1,116 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.mako"/>
2 <%inherit file="/base/base.mako"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 ${_('Authentication Settings')}
5 ${_('Authentication Settings')}
6 %if c.rhodecode_name:
6 %if c.rhodecode_name:
7 &middot; ${h.branding(c.rhodecode_name)}}
7 &middot; ${h.branding(c.rhodecode_name)}}
8 %endif
8 %endif
9 </%def>
9 </%def>
10
10
11 <%def name="breadcrumbs_links()">
11 <%def name="breadcrumbs_links()">
12 ${h.link_to(_('Admin'),h.url('admin_home'))}
12 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
13 &raquo;
13 &raquo;
14 ${_('Authentication Plugins')}
14 ${_('Authentication Plugins')}
15 </%def>
15 </%def>
16
16
17 <%def name="menu_bar_nav()">
17 <%def name="menu_bar_nav()">
18 ${self.menu_items(active='admin')}
18 ${self.menu_items(active='admin')}
19 </%def>
19 </%def>
20
20
21 <%def name="main()">
21 <%def name="main()">
22
22
23 <div class="box">
23 <div class="box">
24 <div class="title">
24 <div class="title">
25 ${self.breadcrumbs()}
25 ${self.breadcrumbs()}
26 </div>
26 </div>
27
27
28 <div class='sidebar-col-wrapper'>
28 <div class='sidebar-col-wrapper'>
29
29
30 <div class="sidebar">
30 <div class="sidebar">
31 <ul class="nav nav-pills nav-stacked">
31 <ul class="nav nav-pills nav-stacked">
32 % for item in resource.get_root().get_nav_list():
32 % for item in resource.get_root().get_nav_list():
33 <li ${'class=active' if item == resource else ''}>
33 <li ${'class=active' if item == resource else ''}>
34 <a href="${request.resource_path(item, route_name='auth_home')}">${item.display_name}</a>
34 <a href="${request.resource_path(item, route_name='auth_home')}">${item.display_name}</a>
35 </li>
35 </li>
36 % endfor
36 % endfor
37 </ul>
37 </ul>
38 </div>
38 </div>
39
39
40 <div class="main-content-full-width">
40 <div class="main-content-full-width">
41 ${h.secure_form(request.resource_path(resource, route_name='auth_home'))}
41 ${h.secure_form(request.resource_path(resource, route_name='auth_home'))}
42 <div class="form">
42 <div class="form">
43
43
44 <div class="panel panel-default">
44 <div class="panel panel-default">
45
45
46 <div class="panel-heading">
46 <div class="panel-heading">
47 <h3 class="panel-title">${_("Enabled and Available Plugins")}</h3>
47 <h3 class="panel-title">${_("Enabled and Available Plugins")}</h3>
48 </div>
48 </div>
49
49
50 <div class="fields panel-body">
50 <div class="fields panel-body">
51
51
52 <div class="field">
52 <div class="field">
53 <div class="label">${_("Enabled Plugins")}</div>
53 <div class="label">${_("Enabled Plugins")}</div>
54 <div class="textarea text-area editor">
54 <div class="textarea text-area editor">
55 ${h.textarea('auth_plugins',cols=23,rows=5,class_="medium")}
55 ${h.textarea('auth_plugins',cols=23,rows=5,class_="medium")}
56 </div>
56 </div>
57 <p class="help-block">
57 <p class="help-block">
58 ${_('Add a list of plugins, separated by commas. '
58 ${_('Add a list of plugins, separated by commas. '
59 'The order of the plugins is also the order in which '
59 'The order of the plugins is also the order in which '
60 'RhodeCode Enterprise will try to authenticate a user.')}
60 'RhodeCode Enterprise will try to authenticate a user.')}
61 </p>
61 </p>
62 </div>
62 </div>
63
63
64 <div class="field">
64 <div class="field">
65 <div class="label">${_('Available Built-in Plugins')}</div>
65 <div class="label">${_('Available Built-in Plugins')}</div>
66 <ul class="auth_plugins">
66 <ul class="auth_plugins">
67 %for plugin in available_plugins:
67 %for plugin in available_plugins:
68 <li>
68 <li>
69 <div class="auth_buttons">
69 <div class="auth_buttons">
70 <span plugin_id="${plugin.get_id()}" class="toggle-plugin btn ${'btn-success' if plugin.get_id() in enabled_plugins else ''}">
70 <span plugin_id="${plugin.get_id()}" class="toggle-plugin btn ${'btn-success' if plugin.get_id() in enabled_plugins else ''}">
71 ${_('enabled') if plugin.get_id() in enabled_plugins else _('disabled')}
71 ${_('enabled') if plugin.get_id() in enabled_plugins else _('disabled')}
72 </span>
72 </span>
73 ${plugin.get_display_name()} (${plugin.get_id()})
73 ${plugin.get_display_name()} (${plugin.get_id()})
74 </div>
74 </div>
75 </li>
75 </li>
76 %endfor
76 %endfor
77 </ul>
77 </ul>
78 </div>
78 </div>
79
79
80 <div class="buttons">
80 <div class="buttons">
81 ${h.submit('save',_('Save'),class_="btn")}
81 ${h.submit('save',_('Save'),class_="btn")}
82 </div>
82 </div>
83 </div>
83 </div>
84 </div>
84 </div>
85 </div>
85 </div>
86 ${h.end_form()}
86 ${h.end_form()}
87 </div>
87 </div>
88 </div>
88 </div>
89 </div>
89 </div>
90
90
91 <script>
91 <script>
92 $('.toggle-plugin').click(function(e){
92 $('.toggle-plugin').click(function(e){
93 var auth_plugins_input = $('#auth_plugins');
93 var auth_plugins_input = $('#auth_plugins');
94 var notEmpty = function(element, index, array) {
94 var notEmpty = function(element, index, array) {
95 return (element != "");
95 return (element != "");
96 };
96 };
97 var elems = auth_plugins_input.val().split(',').filter(notEmpty);
97 var elems = auth_plugins_input.val().split(',').filter(notEmpty);
98 var cur_button = e.currentTarget;
98 var cur_button = e.currentTarget;
99 var plugin_id = $(cur_button).attr('plugin_id');
99 var plugin_id = $(cur_button).attr('plugin_id');
100 if($(cur_button).hasClass('btn-success')){
100 if($(cur_button).hasClass('btn-success')){
101 elems.splice(elems.indexOf(plugin_id), 1);
101 elems.splice(elems.indexOf(plugin_id), 1);
102 auth_plugins_input.val(elems.join(','));
102 auth_plugins_input.val(elems.join(','));
103 $(cur_button).removeClass('btn-success');
103 $(cur_button).removeClass('btn-success');
104 cur_button.innerHTML = _gettext('disabled');
104 cur_button.innerHTML = _gettext('disabled');
105 }
105 }
106 else{
106 else{
107 if(elems.indexOf(plugin_id) == -1){
107 if(elems.indexOf(plugin_id) == -1){
108 elems.push(plugin_id);
108 elems.push(plugin_id);
109 }
109 }
110 auth_plugins_input.val(elems.join(','));
110 auth_plugins_input.val(elems.join(','));
111 $(cur_button).addClass('btn-success');
111 $(cur_button).addClass('btn-success');
112 cur_button.innerHTML = _gettext('enabled');
112 cur_button.innerHTML = _gettext('enabled');
113 }
113 }
114 });
114 });
115 </script>
115 </script>
116 </%def>
116 </%def>
@@ -1,118 +1,118 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.mako"/>
2 <%inherit file="/base/base.mako"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 ${_('Authentication Settings')}
5 ${_('Authentication Settings')}
6 %if c.rhodecode_name:
6 %if c.rhodecode_name:
7 &middot; ${h.branding(c.rhodecode_name)}}
7 &middot; ${h.branding(c.rhodecode_name)}}
8 %endif
8 %endif
9 </%def>
9 </%def>
10
10
11 <%def name="breadcrumbs_links()">
11 <%def name="breadcrumbs_links()">
12 ${h.link_to(_('Admin'),h.url('admin_home'))}
12 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
13 &raquo;
13 &raquo;
14 ${h.link_to(_('Authentication Plugins'),request.resource_path(resource.__parent__, route_name='auth_home'))}
14 ${h.link_to(_('Authentication Plugins'),request.resource_path(resource.__parent__, route_name='auth_home'))}
15 &raquo;
15 &raquo;
16 ${resource.display_name}
16 ${resource.display_name}
17 </%def>
17 </%def>
18
18
19 <%def name="menu_bar_nav()">
19 <%def name="menu_bar_nav()">
20 ${self.menu_items(active='admin')}
20 ${self.menu_items(active='admin')}
21 </%def>
21 </%def>
22
22
23 <%def name="main()">
23 <%def name="main()">
24 <div class="box">
24 <div class="box">
25 <div class="title">
25 <div class="title">
26 ${self.breadcrumbs()}
26 ${self.breadcrumbs()}
27 </div>
27 </div>
28 <div class='sidebar-col-wrapper'>
28 <div class='sidebar-col-wrapper'>
29
29
30 ## TODO: This is repeated in the auth root template and should be merged
30 ## TODO: This is repeated in the auth root template and should be merged
31 ## into a single solution.
31 ## into a single solution.
32 <div class="sidebar">
32 <div class="sidebar">
33 <ul class="nav nav-pills nav-stacked">
33 <ul class="nav nav-pills nav-stacked">
34 % for item in resource.get_root().get_nav_list():
34 % for item in resource.get_root().get_nav_list():
35 <li ${'class=active' if item == resource else ''}>
35 <li ${'class=active' if item == resource else ''}>
36 <a href="${request.resource_path(item, route_name='auth_home')}">${item.display_name}</a>
36 <a href="${request.resource_path(item, route_name='auth_home')}">${item.display_name}</a>
37 </li>
37 </li>
38 % endfor
38 % endfor
39 </ul>
39 </ul>
40 </div>
40 </div>
41
41
42 <div class="main-content-full-width">
42 <div class="main-content-full-width">
43 <div class="panel panel-default">
43 <div class="panel panel-default">
44 <div class="panel-heading">
44 <div class="panel-heading">
45 <h3 class="panel-title">${_('Plugin')}: ${resource.display_name}</h3>
45 <h3 class="panel-title">${_('Plugin')}: ${resource.display_name}</h3>
46 </div>
46 </div>
47 <div class="panel-body">
47 <div class="panel-body">
48 <div class="plugin_form">
48 <div class="plugin_form">
49 <div class="fields">
49 <div class="fields">
50 ${h.secure_form(request.resource_path(resource, route_name='auth_home'))}
50 ${h.secure_form(request.resource_path(resource, route_name='auth_home'))}
51 <div class="form">
51 <div class="form">
52
52
53 %for node in plugin.get_settings_schema():
53 %for node in plugin.get_settings_schema():
54 <% label_css_class = ("label-checkbox" if (node.widget == "bool") else "") %>
54 <% label_css_class = ("label-checkbox" if (node.widget == "bool") else "") %>
55 <div class="field">
55 <div class="field">
56 <div class="label ${label_css_class}"><label for="${node.name}">${node.title}</label></div>
56 <div class="label ${label_css_class}"><label for="${node.name}">${node.title}</label></div>
57 <div class="input">
57 <div class="input">
58 %if node.widget in ["string", "int", "unicode"]:
58 %if node.widget in ["string", "int", "unicode"]:
59 ${h.text(node.name, defaults.get(node.name), class_="medium")}
59 ${h.text(node.name, defaults.get(node.name), class_="medium")}
60 %elif node.widget == "password":
60 %elif node.widget == "password":
61 ${h.password(node.name, defaults.get(node.name), class_="medium")}
61 ${h.password(node.name, defaults.get(node.name), class_="medium")}
62 %elif node.widget == "bool":
62 %elif node.widget == "bool":
63 <div class="checkbox">${h.checkbox(node.name, True, checked=defaults.get(node.name))}</div>
63 <div class="checkbox">${h.checkbox(node.name, True, checked=defaults.get(node.name))}</div>
64 %elif node.widget == "select":
64 %elif node.widget == "select":
65 ${h.select(node.name, defaults.get(node.name), node.validator.choices)}
65 ${h.select(node.name, defaults.get(node.name), node.validator.choices)}
66 %elif node.widget == "readonly":
66 %elif node.widget == "readonly":
67 ${node.default}
67 ${node.default}
68 %else:
68 %else:
69 This field is of type ${node.typ}, which cannot be displayed. Must be one of [string|int|bool|select].
69 This field is of type ${node.typ}, which cannot be displayed. Must be one of [string|int|bool|select].
70 %endif
70 %endif
71 %if node.name in errors:
71 %if node.name in errors:
72 <span class="error-message">${errors.get(node.name)}</span>
72 <span class="error-message">${errors.get(node.name)}</span>
73 <br />
73 <br />
74 %endif
74 %endif
75 <p class="help-block pre-formatting">${node.description}</p>
75 <p class="help-block pre-formatting">${node.description}</p>
76 </div>
76 </div>
77 </div>
77 </div>
78 %endfor
78 %endfor
79
79
80 ## Allow derived templates to add something below the form
80 ## Allow derived templates to add something below the form
81 ## input fields
81 ## input fields
82 %if hasattr(next, 'below_form_fields'):
82 %if hasattr(next, 'below_form_fields'):
83 ${next.below_form_fields()}
83 ${next.below_form_fields()}
84 %endif
84 %endif
85
85
86 <div class="buttons">
86 <div class="buttons">
87 ${h.submit('save',_('Save'),class_="btn")}
87 ${h.submit('save',_('Save'),class_="btn")}
88 </div>
88 </div>
89
89
90 </div>
90 </div>
91 ${h.end_form()}
91 ${h.end_form()}
92 </div>
92 </div>
93 </div>
93 </div>
94 </div>
94 </div>
95 </div>
95 </div>
96 </div>
96 </div>
97
97
98 </div>
98 </div>
99 </div>
99 </div>
100
100
101 ## TODO: Ugly hack to get ldap select elements to work.
101 ## TODO: Ugly hack to get ldap select elements to work.
102 ## Find a solution to integrate this nicely.
102 ## Find a solution to integrate this nicely.
103 <script>
103 <script>
104 $(document).ready(function() {
104 $(document).ready(function() {
105 var select2Options = {
105 var select2Options = {
106 containerCssClass: 'drop-menu',
106 containerCssClass: 'drop-menu',
107 dropdownCssClass: 'drop-menu-dropdown',
107 dropdownCssClass: 'drop-menu-dropdown',
108 dropdownAutoWidth: true,
108 dropdownAutoWidth: true,
109 minimumResultsForSearch: -1
109 minimumResultsForSearch: -1
110 };
110 };
111 $("#tls_kind").select2(select2Options);
111 $("#tls_kind").select2(select2Options);
112 $("#tls_reqcert").select2(select2Options);
112 $("#tls_reqcert").select2(select2Options);
113 $("#search_scope").select2(select2Options);
113 $("#search_scope").select2(select2Options);
114 $("#group_extraction_type").select2(select2Options);
114 $("#group_extraction_type").select2(select2Options);
115 $("#admin_groups_sync").select2(select2Options);
115 $("#admin_groups_sync").select2(select2Options);
116 });
116 });
117 </script>
117 </script>
118 </%def>
118 </%def>
@@ -1,42 +1,42 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.mako"/>
2 <%inherit file="/base/base.mako"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 ${_('Repositories defaults')}
5 ${_('Repositories defaults')}
6 %if c.rhodecode_name:
6 %if c.rhodecode_name:
7 &middot; ${h.branding(c.rhodecode_name)}
7 &middot; ${h.branding(c.rhodecode_name)}
8 %endif
8 %endif
9 </%def>
9 </%def>
10
10
11 <%def name="breadcrumbs_links()">
11 <%def name="breadcrumbs_links()">
12 ${h.link_to(_('Admin'),h.url('admin_home'))}
12 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
13 &raquo;
13 &raquo;
14 ${_('Repositories defaults')}
14 ${_('Repositories defaults')}
15 </%def>
15 </%def>
16
16
17 <%def name="menu_bar_nav()">
17 <%def name="menu_bar_nav()">
18 ${self.menu_items(active='admin')}
18 ${self.menu_items(active='admin')}
19 </%def>
19 </%def>
20
20
21 <%def name="main()">
21 <%def name="main()">
22 <div class="box">
22 <div class="box">
23 <div class="title">
23 <div class="title">
24 ${self.breadcrumbs()}
24 ${self.breadcrumbs()}
25 </div>
25 </div>
26
26
27 ##main
27 ##main
28 <div class="sidebar-col-wrapper">
28 <div class="sidebar-col-wrapper">
29 <div class="sidebar">
29 <div class="sidebar">
30 <ul class="nav nav-pills nav-stacked">
30 <ul class="nav nav-pills nav-stacked">
31 <li class="${'active' if c.active=='repositories' else ''}"><a href="${h.url('admin_defaults_repositories')}">${_('Repository')}</a></li>
31 <li class="${'active' if c.active=='repositories' else ''}"><a href="${h.url('admin_defaults_repositories')}">${_('Repository')}</a></li>
32 </ul>
32 </ul>
33 </div>
33 </div>
34
34
35 <div class="main-content-full-width">
35 <div class="main-content-full-width">
36 <%include file="/admin/defaults/defaults_${c.active}.mako"/>
36 <%include file="/admin/defaults/defaults_${c.active}.mako"/>
37 </div>
37 </div>
38
38
39 </div>
39 </div>
40 </div>
40 </div>
41
41
42 </%def>
42 </%def>
@@ -1,42 +1,42 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%!
2 <%!
3 def inherit(context):
3 def inherit(context):
4 if context['c'].repo:
4 if context['c'].repo:
5 return "/admin/repos/repo_edit.mako"
5 return "/admin/repos/repo_edit.mako"
6 elif context['c'].repo_group:
6 elif context['c'].repo_group:
7 return "/admin/repo_groups/repo_group_edit.mako"
7 return "/admin/repo_groups/repo_group_edit.mako"
8 else:
8 else:
9 return "/admin/settings/settings.mako"
9 return "/admin/settings/settings.mako"
10 %>
10 %>
11 <%inherit file="${inherit(context)}" />
11 <%inherit file="${inherit(context)}" />
12
12
13 <%def name="title()">
13 <%def name="title()">
14 ${_('Integrations Settings')}
14 ${_('Integrations Settings')}
15 %if c.rhodecode_name:
15 %if c.rhodecode_name:
16 &middot; ${h.branding(c.rhodecode_name)}
16 &middot; ${h.branding(c.rhodecode_name)}
17 %endif
17 %endif
18 </%def>
18 </%def>
19
19
20 <%def name="breadcrumbs_links()">
20 <%def name="breadcrumbs_links()">
21 ${h.link_to(_('Admin'),h.url('admin_home'))}
21 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
22 &raquo;
22 &raquo;
23 ${_('Integrations')}
23 ${_('Integrations')}
24 </%def>
24 </%def>
25
25
26 <%def name="menu_bar_nav()">
26 <%def name="menu_bar_nav()">
27 %if c.repo:
27 %if c.repo:
28 ${self.menu_items(active='repositories')}
28 ${self.menu_items(active='repositories')}
29 %else:
29 %else:
30 ${self.menu_items(active='admin')}
30 ${self.menu_items(active='admin')}
31 %endif
31 %endif
32 </%def>
32 </%def>
33
33
34 <%def name="menu_bar_subnav()">
34 <%def name="menu_bar_subnav()">
35 %if c.repo:
35 %if c.repo:
36 ${self.repo_menu(active='options')}
36 ${self.repo_menu(active='options')}
37 %endif
37 %endif
38 </%def>
38 </%def>
39
39
40 <%def name="main_content()">
40 <%def name="main_content()">
41 ${next.body()}
41 ${next.body()}
42 </%def>
42 </%def>
@@ -1,69 +1,69 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="base.mako"/>
2 <%inherit file="base.mako"/>
3
3
4 <%def name="breadcrumbs_links()">
4 <%def name="breadcrumbs_links()">
5 %if c.repo:
5 %if c.repo:
6 ${h.link_to('Settings',h.route_path('edit_repo', repo_name=c.repo.repo_name))}
6 ${h.link_to('Settings',h.route_path('edit_repo', repo_name=c.repo.repo_name))}
7 &raquo;
7 &raquo;
8 ${h.link_to(_('Integrations'),request.route_url(route_name='repo_integrations_home', repo_name=c.repo.repo_name))}
8 ${h.link_to(_('Integrations'),request.route_url(route_name='repo_integrations_home', repo_name=c.repo.repo_name))}
9 &raquo;
9 &raquo;
10 ${h.link_to(current_IntegrationType.display_name,
10 ${h.link_to(current_IntegrationType.display_name,
11 request.route_url(route_name='repo_integrations_list',
11 request.route_url(route_name='repo_integrations_list',
12 repo_name=c.repo.repo_name,
12 repo_name=c.repo.repo_name,
13 integration=current_IntegrationType.key))}
13 integration=current_IntegrationType.key))}
14 %elif c.repo_group:
14 %elif c.repo_group:
15 ${h.link_to(_('Admin'),h.url('admin_home'))}
15 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
16 &raquo;
16 &raquo;
17 ${h.link_to(_('Repository Groups'),h.url('repo_groups'))}
17 ${h.link_to(_('Repository Groups'),h.url('repo_groups'))}
18 &raquo;
18 &raquo;
19 ${h.link_to(c.repo_group.group_name,h.url('edit_repo_group', group_name=c.repo_group.group_name))}
19 ${h.link_to(c.repo_group.group_name,h.url('edit_repo_group', group_name=c.repo_group.group_name))}
20 &raquo;
20 &raquo;
21 ${h.link_to(_('Integrations'),request.route_url(route_name='repo_group_integrations_home', repo_group_name=c.repo_group.group_name))}
21 ${h.link_to(_('Integrations'),request.route_url(route_name='repo_group_integrations_home', repo_group_name=c.repo_group.group_name))}
22 &raquo;
22 &raquo;
23 ${h.link_to(current_IntegrationType.display_name,
23 ${h.link_to(current_IntegrationType.display_name,
24 request.route_url(route_name='repo_group_integrations_list',
24 request.route_url(route_name='repo_group_integrations_list',
25 repo_group_name=c.repo_group.group_name,
25 repo_group_name=c.repo_group.group_name,
26 integration=current_IntegrationType.key))}
26 integration=current_IntegrationType.key))}
27 %else:
27 %else:
28 ${h.link_to(_('Admin'),h.url('admin_home'))}
28 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
29 &raquo;
29 &raquo;
30 ${h.link_to(_('Settings'),h.url('admin_settings'))}
30 ${h.link_to(_('Settings'),h.url('admin_settings'))}
31 &raquo;
31 &raquo;
32 ${h.link_to(_('Integrations'),request.route_url(route_name='global_integrations_home'))}
32 ${h.link_to(_('Integrations'),request.route_url(route_name='global_integrations_home'))}
33 &raquo;
33 &raquo;
34 ${h.link_to(current_IntegrationType.display_name,
34 ${h.link_to(current_IntegrationType.display_name,
35 request.route_url(route_name='global_integrations_list',
35 request.route_url(route_name='global_integrations_list',
36 integration=current_IntegrationType.key))}
36 integration=current_IntegrationType.key))}
37 %endif
37 %endif
38
38
39 %if integration:
39 %if integration:
40 &raquo;
40 &raquo;
41 ${integration.name}
41 ${integration.name}
42 %elif current_IntegrationType:
42 %elif current_IntegrationType:
43 &raquo;
43 &raquo;
44 ${current_IntegrationType.display_name}
44 ${current_IntegrationType.display_name}
45 %endif
45 %endif
46 </%def>
46 </%def>
47
47
48 <style>
48 <style>
49 .control-inputs.item-options, .control-inputs.item-settings {
49 .control-inputs.item-options, .control-inputs.item-settings {
50 float: left;
50 float: left;
51 width: 100%;
51 width: 100%;
52 }
52 }
53 </style>
53 </style>
54 <div class="panel panel-default">
54 <div class="panel panel-default">
55 <div class="panel-heading">
55 <div class="panel-heading">
56 <h2 class="panel-title">
56 <h2 class="panel-title">
57 %if integration:
57 %if integration:
58 ${current_IntegrationType.display_name} - ${integration.name}
58 ${current_IntegrationType.display_name} - ${integration.name}
59 %else:
59 %else:
60 ${_('Create New %(integration_type)s Integration') % {
60 ${_('Create New %(integration_type)s Integration') % {
61 'integration_type': current_IntegrationType.display_name
61 'integration_type': current_IntegrationType.display_name
62 }}
62 }}
63 %endif
63 %endif
64 </h2>
64 </h2>
65 </div>
65 </div>
66 <div class="panel-body">
66 <div class="panel-body">
67 ${form.render() | n}
67 ${form.render() | n}
68 </div>
68 </div>
69 </div>
69 </div>
@@ -1,252 +1,252 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="base.mako"/>
2 <%inherit file="base.mako"/>
3
3
4 <%def name="breadcrumbs_links()">
4 <%def name="breadcrumbs_links()">
5 %if c.repo:
5 %if c.repo:
6 ${h.link_to('Settings',h.route_path('edit_repo', repo_name=c.repo.repo_name))}
6 ${h.link_to('Settings',h.route_path('edit_repo', repo_name=c.repo.repo_name))}
7 %elif c.repo_group:
7 %elif c.repo_group:
8 ${h.link_to(_('Admin'),h.url('admin_home'))}
8 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
9 &raquo;
9 &raquo;
10 ${h.link_to(_('Repository Groups'),h.url('repo_groups'))}
10 ${h.link_to(_('Repository Groups'),h.url('repo_groups'))}
11 &raquo;
11 &raquo;
12 ${h.link_to(c.repo_group.group_name,h.url('edit_repo_group', group_name=c.repo_group.group_name))}
12 ${h.link_to(c.repo_group.group_name,h.url('edit_repo_group', group_name=c.repo_group.group_name))}
13 %else:
13 %else:
14 ${h.link_to(_('Admin'),h.url('admin_home'))}
14 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
15 &raquo;
15 &raquo;
16 ${h.link_to(_('Settings'),h.url('admin_settings'))}
16 ${h.link_to(_('Settings'),h.url('admin_settings'))}
17 %endif
17 %endif
18 %if current_IntegrationType:
18 %if current_IntegrationType:
19 &raquo;
19 &raquo;
20 %if c.repo:
20 %if c.repo:
21 ${h.link_to(_('Integrations'),
21 ${h.link_to(_('Integrations'),
22 request.route_url(route_name='repo_integrations_home',
22 request.route_url(route_name='repo_integrations_home',
23 repo_name=c.repo.repo_name))}
23 repo_name=c.repo.repo_name))}
24 %elif c.repo_group:
24 %elif c.repo_group:
25 ${h.link_to(_('Integrations'),
25 ${h.link_to(_('Integrations'),
26 request.route_url(route_name='repo_group_integrations_home',
26 request.route_url(route_name='repo_group_integrations_home',
27 repo_group_name=c.repo_group.group_name))}
27 repo_group_name=c.repo_group.group_name))}
28 %else:
28 %else:
29 ${h.link_to(_('Integrations'),
29 ${h.link_to(_('Integrations'),
30 request.route_url(route_name='global_integrations_home'))}
30 request.route_url(route_name='global_integrations_home'))}
31 %endif
31 %endif
32 &raquo;
32 &raquo;
33 ${current_IntegrationType.display_name}
33 ${current_IntegrationType.display_name}
34 %else:
34 %else:
35 &raquo;
35 &raquo;
36 ${_('Integrations')}
36 ${_('Integrations')}
37 %endif
37 %endif
38 </%def>
38 </%def>
39
39
40 <div class="panel panel-default">
40 <div class="panel panel-default">
41 <div class="panel-heading">
41 <div class="panel-heading">
42 <h3 class="panel-title">
42 <h3 class="panel-title">
43 %if c.repo:
43 %if c.repo:
44 ${_('Current Integrations for Repository: {repo_name}').format(repo_name=c.repo.repo_name)}
44 ${_('Current Integrations for Repository: {repo_name}').format(repo_name=c.repo.repo_name)}
45 %elif c.repo_group:
45 %elif c.repo_group:
46 ${_('Current Integrations for repository group: {repo_group_name}').format(repo_group_name=c.repo_group.group_name)}
46 ${_('Current Integrations for repository group: {repo_group_name}').format(repo_group_name=c.repo_group.group_name)}
47 %else:
47 %else:
48 ${_('Current Integrations')}
48 ${_('Current Integrations')}
49 %endif
49 %endif
50 </h3>
50 </h3>
51 </div>
51 </div>
52 <div class="panel-body">
52 <div class="panel-body">
53 <%
53 <%
54 if c.repo:
54 if c.repo:
55 home_url = request.route_path('repo_integrations_home',
55 home_url = request.route_path('repo_integrations_home',
56 repo_name=c.repo.repo_name)
56 repo_name=c.repo.repo_name)
57 elif c.repo_group:
57 elif c.repo_group:
58 home_url = request.route_path('repo_group_integrations_home',
58 home_url = request.route_path('repo_group_integrations_home',
59 repo_group_name=c.repo_group.group_name)
59 repo_group_name=c.repo_group.group_name)
60 else:
60 else:
61 home_url = request.route_path('global_integrations_home')
61 home_url = request.route_path('global_integrations_home')
62 %>
62 %>
63
63
64 <a href="${home_url}" class="btn ${not current_IntegrationType and 'btn-primary' or ''}">${_('All')}</a>
64 <a href="${home_url}" class="btn ${not current_IntegrationType and 'btn-primary' or ''}">${_('All')}</a>
65
65
66 %for integration_key, IntegrationType in available_integrations.items():
66 %for integration_key, IntegrationType in available_integrations.items():
67 <%
67 <%
68 if c.repo:
68 if c.repo:
69 list_url = request.route_path('repo_integrations_list',
69 list_url = request.route_path('repo_integrations_list',
70 repo_name=c.repo.repo_name,
70 repo_name=c.repo.repo_name,
71 integration=integration_key)
71 integration=integration_key)
72 elif c.repo_group:
72 elif c.repo_group:
73 list_url = request.route_path('repo_group_integrations_list',
73 list_url = request.route_path('repo_group_integrations_list',
74 repo_group_name=c.repo_group.group_name,
74 repo_group_name=c.repo_group.group_name,
75 integration=integration_key)
75 integration=integration_key)
76 else:
76 else:
77 list_url = request.route_path('global_integrations_list',
77 list_url = request.route_path('global_integrations_list',
78 integration=integration_key)
78 integration=integration_key)
79 %>
79 %>
80 <a href="${list_url}"
80 <a href="${list_url}"
81 class="btn ${current_IntegrationType and integration_key == current_IntegrationType.key and 'btn-primary' or ''}">
81 class="btn ${current_IntegrationType and integration_key == current_IntegrationType.key and 'btn-primary' or ''}">
82 ${IntegrationType.display_name}
82 ${IntegrationType.display_name}
83 </a>
83 </a>
84 %endfor
84 %endfor
85
85
86 <%
86 <%
87 if c.repo:
87 if c.repo:
88 create_url = h.route_path('repo_integrations_new', repo_name=c.repo.repo_name)
88 create_url = h.route_path('repo_integrations_new', repo_name=c.repo.repo_name)
89 elif c.repo_group:
89 elif c.repo_group:
90 create_url = h.route_path('repo_group_integrations_new', repo_group_name=c.repo_group.group_name)
90 create_url = h.route_path('repo_group_integrations_new', repo_group_name=c.repo_group.group_name)
91 else:
91 else:
92 create_url = h.route_path('global_integrations_new')
92 create_url = h.route_path('global_integrations_new')
93 %>
93 %>
94 <p class="pull-right">
94 <p class="pull-right">
95 <a href="${create_url}" class="btn btn-small btn-success">${_(u'Create new integration')}</a>
95 <a href="${create_url}" class="btn btn-small btn-success">${_(u'Create new integration')}</a>
96 </p>
96 </p>
97
97
98 <table class="rctable integrations">
98 <table class="rctable integrations">
99 <thead>
99 <thead>
100 <tr>
100 <tr>
101 <th><a href="?sort=enabled:${rev_sort_dir}">${_('Enabled')}</a></th>
101 <th><a href="?sort=enabled:${rev_sort_dir}">${_('Enabled')}</a></th>
102 <th><a href="?sort=name:${rev_sort_dir}">${_('Name')}</a></th>
102 <th><a href="?sort=name:${rev_sort_dir}">${_('Name')}</a></th>
103 <th colspan="2"><a href="?sort=integration_type:${rev_sort_dir}">${_('Type')}</a></th>
103 <th colspan="2"><a href="?sort=integration_type:${rev_sort_dir}">${_('Type')}</a></th>
104 <th><a href="?sort=scope:${rev_sort_dir}">${_('Scope')}</a></th>
104 <th><a href="?sort=scope:${rev_sort_dir}">${_('Scope')}</a></th>
105 <th>${_('Actions')}</th>
105 <th>${_('Actions')}</th>
106 <th></th>
106 <th></th>
107 </tr>
107 </tr>
108 </thead>
108 </thead>
109 <tbody>
109 <tbody>
110 %if not integrations_list:
110 %if not integrations_list:
111 <tr>
111 <tr>
112 <td colspan="7">
112 <td colspan="7">
113 <% integration_type = current_IntegrationType and current_IntegrationType.display_name or '' %>
113 <% integration_type = current_IntegrationType and current_IntegrationType.display_name or '' %>
114 %if c.repo:
114 %if c.repo:
115 ${_('No {type} integrations for repo {repo} exist yet.').format(type=integration_type, repo=c.repo.repo_name)}
115 ${_('No {type} integrations for repo {repo} exist yet.').format(type=integration_type, repo=c.repo.repo_name)}
116 %elif c.repo_group:
116 %elif c.repo_group:
117 ${_('No {type} integrations for repogroup {repogroup} exist yet.').format(type=integration_type, repogroup=c.repo_group.group_name)}
117 ${_('No {type} integrations for repogroup {repogroup} exist yet.').format(type=integration_type, repogroup=c.repo_group.group_name)}
118 %else:
118 %else:
119 ${_('No {type} integrations exist yet.').format(type=integration_type)}
119 ${_('No {type} integrations exist yet.').format(type=integration_type)}
120 %endif
120 %endif
121
121
122 %if current_IntegrationType:
122 %if current_IntegrationType:
123 <%
123 <%
124 if c.repo:
124 if c.repo:
125 create_url = h.route_path('repo_integrations_create', repo_name=c.repo.repo_name, integration=current_IntegrationType.key)
125 create_url = h.route_path('repo_integrations_create', repo_name=c.repo.repo_name, integration=current_IntegrationType.key)
126 elif c.repo_group:
126 elif c.repo_group:
127 create_url = h.route_path('repo_group_integrations_create', repo_group_name=c.repo_group.group_name, integration=current_IntegrationType.key)
127 create_url = h.route_path('repo_group_integrations_create', repo_group_name=c.repo_group.group_name, integration=current_IntegrationType.key)
128 else:
128 else:
129 create_url = h.route_path('global_integrations_create', integration=current_IntegrationType.key)
129 create_url = h.route_path('global_integrations_create', integration=current_IntegrationType.key)
130 %>
130 %>
131 %endif
131 %endif
132
132
133 <a href="${create_url}">${_(u'Create one')}</a>
133 <a href="${create_url}">${_(u'Create one')}</a>
134 </td>
134 </td>
135 </tr>
135 </tr>
136 %endif
136 %endif
137 %for IntegrationType, integration in integrations_list:
137 %for IntegrationType, integration in integrations_list:
138 <tr id="integration_${integration.integration_id}">
138 <tr id="integration_${integration.integration_id}">
139 <td class="td-enabled">
139 <td class="td-enabled">
140 %if integration.enabled:
140 %if integration.enabled:
141 <div class="flag_status approved pull-left"></div>
141 <div class="flag_status approved pull-left"></div>
142 %else:
142 %else:
143 <div class="flag_status rejected pull-left"></div>
143 <div class="flag_status rejected pull-left"></div>
144 %endif
144 %endif
145 </td>
145 </td>
146 <td class="td-description">
146 <td class="td-description">
147 ${integration.name}
147 ${integration.name}
148 </td>
148 </td>
149 <td class="td-icon">
149 <td class="td-icon">
150 %if integration.integration_type in available_integrations:
150 %if integration.integration_type in available_integrations:
151 <div class="integration-icon">
151 <div class="integration-icon">
152 ${available_integrations[integration.integration_type].icon|n}
152 ${available_integrations[integration.integration_type].icon|n}
153 </div>
153 </div>
154 %else:
154 %else:
155 ?
155 ?
156 %endif
156 %endif
157 </td>
157 </td>
158 <td class="td-type">
158 <td class="td-type">
159 ${integration.integration_type}
159 ${integration.integration_type}
160 </td>
160 </td>
161 <td class="td-scope">
161 <td class="td-scope">
162 %if integration.repo:
162 %if integration.repo:
163 <a href="${h.url('summary_home', repo_name=integration.repo.repo_name)}">
163 <a href="${h.url('summary_home', repo_name=integration.repo.repo_name)}">
164 ${_('repo')}:${integration.repo.repo_name}
164 ${_('repo')}:${integration.repo.repo_name}
165 </a>
165 </a>
166 %elif integration.repo_group:
166 %elif integration.repo_group:
167 <a href="${h.url('repo_group_home', group_name=integration.repo_group.group_name)}">
167 <a href="${h.url('repo_group_home', group_name=integration.repo_group.group_name)}">
168 ${_('repogroup')}:${integration.repo_group.group_name}
168 ${_('repogroup')}:${integration.repo_group.group_name}
169 %if integration.child_repos_only:
169 %if integration.child_repos_only:
170 ${_('child repos only')}
170 ${_('child repos only')}
171 %else:
171 %else:
172 ${_('cascade to all')}
172 ${_('cascade to all')}
173 %endif
173 %endif
174 </a>
174 </a>
175 %else:
175 %else:
176 %if integration.child_repos_only:
176 %if integration.child_repos_only:
177 ${_('top level repos only')}
177 ${_('top level repos only')}
178 %else:
178 %else:
179 ${_('global')}
179 ${_('global')}
180 %endif
180 %endif
181 </td>
181 </td>
182 %endif
182 %endif
183 <td class="td-action">
183 <td class="td-action">
184 %if not IntegrationType:
184 %if not IntegrationType:
185 ${_('unknown integration')}
185 ${_('unknown integration')}
186 %else:
186 %else:
187 <%
187 <%
188 if c.repo:
188 if c.repo:
189 edit_url = request.route_path('repo_integrations_edit',
189 edit_url = request.route_path('repo_integrations_edit',
190 repo_name=c.repo.repo_name,
190 repo_name=c.repo.repo_name,
191 integration=integration.integration_type,
191 integration=integration.integration_type,
192 integration_id=integration.integration_id)
192 integration_id=integration.integration_id)
193 elif c.repo_group:
193 elif c.repo_group:
194 edit_url = request.route_path('repo_group_integrations_edit',
194 edit_url = request.route_path('repo_group_integrations_edit',
195 repo_group_name=c.repo_group.group_name,
195 repo_group_name=c.repo_group.group_name,
196 integration=integration.integration_type,
196 integration=integration.integration_type,
197 integration_id=integration.integration_id)
197 integration_id=integration.integration_id)
198 else:
198 else:
199 edit_url = request.route_path('global_integrations_edit',
199 edit_url = request.route_path('global_integrations_edit',
200 integration=integration.integration_type,
200 integration=integration.integration_type,
201 integration_id=integration.integration_id)
201 integration_id=integration.integration_id)
202 %>
202 %>
203 <div class="grid_edit">
203 <div class="grid_edit">
204 <a href="${edit_url}">${_('Edit')}</a>
204 <a href="${edit_url}">${_('Edit')}</a>
205 </div>
205 </div>
206 <div class="grid_delete">
206 <div class="grid_delete">
207 <a href="${edit_url}"
207 <a href="${edit_url}"
208 class="btn btn-link btn-danger delete_integration_entry"
208 class="btn btn-link btn-danger delete_integration_entry"
209 data-desc="${integration.name}"
209 data-desc="${integration.name}"
210 data-uid="${integration.integration_id}">
210 data-uid="${integration.integration_id}">
211 ${_('Delete')}
211 ${_('Delete')}
212 </a>
212 </a>
213 </div>
213 </div>
214 %endif
214 %endif
215 </td>
215 </td>
216 </tr>
216 </tr>
217 %endfor
217 %endfor
218 <tr id="last-row"></tr>
218 <tr id="last-row"></tr>
219 </tbody>
219 </tbody>
220 </table>
220 </table>
221 <div class="integrations-paginator">
221 <div class="integrations-paginator">
222 <div class="pagination-wh pagination-left">
222 <div class="pagination-wh pagination-left">
223 ${integrations_list.pager('$link_previous ~2~ $link_next')}
223 ${integrations_list.pager('$link_previous ~2~ $link_next')}
224 </div>
224 </div>
225 </div>
225 </div>
226 </div>
226 </div>
227 </div>
227 </div>
228 <script type="text/javascript">
228 <script type="text/javascript">
229 var delete_integration = function(entry) {
229 var delete_integration = function(entry) {
230 if (confirm("Confirm to remove this integration: "+$(entry).data('desc'))) {
230 if (confirm("Confirm to remove this integration: "+$(entry).data('desc'))) {
231 var request = $.ajax({
231 var request = $.ajax({
232 type: "POST",
232 type: "POST",
233 url: $(entry).attr('href'),
233 url: $(entry).attr('href'),
234 data: {
234 data: {
235 'delete': 'delete',
235 'delete': 'delete',
236 'csrf_token': CSRF_TOKEN
236 'csrf_token': CSRF_TOKEN
237 },
237 },
238 success: function(){
238 success: function(){
239 location.reload();
239 location.reload();
240 },
240 },
241 error: function(data, textStatus, errorThrown){
241 error: function(data, textStatus, errorThrown){
242 alert("Error while deleting entry.\nError code {0} ({1}). URL: {2}".format(data.status,data.statusText,$(entry)[0].url));
242 alert("Error while deleting entry.\nError code {0} ({1}). URL: {2}".format(data.status,data.statusText,$(entry)[0].url));
243 }
243 }
244 });
244 });
245 };
245 };
246 };
246 };
247
247
248 $('.delete_integration_entry').on('click', function(e){
248 $('.delete_integration_entry').on('click', function(e){
249 e.preventDefault();
249 e.preventDefault();
250 delete_integration(this);
250 delete_integration(this);
251 });
251 });
252 </script> No newline at end of file
252 </script>
@@ -1,66 +1,66 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="base.mako"/>
2 <%inherit file="base.mako"/>
3 <%namespace name="widgets" file="/widgets.mako"/>
3 <%namespace name="widgets" file="/widgets.mako"/>
4
4
5 <%def name="breadcrumbs_links()">
5 <%def name="breadcrumbs_links()">
6 %if c.repo:
6 %if c.repo:
7 ${h.link_to('Settings',h.route_path('edit_repo', repo_name=c.repo.repo_name))}
7 ${h.link_to('Settings',h.route_path('edit_repo', repo_name=c.repo.repo_name))}
8 &raquo;
8 &raquo;
9 ${h.link_to(_('Integrations'),request.route_url(route_name='repo_integrations_home', repo_name=c.repo.repo_name))}
9 ${h.link_to(_('Integrations'),request.route_url(route_name='repo_integrations_home', repo_name=c.repo.repo_name))}
10 %elif c.repo_group:
10 %elif c.repo_group:
11 ${h.link_to(_('Admin'),h.url('admin_home'))}
11 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
12 &raquo;
12 &raquo;
13 ${h.link_to(_('Repository Groups'),h.url('repo_groups'))}
13 ${h.link_to(_('Repository Groups'),h.url('repo_groups'))}
14 &raquo;
14 &raquo;
15 ${h.link_to(c.repo_group.group_name,h.url('edit_repo_group', group_name=c.repo_group.group_name))}
15 ${h.link_to(c.repo_group.group_name,h.url('edit_repo_group', group_name=c.repo_group.group_name))}
16 &raquo;
16 &raquo;
17 ${h.link_to(_('Integrations'),request.route_url(route_name='repo_group_integrations_home', repo_group_name=c.repo_group.group_name))}
17 ${h.link_to(_('Integrations'),request.route_url(route_name='repo_group_integrations_home', repo_group_name=c.repo_group.group_name))}
18 %else:
18 %else:
19 ${h.link_to(_('Admin'),h.url('admin_home'))}
19 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
20 &raquo;
20 &raquo;
21 ${h.link_to(_('Settings'),h.url('admin_settings'))}
21 ${h.link_to(_('Settings'),h.url('admin_settings'))}
22 &raquo;
22 &raquo;
23 ${h.link_to(_('Integrations'),request.route_url(route_name='global_integrations_home'))}
23 ${h.link_to(_('Integrations'),request.route_url(route_name='global_integrations_home'))}
24 %endif
24 %endif
25 &raquo;
25 &raquo;
26 ${_('Create new integration')}
26 ${_('Create new integration')}
27 </%def>
27 </%def>
28 <%widgets:panel class_='integrations'>
28 <%widgets:panel class_='integrations'>
29 <%def name="title()">
29 <%def name="title()">
30 %if c.repo:
30 %if c.repo:
31 ${_('Create New Integration for repository: {repo_name}').format(repo_name=c.repo.repo_name)}
31 ${_('Create New Integration for repository: {repo_name}').format(repo_name=c.repo.repo_name)}
32 %elif c.repo_group:
32 %elif c.repo_group:
33 ${_('Create New Integration for repository group: {repo_group_name}').format(repo_group_name=c.repo_group.group_name)}
33 ${_('Create New Integration for repository group: {repo_group_name}').format(repo_group_name=c.repo_group.group_name)}
34 %else:
34 %else:
35 ${_('Create New Global Integration')}
35 ${_('Create New Global Integration')}
36 %endif
36 %endif
37 </%def>
37 </%def>
38
38
39 %for integration, IntegrationType in available_integrations.items():
39 %for integration, IntegrationType in available_integrations.items():
40 <%
40 <%
41 if c.repo:
41 if c.repo:
42 create_url = request.route_path('repo_integrations_create',
42 create_url = request.route_path('repo_integrations_create',
43 repo_name=c.repo.repo_name,
43 repo_name=c.repo.repo_name,
44 integration=integration)
44 integration=integration)
45 elif c.repo_group:
45 elif c.repo_group:
46 create_url = request.route_path('repo_group_integrations_create',
46 create_url = request.route_path('repo_group_integrations_create',
47 repo_group_name=c.repo_group.group_name,
47 repo_group_name=c.repo_group.group_name,
48 integration=integration)
48 integration=integration)
49 else:
49 else:
50 create_url = request.route_path('global_integrations_create',
50 create_url = request.route_path('global_integrations_create',
51 integration=integration)
51 integration=integration)
52 %>
52 %>
53 <a href="${create_url}" class="integration-box">
53 <a href="${create_url}" class="integration-box">
54 <%widgets:panel>
54 <%widgets:panel>
55 <h2>
55 <h2>
56 <div class="integration-icon">
56 <div class="integration-icon">
57 ${IntegrationType.icon|n}
57 ${IntegrationType.icon|n}
58 </div>
58 </div>
59 ${IntegrationType.display_name}
59 ${IntegrationType.display_name}
60 </h2>
60 </h2>
61 ${IntegrationType.description or _('No description available')}
61 ${IntegrationType.description or _('No description available')}
62 </%widgets:panel>
62 </%widgets:panel>
63 </a>
63 </a>
64 %endfor
64 %endfor
65 <div style="clear:both"></div>
65 <div style="clear:both"></div>
66 </%widgets:panel>
66 </%widgets:panel>
@@ -1,56 +1,56 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.mako"/>
2 <%inherit file="/base/base.mako"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 ${_('Permissions Administration')}
5 ${_('Permissions Administration')}
6 %if c.rhodecode_name:
6 %if c.rhodecode_name:
7 &middot; ${h.branding(c.rhodecode_name)}
7 &middot; ${h.branding(c.rhodecode_name)}
8 %endif
8 %endif
9 </%def>
9 </%def>
10
10
11 <%def name="breadcrumbs_links()">
11 <%def name="breadcrumbs_links()">
12 ${h.link_to(_('Admin'),h.url('admin_home'))}
12 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
13 &raquo;
13 &raquo;
14 ${_('Permissions')}
14 ${_('Permissions')}
15 </%def>
15 </%def>
16
16
17 <%def name="menu_bar_nav()">
17 <%def name="menu_bar_nav()">
18 ${self.menu_items(active='admin')}
18 ${self.menu_items(active='admin')}
19 </%def>
19 </%def>
20
20
21
21
22 <%def name="main()">
22 <%def name="main()">
23 <div class="box">
23 <div class="box">
24 <div class="title">
24 <div class="title">
25 ${self.breadcrumbs()}
25 ${self.breadcrumbs()}
26 </div>
26 </div>
27
27
28 <div class="sidebar-col-wrapper scw-small">
28 <div class="sidebar-col-wrapper scw-small">
29 ##main
29 ##main
30 <div class="sidebar">
30 <div class="sidebar">
31 <ul class="nav nav-pills nav-stacked">
31 <ul class="nav nav-pills nav-stacked">
32 <li class="${'active' if c.active=='application' else ''}">
32 <li class="${'active' if c.active=='application' else ''}">
33 <a href="${h.url('admin_permissions_application')}">${_('Application')}</a>
33 <a href="${h.url('admin_permissions_application')}">${_('Application')}</a>
34 </li>
34 </li>
35 <li class="${'active' if c.active=='global' else ''}">
35 <li class="${'active' if c.active=='global' else ''}">
36 <a href="${h.url('admin_permissions_global')}">${_('Global')}</a>
36 <a href="${h.url('admin_permissions_global')}">${_('Global')}</a>
37 </li>
37 </li>
38 <li class="${'active' if c.active=='objects' else ''}">
38 <li class="${'active' if c.active=='objects' else ''}">
39 <a href="${h.url('admin_permissions_object')}">${_('Object')}</a>
39 <a href="${h.url('admin_permissions_object')}">${_('Object')}</a>
40 </li>
40 </li>
41 <li class="${'active' if c.active=='ips' else ''}">
41 <li class="${'active' if c.active=='ips' else ''}">
42 <a href="${h.url('admin_permissions_ips')}">${_('IP Whitelist')}</a>
42 <a href="${h.url('admin_permissions_ips')}">${_('IP Whitelist')}</a>
43 </li>
43 </li>
44 <li class="${'active' if c.active=='perms' else ''}">
44 <li class="${'active' if c.active=='perms' else ''}">
45 <a href="${h.url('admin_permissions_overview')}">${_('Overview')}</a>
45 <a href="${h.url('admin_permissions_overview')}">${_('Overview')}</a>
46 </li>
46 </li>
47 </ul>
47 </ul>
48 </div>
48 </div>
49
49
50 <div class="main-content-full-width">
50 <div class="main-content-full-width">
51 <%include file="/admin/permissions/permissions_${c.active}.mako"/>
51 <%include file="/admin/permissions/permissions_${c.active}.mako"/>
52 </div>
52 </div>
53 </div>
53 </div>
54 </div>
54 </div>
55
55
56 </%def>
56 </%def>
@@ -1,100 +1,100 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.mako"/>
2 <%inherit file="/base/base.mako"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 ${_('Add repository group')}
5 ${_('Add repository group')}
6 %if c.rhodecode_name:
6 %if c.rhodecode_name:
7 &middot; ${h.branding(c.rhodecode_name)}
7 &middot; ${h.branding(c.rhodecode_name)}
8 %endif
8 %endif
9 </%def>
9 </%def>
10
10
11 <%def name="breadcrumbs_links()">
11 <%def name="breadcrumbs_links()">
12 ${h.link_to(_('Admin'),h.url('admin_home'))}
12 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
13 &raquo;
13 &raquo;
14 ${h.link_to(_('Repository groups'),h.url('repo_groups'))}
14 ${h.link_to(_('Repository groups'),h.url('repo_groups'))}
15 &raquo;
15 &raquo;
16 ${_('Add Repository Group')}
16 ${_('Add Repository Group')}
17 </%def>
17 </%def>
18
18
19 <%def name="menu_bar_nav()">
19 <%def name="menu_bar_nav()">
20 ${self.menu_items(active='admin')}
20 ${self.menu_items(active='admin')}
21 </%def>
21 </%def>
22
22
23 <%def name="main()">
23 <%def name="main()">
24 <div class="box">
24 <div class="box">
25 <!-- box / title -->
25 <!-- box / title -->
26 <div class="title">
26 <div class="title">
27 ${self.breadcrumbs()}
27 ${self.breadcrumbs()}
28 </div>
28 </div>
29 <!-- end box / title -->
29 <!-- end box / title -->
30 ${h.secure_form(url('repo_groups'), method='post')}
30 ${h.secure_form(url('repo_groups'), method='post')}
31 <div class="form">
31 <div class="form">
32 <!-- fields -->
32 <!-- fields -->
33 <div class="fields">
33 <div class="fields">
34 <div class="field">
34 <div class="field">
35 <div class="label">
35 <div class="label">
36 <label for="group_name">${_('Group Name')}:</label>
36 <label for="group_name">${_('Group Name')}:</label>
37 </div>
37 </div>
38 <div class="input">
38 <div class="input">
39 ${h.text('group_name', class_="medium")}
39 ${h.text('group_name', class_="medium")}
40 </div>
40 </div>
41 </div>
41 </div>
42
42
43 <div class="field">
43 <div class="field">
44 <div class="label">
44 <div class="label">
45 <label for="group_description">${_('Description')}:</label>
45 <label for="group_description">${_('Description')}:</label>
46 </div>
46 </div>
47 <div class="textarea editor">
47 <div class="textarea editor">
48 ${h.textarea('group_description',cols=23,rows=5,class_="medium")}
48 ${h.textarea('group_description',cols=23,rows=5,class_="medium")}
49 </div>
49 </div>
50 </div>
50 </div>
51
51
52 <div class="field">
52 <div class="field">
53 <div class="label">
53 <div class="label">
54 <label for="group_parent_id">${_('Group Parent')}:</label>
54 <label for="group_parent_id">${_('Group Parent')}:</label>
55 </div>
55 </div>
56 <div class="select">
56 <div class="select">
57 ${h.select('group_parent_id',request.GET.get('parent_group'),c.repo_groups,class_="medium")}
57 ${h.select('group_parent_id',request.GET.get('parent_group'),c.repo_groups,class_="medium")}
58 </div>
58 </div>
59 </div>
59 </div>
60
60
61 <div id="copy_perms" class="field">
61 <div id="copy_perms" class="field">
62 <div class="label label-checkbox">
62 <div class="label label-checkbox">
63 <label for="group_copy_permissions">${_('Copy Parent Group Permissions')}:</label>
63 <label for="group_copy_permissions">${_('Copy Parent Group Permissions')}:</label>
64 </div>
64 </div>
65 <div class="checkboxes">
65 <div class="checkboxes">
66 ${h.checkbox('group_copy_permissions', value="True", checked="checked")}
66 ${h.checkbox('group_copy_permissions', value="True", checked="checked")}
67 <span class="help-block">${_('Copy permission settings from parent repository group.')}</span>
67 <span class="help-block">${_('Copy permission settings from parent repository group.')}</span>
68 </div>
68 </div>
69 </div>
69 </div>
70
70
71 <div class="buttons">
71 <div class="buttons">
72 ${h.submit('save',_('Save'),class_="btn")}
72 ${h.submit('save',_('Save'),class_="btn")}
73 </div>
73 </div>
74 </div>
74 </div>
75 </div>
75 </div>
76 ${h.end_form()}
76 ${h.end_form()}
77 </div>
77 </div>
78 <script>
78 <script>
79 $(document).ready(function(){
79 $(document).ready(function(){
80 var setCopyPermsOption = function(group_val){
80 var setCopyPermsOption = function(group_val){
81 if(group_val != "-1"){
81 if(group_val != "-1"){
82 $('#copy_perms').show()
82 $('#copy_perms').show()
83 }
83 }
84 else{
84 else{
85 $('#copy_perms').hide();
85 $('#copy_perms').hide();
86 }
86 }
87 }
87 }
88 $("#group_parent_id").select2({
88 $("#group_parent_id").select2({
89 'containerCssClass': "drop-menu",
89 'containerCssClass': "drop-menu",
90 'dropdownCssClass': "drop-menu-dropdown",
90 'dropdownCssClass': "drop-menu-dropdown",
91 'dropdownAutoWidth': true
91 'dropdownAutoWidth': true
92 });
92 });
93 setCopyPermsOption($('#group_parent_id').val())
93 setCopyPermsOption($('#group_parent_id').val())
94 $("#group_parent_id").on("change", function(e) {
94 $("#group_parent_id").on("change", function(e) {
95 setCopyPermsOption(e.val)
95 setCopyPermsOption(e.val)
96 })
96 })
97 $('#group_name').focus();
97 $('#group_name').focus();
98 })
98 })
99 </script>
99 </script>
100 </%def>
100 </%def>
@@ -1,61 +1,61 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.mako"/>
2 <%inherit file="/base/base.mako"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 ${_('%s repository group settings') % c.repo_group.name}
5 ${_('%s repository group settings') % c.repo_group.name}
6 %if c.rhodecode_name:
6 %if c.rhodecode_name:
7 &middot; ${h.branding(c.rhodecode_name)}
7 &middot; ${h.branding(c.rhodecode_name)}
8 %endif
8 %endif
9 </%def>
9 </%def>
10
10
11 <%def name="breadcrumbs_links()">
11 <%def name="breadcrumbs_links()">
12 ${h.link_to(_('Admin'),h.url('admin_home'))}
12 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
13 &raquo;
13 &raquo;
14 ${h.link_to(_('Repository Groups'),h.url('repo_groups'))}
14 ${h.link_to(_('Repository Groups'),h.url('repo_groups'))}
15 %if c.repo_group.parent_group:
15 %if c.repo_group.parent_group:
16 &raquo; ${h.link_to(c.repo_group.parent_group.name,h.url('repo_group_home',group_name=c.repo_group.parent_group.group_name))}
16 &raquo; ${h.link_to(c.repo_group.parent_group.name,h.url('repo_group_home',group_name=c.repo_group.parent_group.group_name))}
17 %endif
17 %endif
18 &raquo; ${c.repo_group.name}
18 &raquo; ${c.repo_group.name}
19 </%def>
19 </%def>
20
20
21 <%def name="breadcrumbs_side_links()">
21 <%def name="breadcrumbs_side_links()">
22 <ul class="links">
22 <ul class="links">
23 <li>
23 <li>
24 <a href="${h.url('new_repo_group', parent_group=c.repo_group.group_id)}" class="btn btn-small btn-success">${_(u'Add Child Group')}</a>
24 <a href="${h.url('new_repo_group', parent_group=c.repo_group.group_id)}" class="btn btn-small btn-success">${_(u'Add Child Group')}</a>
25 </li>
25 </li>
26 </ul>
26 </ul>
27 </%def>
27 </%def>
28
28
29 <%def name="menu_bar_nav()">
29 <%def name="menu_bar_nav()">
30 ${self.menu_items(active='admin')}
30 ${self.menu_items(active='admin')}
31 </%def>
31 </%def>
32
32
33 <%def name="main_content()">
33 <%def name="main_content()">
34 <%include file="/admin/repo_groups/repo_group_edit_${c.active}.mako"/>
34 <%include file="/admin/repo_groups/repo_group_edit_${c.active}.mako"/>
35 </%def>
35 </%def>
36
36
37 <%def name="main()">
37 <%def name="main()">
38 <div class="box">
38 <div class="box">
39 <div class="title">
39 <div class="title">
40 ${self.breadcrumbs()}
40 ${self.breadcrumbs()}
41 ${self.breadcrumbs_side_links()}
41 ${self.breadcrumbs_side_links()}
42 </div>
42 </div>
43
43
44 <div class="sidebar-col-wrapper">
44 <div class="sidebar-col-wrapper">
45 ##main
45 ##main
46 <div class="sidebar">
46 <div class="sidebar">
47 <ul class="nav nav-pills nav-stacked">
47 <ul class="nav nav-pills nav-stacked">
48 <li class="${'active' if c.active=='settings' else ''}"><a href="${h.url('edit_repo_group', group_name=c.repo_group.group_name)}">${_('Settings')}</a></li>
48 <li class="${'active' if c.active=='settings' else ''}"><a href="${h.url('edit_repo_group', group_name=c.repo_group.group_name)}">${_('Settings')}</a></li>
49 <li class="${'active' if c.active=='perms' else ''}"><a href="${h.url('edit_repo_group_perms', group_name=c.repo_group.group_name)}">${_('Permissions')}</a></li>
49 <li class="${'active' if c.active=='perms' else ''}"><a href="${h.url('edit_repo_group_perms', group_name=c.repo_group.group_name)}">${_('Permissions')}</a></li>
50 <li class="${'active' if c.active=='advanced' else ''}"><a href="${h.url('edit_repo_group_advanced', group_name=c.repo_group.group_name)}">${_('Advanced')}</a></li>
50 <li class="${'active' if c.active=='advanced' else ''}"><a href="${h.url('edit_repo_group_advanced', group_name=c.repo_group.group_name)}">${_('Advanced')}</a></li>
51 <li class="${'active' if c.active=='integrations' else ''}"><a href="${h.route_path('repo_group_integrations_home', repo_group_name=c.repo_group.group_name)}">${_('Integrations')}</a></li>
51 <li class="${'active' if c.active=='integrations' else ''}"><a href="${h.route_path('repo_group_integrations_home', repo_group_name=c.repo_group.group_name)}">${_('Integrations')}</a></li>
52 </ul>
52 </ul>
53 </div>
53 </div>
54
54
55 <div class="main-content-full-width">
55 <div class="main-content-full-width">
56 ${self.main_content()}
56 ${self.main_content()}
57 </div>
57 </div>
58
58
59 </div>
59 </div>
60 </div>
60 </div>
61 </%def>
61 </%def>
@@ -1,94 +1,94 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.mako"/>
2 <%inherit file="/base/base.mako"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 ${_('Repository groups administration')}
5 ${_('Repository groups administration')}
6 %if c.rhodecode_name:
6 %if c.rhodecode_name:
7 &middot; ${h.branding(c.rhodecode_name)}
7 &middot; ${h.branding(c.rhodecode_name)}
8 %endif
8 %endif
9 </%def>
9 </%def>
10
10
11 <%def name="breadcrumbs_links()">
11 <%def name="breadcrumbs_links()">
12 <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" placeholder="${_('quick filter...')}" value=""/>
12 <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" placeholder="${_('quick filter...')}" value=""/>
13 ${h.link_to(_('Admin'),h.url('admin_home'))} &raquo; <span id="repo_group_count">0</span> ${_('repository groups')}
13 ${h.link_to(_('Admin'),h.route_path('admin_home'))} &raquo; <span id="repo_group_count">0</span> ${_('repository groups')}
14 </%def>
14 </%def>
15
15
16 <%def name="menu_bar_nav()">
16 <%def name="menu_bar_nav()">
17 ${self.menu_items(active='admin')}
17 ${self.menu_items(active='admin')}
18 </%def>
18 </%def>
19
19
20 <%def name="main()">
20 <%def name="main()">
21 <div class="box">
21 <div class="box">
22 <div class="title">
22 <div class="title">
23 ${self.breadcrumbs()}
23 ${self.breadcrumbs()}
24 <ul class="links">
24 <ul class="links">
25 %if h.HasPermissionAny('hg.admin','hg.repogroup.create.true')():
25 %if h.HasPermissionAny('hg.admin','hg.repogroup.create.true')():
26 <li>
26 <li>
27 <a href="${h.url('new_repo_group')}" class="btn btn-small btn-success">${_(u'Add Repository Group')}</a>
27 <a href="${h.url('new_repo_group')}" class="btn btn-small btn-success">${_(u'Add Repository Group')}</a>
28 </li>
28 </li>
29 %endif
29 %endif
30 </ul>
30 </ul>
31 </div>
31 </div>
32 <div id="repos_list_wrap">
32 <div id="repos_list_wrap">
33 <table id="group_list_table" class="display"></table>
33 <table id="group_list_table" class="display"></table>
34 </div>
34 </div>
35 </div>
35 </div>
36
36
37 <script>
37 <script>
38 $(document).ready(function() {
38 $(document).ready(function() {
39
39
40 var get_datatable_count = function(){
40 var get_datatable_count = function(){
41 var api = $('#group_list_table').dataTable().api();
41 var api = $('#group_list_table').dataTable().api();
42 $('#repo_group_count').text(api.page.info().recordsDisplay);
42 $('#repo_group_count').text(api.page.info().recordsDisplay);
43 };
43 };
44
44
45 // repo group list
45 // repo group list
46 $('#group_list_table').DataTable({
46 $('#group_list_table').DataTable({
47 data: ${c.data|n},
47 data: ${c.data|n},
48 dom: 'rtp',
48 dom: 'rtp',
49 pageLength: ${c.visual.admin_grid_items},
49 pageLength: ${c.visual.admin_grid_items},
50 order: [[ 0, "asc" ]],
50 order: [[ 0, "asc" ]],
51 columns: [
51 columns: [
52 { data: {"_": "name",
52 { data: {"_": "name",
53 "sort": "name_raw"}, title: "${_('Name')}", className: "td-componentname" },
53 "sort": "name_raw"}, title: "${_('Name')}", className: "td-componentname" },
54 { data: 'menu', "bSortable": false, className: "quick_repo_menu" },
54 { data: 'menu', "bSortable": false, className: "quick_repo_menu" },
55 { data: {"_": "desc",
55 { data: {"_": "desc",
56 "sort": "desc"}, title: "${_('Description')}", className: "td-description" },
56 "sort": "desc"}, title: "${_('Description')}", className: "td-description" },
57 { data: {"_": "top_level_repos",
57 { data: {"_": "top_level_repos",
58 "sort": "top_level_repos"}, title: "${_('Number of top level repositories')}" },
58 "sort": "top_level_repos"}, title: "${_('Number of top level repositories')}" },
59 { data: {"_": "owner",
59 { data: {"_": "owner",
60 "sort": "owner"}, title: "${_('Owner')}", className: "td-user" },
60 "sort": "owner"}, title: "${_('Owner')}", className: "td-user" },
61 { data: {"_": "action",
61 { data: {"_": "action",
62 "sort": "action"}, title: "${_('Action')}", className: "td-action" }
62 "sort": "action"}, title: "${_('Action')}", className: "td-action" }
63 ],
63 ],
64 language: {
64 language: {
65 paginate: DEFAULT_GRID_PAGINATION,
65 paginate: DEFAULT_GRID_PAGINATION,
66 emptyTable: _gettext("No repository groups available yet.")
66 emptyTable: _gettext("No repository groups available yet.")
67 },
67 },
68 "initComplete": function( settings, json ) {
68 "initComplete": function( settings, json ) {
69 get_datatable_count();
69 get_datatable_count();
70 quick_repo_menu();
70 quick_repo_menu();
71 }
71 }
72 });
72 });
73
73
74 // update the counter when doing search
74 // update the counter when doing search
75 $('#group_list_table').on( 'search.dt', function (e,settings) {
75 $('#group_list_table').on( 'search.dt', function (e,settings) {
76 get_datatable_count();
76 get_datatable_count();
77 });
77 });
78
78
79 // filter, filter both grids
79 // filter, filter both grids
80 $('#q_filter').on( 'keyup', function () {
80 $('#q_filter').on( 'keyup', function () {
81
81
82 var repo_group_api = $('#group_list_table').dataTable().api();
82 var repo_group_api = $('#group_list_table').dataTable().api();
83 repo_group_api
83 repo_group_api
84 .columns(0)
84 .columns(0)
85 .search(this.value)
85 .search(this.value)
86 .draw();
86 .draw();
87 });
87 });
88
88
89 // refilter table if page load via back button
89 // refilter table if page load via back button
90 $("#q_filter").trigger('keyup');
90 $("#q_filter").trigger('keyup');
91 });
91 });
92 </script>
92 </script>
93 </%def>
93 </%def>
94
94
@@ -1,37 +1,37 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.mako"/>
2 <%inherit file="/base/base.mako"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 ${_('Add repository')}
5 ${_('Add repository')}
6 %if c.rhodecode_name:
6 %if c.rhodecode_name:
7 &middot; ${h.branding(c.rhodecode_name)}
7 &middot; ${h.branding(c.rhodecode_name)}
8 %endif
8 %endif
9 </%def>
9 </%def>
10
10
11 <%def name="breadcrumbs_links()">
11 <%def name="breadcrumbs_links()">
12 %if c.rhodecode_user.is_admin:
12 %if c.rhodecode_user.is_admin:
13 ${h.link_to(_('Admin'),h.url('admin_home'))}
13 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
14 &raquo;
14 &raquo;
15 ${h.link_to(_('Repositories'),h.url('repos'))}
15 ${h.link_to(_('Repositories'),h.url('repos'))}
16 %else:
16 %else:
17 ${_('Admin')}
17 ${_('Admin')}
18 &raquo;
18 &raquo;
19 ${_('Repositories')}
19 ${_('Repositories')}
20 %endif
20 %endif
21 &raquo;
21 &raquo;
22 ${_('Add Repository')}
22 ${_('Add Repository')}
23 </%def>
23 </%def>
24
24
25 <%def name="menu_bar_nav()">
25 <%def name="menu_bar_nav()">
26 ${self.menu_items(active='admin')}
26 ${self.menu_items(active='admin')}
27 </%def>
27 </%def>
28
28
29 <%def name="main()">
29 <%def name="main()">
30 <div class="box">
30 <div class="box">
31 <!-- box / title -->
31 <!-- box / title -->
32 <div class="title">
32 <div class="title">
33 ${self.breadcrumbs()}
33 ${self.breadcrumbs()}
34 </div>
34 </div>
35 <%include file="repo_add_base.mako"/>
35 <%include file="repo_add_base.mako"/>
36 </div>
36 </div>
37 </%def>
37 </%def>
@@ -1,101 +1,101 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.mako"/>
2 <%inherit file="/base/base.mako"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 ${_('Repositories administration')}
5 ${_('Repositories administration')}
6 %if c.rhodecode_name:
6 %if c.rhodecode_name:
7 &middot; ${h.branding(c.rhodecode_name)}
7 &middot; ${h.branding(c.rhodecode_name)}
8 %endif
8 %endif
9 </%def>
9 </%def>
10
10
11 <%def name="breadcrumbs_links()">
11 <%def name="breadcrumbs_links()">
12 <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" placeholder="${_('quick filter...')}" value=""/>
12 <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" placeholder="${_('quick filter...')}" value=""/>
13 ${h.link_to(_('Admin'),h.url('admin_home'))} &raquo; <span id="repo_count">0</span> ${_('repositories')}
13 ${h.link_to(_('Admin'),h.route_path('admin_home'))} &raquo; <span id="repo_count">0</span> ${_('repositories')}
14 </%def>
14 </%def>
15
15
16 <%def name="menu_bar_nav()">
16 <%def name="menu_bar_nav()">
17 ${self.menu_items(active='admin')}
17 ${self.menu_items(active='admin')}
18 </%def>
18 </%def>
19
19
20 <%def name="main()">
20 <%def name="main()">
21 <div class="box">
21 <div class="box">
22 <div class="title">
22 <div class="title">
23 ${self.breadcrumbs()}
23 ${self.breadcrumbs()}
24 <ul class="links">
24 <ul class="links">
25 %if h.HasPermissionAny('hg.admin','hg.create.repository')():
25 %if h.HasPermissionAny('hg.admin','hg.create.repository')():
26 <li>
26 <li>
27 <a href="${h.url('new_repo')}" class="btn btn-small btn-success">${_(u'Add Repository')}</a>
27 <a href="${h.url('new_repo')}" class="btn btn-small btn-success">${_(u'Add Repository')}</a>
28 </li>
28 </li>
29 %endif
29 %endif
30 </ul>
30 </ul>
31 </div>
31 </div>
32 <div id="repos_list_wrap">
32 <div id="repos_list_wrap">
33 <table id="repo_list_table" class="display"></table>
33 <table id="repo_list_table" class="display"></table>
34 </div>
34 </div>
35 </div>
35 </div>
36
36
37 <script>
37 <script>
38 $(document).ready(function() {
38 $(document).ready(function() {
39
39
40 var get_datatable_count = function(){
40 var get_datatable_count = function(){
41 var api = $('#repo_list_table').dataTable().api();
41 var api = $('#repo_list_table').dataTable().api();
42 $('#repo_count').text(api.page.info().recordsDisplay);
42 $('#repo_count').text(api.page.info().recordsDisplay);
43 };
43 };
44
44
45
45
46 // repo list
46 // repo list
47 $('#repo_list_table').DataTable({
47 $('#repo_list_table').DataTable({
48 data: ${c.data|n},
48 data: ${c.data|n},
49 dom: 'rtp',
49 dom: 'rtp',
50 pageLength: ${c.visual.admin_grid_items},
50 pageLength: ${c.visual.admin_grid_items},
51 order: [[ 0, "asc" ]],
51 order: [[ 0, "asc" ]],
52 columns: [
52 columns: [
53 { data: {"_": "name",
53 { data: {"_": "name",
54 "sort": "name_raw"}, title: "${_('Name')}", className: "td-componentname" },
54 "sort": "name_raw"}, title: "${_('Name')}", className: "td-componentname" },
55 { data: 'menu', "bSortable": false, className: "quick_repo_menu" },
55 { data: 'menu', "bSortable": false, className: "quick_repo_menu" },
56 { data: {"_": "desc",
56 { data: {"_": "desc",
57 "sort": "desc"}, title: "${_('Description')}", className: "td-description" },
57 "sort": "desc"}, title: "${_('Description')}", className: "td-description" },
58 { data: {"_": "last_change",
58 { data: {"_": "last_change",
59 "sort": "last_change_raw",
59 "sort": "last_change_raw",
60 "type": Number}, title: "${_('Last Change')}", className: "td-time" },
60 "type": Number}, title: "${_('Last Change')}", className: "td-time" },
61 { data: {"_": "last_changeset",
61 { data: {"_": "last_changeset",
62 "sort": "last_changeset_raw",
62 "sort": "last_changeset_raw",
63 "type": Number}, title: "${_('Commit')}", className: "td-commit" },
63 "type": Number}, title: "${_('Commit')}", className: "td-commit" },
64 { data: {"_": "owner",
64 { data: {"_": "owner",
65 "sort": "owner"}, title: "${_('Owner')}", className: "td-user" },
65 "sort": "owner"}, title: "${_('Owner')}", className: "td-user" },
66 { data: {"_": "state",
66 { data: {"_": "state",
67 "sort": "state"}, title: "${_('State')}", className: "td-tags td-state" },
67 "sort": "state"}, title: "${_('State')}", className: "td-tags td-state" },
68 { data: {"_": "action",
68 { data: {"_": "action",
69 "sort": "action"}, title: "${_('Action')}", className: "td-action" }
69 "sort": "action"}, title: "${_('Action')}", className: "td-action" }
70 ],
70 ],
71 language: {
71 language: {
72 paginate: DEFAULT_GRID_PAGINATION,
72 paginate: DEFAULT_GRID_PAGINATION,
73 emptyTable:_gettext("No repositories available yet.")
73 emptyTable:_gettext("No repositories available yet.")
74 },
74 },
75 "initComplete": function( settings, json ) {
75 "initComplete": function( settings, json ) {
76 get_datatable_count();
76 get_datatable_count();
77 quick_repo_menu();
77 quick_repo_menu();
78 }
78 }
79 });
79 });
80
80
81 // update the counter when doing search
81 // update the counter when doing search
82 $('#repo_list_table').on( 'search.dt', function (e,settings) {
82 $('#repo_list_table').on( 'search.dt', function (e,settings) {
83 get_datatable_count();
83 get_datatable_count();
84 });
84 });
85
85
86 // filter, filter both grids
86 // filter, filter both grids
87 $('#q_filter').on( 'keyup', function () {
87 $('#q_filter').on( 'keyup', function () {
88 var repo_api = $('#repo_list_table').dataTable().api();
88 var repo_api = $('#repo_list_table').dataTable().api();
89 repo_api
89 repo_api
90 .columns(0)
90 .columns(0)
91 .search(this.value)
91 .search(this.value)
92 .draw();
92 .draw();
93 });
93 });
94
94
95 // refilter table if page load via back button
95 // refilter table if page load via back button
96 $("#q_filter").trigger('keyup');
96 $("#q_filter").trigger('keyup');
97 });
97 });
98
98
99 </script>
99 </script>
100
100
101 </%def>
101 </%def>
@@ -1,53 +1,53 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.mako"/>
2 <%inherit file="/base/base.mako"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 ${_('Settings administration')}
5 ${_('Settings administration')}
6 %if c.rhodecode_name:
6 %if c.rhodecode_name:
7 &middot; ${h.branding(c.rhodecode_name)}
7 &middot; ${h.branding(c.rhodecode_name)}
8 %endif
8 %endif
9 </%def>
9 </%def>
10
10
11 <%def name="breadcrumbs_links()">
11 <%def name="breadcrumbs_links()">
12 ${h.link_to(_('Admin'),h.url('admin_home'))}
12 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
13 &raquo;
13 &raquo;
14 ${_('Settings')}
14 ${_('Settings')}
15 </%def>
15 </%def>
16
16
17 <%def name="menu_bar_nav()">
17 <%def name="menu_bar_nav()">
18 ${self.menu_items(active='admin')}
18 ${self.menu_items(active='admin')}
19 </%def>
19 </%def>
20
20
21 <%def name="side_bar_nav()">
21 <%def name="side_bar_nav()">
22 % for navitem in c.navlist:
22 % for navitem in c.navlist:
23 <li class="${'active' if c.active==navitem.key else ''}">
23 <li class="${'active' if c.active==navitem.key else ''}">
24 <a href="${navitem.url}">${navitem.name}</a>
24 <a href="${navitem.url}">${navitem.name}</a>
25 </li>
25 </li>
26 % endfor
26 % endfor
27 </%def>
27 </%def>
28
28
29 <%def name="main_content()">
29 <%def name="main_content()">
30 <%include file="/admin/settings/settings_${c.active}.mako"/>
30 <%include file="/admin/settings/settings_${c.active}.mako"/>
31 </%def>
31 </%def>
32
32
33 <%def name="main()">
33 <%def name="main()">
34 <div class="box">
34 <div class="box">
35 <div class="title">
35 <div class="title">
36 ${self.breadcrumbs()}
36 ${self.breadcrumbs()}
37 </div>
37 </div>
38
38
39 ##main
39 ##main
40 <div class='sidebar-col-wrapper'>
40 <div class='sidebar-col-wrapper'>
41 <div class="sidebar">
41 <div class="sidebar">
42 <ul class="nav nav-pills nav-stacked">
42 <ul class="nav nav-pills nav-stacked">
43 ${self.side_bar_nav()}
43 ${self.side_bar_nav()}
44 </ul>
44 </ul>
45 </div>
45 </div>
46
46
47 <div class="main-content-full-width">
47 <div class="main-content-full-width">
48 ${self.main_content()}
48 ${self.main_content()}
49 </div>
49 </div>
50 </div>
50 </div>
51 </div>
51 </div>
52
52
53 </%def> No newline at end of file
53 </%def>
@@ -1,72 +1,72 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.mako"/>
2 <%inherit file="/base/base.mako"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 ${_('Add user group')}
5 ${_('Add user group')}
6 %if c.rhodecode_name:
6 %if c.rhodecode_name:
7 &middot; ${h.branding(c.rhodecode_name)}
7 &middot; ${h.branding(c.rhodecode_name)}
8 %endif
8 %endif
9 </%def>
9 </%def>
10 <%def name="breadcrumbs_links()">
10 <%def name="breadcrumbs_links()">
11 ${h.link_to(_('Admin'),h.url('admin_home'))}
11 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
12 &raquo;
12 &raquo;
13 ${h.link_to(_('User groups'),h.url('users_groups'))}
13 ${h.link_to(_('User groups'),h.url('users_groups'))}
14 &raquo;
14 &raquo;
15 ${_('Add User Group')}
15 ${_('Add User Group')}
16 </%def>
16 </%def>
17
17
18 <%def name="menu_bar_nav()">
18 <%def name="menu_bar_nav()">
19 ${self.menu_items(active='admin')}
19 ${self.menu_items(active='admin')}
20 </%def>
20 </%def>
21
21
22 <%def name="main()">
22 <%def name="main()">
23 <div class="box main-content">
23 <div class="box main-content">
24 <!-- box / title -->
24 <!-- box / title -->
25 <div class="title">
25 <div class="title">
26 ${self.breadcrumbs()}
26 ${self.breadcrumbs()}
27 </div>
27 </div>
28 <!-- end box / title -->
28 <!-- end box / title -->
29 ${h.secure_form(url('users_groups'))}
29 ${h.secure_form(url('users_groups'))}
30 <div class="form">
30 <div class="form">
31 <!-- fields -->
31 <!-- fields -->
32 <div class="fields">
32 <div class="fields">
33 <div class="field">
33 <div class="field">
34 <div class="label">
34 <div class="label">
35 <label for="users_group_name">${_('Group name')}:</label>
35 <label for="users_group_name">${_('Group name')}:</label>
36 </div>
36 </div>
37 <div class="input">
37 <div class="input">
38 ${h.text('users_group_name', class_='medium')}
38 ${h.text('users_group_name', class_='medium')}
39 </div>
39 </div>
40 </div>
40 </div>
41 <div class="field">
41 <div class="field">
42 <div class="label">
42 <div class="label">
43 <label for="user_group_description">${_('Description')}:</label>
43 <label for="user_group_description">${_('Description')}:</label>
44 </div>
44 </div>
45 <div class="textarea editor">
45 <div class="textarea editor">
46 ${h.textarea('user_group_description')}
46 ${h.textarea('user_group_description')}
47 <span class="help-block">${_('Short, optional description for this user group.')}</span>
47 <span class="help-block">${_('Short, optional description for this user group.')}</span>
48 </div>
48 </div>
49 </div>
49 </div>
50 <div class="field">
50 <div class="field">
51 <div class="label">
51 <div class="label">
52 <label for="users_group_active">${_('Active')}:</label>
52 <label for="users_group_active">${_('Active')}:</label>
53 </div>
53 </div>
54 <div class="checkboxes">
54 <div class="checkboxes">
55 ${h.checkbox('users_group_active',value=True, checked='checked')}
55 ${h.checkbox('users_group_active',value=True, checked='checked')}
56 </div>
56 </div>
57 </div>
57 </div>
58
58
59 <div class="buttons">
59 <div class="buttons">
60 ${h.submit('save',_('Save'),class_="btn")}
60 ${h.submit('save',_('Save'),class_="btn")}
61 </div>
61 </div>
62 </div>
62 </div>
63 </div>
63 </div>
64 ${h.end_form()}
64 ${h.end_form()}
65 </div>
65 </div>
66 </%def>
66 </%def>
67
67
68 <script>
68 <script>
69 $(document).ready(function(){
69 $(document).ready(function(){
70 $('#users_group_name').focus();
70 $('#users_group_name').focus();
71 })
71 })
72 </script>
72 </script>
@@ -1,46 +1,46 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.mako"/>
2 <%inherit file="/base/base.mako"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 ${_('%s user group settings') % c.user_group.users_group_name}
5 ${_('%s user group settings') % c.user_group.users_group_name}
6 %if c.rhodecode_name:
6 %if c.rhodecode_name:
7 &middot; ${h.branding(c.rhodecode_name)}
7 &middot; ${h.branding(c.rhodecode_name)}
8 %endif
8 %endif
9 </%def>
9 </%def>
10
10
11 <%def name="breadcrumbs_links()">
11 <%def name="breadcrumbs_links()">
12 ${h.link_to(_('Admin'),h.url('admin_home'))}
12 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
13 &raquo;
13 &raquo;
14 ${h.link_to(_('User Groups'),h.url('users_groups'))}
14 ${h.link_to(_('User Groups'),h.url('users_groups'))}
15 &raquo;
15 &raquo;
16 ${c.user_group.users_group_name}
16 ${c.user_group.users_group_name}
17 </%def>
17 </%def>
18
18
19 <%def name="menu_bar_nav()">
19 <%def name="menu_bar_nav()">
20 ${self.menu_items(active='admin')}
20 ${self.menu_items(active='admin')}
21 </%def>
21 </%def>
22
22
23 <%def name="main()">
23 <%def name="main()">
24 <div class="box">
24 <div class="box">
25 <div class="title">
25 <div class="title">
26 ${self.breadcrumbs()}
26 ${self.breadcrumbs()}
27 </div>
27 </div>
28
28
29 ##main
29 ##main
30 <div class="sidebar-col-wrapper">
30 <div class="sidebar-col-wrapper">
31 <div class="sidebar">
31 <div class="sidebar">
32 <ul class="nav nav-pills nav-stacked">
32 <ul class="nav nav-pills nav-stacked">
33 <li class="${'active' if c.active=='settings' else ''}"><a href="${h.url('edit_users_group', user_group_id=c.user_group.users_group_id)}">${_('Settings')}</a></li>
33 <li class="${'active' if c.active=='settings' else ''}"><a href="${h.url('edit_users_group', user_group_id=c.user_group.users_group_id)}">${_('Settings')}</a></li>
34 <li class="${'active' if c.active=='perms' else ''}"><a href="${h.url('edit_user_group_perms', user_group_id=c.user_group.users_group_id)}">${_('Permissions')}</a></li>
34 <li class="${'active' if c.active=='perms' else ''}"><a href="${h.url('edit_user_group_perms', user_group_id=c.user_group.users_group_id)}">${_('Permissions')}</a></li>
35 <li class="${'active' if c.active=='advanced' else ''}"><a href="${h.url('edit_user_group_advanced', user_group_id=c.user_group.users_group_id)}">${_('Advanced')}</a></li>
35 <li class="${'active' if c.active=='advanced' else ''}"><a href="${h.url('edit_user_group_advanced', user_group_id=c.user_group.users_group_id)}">${_('Advanced')}</a></li>
36 <li class="${'active' if c.active=='global_perms' else ''}"><a href="${h.url('edit_user_group_global_perms', user_group_id=c.user_group.users_group_id)}">${_('Global permissions')}</a></li>
36 <li class="${'active' if c.active=='global_perms' else ''}"><a href="${h.url('edit_user_group_global_perms', user_group_id=c.user_group.users_group_id)}">${_('Global permissions')}</a></li>
37 <li class="${'active' if c.active=='perms_summary' else ''}"><a href="${h.url('edit_user_group_perms_summary', user_group_id=c.user_group.users_group_id)}">${_('Permissions summary')}</a></li>
37 <li class="${'active' if c.active=='perms_summary' else ''}"><a href="${h.url('edit_user_group_perms_summary', user_group_id=c.user_group.users_group_id)}">${_('Permissions summary')}</a></li>
38 </ul>
38 </ul>
39 </div>
39 </div>
40
40
41 <div class="main-content-full-width">
41 <div class="main-content-full-width">
42 <%include file="/admin/user_groups/user_group_edit_${c.active}.mako"/>
42 <%include file="/admin/user_groups/user_group_edit_${c.active}.mako"/>
43 </div>
43 </div>
44 </div>
44 </div>
45 </div>
45 </div>
46 </%def>
46 </%def>
@@ -1,100 +1,100 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.mako"/>
2 <%inherit file="/base/base.mako"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 ${_('User groups administration')}
5 ${_('User groups administration')}
6 %if c.rhodecode_name:
6 %if c.rhodecode_name:
7 &middot; ${h.branding(c.rhodecode_name)}
7 &middot; ${h.branding(c.rhodecode_name)}
8 %endif
8 %endif
9 </%def>
9 </%def>
10
10
11 <%def name="breadcrumbs_links()">
11 <%def name="breadcrumbs_links()">
12 <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" placeholder="${_('quick filter...')}" value=""/>
12 <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" placeholder="${_('quick filter...')}" value=""/>
13 ${h.link_to(_('Admin'),h.url('admin_home'))} &raquo; <span id="user_group_count">0</span> ${_('user groups')}
13 ${h.link_to(_('Admin'),h.route_path('admin_home'))} &raquo; <span id="user_group_count">0</span> ${_('user groups')}
14 </%def>
14 </%def>
15
15
16 <%def name="menu_bar_nav()">
16 <%def name="menu_bar_nav()">
17 ${self.menu_items(active='admin')}
17 ${self.menu_items(active='admin')}
18 </%def>
18 </%def>
19
19
20 <%def name="main()">
20 <%def name="main()">
21 <div class="box">
21 <div class="box">
22
22
23 <div class="title">
23 <div class="title">
24 ${self.breadcrumbs()}
24 ${self.breadcrumbs()}
25 <ul class="links">
25 <ul class="links">
26 %if h.HasPermissionAny('hg.admin', 'hg.usergroup.create.true')():
26 %if h.HasPermissionAny('hg.admin', 'hg.usergroup.create.true')():
27 <li>
27 <li>
28 <a href="${h.url('new_users_group')}" class="btn btn-small btn-success">${_(u'Add User Group')}</a>
28 <a href="${h.url('new_users_group')}" class="btn btn-small btn-success">${_(u'Add User Group')}</a>
29 </li>
29 </li>
30 %endif
30 %endif
31 </ul>
31 </ul>
32 </div>
32 </div>
33
33
34 <div id="repos_list_wrap">
34 <div id="repos_list_wrap">
35 <table id="user_group_list_table" class="display"></table>
35 <table id="user_group_list_table" class="display"></table>
36 </div>
36 </div>
37
37
38 </div>
38 </div>
39 <script>
39 <script>
40 $(document).ready(function() {
40 $(document).ready(function() {
41
41
42 var get_datatable_count = function(){
42 var get_datatable_count = function(){
43 var api = $('#user_group_list_table').dataTable().api();
43 var api = $('#user_group_list_table').dataTable().api();
44 $('#user_group_count').text(api.page.info().recordsDisplay);
44 $('#user_group_count').text(api.page.info().recordsDisplay);
45 };
45 };
46
46
47 // user list
47 // user list
48 $('#user_group_list_table').DataTable({
48 $('#user_group_list_table').DataTable({
49 data: ${c.data|n},
49 data: ${c.data|n},
50 dom: 'rtp',
50 dom: 'rtp',
51 pageLength: ${c.visual.admin_grid_items},
51 pageLength: ${c.visual.admin_grid_items},
52 order: [[ 0, "asc" ]],
52 order: [[ 0, "asc" ]],
53 columns: [
53 columns: [
54 { data: {"_": "group_name",
54 { data: {"_": "group_name",
55 "sort": "group_name_raw"}, title: "${_('Name')}", className: "td-componentname" },
55 "sort": "group_name_raw"}, title: "${_('Name')}", className: "td-componentname" },
56 { data: {"_": "desc",
56 { data: {"_": "desc",
57 "sort": "desc"}, title: "${_('Description')}", className: "td-description" },
57 "sort": "desc"}, title: "${_('Description')}", className: "td-description" },
58 { data: {"_": "members",
58 { data: {"_": "members",
59 "sort": "members",
59 "sort": "members",
60 "type": Number}, title: "${_('Members')}", className: "td-number" },
60 "type": Number}, title: "${_('Members')}", className: "td-number" },
61 { data: {"_": "sync",
61 { data: {"_": "sync",
62 "sort": "sync"}, title: "${_('Sync')}", className: "td-sync" },
62 "sort": "sync"}, title: "${_('Sync')}", className: "td-sync" },
63 { data: {"_": "active",
63 { data: {"_": "active",
64 "sort": "active"}, title: "${_('Active')}", className: "td-active" },
64 "sort": "active"}, title: "${_('Active')}", className: "td-active" },
65 { data: {"_": "owner",
65 { data: {"_": "owner",
66 "sort": "owner"}, title: "${_('Owner')}", className: "td-user" },
66 "sort": "owner"}, title: "${_('Owner')}", className: "td-user" },
67 { data: {"_": "action",
67 { data: {"_": "action",
68 "sort": "action"}, title: "${_('Action')}", className: "td-action" }
68 "sort": "action"}, title: "${_('Action')}", className: "td-action" }
69 ],
69 ],
70 language: {
70 language: {
71 paginate: DEFAULT_GRID_PAGINATION,
71 paginate: DEFAULT_GRID_PAGINATION,
72 emptyTable: _gettext("No user groups available yet.")
72 emptyTable: _gettext("No user groups available yet.")
73 },
73 },
74 "initComplete": function( settings, json ) {
74 "initComplete": function( settings, json ) {
75 get_datatable_count();
75 get_datatable_count();
76 }
76 }
77 });
77 });
78
78
79 // update the counter when doing search
79 // update the counter when doing search
80 $('#user_group_list_table').on( 'search.dt', function (e,settings) {
80 $('#user_group_list_table').on( 'search.dt', function (e,settings) {
81 get_datatable_count();
81 get_datatable_count();
82 });
82 });
83
83
84 // filter, filter both grids
84 // filter, filter both grids
85 $('#q_filter').on( 'keyup', function () {
85 $('#q_filter').on( 'keyup', function () {
86 var user_api = $('#user_group_list_table').dataTable().api();
86 var user_api = $('#user_group_list_table').dataTable().api();
87 user_api
87 user_api
88 .columns(0)
88 .columns(0)
89 .search(this.value)
89 .search(this.value)
90 .draw();
90 .draw();
91 });
91 });
92
92
93 // refilter table if page load via back button
93 // refilter table if page load via back button
94 $("#q_filter").trigger('keyup');
94 $("#q_filter").trigger('keyup');
95
95
96 });
96 });
97
97
98 </script>
98 </script>
99
99
100 </%def>
100 </%def>
@@ -1,147 +1,147 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.mako"/>
2 <%inherit file="/base/base.mako"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 ${_('Add user')}
5 ${_('Add user')}
6 %if c.rhodecode_name:
6 %if c.rhodecode_name:
7 &middot; ${h.branding(c.rhodecode_name)}
7 &middot; ${h.branding(c.rhodecode_name)}
8 %endif
8 %endif
9 </%def>
9 </%def>
10 <%def name="breadcrumbs_links()">
10 <%def name="breadcrumbs_links()">
11 ${h.link_to(_('Admin'),h.url('admin_home'))}
11 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
12 &raquo;
12 &raquo;
13 ${h.link_to(_('Users'),h.route_path('users'))}
13 ${h.link_to(_('Users'),h.route_path('users'))}
14 &raquo;
14 &raquo;
15 ${_('Add User')}
15 ${_('Add User')}
16 </%def>
16 </%def>
17
17
18 <%def name="menu_bar_nav()">
18 <%def name="menu_bar_nav()">
19 ${self.menu_items(active='admin')}
19 ${self.menu_items(active='admin')}
20 </%def>
20 </%def>
21
21
22 <%def name="main()">
22 <%def name="main()">
23 <div class="box">
23 <div class="box">
24 <!-- box / title -->
24 <!-- box / title -->
25 <div class="title">
25 <div class="title">
26 ${self.breadcrumbs()}
26 ${self.breadcrumbs()}
27 </div>
27 </div>
28 <!-- end box / title -->
28 <!-- end box / title -->
29 ${h.secure_form(url('users'))}
29 ${h.secure_form(url('users'))}
30 <div class="form">
30 <div class="form">
31 <!-- fields -->
31 <!-- fields -->
32 <div class="fields">
32 <div class="fields">
33 <div class="field">
33 <div class="field">
34 <div class="label">
34 <div class="label">
35 <label for="username">${_('Username')}:</label>
35 <label for="username">${_('Username')}:</label>
36 </div>
36 </div>
37 <div class="input">
37 <div class="input">
38 ${h.text('username', class_='medium')}
38 ${h.text('username', class_='medium')}
39 </div>
39 </div>
40 </div>
40 </div>
41
41
42 <div class="field">
42 <div class="field">
43 <div class="label">
43 <div class="label">
44 <label for="password">${_('Password')}:</label>
44 <label for="password">${_('Password')}:</label>
45 </div>
45 </div>
46 <div class="input">
46 <div class="input">
47 ${h.password('password', class_='medium')}
47 ${h.password('password', class_='medium')}
48 </div>
48 </div>
49 </div>
49 </div>
50
50
51 <div class="field">
51 <div class="field">
52 <div class="label">
52 <div class="label">
53 <label for="password_confirmation">${_('Password confirmation')}:</label>
53 <label for="password_confirmation">${_('Password confirmation')}:</label>
54 </div>
54 </div>
55 <div class="input">
55 <div class="input">
56 ${h.password('password_confirmation',autocomplete="off", class_='medium')}
56 ${h.password('password_confirmation',autocomplete="off", class_='medium')}
57 <div class="info-block">
57 <div class="info-block">
58 <a id="generate_password" href="#">
58 <a id="generate_password" href="#">
59 <i class="icon-lock"></i> ${_('Generate password')}
59 <i class="icon-lock"></i> ${_('Generate password')}
60 </a>
60 </a>
61 <span id="generate_password_preview"></span>
61 <span id="generate_password_preview"></span>
62 </div>
62 </div>
63 </div>
63 </div>
64 </div>
64 </div>
65
65
66 <div class="field">
66 <div class="field">
67 <div class="label">
67 <div class="label">
68 <label for="firstname">${_('First Name')}:</label>
68 <label for="firstname">${_('First Name')}:</label>
69 </div>
69 </div>
70 <div class="input">
70 <div class="input">
71 ${h.text('firstname', class_='medium')}
71 ${h.text('firstname', class_='medium')}
72 </div>
72 </div>
73 </div>
73 </div>
74
74
75 <div class="field">
75 <div class="field">
76 <div class="label">
76 <div class="label">
77 <label for="lastname">${_('Last Name')}:</label>
77 <label for="lastname">${_('Last Name')}:</label>
78 </div>
78 </div>
79 <div class="input">
79 <div class="input">
80 ${h.text('lastname', class_='medium')}
80 ${h.text('lastname', class_='medium')}
81 </div>
81 </div>
82 </div>
82 </div>
83
83
84 <div class="field">
84 <div class="field">
85 <div class="label">
85 <div class="label">
86 <label for="email">${_('Email')}:</label>
86 <label for="email">${_('Email')}:</label>
87 </div>
87 </div>
88 <div class="input">
88 <div class="input">
89 ${h.text('email', class_='medium')}
89 ${h.text('email', class_='medium')}
90 ${h.hidden('extern_name', c.default_extern_type)}
90 ${h.hidden('extern_name', c.default_extern_type)}
91 ${h.hidden('extern_type', c.default_extern_type)}
91 ${h.hidden('extern_type', c.default_extern_type)}
92 </div>
92 </div>
93 </div>
93 </div>
94
94
95 <div class="field">
95 <div class="field">
96 <div class="label label-checkbox">
96 <div class="label label-checkbox">
97 <label for="active">${_('Active')}:</label>
97 <label for="active">${_('Active')}:</label>
98 </div>
98 </div>
99 <div class="checkboxes">
99 <div class="checkboxes">
100 ${h.checkbox('active',value=True,checked='checked')}
100 ${h.checkbox('active',value=True,checked='checked')}
101 </div>
101 </div>
102 </div>
102 </div>
103
103
104 <div class="field">
104 <div class="field">
105 <div class="label label-checkbox">
105 <div class="label label-checkbox">
106 <label for="password_change">${_('Password change')}:</label>
106 <label for="password_change">${_('Password change')}:</label>
107 </div>
107 </div>
108 <div class="checkboxes">
108 <div class="checkboxes">
109 ${h.checkbox('password_change',value=True)}
109 ${h.checkbox('password_change',value=True)}
110 <span class="help-block">${_('Force user to change his password on the next login')}</span>
110 <span class="help-block">${_('Force user to change his password on the next login')}</span>
111 </div>
111 </div>
112 </div>
112 </div>
113
113
114 <div class="field">
114 <div class="field">
115 <div class="label label-checkbox">
115 <div class="label label-checkbox">
116 <label for="create_repo_group">${_('Add personal repository group')}:</label>
116 <label for="create_repo_group">${_('Add personal repository group')}:</label>
117 </div>
117 </div>
118 <div class="checkboxes">
118 <div class="checkboxes">
119 ${h.checkbox('create_repo_group',value=True, checked=c.default_create_repo_group)}
119 ${h.checkbox('create_repo_group',value=True, checked=c.default_create_repo_group)}
120 <span class="help-block">
120 <span class="help-block">
121 ${_('New group will be created at: `/%(path)s`') % {'path': c.personal_repo_group_name}}<br/>
121 ${_('New group will be created at: `/%(path)s`') % {'path': c.personal_repo_group_name}}<br/>
122 ${_('User will be automatically set as this group owner.')}
122 ${_('User will be automatically set as this group owner.')}
123 </span>
123 </span>
124 </div>
124 </div>
125 </div>
125 </div>
126
126
127 <div class="buttons">
127 <div class="buttons">
128 ${h.submit('save',_('Save'),class_="btn")}
128 ${h.submit('save',_('Save'),class_="btn")}
129 </div>
129 </div>
130 </div>
130 </div>
131 </div>
131 </div>
132 ${h.end_form()}
132 ${h.end_form()}
133 </div>
133 </div>
134 <script>
134 <script>
135 $(document).ready(function(){
135 $(document).ready(function(){
136 $('#username').focus();
136 $('#username').focus();
137
137
138 $('#generate_password').on('click', function(e){
138 $('#generate_password').on('click', function(e){
139 var tmpl = "(${_('generated password:')} {0})";
139 var tmpl = "(${_('generated password:')} {0})";
140 var new_passwd = generatePassword(12);
140 var new_passwd = generatePassword(12);
141 $('#generate_password_preview').html(tmpl.format(new_passwd));
141 $('#generate_password_preview').html(tmpl.format(new_passwd));
142 $('#password').val(new_passwd);
142 $('#password').val(new_passwd);
143 $('#password_confirmation').val(new_passwd);
143 $('#password_confirmation').val(new_passwd);
144 })
144 })
145 })
145 })
146 </script>
146 </script>
147 </%def>
147 </%def>
@@ -1,56 +1,56 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.mako"/>
2 <%inherit file="/base/base.mako"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 ${_('%s user settings') % c.user.username}
5 ${_('%s user settings') % c.user.username}
6 %if c.rhodecode_name:
6 %if c.rhodecode_name:
7 &middot; ${h.branding(c.rhodecode_name)}
7 &middot; ${h.branding(c.rhodecode_name)}
8 %endif
8 %endif
9 </%def>
9 </%def>
10
10
11 <%def name="breadcrumbs_links()">
11 <%def name="breadcrumbs_links()">
12 ${h.link_to(_('Admin'),h.url('admin_home'))}
12 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
13 &raquo;
13 &raquo;
14 ${h.link_to(_('Users'),h.route_path('users'))}
14 ${h.link_to(_('Users'),h.route_path('users'))}
15 &raquo;
15 &raquo;
16 % if c.user.active:
16 % if c.user.active:
17 ${c.user.username}
17 ${c.user.username}
18 % else:
18 % else:
19 <strike title="${_('This user is set as disabled')}">${c.user.username}</strike>
19 <strike title="${_('This user is set as disabled')}">${c.user.username}</strike>
20 % endif
20 % endif
21
21
22 </%def>
22 </%def>
23
23
24 <%def name="menu_bar_nav()">
24 <%def name="menu_bar_nav()">
25 ${self.menu_items(active='admin')}
25 ${self.menu_items(active='admin')}
26 </%def>
26 </%def>
27
27
28 <%def name="main()">
28 <%def name="main()">
29 <div class="box user_settings">
29 <div class="box user_settings">
30 <div class="title">
30 <div class="title">
31 ${self.breadcrumbs()}
31 ${self.breadcrumbs()}
32 </div>
32 </div>
33
33
34 ##main
34 ##main
35 <div class="sidebar-col-wrapper">
35 <div class="sidebar-col-wrapper">
36 <div class="sidebar">
36 <div class="sidebar">
37 <ul class="nav nav-pills nav-stacked">
37 <ul class="nav nav-pills nav-stacked">
38 <li class="${'active' if c.active=='profile' else ''}"><a href="${h.url('edit_user', user_id=c.user.user_id)}">${_('User Profile')}</a></li>
38 <li class="${'active' if c.active=='profile' else ''}"><a href="${h.url('edit_user', user_id=c.user.user_id)}">${_('User Profile')}</a></li>
39 <li class="${'active' if c.active=='auth_tokens' else ''}"><a href="${h.route_path('edit_user_auth_tokens', user_id=c.user.user_id)}">${_('Auth tokens')}</a></li>
39 <li class="${'active' if c.active=='auth_tokens' else ''}"><a href="${h.route_path('edit_user_auth_tokens', user_id=c.user.user_id)}">${_('Auth tokens')}</a></li>
40 <li class="${'active' if c.active=='advanced' else ''}"><a href="${h.url('edit_user_advanced', user_id=c.user.user_id)}">${_('Advanced')}</a></li>
40 <li class="${'active' if c.active=='advanced' else ''}"><a href="${h.url('edit_user_advanced', user_id=c.user.user_id)}">${_('Advanced')}</a></li>
41 <li class="${'active' if c.active=='global_perms' else ''}"><a href="${h.url('edit_user_global_perms', user_id=c.user.user_id)}">${_('Global permissions')}</a></li>
41 <li class="${'active' if c.active=='global_perms' else ''}"><a href="${h.url('edit_user_global_perms', user_id=c.user.user_id)}">${_('Global permissions')}</a></li>
42 <li class="${'active' if c.active=='perms_summary' else ''}"><a href="${h.url('edit_user_perms_summary', user_id=c.user.user_id)}">${_('Permissions summary')}</a></li>
42 <li class="${'active' if c.active=='perms_summary' else ''}"><a href="${h.url('edit_user_perms_summary', user_id=c.user.user_id)}">${_('Permissions summary')}</a></li>
43 <li class="${'active' if c.active=='emails' else ''}"><a href="${h.url('edit_user_emails', user_id=c.user.user_id)}">${_('Emails')}</a></li>
43 <li class="${'active' if c.active=='emails' else ''}"><a href="${h.url('edit_user_emails', user_id=c.user.user_id)}">${_('Emails')}</a></li>
44 <li class="${'active' if c.active=='ips' else ''}"><a href="${h.url('edit_user_ips', user_id=c.user.user_id)}">${_('Ip Whitelist')}</a></li>
44 <li class="${'active' if c.active=='ips' else ''}"><a href="${h.url('edit_user_ips', user_id=c.user.user_id)}">${_('Ip Whitelist')}</a></li>
45 <li class="${'active' if c.active=='groups' else ''}"><a href="${h.route_path('edit_user_groups_management', user_id=c.user.user_id)}">${_('User Groups Management')}</a></li>
45 <li class="${'active' if c.active=='groups' else ''}"><a href="${h.route_path('edit_user_groups_management', user_id=c.user.user_id)}">${_('User Groups Management')}</a></li>
46 <li class="${'active' if c.active=='audit' else ''}"><a href="${h.route_path('edit_user_audit_logs', user_id=c.user.user_id)}">${_('User audit')}</a></li>
46 <li class="${'active' if c.active=='audit' else ''}"><a href="${h.route_path('edit_user_audit_logs', user_id=c.user.user_id)}">${_('User audit')}</a></li>
47 </ul>
47 </ul>
48 </div>
48 </div>
49
49
50 <div class="main-content-full-width">
50 <div class="main-content-full-width">
51 <%include file="/admin/users/user_edit_${c.active}.mako"/>
51 <%include file="/admin/users/user_edit_${c.active}.mako"/>
52 </div>
52 </div>
53 </div>
53 </div>
54 </div>
54 </div>
55
55
56 </%def>
56 </%def>
@@ -1,120 +1,120 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.mako"/>
2 <%inherit file="/base/base.mako"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 ${_('Users administration')}
5 ${_('Users administration')}
6 %if c.rhodecode_name:
6 %if c.rhodecode_name:
7 &middot; ${h.branding(c.rhodecode_name)}
7 &middot; ${h.branding(c.rhodecode_name)}
8 %endif
8 %endif
9 </%def>
9 </%def>
10
10
11 <%def name="breadcrumbs_links()">
11 <%def name="breadcrumbs_links()">
12 <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" placeholder="${_('quick filter...')}" value=""/>
12 <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" placeholder="${_('quick filter...')}" value=""/>
13 ${h.link_to(_('Admin'),h.url('admin_home'))} &raquo; <span id="user_count">0</span>
13 ${h.link_to(_('Admin'),h.route_path('admin_home'))} &raquo; <span id="user_count">0</span>
14 </%def>
14 </%def>
15
15
16 <%def name="menu_bar_nav()">
16 <%def name="menu_bar_nav()">
17 ${self.menu_items(active='admin')}
17 ${self.menu_items(active='admin')}
18 </%def>
18 </%def>
19
19
20 <%def name="main()">
20 <%def name="main()">
21
21
22 <div class="box">
22 <div class="box">
23
23
24 <div class="title">
24 <div class="title">
25 ${self.breadcrumbs()}
25 ${self.breadcrumbs()}
26 <ul class="links">
26 <ul class="links">
27 <li>
27 <li>
28 <a href="${h.url('new_user')}" class="btn btn-small btn-success">${_(u'Add User')}</a>
28 <a href="${h.url('new_user')}" class="btn btn-small btn-success">${_(u'Add User')}</a>
29 </li>
29 </li>
30 </ul>
30 </ul>
31 </div>
31 </div>
32
32
33 <div id="repos_list_wrap">
33 <div id="repos_list_wrap">
34 <table id="user_list_table" class="display"></table>
34 <table id="user_list_table" class="display"></table>
35 </div>
35 </div>
36 </div>
36 </div>
37
37
38 <script type="text/javascript">
38 <script type="text/javascript">
39
39
40 $(document).ready(function() {
40 $(document).ready(function() {
41 var $userListTable = $('#user_list_table');
41 var $userListTable = $('#user_list_table');
42
42
43 var getDatatableCount = function(){
43 var getDatatableCount = function(){
44 var table = $userListTable.dataTable();
44 var table = $userListTable.dataTable();
45 var page = table.api().page.info();
45 var page = table.api().page.info();
46 var active = page.recordsDisplay;
46 var active = page.recordsDisplay;
47 var total = page.recordsTotal;
47 var total = page.recordsTotal;
48
48
49 var _text = _gettext("{0} out of {1} users").format(active, total);
49 var _text = _gettext("{0} out of {1} users").format(active, total);
50 $('#user_count').text(_text);
50 $('#user_count').text(_text);
51 };
51 };
52
52
53 // user list
53 // user list
54 $userListTable.DataTable({
54 $userListTable.DataTable({
55 processing: true,
55 processing: true,
56 serverSide: true,
56 serverSide: true,
57 ajax: "${h.route_path('users_data')}",
57 ajax: "${h.route_path('users_data')}",
58 dom: 'rtp',
58 dom: 'rtp',
59 pageLength: ${c.visual.admin_grid_items},
59 pageLength: ${c.visual.admin_grid_items},
60 order: [[ 0, "asc" ]],
60 order: [[ 0, "asc" ]],
61 columns: [
61 columns: [
62 { data: {"_": "username",
62 { data: {"_": "username",
63 "sort": "username"}, title: "${_('Username')}", className: "td-user" },
63 "sort": "username"}, title: "${_('Username')}", className: "td-user" },
64 { data: {"_": "email",
64 { data: {"_": "email",
65 "sort": "email"}, title: "${_('Email')}", className: "td-email" },
65 "sort": "email"}, title: "${_('Email')}", className: "td-email" },
66 { data: {"_": "first_name",
66 { data: {"_": "first_name",
67 "sort": "first_name"}, title: "${_('First Name')}", className: "td-user" },
67 "sort": "first_name"}, title: "${_('First Name')}", className: "td-user" },
68 { data: {"_": "last_name",
68 { data: {"_": "last_name",
69 "sort": "last_name"}, title: "${_('Last Name')}", className: "td-user" },
69 "sort": "last_name"}, title: "${_('Last Name')}", className: "td-user" },
70 { data: {"_": "last_activity",
70 { data: {"_": "last_activity",
71 "sort": "last_activity",
71 "sort": "last_activity",
72 "type": Number}, title: "${_('Last activity')}", className: "td-time" },
72 "type": Number}, title: "${_('Last activity')}", className: "td-time" },
73 { data: {"_": "active",
73 { data: {"_": "active",
74 "sort": "active"}, title: "${_('Active')}", className: "td-active" },
74 "sort": "active"}, title: "${_('Active')}", className: "td-active" },
75 { data: {"_": "admin",
75 { data: {"_": "admin",
76 "sort": "admin"}, title: "${_('Admin')}", className: "td-admin" },
76 "sort": "admin"}, title: "${_('Admin')}", className: "td-admin" },
77 { data: {"_": "extern_type",
77 { data: {"_": "extern_type",
78 "sort": "extern_type"}, title: "${_('Auth type')}", className: "td-type" },
78 "sort": "extern_type"}, title: "${_('Auth type')}", className: "td-type" },
79 { data: {"_": "action",
79 { data: {"_": "action",
80 "sort": "action"}, title: "${_('Action')}", className: "td-action", orderable: false }
80 "sort": "action"}, title: "${_('Action')}", className: "td-action", orderable: false }
81 ],
81 ],
82 language: {
82 language: {
83 paginate: DEFAULT_GRID_PAGINATION,
83 paginate: DEFAULT_GRID_PAGINATION,
84 sProcessing: _gettext('loading...'),
84 sProcessing: _gettext('loading...'),
85 emptyTable: _gettext("No users available yet.")
85 emptyTable: _gettext("No users available yet.")
86 },
86 },
87
87
88 "createdRow": function ( row, data, index ) {
88 "createdRow": function ( row, data, index ) {
89 if (!data['active_raw']){
89 if (!data['active_raw']){
90 $(row).addClass('closed')
90 $(row).addClass('closed')
91 }
91 }
92 }
92 }
93 });
93 });
94
94
95 $userListTable.on('xhr.dt', function(e, settings, json, xhr){
95 $userListTable.on('xhr.dt', function(e, settings, json, xhr){
96 $userListTable.css('opacity', 1);
96 $userListTable.css('opacity', 1);
97 });
97 });
98
98
99 $userListTable.on('preXhr.dt', function(e, settings, data){
99 $userListTable.on('preXhr.dt', function(e, settings, data){
100 $userListTable.css('opacity', 0.3);
100 $userListTable.css('opacity', 0.3);
101 });
101 });
102
102
103 // refresh counters on draw
103 // refresh counters on draw
104 $userListTable.on('draw.dt', function(){
104 $userListTable.on('draw.dt', function(){
105 getDatatableCount();
105 getDatatableCount();
106 });
106 });
107
107
108 // filter
108 // filter
109 $('#q_filter').on('keyup',
109 $('#q_filter').on('keyup',
110 $.debounce(250, function() {
110 $.debounce(250, function() {
111 $userListTable.DataTable().search(
111 $userListTable.DataTable().search(
112 $('#q_filter').val()
112 $('#q_filter').val()
113 ).draw();
113 ).draw();
114 })
114 })
115 );
115 );
116
116
117 });
117 });
118 </script>
118 </script>
119
119
120 </%def>
120 </%def>
@@ -1,602 +1,602 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="root.mako"/>
2 <%inherit file="root.mako"/>
3
3
4 <div class="outerwrapper">
4 <div class="outerwrapper">
5 <!-- HEADER -->
5 <!-- HEADER -->
6 <div class="header">
6 <div class="header">
7 <div id="header-inner" class="wrapper">
7 <div id="header-inner" class="wrapper">
8 <div id="logo">
8 <div id="logo">
9 <div class="logo-wrapper">
9 <div class="logo-wrapper">
10 <a href="${h.url('home')}"><img src="${h.asset('images/rhodecode-logo-white-216x60.png')}" alt="RhodeCode"/></a>
10 <a href="${h.url('home')}"><img src="${h.asset('images/rhodecode-logo-white-216x60.png')}" alt="RhodeCode"/></a>
11 </div>
11 </div>
12 %if c.rhodecode_name:
12 %if c.rhodecode_name:
13 <div class="branding">- ${h.branding(c.rhodecode_name)}</div>
13 <div class="branding">- ${h.branding(c.rhodecode_name)}</div>
14 %endif
14 %endif
15 </div>
15 </div>
16 <!-- MENU BAR NAV -->
16 <!-- MENU BAR NAV -->
17 ${self.menu_bar_nav()}
17 ${self.menu_bar_nav()}
18 <!-- END MENU BAR NAV -->
18 <!-- END MENU BAR NAV -->
19 </div>
19 </div>
20 </div>
20 </div>
21 ${self.menu_bar_subnav()}
21 ${self.menu_bar_subnav()}
22 <!-- END HEADER -->
22 <!-- END HEADER -->
23
23
24 <!-- CONTENT -->
24 <!-- CONTENT -->
25 <div id="content" class="wrapper">
25 <div id="content" class="wrapper">
26
26
27 <rhodecode-toast id="notifications"></rhodecode-toast>
27 <rhodecode-toast id="notifications"></rhodecode-toast>
28
28
29 <div class="main">
29 <div class="main">
30 ${next.main()}
30 ${next.main()}
31 </div>
31 </div>
32 </div>
32 </div>
33 <!-- END CONTENT -->
33 <!-- END CONTENT -->
34
34
35 </div>
35 </div>
36 <!-- FOOTER -->
36 <!-- FOOTER -->
37 <div id="footer">
37 <div id="footer">
38 <div id="footer-inner" class="title wrapper">
38 <div id="footer-inner" class="title wrapper">
39 <div>
39 <div>
40 <p class="footer-link-right">
40 <p class="footer-link-right">
41 % if c.visual.show_version:
41 % if c.visual.show_version:
42 RhodeCode Enterprise ${c.rhodecode_version} ${c.rhodecode_edition}
42 RhodeCode Enterprise ${c.rhodecode_version} ${c.rhodecode_edition}
43 % endif
43 % endif
44 &copy; 2010-${h.datetime.today().year}, <a href="${h.route_url('rhodecode_official')}" target="_blank">RhodeCode GmbH</a>. All rights reserved.
44 &copy; 2010-${h.datetime.today().year}, <a href="${h.route_url('rhodecode_official')}" target="_blank">RhodeCode GmbH</a>. All rights reserved.
45 % if c.visual.rhodecode_support_url:
45 % if c.visual.rhodecode_support_url:
46 <a href="${c.visual.rhodecode_support_url}" target="_blank">${_('Support')}</a>
46 <a href="${c.visual.rhodecode_support_url}" target="_blank">${_('Support')}</a>
47 % endif
47 % endif
48 </p>
48 </p>
49 <% sid = 'block' if request.GET.get('showrcid') else 'none' %>
49 <% sid = 'block' if request.GET.get('showrcid') else 'none' %>
50 <p class="server-instance" style="display:${sid}">
50 <p class="server-instance" style="display:${sid}">
51 ## display hidden instance ID if specially defined
51 ## display hidden instance ID if specially defined
52 % if c.rhodecode_instanceid:
52 % if c.rhodecode_instanceid:
53 ${_('RhodeCode instance id: %s') % c.rhodecode_instanceid}
53 ${_('RhodeCode instance id: %s') % c.rhodecode_instanceid}
54 % endif
54 % endif
55 </p>
55 </p>
56 </div>
56 </div>
57 </div>
57 </div>
58 </div>
58 </div>
59
59
60 <!-- END FOOTER -->
60 <!-- END FOOTER -->
61
61
62 ### MAKO DEFS ###
62 ### MAKO DEFS ###
63
63
64 <%def name="menu_bar_subnav()">
64 <%def name="menu_bar_subnav()">
65 </%def>
65 </%def>
66
66
67 <%def name="breadcrumbs(class_='breadcrumbs')">
67 <%def name="breadcrumbs(class_='breadcrumbs')">
68 <div class="${class_}">
68 <div class="${class_}">
69 ${self.breadcrumbs_links()}
69 ${self.breadcrumbs_links()}
70 </div>
70 </div>
71 </%def>
71 </%def>
72
72
73 <%def name="admin_menu()">
73 <%def name="admin_menu()">
74 <ul class="admin_menu submenu">
74 <ul class="admin_menu submenu">
75 <li><a href="${h.url('admin_home')}">${_('Admin journal')}</a></li>
75 <li><a href="${h.route_path('admin_audit_logs')}">${_('Admin audit logs')}</a></li>
76 <li><a href="${h.url('repos')}">${_('Repositories')}</a></li>
76 <li><a href="${h.url('repos')}">${_('Repositories')}</a></li>
77 <li><a href="${h.url('repo_groups')}">${_('Repository groups')}</a></li>
77 <li><a href="${h.url('repo_groups')}">${_('Repository groups')}</a></li>
78 <li><a href="${h.route_path('users')}">${_('Users')}</a></li>
78 <li><a href="${h.route_path('users')}">${_('Users')}</a></li>
79 <li><a href="${h.url('users_groups')}">${_('User groups')}</a></li>
79 <li><a href="${h.url('users_groups')}">${_('User groups')}</a></li>
80 <li><a href="${h.url('admin_permissions_application')}">${_('Permissions')}</a></li>
80 <li><a href="${h.url('admin_permissions_application')}">${_('Permissions')}</a></li>
81 <li><a href="${h.route_path('auth_home', traverse='')}">${_('Authentication')}</a></li>
81 <li><a href="${h.route_path('auth_home', traverse='')}">${_('Authentication')}</a></li>
82 <li><a href="${h.route_path('global_integrations_home')}">${_('Integrations')}</a></li>
82 <li><a href="${h.route_path('global_integrations_home')}">${_('Integrations')}</a></li>
83 <li><a href="${h.url('admin_defaults_repositories')}">${_('Defaults')}</a></li>
83 <li><a href="${h.url('admin_defaults_repositories')}">${_('Defaults')}</a></li>
84 <li class="last"><a href="${h.url('admin_settings')}">${_('Settings')}</a></li>
84 <li class="last"><a href="${h.url('admin_settings')}">${_('Settings')}</a></li>
85 </ul>
85 </ul>
86 </%def>
86 </%def>
87
87
88
88
89 <%def name="dt_info_panel(elements)">
89 <%def name="dt_info_panel(elements)">
90 <dl class="dl-horizontal">
90 <dl class="dl-horizontal">
91 %for dt, dd, title, show_items in elements:
91 %for dt, dd, title, show_items in elements:
92 <dt>${dt}:</dt>
92 <dt>${dt}:</dt>
93 <dd title="${title}">
93 <dd title="${title}">
94 %if callable(dd):
94 %if callable(dd):
95 ## allow lazy evaluation of elements
95 ## allow lazy evaluation of elements
96 ${dd()}
96 ${dd()}
97 %else:
97 %else:
98 ${dd}
98 ${dd}
99 %endif
99 %endif
100 %if show_items:
100 %if show_items:
101 <span class="btn-collapse" data-toggle="item-${h.md5_safe(dt)[:6]}-details">${_('Show More')} </span>
101 <span class="btn-collapse" data-toggle="item-${h.md5_safe(dt)[:6]}-details">${_('Show More')} </span>
102 %endif
102 %endif
103 </dd>
103 </dd>
104
104
105 %if show_items:
105 %if show_items:
106 <div class="collapsable-content" data-toggle="item-${h.md5_safe(dt)[:6]}-details" style="display: none">
106 <div class="collapsable-content" data-toggle="item-${h.md5_safe(dt)[:6]}-details" style="display: none">
107 %for item in show_items:
107 %for item in show_items:
108 <dt></dt>
108 <dt></dt>
109 <dd>${item}</dd>
109 <dd>${item}</dd>
110 %endfor
110 %endfor
111 </div>
111 </div>
112 %endif
112 %endif
113
113
114 %endfor
114 %endfor
115 </dl>
115 </dl>
116 </%def>
116 </%def>
117
117
118
118
119 <%def name="gravatar(email, size=16)">
119 <%def name="gravatar(email, size=16)">
120 <%
120 <%
121 if (size > 16):
121 if (size > 16):
122 gravatar_class = 'gravatar gravatar-large'
122 gravatar_class = 'gravatar gravatar-large'
123 else:
123 else:
124 gravatar_class = 'gravatar'
124 gravatar_class = 'gravatar'
125 %>
125 %>
126 <%doc>
126 <%doc>
127 TODO: johbo: For now we serve double size images to make it smooth
127 TODO: johbo: For now we serve double size images to make it smooth
128 for retina. This is how it worked until now. Should be replaced
128 for retina. This is how it worked until now. Should be replaced
129 with a better solution at some point.
129 with a better solution at some point.
130 </%doc>
130 </%doc>
131 <img class="${gravatar_class}" src="${h.gravatar_url(email, size * 2)}" height="${size}" width="${size}">
131 <img class="${gravatar_class}" src="${h.gravatar_url(email, size * 2)}" height="${size}" width="${size}">
132 </%def>
132 </%def>
133
133
134
134
135 <%def name="gravatar_with_user(contact, size=16, show_disabled=False)">
135 <%def name="gravatar_with_user(contact, size=16, show_disabled=False)">
136 <% email = h.email_or_none(contact) %>
136 <% email = h.email_or_none(contact) %>
137 <div class="rc-user tooltip" title="${h.author_string(email)}">
137 <div class="rc-user tooltip" title="${h.author_string(email)}">
138 ${self.gravatar(email, size)}
138 ${self.gravatar(email, size)}
139 <span class="${'user user-disabled' if show_disabled else 'user'}"> ${h.link_to_user(contact)}</span>
139 <span class="${'user user-disabled' if show_disabled else 'user'}"> ${h.link_to_user(contact)}</span>
140 </div>
140 </div>
141 </%def>
141 </%def>
142
142
143
143
144 ## admin menu used for people that have some admin resources
144 ## admin menu used for people that have some admin resources
145 <%def name="admin_menu_simple(repositories=None, repository_groups=None, user_groups=None)">
145 <%def name="admin_menu_simple(repositories=None, repository_groups=None, user_groups=None)">
146 <ul class="submenu">
146 <ul class="submenu">
147 %if repositories:
147 %if repositories:
148 <li class="local-admin-repos"><a href="${h.url('repos')}">${_('Repositories')}</a></li>
148 <li class="local-admin-repos"><a href="${h.url('repos')}">${_('Repositories')}</a></li>
149 %endif
149 %endif
150 %if repository_groups:
150 %if repository_groups:
151 <li class="local-admin-repo-groups"><a href="${h.url('repo_groups')}">${_('Repository groups')}</a></li>
151 <li class="local-admin-repo-groups"><a href="${h.url('repo_groups')}">${_('Repository groups')}</a></li>
152 %endif
152 %endif
153 %if user_groups:
153 %if user_groups:
154 <li class="local-admin-user-groups"><a href="${h.url('users_groups')}">${_('User groups')}</a></li>
154 <li class="local-admin-user-groups"><a href="${h.url('users_groups')}">${_('User groups')}</a></li>
155 %endif
155 %endif
156 </ul>
156 </ul>
157 </%def>
157 </%def>
158
158
159 <%def name="repo_page_title(repo_instance)">
159 <%def name="repo_page_title(repo_instance)">
160 <div class="title-content">
160 <div class="title-content">
161 <div class="title-main">
161 <div class="title-main">
162 ## SVN/HG/GIT icons
162 ## SVN/HG/GIT icons
163 %if h.is_hg(repo_instance):
163 %if h.is_hg(repo_instance):
164 <i class="icon-hg"></i>
164 <i class="icon-hg"></i>
165 %endif
165 %endif
166 %if h.is_git(repo_instance):
166 %if h.is_git(repo_instance):
167 <i class="icon-git"></i>
167 <i class="icon-git"></i>
168 %endif
168 %endif
169 %if h.is_svn(repo_instance):
169 %if h.is_svn(repo_instance):
170 <i class="icon-svn"></i>
170 <i class="icon-svn"></i>
171 %endif
171 %endif
172
172
173 ## public/private
173 ## public/private
174 %if repo_instance.private:
174 %if repo_instance.private:
175 <i class="icon-repo-private"></i>
175 <i class="icon-repo-private"></i>
176 %else:
176 %else:
177 <i class="icon-repo-public"></i>
177 <i class="icon-repo-public"></i>
178 %endif
178 %endif
179
179
180 ## repo name with group name
180 ## repo name with group name
181 ${h.breadcrumb_repo_link(c.rhodecode_db_repo)}
181 ${h.breadcrumb_repo_link(c.rhodecode_db_repo)}
182
182
183 </div>
183 </div>
184
184
185 ## FORKED
185 ## FORKED
186 %if repo_instance.fork:
186 %if repo_instance.fork:
187 <p>
187 <p>
188 <i class="icon-code-fork"></i> ${_('Fork of')}
188 <i class="icon-code-fork"></i> ${_('Fork of')}
189 <a href="${h.url('summary_home',repo_name=repo_instance.fork.repo_name)}">${repo_instance.fork.repo_name}</a>
189 <a href="${h.url('summary_home',repo_name=repo_instance.fork.repo_name)}">${repo_instance.fork.repo_name}</a>
190 </p>
190 </p>
191 %endif
191 %endif
192
192
193 ## IMPORTED FROM REMOTE
193 ## IMPORTED FROM REMOTE
194 %if repo_instance.clone_uri:
194 %if repo_instance.clone_uri:
195 <p>
195 <p>
196 <i class="icon-code-fork"></i> ${_('Clone from')}
196 <i class="icon-code-fork"></i> ${_('Clone from')}
197 <a href="${h.url(h.safe_str(h.hide_credentials(repo_instance.clone_uri)))}">${h.hide_credentials(repo_instance.clone_uri)}</a>
197 <a href="${h.url(h.safe_str(h.hide_credentials(repo_instance.clone_uri)))}">${h.hide_credentials(repo_instance.clone_uri)}</a>
198 </p>
198 </p>
199 %endif
199 %endif
200
200
201 ## LOCKING STATUS
201 ## LOCKING STATUS
202 %if repo_instance.locked[0]:
202 %if repo_instance.locked[0]:
203 <p class="locking_locked">
203 <p class="locking_locked">
204 <i class="icon-repo-lock"></i>
204 <i class="icon-repo-lock"></i>
205 ${_('Repository locked by %(user)s') % {'user': h.person_by_id(repo_instance.locked[0])}}
205 ${_('Repository locked by %(user)s') % {'user': h.person_by_id(repo_instance.locked[0])}}
206 </p>
206 </p>
207 %elif repo_instance.enable_locking:
207 %elif repo_instance.enable_locking:
208 <p class="locking_unlocked">
208 <p class="locking_unlocked">
209 <i class="icon-repo-unlock"></i>
209 <i class="icon-repo-unlock"></i>
210 ${_('Repository not locked. Pull repository to lock it.')}
210 ${_('Repository not locked. Pull repository to lock it.')}
211 </p>
211 </p>
212 %endif
212 %endif
213
213
214 </div>
214 </div>
215 </%def>
215 </%def>
216
216
217 <%def name="repo_menu(active=None)">
217 <%def name="repo_menu(active=None)">
218 <%
218 <%
219 def is_active(selected):
219 def is_active(selected):
220 if selected == active:
220 if selected == active:
221 return "active"
221 return "active"
222 %>
222 %>
223
223
224 <!--- CONTEXT BAR -->
224 <!--- CONTEXT BAR -->
225 <div id="context-bar">
225 <div id="context-bar">
226 <div class="wrapper">
226 <div class="wrapper">
227 <ul id="context-pages" class="horizontal-list navigation">
227 <ul id="context-pages" class="horizontal-list navigation">
228 <li class="${is_active('summary')}"><a class="menulink" href="${h.url('summary_home', repo_name=c.repo_name)}"><div class="menulabel">${_('Summary')}</div></a></li>
228 <li class="${is_active('summary')}"><a class="menulink" href="${h.url('summary_home', repo_name=c.repo_name)}"><div class="menulabel">${_('Summary')}</div></a></li>
229 <li class="${is_active('changelog')}"><a class="menulink" href="${h.url('changelog_home', repo_name=c.repo_name)}"><div class="menulabel">${_('Changelog')}</div></a></li>
229 <li class="${is_active('changelog')}"><a class="menulink" href="${h.url('changelog_home', repo_name=c.repo_name)}"><div class="menulabel">${_('Changelog')}</div></a></li>
230 <li class="${is_active('files')}"><a class="menulink" href="${h.url('files_home', repo_name=c.repo_name, revision=c.rhodecode_db_repo.landing_rev[1])}"><div class="menulabel">${_('Files')}</div></a></li>
230 <li class="${is_active('files')}"><a class="menulink" href="${h.url('files_home', repo_name=c.repo_name, revision=c.rhodecode_db_repo.landing_rev[1])}"><div class="menulabel">${_('Files')}</div></a></li>
231 <li class="${is_active('compare')}">
231 <li class="${is_active('compare')}">
232 <a class="menulink" href="${h.url('compare_home',repo_name=c.repo_name)}"><div class="menulabel">${_('Compare')}</div></a>
232 <a class="menulink" href="${h.url('compare_home',repo_name=c.repo_name)}"><div class="menulabel">${_('Compare')}</div></a>
233 </li>
233 </li>
234 ## TODO: anderson: ideally it would have a function on the scm_instance "enable_pullrequest() and enable_fork()"
234 ## TODO: anderson: ideally it would have a function on the scm_instance "enable_pullrequest() and enable_fork()"
235 %if c.rhodecode_db_repo.repo_type in ['git','hg']:
235 %if c.rhodecode_db_repo.repo_type in ['git','hg']:
236 <li class="${is_active('showpullrequest')}">
236 <li class="${is_active('showpullrequest')}">
237 <a class="menulink" href="${h.url('pullrequest_show_all',repo_name=c.repo_name)}" title="${_('Show Pull Requests for %s') % c.repo_name}">
237 <a class="menulink" href="${h.url('pullrequest_show_all',repo_name=c.repo_name)}" title="${_('Show Pull Requests for %s') % c.repo_name}">
238 %if c.repository_pull_requests:
238 %if c.repository_pull_requests:
239 <span class="pr_notifications">${c.repository_pull_requests}</span>
239 <span class="pr_notifications">${c.repository_pull_requests}</span>
240 %endif
240 %endif
241 <div class="menulabel">${_('Pull Requests')}</div>
241 <div class="menulabel">${_('Pull Requests')}</div>
242 </a>
242 </a>
243 </li>
243 </li>
244 %endif
244 %endif
245 <li class="${is_active('options')}">
245 <li class="${is_active('options')}">
246 <a class="menulink" href="#" class="dropdown"><div class="menulabel">${_('Options')} <div class="show_more"></div></div></a>
246 <a class="menulink" href="#" class="dropdown"><div class="menulabel">${_('Options')} <div class="show_more"></div></div></a>
247 <ul class="submenu">
247 <ul class="submenu">
248 %if h.HasRepoPermissionAll('repository.admin')(c.repo_name):
248 %if h.HasRepoPermissionAll('repository.admin')(c.repo_name):
249 <li><a href="${h.route_path('edit_repo',repo_name=c.repo_name)}">${_('Settings')}</a></li>
249 <li><a href="${h.route_path('edit_repo',repo_name=c.repo_name)}">${_('Settings')}</a></li>
250 %endif
250 %endif
251 %if c.rhodecode_db_repo.fork:
251 %if c.rhodecode_db_repo.fork:
252 <li><a href="${h.url('compare_url',repo_name=c.rhodecode_db_repo.fork.repo_name,source_ref_type=c.rhodecode_db_repo.landing_rev[0],source_ref=c.rhodecode_db_repo.landing_rev[1], target_repo=c.repo_name,target_ref_type='branch' if request.GET.get('branch') else c.rhodecode_db_repo.landing_rev[0],target_ref=request.GET.get('branch') or c.rhodecode_db_repo.landing_rev[1], merge=1)}">
252 <li><a href="${h.url('compare_url',repo_name=c.rhodecode_db_repo.fork.repo_name,source_ref_type=c.rhodecode_db_repo.landing_rev[0],source_ref=c.rhodecode_db_repo.landing_rev[1], target_repo=c.repo_name,target_ref_type='branch' if request.GET.get('branch') else c.rhodecode_db_repo.landing_rev[0],target_ref=request.GET.get('branch') or c.rhodecode_db_repo.landing_rev[1], merge=1)}">
253 ${_('Compare fork')}</a></li>
253 ${_('Compare fork')}</a></li>
254 %endif
254 %endif
255
255
256 <li><a href="${h.route_path('search_repo',repo_name=c.repo_name)}">${_('Search')}</a></li>
256 <li><a href="${h.route_path('search_repo',repo_name=c.repo_name)}">${_('Search')}</a></li>
257
257
258 %if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name) and c.rhodecode_db_repo.enable_locking:
258 %if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name) and c.rhodecode_db_repo.enable_locking:
259 %if c.rhodecode_db_repo.locked[0]:
259 %if c.rhodecode_db_repo.locked[0]:
260 <li><a class="locking_del" href="${h.url('toggle_locking',repo_name=c.repo_name)}">${_('Unlock')}</a></li>
260 <li><a class="locking_del" href="${h.url('toggle_locking',repo_name=c.repo_name)}">${_('Unlock')}</a></li>
261 %else:
261 %else:
262 <li><a class="locking_add" href="${h.url('toggle_locking',repo_name=c.repo_name)}">${_('Lock')}</a></li>
262 <li><a class="locking_add" href="${h.url('toggle_locking',repo_name=c.repo_name)}">${_('Lock')}</a></li>
263 %endif
263 %endif
264 %endif
264 %endif
265 %if c.rhodecode_user.username != h.DEFAULT_USER:
265 %if c.rhodecode_user.username != h.DEFAULT_USER:
266 %if c.rhodecode_db_repo.repo_type in ['git','hg']:
266 %if c.rhodecode_db_repo.repo_type in ['git','hg']:
267 <li><a href="${h.url('repo_fork_home',repo_name=c.repo_name)}">${_('Fork')}</a></li>
267 <li><a href="${h.url('repo_fork_home',repo_name=c.repo_name)}">${_('Fork')}</a></li>
268 <li><a href="${h.url('pullrequest_home',repo_name=c.repo_name)}">${_('Create Pull Request')}</a></li>
268 <li><a href="${h.url('pullrequest_home',repo_name=c.repo_name)}">${_('Create Pull Request')}</a></li>
269 %endif
269 %endif
270 %endif
270 %endif
271 </ul>
271 </ul>
272 </li>
272 </li>
273 </ul>
273 </ul>
274 </div>
274 </div>
275 <div class="clear"></div>
275 <div class="clear"></div>
276 </div>
276 </div>
277 <!--- END CONTEXT BAR -->
277 <!--- END CONTEXT BAR -->
278
278
279 </%def>
279 </%def>
280
280
281 <%def name="usermenu(active=False)">
281 <%def name="usermenu(active=False)">
282 ## USER MENU
282 ## USER MENU
283 <li id="quick_login_li" class="${'active' if active else ''}">
283 <li id="quick_login_li" class="${'active' if active else ''}">
284 <a id="quick_login_link" class="menulink childs">
284 <a id="quick_login_link" class="menulink childs">
285 ${gravatar(c.rhodecode_user.email, 20)}
285 ${gravatar(c.rhodecode_user.email, 20)}
286 <span class="user">
286 <span class="user">
287 %if c.rhodecode_user.username != h.DEFAULT_USER:
287 %if c.rhodecode_user.username != h.DEFAULT_USER:
288 <span class="menu_link_user">${c.rhodecode_user.username}</span><div class="show_more"></div>
288 <span class="menu_link_user">${c.rhodecode_user.username}</span><div class="show_more"></div>
289 %else:
289 %else:
290 <span>${_('Sign in')}</span>
290 <span>${_('Sign in')}</span>
291 %endif
291 %endif
292 </span>
292 </span>
293 </a>
293 </a>
294
294
295 <div class="user-menu submenu">
295 <div class="user-menu submenu">
296 <div id="quick_login">
296 <div id="quick_login">
297 %if c.rhodecode_user.username == h.DEFAULT_USER:
297 %if c.rhodecode_user.username == h.DEFAULT_USER:
298 <h4>${_('Sign in to your account')}</h4>
298 <h4>${_('Sign in to your account')}</h4>
299 ${h.form(h.route_path('login', _query={'came_from': h.url.current()}), needs_csrf_token=False)}
299 ${h.form(h.route_path('login', _query={'came_from': h.url.current()}), needs_csrf_token=False)}
300 <div class="form form-vertical">
300 <div class="form form-vertical">
301 <div class="fields">
301 <div class="fields">
302 <div class="field">
302 <div class="field">
303 <div class="label">
303 <div class="label">
304 <label for="username">${_('Username')}:</label>
304 <label for="username">${_('Username')}:</label>
305 </div>
305 </div>
306 <div class="input">
306 <div class="input">
307 ${h.text('username',class_='focus',tabindex=1)}
307 ${h.text('username',class_='focus',tabindex=1)}
308 </div>
308 </div>
309
309
310 </div>
310 </div>
311 <div class="field">
311 <div class="field">
312 <div class="label">
312 <div class="label">
313 <label for="password">${_('Password')}:</label>
313 <label for="password">${_('Password')}:</label>
314 %if h.HasPermissionAny('hg.password_reset.enabled')():
314 %if h.HasPermissionAny('hg.password_reset.enabled')():
315 <span class="forgot_password">${h.link_to(_('(Forgot password?)'),h.route_path('reset_password'), class_='pwd_reset')}</span>
315 <span class="forgot_password">${h.link_to(_('(Forgot password?)'),h.route_path('reset_password'), class_='pwd_reset')}</span>
316 %endif
316 %endif
317 </div>
317 </div>
318 <div class="input">
318 <div class="input">
319 ${h.password('password',class_='focus',tabindex=2)}
319 ${h.password('password',class_='focus',tabindex=2)}
320 </div>
320 </div>
321 </div>
321 </div>
322 <div class="buttons">
322 <div class="buttons">
323 <div class="register">
323 <div class="register">
324 %if h.HasPermissionAny('hg.admin', 'hg.register.auto_activate', 'hg.register.manual_activate')():
324 %if h.HasPermissionAny('hg.admin', 'hg.register.auto_activate', 'hg.register.manual_activate')():
325 ${h.link_to(_("Don't have an account?"),h.route_path('register'))} <br/>
325 ${h.link_to(_("Don't have an account?"),h.route_path('register'))} <br/>
326 %endif
326 %endif
327 ${h.link_to(_("Using external auth? Login here."),h.route_path('login'))}
327 ${h.link_to(_("Using external auth? Login here."),h.route_path('login'))}
328 </div>
328 </div>
329 <div class="submit">
329 <div class="submit">
330 ${h.submit('sign_in',_('Sign In'),class_="btn btn-small",tabindex=3)}
330 ${h.submit('sign_in',_('Sign In'),class_="btn btn-small",tabindex=3)}
331 </div>
331 </div>
332 </div>
332 </div>
333 </div>
333 </div>
334 </div>
334 </div>
335 ${h.end_form()}
335 ${h.end_form()}
336 %else:
336 %else:
337 <div class="">
337 <div class="">
338 <div class="big_gravatar">${gravatar(c.rhodecode_user.email, 48)}</div>
338 <div class="big_gravatar">${gravatar(c.rhodecode_user.email, 48)}</div>
339 <div class="full_name">${c.rhodecode_user.full_name_or_username}</div>
339 <div class="full_name">${c.rhodecode_user.full_name_or_username}</div>
340 <div class="email">${c.rhodecode_user.email}</div>
340 <div class="email">${c.rhodecode_user.email}</div>
341 </div>
341 </div>
342 <div class="">
342 <div class="">
343 <ol class="links">
343 <ol class="links">
344 <li>${h.link_to(_(u'My account'),h.route_path('my_account_profile'))}</li>
344 <li>${h.link_to(_(u'My account'),h.route_path('my_account_profile'))}</li>
345 % if c.rhodecode_user.personal_repo_group:
345 % if c.rhodecode_user.personal_repo_group:
346 <li>${h.link_to(_(u'My personal group'), h.url('repo_group_home', group_name=c.rhodecode_user.personal_repo_group.group_name))}</li>
346 <li>${h.link_to(_(u'My personal group'), h.url('repo_group_home', group_name=c.rhodecode_user.personal_repo_group.group_name))}</li>
347 % endif
347 % endif
348 <li class="logout">
348 <li class="logout">
349 ${h.secure_form(h.route_path('logout'))}
349 ${h.secure_form(h.route_path('logout'))}
350 ${h.submit('log_out', _(u'Sign Out'),class_="btn btn-primary")}
350 ${h.submit('log_out', _(u'Sign Out'),class_="btn btn-primary")}
351 ${h.end_form()}
351 ${h.end_form()}
352 </li>
352 </li>
353 </ol>
353 </ol>
354 </div>
354 </div>
355 %endif
355 %endif
356 </div>
356 </div>
357 </div>
357 </div>
358 %if c.rhodecode_user.username != h.DEFAULT_USER:
358 %if c.rhodecode_user.username != h.DEFAULT_USER:
359 <div class="pill_container">
359 <div class="pill_container">
360 % if c.unread_notifications == 0:
360 % if c.unread_notifications == 0:
361 <a class="menu_link_notifications empty" href="${h.url('notifications')}">${c.unread_notifications}</a>
361 <a class="menu_link_notifications empty" href="${h.url('notifications')}">${c.unread_notifications}</a>
362 % else:
362 % else:
363 <a class="menu_link_notifications" href="${h.url('notifications')}">${c.unread_notifications}</a>
363 <a class="menu_link_notifications" href="${h.url('notifications')}">${c.unread_notifications}</a>
364 % endif
364 % endif
365 </div>
365 </div>
366 % endif
366 % endif
367 </li>
367 </li>
368 </%def>
368 </%def>
369
369
370 <%def name="menu_items(active=None)">
370 <%def name="menu_items(active=None)">
371 <%
371 <%
372 def is_active(selected):
372 def is_active(selected):
373 if selected == active:
373 if selected == active:
374 return "active"
374 return "active"
375 return ""
375 return ""
376 %>
376 %>
377 <ul id="quick" class="main_nav navigation horizontal-list">
377 <ul id="quick" class="main_nav navigation horizontal-list">
378 <!-- repo switcher -->
378 <!-- repo switcher -->
379 <li class="${is_active('repositories')} repo_switcher_li has_select2">
379 <li class="${is_active('repositories')} repo_switcher_li has_select2">
380 <input id="repo_switcher" name="repo_switcher" type="hidden">
380 <input id="repo_switcher" name="repo_switcher" type="hidden">
381 </li>
381 </li>
382
382
383 ## ROOT MENU
383 ## ROOT MENU
384 %if c.rhodecode_user.username != h.DEFAULT_USER:
384 %if c.rhodecode_user.username != h.DEFAULT_USER:
385 <li class="${is_active('journal')}">
385 <li class="${is_active('journal')}">
386 <a class="menulink" title="${_('Show activity journal')}" href="${h.url('journal')}">
386 <a class="menulink" title="${_('Show activity journal')}" href="${h.url('journal')}">
387 <div class="menulabel">${_('Journal')}</div>
387 <div class="menulabel">${_('Journal')}</div>
388 </a>
388 </a>
389 </li>
389 </li>
390 %else:
390 %else:
391 <li class="${is_active('journal')}">
391 <li class="${is_active('journal')}">
392 <a class="menulink" title="${_('Show Public activity journal')}" href="${h.url('public_journal')}">
392 <a class="menulink" title="${_('Show Public activity journal')}" href="${h.url('public_journal')}">
393 <div class="menulabel">${_('Public journal')}</div>
393 <div class="menulabel">${_('Public journal')}</div>
394 </a>
394 </a>
395 </li>
395 </li>
396 %endif
396 %endif
397 <li class="${is_active('gists')}">
397 <li class="${is_active('gists')}">
398 <a class="menulink childs" title="${_('Show Gists')}" href="${h.url('gists')}">
398 <a class="menulink childs" title="${_('Show Gists')}" href="${h.url('gists')}">
399 <div class="menulabel">${_('Gists')}</div>
399 <div class="menulabel">${_('Gists')}</div>
400 </a>
400 </a>
401 </li>
401 </li>
402 <li class="${is_active('search')}">
402 <li class="${is_active('search')}">
403 <a class="menulink" title="${_('Search in repositories you have access to')}" href="${h.route_path('search')}">
403 <a class="menulink" title="${_('Search in repositories you have access to')}" href="${h.route_path('search')}">
404 <div class="menulabel">${_('Search')}</div>
404 <div class="menulabel">${_('Search')}</div>
405 </a>
405 </a>
406 </li>
406 </li>
407 % if h.HasPermissionAll('hg.admin')('access admin main page'):
407 % if h.HasPermissionAll('hg.admin')('access admin main page'):
408 <li class="${is_active('admin')}">
408 <li class="${is_active('admin')}">
409 <a class="menulink childs" title="${_('Admin settings')}" href="#" onclick="return false;">
409 <a class="menulink childs" title="${_('Admin settings')}" href="#" onclick="return false;">
410 <div class="menulabel">${_('Admin')} <div class="show_more"></div></div>
410 <div class="menulabel">${_('Admin')} <div class="show_more"></div></div>
411 </a>
411 </a>
412 ${admin_menu()}
412 ${admin_menu()}
413 </li>
413 </li>
414 % elif c.rhodecode_user.repositories_admin or c.rhodecode_user.repository_groups_admin or c.rhodecode_user.user_groups_admin:
414 % elif c.rhodecode_user.repositories_admin or c.rhodecode_user.repository_groups_admin or c.rhodecode_user.user_groups_admin:
415 <li class="${is_active('admin')}">
415 <li class="${is_active('admin')}">
416 <a class="menulink childs" title="${_('Delegated Admin settings')}">
416 <a class="menulink childs" title="${_('Delegated Admin settings')}">
417 <div class="menulabel">${_('Admin')} <div class="show_more"></div></div>
417 <div class="menulabel">${_('Admin')} <div class="show_more"></div></div>
418 </a>
418 </a>
419 ${admin_menu_simple(c.rhodecode_user.repositories_admin,
419 ${admin_menu_simple(c.rhodecode_user.repositories_admin,
420 c.rhodecode_user.repository_groups_admin,
420 c.rhodecode_user.repository_groups_admin,
421 c.rhodecode_user.user_groups_admin or h.HasPermissionAny('hg.usergroup.create.true')())}
421 c.rhodecode_user.user_groups_admin or h.HasPermissionAny('hg.usergroup.create.true')())}
422 </li>
422 </li>
423 % endif
423 % endif
424 % if c.debug_style:
424 % if c.debug_style:
425 <li class="${is_active('debug_style')}">
425 <li class="${is_active('debug_style')}">
426 <a class="menulink" title="${_('Style')}" href="${h.url('debug_style_home')}">
426 <a class="menulink" title="${_('Style')}" href="${h.url('debug_style_home')}">
427 <div class="menulabel">${_('Style')}</div>
427 <div class="menulabel">${_('Style')}</div>
428 </a>
428 </a>
429 </li>
429 </li>
430 % endif
430 % endif
431 ## render extra user menu
431 ## render extra user menu
432 ${usermenu(active=(active=='my_account'))}
432 ${usermenu(active=(active=='my_account'))}
433 </ul>
433 </ul>
434
434
435 <script type="text/javascript">
435 <script type="text/javascript">
436 var visual_show_public_icon = "${c.visual.show_public_icon}" == "True";
436 var visual_show_public_icon = "${c.visual.show_public_icon}" == "True";
437
437
438 /*format the look of items in the list*/
438 /*format the look of items in the list*/
439 var format = function(state, escapeMarkup){
439 var format = function(state, escapeMarkup){
440 if (!state.id){
440 if (!state.id){
441 return state.text; // optgroup
441 return state.text; // optgroup
442 }
442 }
443 var obj_dict = state.obj;
443 var obj_dict = state.obj;
444 var tmpl = '';
444 var tmpl = '';
445
445
446 if(obj_dict && state.type == 'repo'){
446 if(obj_dict && state.type == 'repo'){
447 if(obj_dict['repo_type'] === 'hg'){
447 if(obj_dict['repo_type'] === 'hg'){
448 tmpl += '<i class="icon-hg"></i> ';
448 tmpl += '<i class="icon-hg"></i> ';
449 }
449 }
450 else if(obj_dict['repo_type'] === 'git'){
450 else if(obj_dict['repo_type'] === 'git'){
451 tmpl += '<i class="icon-git"></i> ';
451 tmpl += '<i class="icon-git"></i> ';
452 }
452 }
453 else if(obj_dict['repo_type'] === 'svn'){
453 else if(obj_dict['repo_type'] === 'svn'){
454 tmpl += '<i class="icon-svn"></i> ';
454 tmpl += '<i class="icon-svn"></i> ';
455 }
455 }
456 if(obj_dict['private']){
456 if(obj_dict['private']){
457 tmpl += '<i class="icon-lock" ></i> ';
457 tmpl += '<i class="icon-lock" ></i> ';
458 }
458 }
459 else if(visual_show_public_icon){
459 else if(visual_show_public_icon){
460 tmpl += '<i class="icon-unlock-alt"></i> ';
460 tmpl += '<i class="icon-unlock-alt"></i> ';
461 }
461 }
462 }
462 }
463 if(obj_dict && state.type == 'commit') {
463 if(obj_dict && state.type == 'commit') {
464 tmpl += '<i class="icon-tag"></i>';
464 tmpl += '<i class="icon-tag"></i>';
465 }
465 }
466 if(obj_dict && state.type == 'group'){
466 if(obj_dict && state.type == 'group'){
467 tmpl += '<i class="icon-folder-close"></i> ';
467 tmpl += '<i class="icon-folder-close"></i> ';
468 }
468 }
469 tmpl += escapeMarkup(state.text);
469 tmpl += escapeMarkup(state.text);
470 return tmpl;
470 return tmpl;
471 };
471 };
472
472
473 var formatResult = function(result, container, query, escapeMarkup) {
473 var formatResult = function(result, container, query, escapeMarkup) {
474 return format(result, escapeMarkup);
474 return format(result, escapeMarkup);
475 };
475 };
476
476
477 var formatSelection = function(data, container, escapeMarkup) {
477 var formatSelection = function(data, container, escapeMarkup) {
478 return format(data, escapeMarkup);
478 return format(data, escapeMarkup);
479 };
479 };
480
480
481 $("#repo_switcher").select2({
481 $("#repo_switcher").select2({
482 cachedDataSource: {},
482 cachedDataSource: {},
483 minimumInputLength: 2,
483 minimumInputLength: 2,
484 placeholder: '<div class="menulabel">${_('Go to')} <div class="show_more"></div></div>',
484 placeholder: '<div class="menulabel">${_('Go to')} <div class="show_more"></div></div>',
485 dropdownAutoWidth: true,
485 dropdownAutoWidth: true,
486 formatResult: formatResult,
486 formatResult: formatResult,
487 formatSelection: formatSelection,
487 formatSelection: formatSelection,
488 containerCssClass: "repo-switcher",
488 containerCssClass: "repo-switcher",
489 dropdownCssClass: "repo-switcher-dropdown",
489 dropdownCssClass: "repo-switcher-dropdown",
490 escapeMarkup: function(m){
490 escapeMarkup: function(m){
491 // don't escape our custom placeholder
491 // don't escape our custom placeholder
492 if(m.substr(0,23) == '<div class="menulabel">'){
492 if(m.substr(0,23) == '<div class="menulabel">'){
493 return m;
493 return m;
494 }
494 }
495
495
496 return Select2.util.escapeMarkup(m);
496 return Select2.util.escapeMarkup(m);
497 },
497 },
498 query: $.debounce(250, function(query){
498 query: $.debounce(250, function(query){
499 self = this;
499 self = this;
500 var cacheKey = query.term;
500 var cacheKey = query.term;
501 var cachedData = self.cachedDataSource[cacheKey];
501 var cachedData = self.cachedDataSource[cacheKey];
502
502
503 if (cachedData) {
503 if (cachedData) {
504 query.callback({results: cachedData.results});
504 query.callback({results: cachedData.results});
505 } else {
505 } else {
506 $.ajax({
506 $.ajax({
507 url: pyroutes.url('goto_switcher_data'),
507 url: pyroutes.url('goto_switcher_data'),
508 data: {'query': query.term},
508 data: {'query': query.term},
509 dataType: 'json',
509 dataType: 'json',
510 type: 'GET',
510 type: 'GET',
511 success: function(data) {
511 success: function(data) {
512 self.cachedDataSource[cacheKey] = data;
512 self.cachedDataSource[cacheKey] = data;
513 query.callback({results: data.results});
513 query.callback({results: data.results});
514 },
514 },
515 error: function(data, textStatus, errorThrown) {
515 error: function(data, textStatus, errorThrown) {
516 alert("Error while fetching entries.\nError code {0} ({1}).".format(data.status, data.statusText));
516 alert("Error while fetching entries.\nError code {0} ({1}).".format(data.status, data.statusText));
517 }
517 }
518 })
518 })
519 }
519 }
520 })
520 })
521 });
521 });
522
522
523 $("#repo_switcher").on('select2-selecting', function(e){
523 $("#repo_switcher").on('select2-selecting', function(e){
524 e.preventDefault();
524 e.preventDefault();
525 window.location = e.choice.url;
525 window.location = e.choice.url;
526 });
526 });
527
527
528 </script>
528 </script>
529 <script src="${h.asset('js/rhodecode/base/keyboard-bindings.js', ver=c.rhodecode_version_hash)}"></script>
529 <script src="${h.asset('js/rhodecode/base/keyboard-bindings.js', ver=c.rhodecode_version_hash)}"></script>
530 </%def>
530 </%def>
531
531
532 <div class="modal" id="help_kb" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
532 <div class="modal" id="help_kb" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
533 <div class="modal-dialog">
533 <div class="modal-dialog">
534 <div class="modal-content">
534 <div class="modal-content">
535 <div class="modal-header">
535 <div class="modal-header">
536 <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
536 <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
537 <h4 class="modal-title" id="myModalLabel">${_('Keyboard shortcuts')}</h4>
537 <h4 class="modal-title" id="myModalLabel">${_('Keyboard shortcuts')}</h4>
538 </div>
538 </div>
539 <div class="modal-body">
539 <div class="modal-body">
540 <div class="block-left">
540 <div class="block-left">
541 <table class="keyboard-mappings">
541 <table class="keyboard-mappings">
542 <tbody>
542 <tbody>
543 <tr>
543 <tr>
544 <th></th>
544 <th></th>
545 <th>${_('Site-wide shortcuts')}</th>
545 <th>${_('Site-wide shortcuts')}</th>
546 </tr>
546 </tr>
547 <%
547 <%
548 elems = [
548 elems = [
549 ('/', 'Open quick search box'),
549 ('/', 'Open quick search box'),
550 ('g h', 'Goto home page'),
550 ('g h', 'Goto home page'),
551 ('g g', 'Goto my private gists page'),
551 ('g g', 'Goto my private gists page'),
552 ('g G', 'Goto my public gists page'),
552 ('g G', 'Goto my public gists page'),
553 ('n r', 'New repository page'),
553 ('n r', 'New repository page'),
554 ('n g', 'New gist page'),
554 ('n g', 'New gist page'),
555 ]
555 ]
556 %>
556 %>
557 %for key, desc in elems:
557 %for key, desc in elems:
558 <tr>
558 <tr>
559 <td class="keys">
559 <td class="keys">
560 <span class="key tag">${key}</span>
560 <span class="key tag">${key}</span>
561 </td>
561 </td>
562 <td>${desc}</td>
562 <td>${desc}</td>
563 </tr>
563 </tr>
564 %endfor
564 %endfor
565 </tbody>
565 </tbody>
566 </table>
566 </table>
567 </div>
567 </div>
568 <div class="block-left">
568 <div class="block-left">
569 <table class="keyboard-mappings">
569 <table class="keyboard-mappings">
570 <tbody>
570 <tbody>
571 <tr>
571 <tr>
572 <th></th>
572 <th></th>
573 <th>${_('Repositories')}</th>
573 <th>${_('Repositories')}</th>
574 </tr>
574 </tr>
575 <%
575 <%
576 elems = [
576 elems = [
577 ('g s', 'Goto summary page'),
577 ('g s', 'Goto summary page'),
578 ('g c', 'Goto changelog page'),
578 ('g c', 'Goto changelog page'),
579 ('g f', 'Goto files page'),
579 ('g f', 'Goto files page'),
580 ('g F', 'Goto files page with file search activated'),
580 ('g F', 'Goto files page with file search activated'),
581 ('g p', 'Goto pull requests page'),
581 ('g p', 'Goto pull requests page'),
582 ('g o', 'Goto repository settings'),
582 ('g o', 'Goto repository settings'),
583 ('g O', 'Goto repository permissions settings'),
583 ('g O', 'Goto repository permissions settings'),
584 ]
584 ]
585 %>
585 %>
586 %for key, desc in elems:
586 %for key, desc in elems:
587 <tr>
587 <tr>
588 <td class="keys">
588 <td class="keys">
589 <span class="key tag">${key}</span>
589 <span class="key tag">${key}</span>
590 </td>
590 </td>
591 <td>${desc}</td>
591 <td>${desc}</td>
592 </tr>
592 </tr>
593 %endfor
593 %endfor
594 </tbody>
594 </tbody>
595 </table>
595 </table>
596 </div>
596 </div>
597 </div>
597 </div>
598 <div class="modal-footer">
598 <div class="modal-footer">
599 </div>
599 </div>
600 </div><!-- /.modal-content -->
600 </div><!-- /.modal-content -->
601 </div><!-- /.modal-dialog -->
601 </div><!-- /.modal-dialog -->
602 </div><!-- /.modal -->
602 </div><!-- /.modal -->
@@ -1,1103 +1,1090 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
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
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
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/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import mock
21 import mock
22 import pytest
22 import pytest
23 from webob.exc import HTTPNotFound
23 from webob.exc import HTTPNotFound
24
24
25 import rhodecode
25 import rhodecode
26 from rhodecode.lib.vcs.nodes import FileNode
26 from rhodecode.lib.vcs.nodes import FileNode
27 from rhodecode.model.changeset_status import ChangesetStatusModel
27 from rhodecode.model.changeset_status import ChangesetStatusModel
28 from rhodecode.model.db import (
28 from rhodecode.model.db import (
29 PullRequest, ChangesetStatus, UserLog, Notification)
29 PullRequest, ChangesetStatus, UserLog, Notification)
30 from rhodecode.model.meta import Session
30 from rhodecode.model.meta import Session
31 from rhodecode.model.pull_request import PullRequestModel
31 from rhodecode.model.pull_request import PullRequestModel
32 from rhodecode.model.user import UserModel
32 from rhodecode.model.user import UserModel
33 from rhodecode.model.repo import RepoModel
33 from rhodecode.model.repo import RepoModel
34 from rhodecode.tests import (
34 from rhodecode.tests import (
35 assert_session_flash, url, TEST_USER_ADMIN_LOGIN, TEST_USER_REGULAR_LOGIN)
35 assert_session_flash, url, TEST_USER_ADMIN_LOGIN, TEST_USER_REGULAR_LOGIN)
36 from rhodecode.tests.utils import AssertResponse
36 from rhodecode.tests.utils import AssertResponse
37
37
38
38
39 @pytest.mark.usefixtures('app', 'autologin_user')
39 @pytest.mark.usefixtures('app', 'autologin_user')
40 @pytest.mark.backends("git", "hg")
40 @pytest.mark.backends("git", "hg")
41 class TestPullrequestsController:
41 class TestPullrequestsController:
42
42
43 def test_index(self, backend):
43 def test_index(self, backend):
44 self.app.get(url(
44 self.app.get(url(
45 controller='pullrequests', action='index',
45 controller='pullrequests', action='index',
46 repo_name=backend.repo_name))
46 repo_name=backend.repo_name))
47
47
48 def test_option_menu_create_pull_request_exists(self, backend):
48 def test_option_menu_create_pull_request_exists(self, backend):
49 repo_name = backend.repo_name
49 repo_name = backend.repo_name
50 response = self.app.get(url('summary_home', repo_name=repo_name))
50 response = self.app.get(url('summary_home', repo_name=repo_name))
51
51
52 create_pr_link = '<a href="%s">Create Pull Request</a>' % url(
52 create_pr_link = '<a href="%s">Create Pull Request</a>' % url(
53 'pullrequest', repo_name=repo_name)
53 'pullrequest', repo_name=repo_name)
54 response.mustcontain(create_pr_link)
54 response.mustcontain(create_pr_link)
55
55
56 def test_global_redirect_of_pr(self, backend, pr_util):
57 pull_request = pr_util.create_pull_request()
58
59 response = self.app.get(
60 url('pull_requests_global',
61 pull_request_id=pull_request.pull_request_id))
62
63 repo_name = pull_request.target_repo.repo_name
64 redirect_url = url('pullrequest_show', repo_name=repo_name,
65 pull_request_id=pull_request.pull_request_id)
66 assert response.status == '302 Found'
67 assert redirect_url in response.location
68
69 def test_create_pr_form_with_raw_commit_id(self, backend):
56 def test_create_pr_form_with_raw_commit_id(self, backend):
70 repo = backend.repo
57 repo = backend.repo
71
58
72 self.app.get(
59 self.app.get(
73 url(controller='pullrequests', action='index',
60 url(controller='pullrequests', action='index',
74 repo_name=repo.repo_name,
61 repo_name=repo.repo_name,
75 commit=repo.get_commit().raw_id),
62 commit=repo.get_commit().raw_id),
76 status=200)
63 status=200)
77
64
78 @pytest.mark.parametrize('pr_merge_enabled', [True, False])
65 @pytest.mark.parametrize('pr_merge_enabled', [True, False])
79 def test_show(self, pr_util, pr_merge_enabled):
66 def test_show(self, pr_util, pr_merge_enabled):
80 pull_request = pr_util.create_pull_request(
67 pull_request = pr_util.create_pull_request(
81 mergeable=pr_merge_enabled, enable_notifications=False)
68 mergeable=pr_merge_enabled, enable_notifications=False)
82
69
83 response = self.app.get(url(
70 response = self.app.get(url(
84 controller='pullrequests', action='show',
71 controller='pullrequests', action='show',
85 repo_name=pull_request.target_repo.scm_instance().name,
72 repo_name=pull_request.target_repo.scm_instance().name,
86 pull_request_id=str(pull_request.pull_request_id)))
73 pull_request_id=str(pull_request.pull_request_id)))
87
74
88 for commit_id in pull_request.revisions:
75 for commit_id in pull_request.revisions:
89 response.mustcontain(commit_id)
76 response.mustcontain(commit_id)
90
77
91 assert pull_request.target_ref_parts.type in response
78 assert pull_request.target_ref_parts.type in response
92 assert pull_request.target_ref_parts.name in response
79 assert pull_request.target_ref_parts.name in response
93 target_clone_url = pull_request.target_repo.clone_url()
80 target_clone_url = pull_request.target_repo.clone_url()
94 assert target_clone_url in response
81 assert target_clone_url in response
95
82
96 assert 'class="pull-request-merge"' in response
83 assert 'class="pull-request-merge"' in response
97 assert (
84 assert (
98 'Server-side pull request merging is disabled.'
85 'Server-side pull request merging is disabled.'
99 in response) != pr_merge_enabled
86 in response) != pr_merge_enabled
100
87
101 def test_close_status_visibility(self, pr_util, user_util, csrf_token):
88 def test_close_status_visibility(self, pr_util, user_util, csrf_token):
102 from rhodecode.tests.functional.test_login import login_url, logut_url
89 from rhodecode.tests.functional.test_login import login_url, logut_url
103 # Logout
90 # Logout
104 response = self.app.post(
91 response = self.app.post(
105 logut_url,
92 logut_url,
106 params={'csrf_token': csrf_token})
93 params={'csrf_token': csrf_token})
107 # Login as regular user
94 # Login as regular user
108 response = self.app.post(login_url,
95 response = self.app.post(login_url,
109 {'username': TEST_USER_REGULAR_LOGIN,
96 {'username': TEST_USER_REGULAR_LOGIN,
110 'password': 'test12'})
97 'password': 'test12'})
111
98
112 pull_request = pr_util.create_pull_request(
99 pull_request = pr_util.create_pull_request(
113 author=TEST_USER_REGULAR_LOGIN)
100 author=TEST_USER_REGULAR_LOGIN)
114
101
115 response = self.app.get(url(
102 response = self.app.get(url(
116 controller='pullrequests', action='show',
103 controller='pullrequests', action='show',
117 repo_name=pull_request.target_repo.scm_instance().name,
104 repo_name=pull_request.target_repo.scm_instance().name,
118 pull_request_id=str(pull_request.pull_request_id)))
105 pull_request_id=str(pull_request.pull_request_id)))
119
106
120 response.mustcontain('Server-side pull request merging is disabled.')
107 response.mustcontain('Server-side pull request merging is disabled.')
121
108
122 assert_response = response.assert_response()
109 assert_response = response.assert_response()
123 # for regular user without a merge permissions, we don't see it
110 # for regular user without a merge permissions, we don't see it
124 assert_response.no_element_exists('#close-pull-request-action')
111 assert_response.no_element_exists('#close-pull-request-action')
125
112
126 user_util.grant_user_permission_to_repo(
113 user_util.grant_user_permission_to_repo(
127 pull_request.target_repo,
114 pull_request.target_repo,
128 UserModel().get_by_username(TEST_USER_REGULAR_LOGIN),
115 UserModel().get_by_username(TEST_USER_REGULAR_LOGIN),
129 'repository.write')
116 'repository.write')
130 response = self.app.get(url(
117 response = self.app.get(url(
131 controller='pullrequests', action='show',
118 controller='pullrequests', action='show',
132 repo_name=pull_request.target_repo.scm_instance().name,
119 repo_name=pull_request.target_repo.scm_instance().name,
133 pull_request_id=str(pull_request.pull_request_id)))
120 pull_request_id=str(pull_request.pull_request_id)))
134
121
135 response.mustcontain('Server-side pull request merging is disabled.')
122 response.mustcontain('Server-side pull request merging is disabled.')
136
123
137 assert_response = response.assert_response()
124 assert_response = response.assert_response()
138 # now regular user has a merge permissions, we have CLOSE button
125 # now regular user has a merge permissions, we have CLOSE button
139 assert_response.one_element_exists('#close-pull-request-action')
126 assert_response.one_element_exists('#close-pull-request-action')
140
127
141 def test_show_invalid_commit_id(self, pr_util):
128 def test_show_invalid_commit_id(self, pr_util):
142 # Simulating invalid revisions which will cause a lookup error
129 # Simulating invalid revisions which will cause a lookup error
143 pull_request = pr_util.create_pull_request()
130 pull_request = pr_util.create_pull_request()
144 pull_request.revisions = ['invalid']
131 pull_request.revisions = ['invalid']
145 Session().add(pull_request)
132 Session().add(pull_request)
146 Session().commit()
133 Session().commit()
147
134
148 response = self.app.get(url(
135 response = self.app.get(url(
149 controller='pullrequests', action='show',
136 controller='pullrequests', action='show',
150 repo_name=pull_request.target_repo.scm_instance().name,
137 repo_name=pull_request.target_repo.scm_instance().name,
151 pull_request_id=str(pull_request.pull_request_id)))
138 pull_request_id=str(pull_request.pull_request_id)))
152
139
153 for commit_id in pull_request.revisions:
140 for commit_id in pull_request.revisions:
154 response.mustcontain(commit_id)
141 response.mustcontain(commit_id)
155
142
156 def test_show_invalid_source_reference(self, pr_util):
143 def test_show_invalid_source_reference(self, pr_util):
157 pull_request = pr_util.create_pull_request()
144 pull_request = pr_util.create_pull_request()
158 pull_request.source_ref = 'branch:b:invalid'
145 pull_request.source_ref = 'branch:b:invalid'
159 Session().add(pull_request)
146 Session().add(pull_request)
160 Session().commit()
147 Session().commit()
161
148
162 self.app.get(url(
149 self.app.get(url(
163 controller='pullrequests', action='show',
150 controller='pullrequests', action='show',
164 repo_name=pull_request.target_repo.scm_instance().name,
151 repo_name=pull_request.target_repo.scm_instance().name,
165 pull_request_id=str(pull_request.pull_request_id)))
152 pull_request_id=str(pull_request.pull_request_id)))
166
153
167 def test_edit_title_description(self, pr_util, csrf_token):
154 def test_edit_title_description(self, pr_util, csrf_token):
168 pull_request = pr_util.create_pull_request()
155 pull_request = pr_util.create_pull_request()
169 pull_request_id = pull_request.pull_request_id
156 pull_request_id = pull_request.pull_request_id
170
157
171 response = self.app.post(
158 response = self.app.post(
172 url(controller='pullrequests', action='update',
159 url(controller='pullrequests', action='update',
173 repo_name=pull_request.target_repo.repo_name,
160 repo_name=pull_request.target_repo.repo_name,
174 pull_request_id=str(pull_request_id)),
161 pull_request_id=str(pull_request_id)),
175 params={
162 params={
176 'edit_pull_request': 'true',
163 'edit_pull_request': 'true',
177 '_method': 'put',
164 '_method': 'put',
178 'title': 'New title',
165 'title': 'New title',
179 'description': 'New description',
166 'description': 'New description',
180 'csrf_token': csrf_token})
167 'csrf_token': csrf_token})
181
168
182 assert_session_flash(
169 assert_session_flash(
183 response, u'Pull request title & description updated.',
170 response, u'Pull request title & description updated.',
184 category='success')
171 category='success')
185
172
186 pull_request = PullRequest.get(pull_request_id)
173 pull_request = PullRequest.get(pull_request_id)
187 assert pull_request.title == 'New title'
174 assert pull_request.title == 'New title'
188 assert pull_request.description == 'New description'
175 assert pull_request.description == 'New description'
189
176
190 def test_edit_title_description_closed(self, pr_util, csrf_token):
177 def test_edit_title_description_closed(self, pr_util, csrf_token):
191 pull_request = pr_util.create_pull_request()
178 pull_request = pr_util.create_pull_request()
192 pull_request_id = pull_request.pull_request_id
179 pull_request_id = pull_request.pull_request_id
193 pr_util.close()
180 pr_util.close()
194
181
195 response = self.app.post(
182 response = self.app.post(
196 url(controller='pullrequests', action='update',
183 url(controller='pullrequests', action='update',
197 repo_name=pull_request.target_repo.repo_name,
184 repo_name=pull_request.target_repo.repo_name,
198 pull_request_id=str(pull_request_id)),
185 pull_request_id=str(pull_request_id)),
199 params={
186 params={
200 'edit_pull_request': 'true',
187 'edit_pull_request': 'true',
201 '_method': 'put',
188 '_method': 'put',
202 'title': 'New title',
189 'title': 'New title',
203 'description': 'New description',
190 'description': 'New description',
204 'csrf_token': csrf_token})
191 'csrf_token': csrf_token})
205
192
206 assert_session_flash(
193 assert_session_flash(
207 response, u'Cannot update closed pull requests.',
194 response, u'Cannot update closed pull requests.',
208 category='error')
195 category='error')
209
196
210 def test_update_invalid_source_reference(self, pr_util, csrf_token):
197 def test_update_invalid_source_reference(self, pr_util, csrf_token):
211 from rhodecode.lib.vcs.backends.base import UpdateFailureReason
198 from rhodecode.lib.vcs.backends.base import UpdateFailureReason
212
199
213 pull_request = pr_util.create_pull_request()
200 pull_request = pr_util.create_pull_request()
214 pull_request.source_ref = 'branch:invalid-branch:invalid-commit-id'
201 pull_request.source_ref = 'branch:invalid-branch:invalid-commit-id'
215 Session().add(pull_request)
202 Session().add(pull_request)
216 Session().commit()
203 Session().commit()
217
204
218 pull_request_id = pull_request.pull_request_id
205 pull_request_id = pull_request.pull_request_id
219
206
220 response = self.app.post(
207 response = self.app.post(
221 url(controller='pullrequests', action='update',
208 url(controller='pullrequests', action='update',
222 repo_name=pull_request.target_repo.repo_name,
209 repo_name=pull_request.target_repo.repo_name,
223 pull_request_id=str(pull_request_id)),
210 pull_request_id=str(pull_request_id)),
224 params={'update_commits': 'true', '_method': 'put',
211 params={'update_commits': 'true', '_method': 'put',
225 'csrf_token': csrf_token})
212 'csrf_token': csrf_token})
226
213
227 expected_msg = PullRequestModel.UPDATE_STATUS_MESSAGES[
214 expected_msg = PullRequestModel.UPDATE_STATUS_MESSAGES[
228 UpdateFailureReason.MISSING_SOURCE_REF]
215 UpdateFailureReason.MISSING_SOURCE_REF]
229 assert_session_flash(response, expected_msg, category='error')
216 assert_session_flash(response, expected_msg, category='error')
230
217
231 def test_missing_target_reference(self, pr_util, csrf_token):
218 def test_missing_target_reference(self, pr_util, csrf_token):
232 from rhodecode.lib.vcs.backends.base import MergeFailureReason
219 from rhodecode.lib.vcs.backends.base import MergeFailureReason
233 pull_request = pr_util.create_pull_request(
220 pull_request = pr_util.create_pull_request(
234 approved=True, mergeable=True)
221 approved=True, mergeable=True)
235 pull_request.target_ref = 'branch:invalid-branch:invalid-commit-id'
222 pull_request.target_ref = 'branch:invalid-branch:invalid-commit-id'
236 Session().add(pull_request)
223 Session().add(pull_request)
237 Session().commit()
224 Session().commit()
238
225
239 pull_request_id = pull_request.pull_request_id
226 pull_request_id = pull_request.pull_request_id
240 pull_request_url = url(
227 pull_request_url = url(
241 controller='pullrequests', action='show',
228 controller='pullrequests', action='show',
242 repo_name=pull_request.target_repo.repo_name,
229 repo_name=pull_request.target_repo.repo_name,
243 pull_request_id=str(pull_request_id))
230 pull_request_id=str(pull_request_id))
244
231
245 response = self.app.get(pull_request_url)
232 response = self.app.get(pull_request_url)
246
233
247 assertr = AssertResponse(response)
234 assertr = AssertResponse(response)
248 expected_msg = PullRequestModel.MERGE_STATUS_MESSAGES[
235 expected_msg = PullRequestModel.MERGE_STATUS_MESSAGES[
249 MergeFailureReason.MISSING_TARGET_REF]
236 MergeFailureReason.MISSING_TARGET_REF]
250 assertr.element_contains(
237 assertr.element_contains(
251 'span[data-role="merge-message"]', str(expected_msg))
238 'span[data-role="merge-message"]', str(expected_msg))
252
239
253 def test_comment_and_close_pull_request(self, pr_util, csrf_token):
240 def test_comment_and_close_pull_request(self, pr_util, csrf_token):
254 pull_request = pr_util.create_pull_request(approved=True)
241 pull_request = pr_util.create_pull_request(approved=True)
255 pull_request_id = pull_request.pull_request_id
242 pull_request_id = pull_request.pull_request_id
256 author = pull_request.user_id
243 author = pull_request.user_id
257 repo = pull_request.target_repo.repo_id
244 repo = pull_request.target_repo.repo_id
258
245
259 self.app.post(
246 self.app.post(
260 url(controller='pullrequests',
247 url(controller='pullrequests',
261 action='comment',
248 action='comment',
262 repo_name=pull_request.target_repo.scm_instance().name,
249 repo_name=pull_request.target_repo.scm_instance().name,
263 pull_request_id=str(pull_request_id)),
250 pull_request_id=str(pull_request_id)),
264 params={
251 params={
265 'changeset_status': ChangesetStatus.STATUS_APPROVED,
252 'changeset_status': ChangesetStatus.STATUS_APPROVED,
266 'close_pull_request': '1',
253 'close_pull_request': '1',
267 'text': 'Closing a PR',
254 'text': 'Closing a PR',
268 'csrf_token': csrf_token},
255 'csrf_token': csrf_token},
269 status=302)
256 status=302)
270
257
271 action = 'user_closed_pull_request:%d' % pull_request_id
258 action = 'user_closed_pull_request:%d' % pull_request_id
272 journal = UserLog.query()\
259 journal = UserLog.query()\
273 .filter(UserLog.user_id == author)\
260 .filter(UserLog.user_id == author)\
274 .filter(UserLog.repository_id == repo)\
261 .filter(UserLog.repository_id == repo)\
275 .filter(UserLog.action == action)\
262 .filter(UserLog.action == action)\
276 .all()
263 .all()
277 assert len(journal) == 1
264 assert len(journal) == 1
278
265
279 pull_request = PullRequest.get(pull_request_id)
266 pull_request = PullRequest.get(pull_request_id)
280 assert pull_request.is_closed()
267 assert pull_request.is_closed()
281
268
282 # check only the latest status, not the review status
269 # check only the latest status, not the review status
283 status = ChangesetStatusModel().get_status(
270 status = ChangesetStatusModel().get_status(
284 pull_request.source_repo, pull_request=pull_request)
271 pull_request.source_repo, pull_request=pull_request)
285 assert status == ChangesetStatus.STATUS_APPROVED
272 assert status == ChangesetStatus.STATUS_APPROVED
286
273
287 def test_reject_and_close_pull_request(self, pr_util, csrf_token):
274 def test_reject_and_close_pull_request(self, pr_util, csrf_token):
288 pull_request = pr_util.create_pull_request()
275 pull_request = pr_util.create_pull_request()
289 pull_request_id = pull_request.pull_request_id
276 pull_request_id = pull_request.pull_request_id
290 response = self.app.post(
277 response = self.app.post(
291 url(controller='pullrequests',
278 url(controller='pullrequests',
292 action='update',
279 action='update',
293 repo_name=pull_request.target_repo.scm_instance().name,
280 repo_name=pull_request.target_repo.scm_instance().name,
294 pull_request_id=str(pull_request.pull_request_id)),
281 pull_request_id=str(pull_request.pull_request_id)),
295 params={'close_pull_request': 'true', '_method': 'put',
282 params={'close_pull_request': 'true', '_method': 'put',
296 'csrf_token': csrf_token})
283 'csrf_token': csrf_token})
297
284
298 pull_request = PullRequest.get(pull_request_id)
285 pull_request = PullRequest.get(pull_request_id)
299
286
300 assert response.json is True
287 assert response.json is True
301 assert pull_request.is_closed()
288 assert pull_request.is_closed()
302
289
303 # check only the latest status, not the review status
290 # check only the latest status, not the review status
304 status = ChangesetStatusModel().get_status(
291 status = ChangesetStatusModel().get_status(
305 pull_request.source_repo, pull_request=pull_request)
292 pull_request.source_repo, pull_request=pull_request)
306 assert status == ChangesetStatus.STATUS_REJECTED
293 assert status == ChangesetStatus.STATUS_REJECTED
307
294
308 def test_comment_force_close_pull_request(self, pr_util, csrf_token):
295 def test_comment_force_close_pull_request(self, pr_util, csrf_token):
309 pull_request = pr_util.create_pull_request()
296 pull_request = pr_util.create_pull_request()
310 pull_request_id = pull_request.pull_request_id
297 pull_request_id = pull_request.pull_request_id
311 reviewers_data = [(1, ['reason']), (2, ['reason2'])]
298 reviewers_data = [(1, ['reason']), (2, ['reason2'])]
312 PullRequestModel().update_reviewers(pull_request_id, reviewers_data)
299 PullRequestModel().update_reviewers(pull_request_id, reviewers_data)
313 author = pull_request.user_id
300 author = pull_request.user_id
314 repo = pull_request.target_repo.repo_id
301 repo = pull_request.target_repo.repo_id
315 self.app.post(
302 self.app.post(
316 url(controller='pullrequests',
303 url(controller='pullrequests',
317 action='comment',
304 action='comment',
318 repo_name=pull_request.target_repo.scm_instance().name,
305 repo_name=pull_request.target_repo.scm_instance().name,
319 pull_request_id=str(pull_request_id)),
306 pull_request_id=str(pull_request_id)),
320 params={
307 params={
321 'changeset_status': 'rejected',
308 'changeset_status': 'rejected',
322 'close_pull_request': '1',
309 'close_pull_request': '1',
323 'csrf_token': csrf_token},
310 'csrf_token': csrf_token},
324 status=302)
311 status=302)
325
312
326 pull_request = PullRequest.get(pull_request_id)
313 pull_request = PullRequest.get(pull_request_id)
327
314
328 action = 'user_closed_pull_request:%d' % pull_request_id
315 action = 'user_closed_pull_request:%d' % pull_request_id
329 journal = UserLog.query().filter(
316 journal = UserLog.query().filter(
330 UserLog.user_id == author,
317 UserLog.user_id == author,
331 UserLog.repository_id == repo,
318 UserLog.repository_id == repo,
332 UserLog.action == action).all()
319 UserLog.action == action).all()
333 assert len(journal) == 1
320 assert len(journal) == 1
334
321
335 # check only the latest status, not the review status
322 # check only the latest status, not the review status
336 status = ChangesetStatusModel().get_status(
323 status = ChangesetStatusModel().get_status(
337 pull_request.source_repo, pull_request=pull_request)
324 pull_request.source_repo, pull_request=pull_request)
338 assert status == ChangesetStatus.STATUS_REJECTED
325 assert status == ChangesetStatus.STATUS_REJECTED
339
326
340 def test_create_pull_request(self, backend, csrf_token):
327 def test_create_pull_request(self, backend, csrf_token):
341 commits = [
328 commits = [
342 {'message': 'ancestor'},
329 {'message': 'ancestor'},
343 {'message': 'change'},
330 {'message': 'change'},
344 {'message': 'change2'},
331 {'message': 'change2'},
345 ]
332 ]
346 commit_ids = backend.create_master_repo(commits)
333 commit_ids = backend.create_master_repo(commits)
347 target = backend.create_repo(heads=['ancestor'])
334 target = backend.create_repo(heads=['ancestor'])
348 source = backend.create_repo(heads=['change2'])
335 source = backend.create_repo(heads=['change2'])
349
336
350 response = self.app.post(
337 response = self.app.post(
351 url(
338 url(
352 controller='pullrequests',
339 controller='pullrequests',
353 action='create',
340 action='create',
354 repo_name=source.repo_name
341 repo_name=source.repo_name
355 ),
342 ),
356 [
343 [
357 ('source_repo', source.repo_name),
344 ('source_repo', source.repo_name),
358 ('source_ref', 'branch:default:' + commit_ids['change2']),
345 ('source_ref', 'branch:default:' + commit_ids['change2']),
359 ('target_repo', target.repo_name),
346 ('target_repo', target.repo_name),
360 ('target_ref', 'branch:default:' + commit_ids['ancestor']),
347 ('target_ref', 'branch:default:' + commit_ids['ancestor']),
361 ('pullrequest_desc', 'Description'),
348 ('pullrequest_desc', 'Description'),
362 ('pullrequest_title', 'Title'),
349 ('pullrequest_title', 'Title'),
363 ('__start__', 'review_members:sequence'),
350 ('__start__', 'review_members:sequence'),
364 ('__start__', 'reviewer:mapping'),
351 ('__start__', 'reviewer:mapping'),
365 ('user_id', '1'),
352 ('user_id', '1'),
366 ('__start__', 'reasons:sequence'),
353 ('__start__', 'reasons:sequence'),
367 ('reason', 'Some reason'),
354 ('reason', 'Some reason'),
368 ('__end__', 'reasons:sequence'),
355 ('__end__', 'reasons:sequence'),
369 ('__end__', 'reviewer:mapping'),
356 ('__end__', 'reviewer:mapping'),
370 ('__end__', 'review_members:sequence'),
357 ('__end__', 'review_members:sequence'),
371 ('__start__', 'revisions:sequence'),
358 ('__start__', 'revisions:sequence'),
372 ('revisions', commit_ids['change']),
359 ('revisions', commit_ids['change']),
373 ('revisions', commit_ids['change2']),
360 ('revisions', commit_ids['change2']),
374 ('__end__', 'revisions:sequence'),
361 ('__end__', 'revisions:sequence'),
375 ('user', ''),
362 ('user', ''),
376 ('csrf_token', csrf_token),
363 ('csrf_token', csrf_token),
377 ],
364 ],
378 status=302)
365 status=302)
379
366
380 location = response.headers['Location']
367 location = response.headers['Location']
381 pull_request_id = int(location.rsplit('/', 1)[1])
368 pull_request_id = int(location.rsplit('/', 1)[1])
382 pull_request = PullRequest.get(pull_request_id)
369 pull_request = PullRequest.get(pull_request_id)
383
370
384 # check that we have now both revisions
371 # check that we have now both revisions
385 assert pull_request.revisions == [commit_ids['change2'], commit_ids['change']]
372 assert pull_request.revisions == [commit_ids['change2'], commit_ids['change']]
386 assert pull_request.source_ref == 'branch:default:' + commit_ids['change2']
373 assert pull_request.source_ref == 'branch:default:' + commit_ids['change2']
387 expected_target_ref = 'branch:default:' + commit_ids['ancestor']
374 expected_target_ref = 'branch:default:' + commit_ids['ancestor']
388 assert pull_request.target_ref == expected_target_ref
375 assert pull_request.target_ref == expected_target_ref
389
376
390 def test_reviewer_notifications(self, backend, csrf_token):
377 def test_reviewer_notifications(self, backend, csrf_token):
391 # We have to use the app.post for this test so it will create the
378 # We have to use the app.post for this test so it will create the
392 # notifications properly with the new PR
379 # notifications properly with the new PR
393 commits = [
380 commits = [
394 {'message': 'ancestor',
381 {'message': 'ancestor',
395 'added': [FileNode('file_A', content='content_of_ancestor')]},
382 'added': [FileNode('file_A', content='content_of_ancestor')]},
396 {'message': 'change',
383 {'message': 'change',
397 'added': [FileNode('file_a', content='content_of_change')]},
384 'added': [FileNode('file_a', content='content_of_change')]},
398 {'message': 'change-child'},
385 {'message': 'change-child'},
399 {'message': 'ancestor-child', 'parents': ['ancestor'],
386 {'message': 'ancestor-child', 'parents': ['ancestor'],
400 'added': [
387 'added': [
401 FileNode('file_B', content='content_of_ancestor_child')]},
388 FileNode('file_B', content='content_of_ancestor_child')]},
402 {'message': 'ancestor-child-2'},
389 {'message': 'ancestor-child-2'},
403 ]
390 ]
404 commit_ids = backend.create_master_repo(commits)
391 commit_ids = backend.create_master_repo(commits)
405 target = backend.create_repo(heads=['ancestor-child'])
392 target = backend.create_repo(heads=['ancestor-child'])
406 source = backend.create_repo(heads=['change'])
393 source = backend.create_repo(heads=['change'])
407
394
408 response = self.app.post(
395 response = self.app.post(
409 url(
396 url(
410 controller='pullrequests',
397 controller='pullrequests',
411 action='create',
398 action='create',
412 repo_name=source.repo_name
399 repo_name=source.repo_name
413 ),
400 ),
414 [
401 [
415 ('source_repo', source.repo_name),
402 ('source_repo', source.repo_name),
416 ('source_ref', 'branch:default:' + commit_ids['change']),
403 ('source_ref', 'branch:default:' + commit_ids['change']),
417 ('target_repo', target.repo_name),
404 ('target_repo', target.repo_name),
418 ('target_ref', 'branch:default:' + commit_ids['ancestor-child']),
405 ('target_ref', 'branch:default:' + commit_ids['ancestor-child']),
419 ('pullrequest_desc', 'Description'),
406 ('pullrequest_desc', 'Description'),
420 ('pullrequest_title', 'Title'),
407 ('pullrequest_title', 'Title'),
421 ('__start__', 'review_members:sequence'),
408 ('__start__', 'review_members:sequence'),
422 ('__start__', 'reviewer:mapping'),
409 ('__start__', 'reviewer:mapping'),
423 ('user_id', '2'),
410 ('user_id', '2'),
424 ('__start__', 'reasons:sequence'),
411 ('__start__', 'reasons:sequence'),
425 ('reason', 'Some reason'),
412 ('reason', 'Some reason'),
426 ('__end__', 'reasons:sequence'),
413 ('__end__', 'reasons:sequence'),
427 ('__end__', 'reviewer:mapping'),
414 ('__end__', 'reviewer:mapping'),
428 ('__end__', 'review_members:sequence'),
415 ('__end__', 'review_members:sequence'),
429 ('__start__', 'revisions:sequence'),
416 ('__start__', 'revisions:sequence'),
430 ('revisions', commit_ids['change']),
417 ('revisions', commit_ids['change']),
431 ('__end__', 'revisions:sequence'),
418 ('__end__', 'revisions:sequence'),
432 ('user', ''),
419 ('user', ''),
433 ('csrf_token', csrf_token),
420 ('csrf_token', csrf_token),
434 ],
421 ],
435 status=302)
422 status=302)
436
423
437 location = response.headers['Location']
424 location = response.headers['Location']
438 pull_request_id = int(location.rsplit('/', 1)[1])
425 pull_request_id = int(location.rsplit('/', 1)[1])
439 pull_request = PullRequest.get(pull_request_id)
426 pull_request = PullRequest.get(pull_request_id)
440
427
441 # Check that a notification was made
428 # Check that a notification was made
442 notifications = Notification.query()\
429 notifications = Notification.query()\
443 .filter(Notification.created_by == pull_request.author.user_id,
430 .filter(Notification.created_by == pull_request.author.user_id,
444 Notification.type_ == Notification.TYPE_PULL_REQUEST,
431 Notification.type_ == Notification.TYPE_PULL_REQUEST,
445 Notification.subject.contains("wants you to review "
432 Notification.subject.contains("wants you to review "
446 "pull request #%d"
433 "pull request #%d"
447 % pull_request_id))
434 % pull_request_id))
448 assert len(notifications.all()) == 1
435 assert len(notifications.all()) == 1
449
436
450 # Change reviewers and check that a notification was made
437 # Change reviewers and check that a notification was made
451 PullRequestModel().update_reviewers(
438 PullRequestModel().update_reviewers(
452 pull_request.pull_request_id, [(1, [])])
439 pull_request.pull_request_id, [(1, [])])
453 assert len(notifications.all()) == 2
440 assert len(notifications.all()) == 2
454
441
455 def test_create_pull_request_stores_ancestor_commit_id(self, backend,
442 def test_create_pull_request_stores_ancestor_commit_id(self, backend,
456 csrf_token):
443 csrf_token):
457 commits = [
444 commits = [
458 {'message': 'ancestor',
445 {'message': 'ancestor',
459 'added': [FileNode('file_A', content='content_of_ancestor')]},
446 'added': [FileNode('file_A', content='content_of_ancestor')]},
460 {'message': 'change',
447 {'message': 'change',
461 'added': [FileNode('file_a', content='content_of_change')]},
448 'added': [FileNode('file_a', content='content_of_change')]},
462 {'message': 'change-child'},
449 {'message': 'change-child'},
463 {'message': 'ancestor-child', 'parents': ['ancestor'],
450 {'message': 'ancestor-child', 'parents': ['ancestor'],
464 'added': [
451 'added': [
465 FileNode('file_B', content='content_of_ancestor_child')]},
452 FileNode('file_B', content='content_of_ancestor_child')]},
466 {'message': 'ancestor-child-2'},
453 {'message': 'ancestor-child-2'},
467 ]
454 ]
468 commit_ids = backend.create_master_repo(commits)
455 commit_ids = backend.create_master_repo(commits)
469 target = backend.create_repo(heads=['ancestor-child'])
456 target = backend.create_repo(heads=['ancestor-child'])
470 source = backend.create_repo(heads=['change'])
457 source = backend.create_repo(heads=['change'])
471
458
472 response = self.app.post(
459 response = self.app.post(
473 url(
460 url(
474 controller='pullrequests',
461 controller='pullrequests',
475 action='create',
462 action='create',
476 repo_name=source.repo_name
463 repo_name=source.repo_name
477 ),
464 ),
478 [
465 [
479 ('source_repo', source.repo_name),
466 ('source_repo', source.repo_name),
480 ('source_ref', 'branch:default:' + commit_ids['change']),
467 ('source_ref', 'branch:default:' + commit_ids['change']),
481 ('target_repo', target.repo_name),
468 ('target_repo', target.repo_name),
482 ('target_ref', 'branch:default:' + commit_ids['ancestor-child']),
469 ('target_ref', 'branch:default:' + commit_ids['ancestor-child']),
483 ('pullrequest_desc', 'Description'),
470 ('pullrequest_desc', 'Description'),
484 ('pullrequest_title', 'Title'),
471 ('pullrequest_title', 'Title'),
485 ('__start__', 'review_members:sequence'),
472 ('__start__', 'review_members:sequence'),
486 ('__start__', 'reviewer:mapping'),
473 ('__start__', 'reviewer:mapping'),
487 ('user_id', '1'),
474 ('user_id', '1'),
488 ('__start__', 'reasons:sequence'),
475 ('__start__', 'reasons:sequence'),
489 ('reason', 'Some reason'),
476 ('reason', 'Some reason'),
490 ('__end__', 'reasons:sequence'),
477 ('__end__', 'reasons:sequence'),
491 ('__end__', 'reviewer:mapping'),
478 ('__end__', 'reviewer:mapping'),
492 ('__end__', 'review_members:sequence'),
479 ('__end__', 'review_members:sequence'),
493 ('__start__', 'revisions:sequence'),
480 ('__start__', 'revisions:sequence'),
494 ('revisions', commit_ids['change']),
481 ('revisions', commit_ids['change']),
495 ('__end__', 'revisions:sequence'),
482 ('__end__', 'revisions:sequence'),
496 ('user', ''),
483 ('user', ''),
497 ('csrf_token', csrf_token),
484 ('csrf_token', csrf_token),
498 ],
485 ],
499 status=302)
486 status=302)
500
487
501 location = response.headers['Location']
488 location = response.headers['Location']
502 pull_request_id = int(location.rsplit('/', 1)[1])
489 pull_request_id = int(location.rsplit('/', 1)[1])
503 pull_request = PullRequest.get(pull_request_id)
490 pull_request = PullRequest.get(pull_request_id)
504
491
505 # target_ref has to point to the ancestor's commit_id in order to
492 # target_ref has to point to the ancestor's commit_id in order to
506 # show the correct diff
493 # show the correct diff
507 expected_target_ref = 'branch:default:' + commit_ids['ancestor']
494 expected_target_ref = 'branch:default:' + commit_ids['ancestor']
508 assert pull_request.target_ref == expected_target_ref
495 assert pull_request.target_ref == expected_target_ref
509
496
510 # Check generated diff contents
497 # Check generated diff contents
511 response = response.follow()
498 response = response.follow()
512 assert 'content_of_ancestor' not in response.body
499 assert 'content_of_ancestor' not in response.body
513 assert 'content_of_ancestor-child' not in response.body
500 assert 'content_of_ancestor-child' not in response.body
514 assert 'content_of_change' in response.body
501 assert 'content_of_change' in response.body
515
502
516 def test_merge_pull_request_enabled(self, pr_util, csrf_token):
503 def test_merge_pull_request_enabled(self, pr_util, csrf_token):
517 # Clear any previous calls to rcextensions
504 # Clear any previous calls to rcextensions
518 rhodecode.EXTENSIONS.calls.clear()
505 rhodecode.EXTENSIONS.calls.clear()
519
506
520 pull_request = pr_util.create_pull_request(
507 pull_request = pr_util.create_pull_request(
521 approved=True, mergeable=True)
508 approved=True, mergeable=True)
522 pull_request_id = pull_request.pull_request_id
509 pull_request_id = pull_request.pull_request_id
523 repo_name = pull_request.target_repo.scm_instance().name,
510 repo_name = pull_request.target_repo.scm_instance().name,
524
511
525 response = self.app.post(
512 response = self.app.post(
526 url(controller='pullrequests',
513 url(controller='pullrequests',
527 action='merge',
514 action='merge',
528 repo_name=str(repo_name[0]),
515 repo_name=str(repo_name[0]),
529 pull_request_id=str(pull_request_id)),
516 pull_request_id=str(pull_request_id)),
530 params={'csrf_token': csrf_token}).follow()
517 params={'csrf_token': csrf_token}).follow()
531
518
532 pull_request = PullRequest.get(pull_request_id)
519 pull_request = PullRequest.get(pull_request_id)
533
520
534 assert response.status_int == 200
521 assert response.status_int == 200
535 assert pull_request.is_closed()
522 assert pull_request.is_closed()
536 assert_pull_request_status(
523 assert_pull_request_status(
537 pull_request, ChangesetStatus.STATUS_APPROVED)
524 pull_request, ChangesetStatus.STATUS_APPROVED)
538
525
539 # Check the relevant log entries were added
526 # Check the relevant log entries were added
540 user_logs = UserLog.query() \
527 user_logs = UserLog.query() \
541 .filter(UserLog.version == UserLog.VERSION_1) \
528 .filter(UserLog.version == UserLog.VERSION_1) \
542 .order_by('-user_log_id').limit(3)
529 .order_by('-user_log_id').limit(3)
543 actions = [log.action for log in user_logs]
530 actions = [log.action for log in user_logs]
544 pr_commit_ids = PullRequestModel()._get_commit_ids(pull_request)
531 pr_commit_ids = PullRequestModel()._get_commit_ids(pull_request)
545 expected_actions = [
532 expected_actions = [
546 u'user_closed_pull_request:%d' % pull_request_id,
533 u'user_closed_pull_request:%d' % pull_request_id,
547 u'user_merged_pull_request:%d' % pull_request_id,
534 u'user_merged_pull_request:%d' % pull_request_id,
548 # The action below reflect that the post push actions were executed
535 # The action below reflect that the post push actions were executed
549 u'user_commented_pull_request:%d' % pull_request_id,
536 u'user_commented_pull_request:%d' % pull_request_id,
550 ]
537 ]
551 assert actions == expected_actions
538 assert actions == expected_actions
552
539
553 user_logs = UserLog.query() \
540 user_logs = UserLog.query() \
554 .filter(UserLog.version == UserLog.VERSION_2) \
541 .filter(UserLog.version == UserLog.VERSION_2) \
555 .order_by('-user_log_id').limit(1)
542 .order_by('-user_log_id').limit(1)
556 actions = [log.action for log in user_logs]
543 actions = [log.action for log in user_logs]
557 assert actions == ['user.push']
544 assert actions == ['user.push']
558 assert user_logs[0].action_data['commit_ids'] == pr_commit_ids
545 assert user_logs[0].action_data['commit_ids'] == pr_commit_ids
559
546
560 # Check post_push rcextension was really executed
547 # Check post_push rcextension was really executed
561 push_calls = rhodecode.EXTENSIONS.calls['post_push']
548 push_calls = rhodecode.EXTENSIONS.calls['post_push']
562 assert len(push_calls) == 1
549 assert len(push_calls) == 1
563 unused_last_call_args, last_call_kwargs = push_calls[0]
550 unused_last_call_args, last_call_kwargs = push_calls[0]
564 assert last_call_kwargs['action'] == 'push'
551 assert last_call_kwargs['action'] == 'push'
565 assert last_call_kwargs['pushed_revs'] == pr_commit_ids
552 assert last_call_kwargs['pushed_revs'] == pr_commit_ids
566
553
567 def test_merge_pull_request_disabled(self, pr_util, csrf_token):
554 def test_merge_pull_request_disabled(self, pr_util, csrf_token):
568 pull_request = pr_util.create_pull_request(mergeable=False)
555 pull_request = pr_util.create_pull_request(mergeable=False)
569 pull_request_id = pull_request.pull_request_id
556 pull_request_id = pull_request.pull_request_id
570 pull_request = PullRequest.get(pull_request_id)
557 pull_request = PullRequest.get(pull_request_id)
571
558
572 response = self.app.post(
559 response = self.app.post(
573 url(controller='pullrequests',
560 url(controller='pullrequests',
574 action='merge',
561 action='merge',
575 repo_name=pull_request.target_repo.scm_instance().name,
562 repo_name=pull_request.target_repo.scm_instance().name,
576 pull_request_id=str(pull_request.pull_request_id)),
563 pull_request_id=str(pull_request.pull_request_id)),
577 params={'csrf_token': csrf_token}).follow()
564 params={'csrf_token': csrf_token}).follow()
578
565
579 assert response.status_int == 200
566 assert response.status_int == 200
580 response.mustcontain(
567 response.mustcontain(
581 'Merge is not currently possible because of below failed checks.')
568 'Merge is not currently possible because of below failed checks.')
582 response.mustcontain('Server-side pull request merging is disabled.')
569 response.mustcontain('Server-side pull request merging is disabled.')
583
570
584 @pytest.mark.skip_backends('svn')
571 @pytest.mark.skip_backends('svn')
585 def test_merge_pull_request_not_approved(self, pr_util, csrf_token):
572 def test_merge_pull_request_not_approved(self, pr_util, csrf_token):
586 pull_request = pr_util.create_pull_request(mergeable=True)
573 pull_request = pr_util.create_pull_request(mergeable=True)
587 pull_request_id = pull_request.pull_request_id
574 pull_request_id = pull_request.pull_request_id
588 repo_name = pull_request.target_repo.scm_instance().name,
575 repo_name = pull_request.target_repo.scm_instance().name,
589
576
590 response = self.app.post(
577 response = self.app.post(
591 url(controller='pullrequests',
578 url(controller='pullrequests',
592 action='merge',
579 action='merge',
593 repo_name=str(repo_name[0]),
580 repo_name=str(repo_name[0]),
594 pull_request_id=str(pull_request_id)),
581 pull_request_id=str(pull_request_id)),
595 params={'csrf_token': csrf_token}).follow()
582 params={'csrf_token': csrf_token}).follow()
596
583
597 assert response.status_int == 200
584 assert response.status_int == 200
598
585
599 response.mustcontain(
586 response.mustcontain(
600 'Merge is not currently possible because of below failed checks.')
587 'Merge is not currently possible because of below failed checks.')
601 response.mustcontain('Pull request reviewer approval is pending.')
588 response.mustcontain('Pull request reviewer approval is pending.')
602
589
603 def test_update_source_revision(self, backend, csrf_token):
590 def test_update_source_revision(self, backend, csrf_token):
604 commits = [
591 commits = [
605 {'message': 'ancestor'},
592 {'message': 'ancestor'},
606 {'message': 'change'},
593 {'message': 'change'},
607 {'message': 'change-2'},
594 {'message': 'change-2'},
608 ]
595 ]
609 commit_ids = backend.create_master_repo(commits)
596 commit_ids = backend.create_master_repo(commits)
610 target = backend.create_repo(heads=['ancestor'])
597 target = backend.create_repo(heads=['ancestor'])
611 source = backend.create_repo(heads=['change'])
598 source = backend.create_repo(heads=['change'])
612
599
613 # create pr from a in source to A in target
600 # create pr from a in source to A in target
614 pull_request = PullRequest()
601 pull_request = PullRequest()
615 pull_request.source_repo = source
602 pull_request.source_repo = source
616 # TODO: johbo: Make sure that we write the source ref this way!
603 # TODO: johbo: Make sure that we write the source ref this way!
617 pull_request.source_ref = 'branch:{branch}:{commit_id}'.format(
604 pull_request.source_ref = 'branch:{branch}:{commit_id}'.format(
618 branch=backend.default_branch_name, commit_id=commit_ids['change'])
605 branch=backend.default_branch_name, commit_id=commit_ids['change'])
619 pull_request.target_repo = target
606 pull_request.target_repo = target
620
607
621 pull_request.target_ref = 'branch:{branch}:{commit_id}'.format(
608 pull_request.target_ref = 'branch:{branch}:{commit_id}'.format(
622 branch=backend.default_branch_name,
609 branch=backend.default_branch_name,
623 commit_id=commit_ids['ancestor'])
610 commit_id=commit_ids['ancestor'])
624 pull_request.revisions = [commit_ids['change']]
611 pull_request.revisions = [commit_ids['change']]
625 pull_request.title = u"Test"
612 pull_request.title = u"Test"
626 pull_request.description = u"Description"
613 pull_request.description = u"Description"
627 pull_request.author = UserModel().get_by_username(
614 pull_request.author = UserModel().get_by_username(
628 TEST_USER_ADMIN_LOGIN)
615 TEST_USER_ADMIN_LOGIN)
629 Session().add(pull_request)
616 Session().add(pull_request)
630 Session().commit()
617 Session().commit()
631 pull_request_id = pull_request.pull_request_id
618 pull_request_id = pull_request.pull_request_id
632
619
633 # source has ancestor - change - change-2
620 # source has ancestor - change - change-2
634 backend.pull_heads(source, heads=['change-2'])
621 backend.pull_heads(source, heads=['change-2'])
635
622
636 # update PR
623 # update PR
637 self.app.post(
624 self.app.post(
638 url(controller='pullrequests', action='update',
625 url(controller='pullrequests', action='update',
639 repo_name=target.repo_name,
626 repo_name=target.repo_name,
640 pull_request_id=str(pull_request_id)),
627 pull_request_id=str(pull_request_id)),
641 params={'update_commits': 'true', '_method': 'put',
628 params={'update_commits': 'true', '_method': 'put',
642 'csrf_token': csrf_token})
629 'csrf_token': csrf_token})
643
630
644 # check that we have now both revisions
631 # check that we have now both revisions
645 pull_request = PullRequest.get(pull_request_id)
632 pull_request = PullRequest.get(pull_request_id)
646 assert pull_request.revisions == [
633 assert pull_request.revisions == [
647 commit_ids['change-2'], commit_ids['change']]
634 commit_ids['change-2'], commit_ids['change']]
648
635
649 # TODO: johbo: this should be a test on its own
636 # TODO: johbo: this should be a test on its own
650 response = self.app.get(url(
637 response = self.app.get(url(
651 controller='pullrequests', action='index',
638 controller='pullrequests', action='index',
652 repo_name=target.repo_name))
639 repo_name=target.repo_name))
653 assert response.status_int == 200
640 assert response.status_int == 200
654 assert 'Pull request updated to' in response.body
641 assert 'Pull request updated to' in response.body
655 assert 'with 1 added, 0 removed commits.' in response.body
642 assert 'with 1 added, 0 removed commits.' in response.body
656
643
657 def test_update_target_revision(self, backend, csrf_token):
644 def test_update_target_revision(self, backend, csrf_token):
658 commits = [
645 commits = [
659 {'message': 'ancestor'},
646 {'message': 'ancestor'},
660 {'message': 'change'},
647 {'message': 'change'},
661 {'message': 'ancestor-new', 'parents': ['ancestor']},
648 {'message': 'ancestor-new', 'parents': ['ancestor']},
662 {'message': 'change-rebased'},
649 {'message': 'change-rebased'},
663 ]
650 ]
664 commit_ids = backend.create_master_repo(commits)
651 commit_ids = backend.create_master_repo(commits)
665 target = backend.create_repo(heads=['ancestor'])
652 target = backend.create_repo(heads=['ancestor'])
666 source = backend.create_repo(heads=['change'])
653 source = backend.create_repo(heads=['change'])
667
654
668 # create pr from a in source to A in target
655 # create pr from a in source to A in target
669 pull_request = PullRequest()
656 pull_request = PullRequest()
670 pull_request.source_repo = source
657 pull_request.source_repo = source
671 # TODO: johbo: Make sure that we write the source ref this way!
658 # TODO: johbo: Make sure that we write the source ref this way!
672 pull_request.source_ref = 'branch:{branch}:{commit_id}'.format(
659 pull_request.source_ref = 'branch:{branch}:{commit_id}'.format(
673 branch=backend.default_branch_name, commit_id=commit_ids['change'])
660 branch=backend.default_branch_name, commit_id=commit_ids['change'])
674 pull_request.target_repo = target
661 pull_request.target_repo = target
675 # TODO: johbo: Target ref should be branch based, since tip can jump
662 # TODO: johbo: Target ref should be branch based, since tip can jump
676 # from branch to branch
663 # from branch to branch
677 pull_request.target_ref = 'branch:{branch}:{commit_id}'.format(
664 pull_request.target_ref = 'branch:{branch}:{commit_id}'.format(
678 branch=backend.default_branch_name,
665 branch=backend.default_branch_name,
679 commit_id=commit_ids['ancestor'])
666 commit_id=commit_ids['ancestor'])
680 pull_request.revisions = [commit_ids['change']]
667 pull_request.revisions = [commit_ids['change']]
681 pull_request.title = u"Test"
668 pull_request.title = u"Test"
682 pull_request.description = u"Description"
669 pull_request.description = u"Description"
683 pull_request.author = UserModel().get_by_username(
670 pull_request.author = UserModel().get_by_username(
684 TEST_USER_ADMIN_LOGIN)
671 TEST_USER_ADMIN_LOGIN)
685 Session().add(pull_request)
672 Session().add(pull_request)
686 Session().commit()
673 Session().commit()
687 pull_request_id = pull_request.pull_request_id
674 pull_request_id = pull_request.pull_request_id
688
675
689 # target has ancestor - ancestor-new
676 # target has ancestor - ancestor-new
690 # source has ancestor - ancestor-new - change-rebased
677 # source has ancestor - ancestor-new - change-rebased
691 backend.pull_heads(target, heads=['ancestor-new'])
678 backend.pull_heads(target, heads=['ancestor-new'])
692 backend.pull_heads(source, heads=['change-rebased'])
679 backend.pull_heads(source, heads=['change-rebased'])
693
680
694 # update PR
681 # update PR
695 self.app.post(
682 self.app.post(
696 url(controller='pullrequests', action='update',
683 url(controller='pullrequests', action='update',
697 repo_name=target.repo_name,
684 repo_name=target.repo_name,
698 pull_request_id=str(pull_request_id)),
685 pull_request_id=str(pull_request_id)),
699 params={'update_commits': 'true', '_method': 'put',
686 params={'update_commits': 'true', '_method': 'put',
700 'csrf_token': csrf_token},
687 'csrf_token': csrf_token},
701 status=200)
688 status=200)
702
689
703 # check that we have now both revisions
690 # check that we have now both revisions
704 pull_request = PullRequest.get(pull_request_id)
691 pull_request = PullRequest.get(pull_request_id)
705 assert pull_request.revisions == [commit_ids['change-rebased']]
692 assert pull_request.revisions == [commit_ids['change-rebased']]
706 assert pull_request.target_ref == 'branch:{branch}:{commit_id}'.format(
693 assert pull_request.target_ref == 'branch:{branch}:{commit_id}'.format(
707 branch=backend.default_branch_name,
694 branch=backend.default_branch_name,
708 commit_id=commit_ids['ancestor-new'])
695 commit_id=commit_ids['ancestor-new'])
709
696
710 # TODO: johbo: This should be a test on its own
697 # TODO: johbo: This should be a test on its own
711 response = self.app.get(url(
698 response = self.app.get(url(
712 controller='pullrequests', action='index',
699 controller='pullrequests', action='index',
713 repo_name=target.repo_name))
700 repo_name=target.repo_name))
714 assert response.status_int == 200
701 assert response.status_int == 200
715 assert 'Pull request updated to' in response.body
702 assert 'Pull request updated to' in response.body
716 assert 'with 1 added, 1 removed commits.' in response.body
703 assert 'with 1 added, 1 removed commits.' in response.body
717
704
718 def test_update_of_ancestor_reference(self, backend, csrf_token):
705 def test_update_of_ancestor_reference(self, backend, csrf_token):
719 commits = [
706 commits = [
720 {'message': 'ancestor'},
707 {'message': 'ancestor'},
721 {'message': 'change'},
708 {'message': 'change'},
722 {'message': 'change-2'},
709 {'message': 'change-2'},
723 {'message': 'ancestor-new', 'parents': ['ancestor']},
710 {'message': 'ancestor-new', 'parents': ['ancestor']},
724 {'message': 'change-rebased'},
711 {'message': 'change-rebased'},
725 ]
712 ]
726 commit_ids = backend.create_master_repo(commits)
713 commit_ids = backend.create_master_repo(commits)
727 target = backend.create_repo(heads=['ancestor'])
714 target = backend.create_repo(heads=['ancestor'])
728 source = backend.create_repo(heads=['change'])
715 source = backend.create_repo(heads=['change'])
729
716
730 # create pr from a in source to A in target
717 # create pr from a in source to A in target
731 pull_request = PullRequest()
718 pull_request = PullRequest()
732 pull_request.source_repo = source
719 pull_request.source_repo = source
733 # TODO: johbo: Make sure that we write the source ref this way!
720 # TODO: johbo: Make sure that we write the source ref this way!
734 pull_request.source_ref = 'branch:{branch}:{commit_id}'.format(
721 pull_request.source_ref = 'branch:{branch}:{commit_id}'.format(
735 branch=backend.default_branch_name,
722 branch=backend.default_branch_name,
736 commit_id=commit_ids['change'])
723 commit_id=commit_ids['change'])
737 pull_request.target_repo = target
724 pull_request.target_repo = target
738 # TODO: johbo: Target ref should be branch based, since tip can jump
725 # TODO: johbo: Target ref should be branch based, since tip can jump
739 # from branch to branch
726 # from branch to branch
740 pull_request.target_ref = 'branch:{branch}:{commit_id}'.format(
727 pull_request.target_ref = 'branch:{branch}:{commit_id}'.format(
741 branch=backend.default_branch_name,
728 branch=backend.default_branch_name,
742 commit_id=commit_ids['ancestor'])
729 commit_id=commit_ids['ancestor'])
743 pull_request.revisions = [commit_ids['change']]
730 pull_request.revisions = [commit_ids['change']]
744 pull_request.title = u"Test"
731 pull_request.title = u"Test"
745 pull_request.description = u"Description"
732 pull_request.description = u"Description"
746 pull_request.author = UserModel().get_by_username(
733 pull_request.author = UserModel().get_by_username(
747 TEST_USER_ADMIN_LOGIN)
734 TEST_USER_ADMIN_LOGIN)
748 Session().add(pull_request)
735 Session().add(pull_request)
749 Session().commit()
736 Session().commit()
750 pull_request_id = pull_request.pull_request_id
737 pull_request_id = pull_request.pull_request_id
751
738
752 # target has ancestor - ancestor-new
739 # target has ancestor - ancestor-new
753 # source has ancestor - ancestor-new - change-rebased
740 # source has ancestor - ancestor-new - change-rebased
754 backend.pull_heads(target, heads=['ancestor-new'])
741 backend.pull_heads(target, heads=['ancestor-new'])
755 backend.pull_heads(source, heads=['change-rebased'])
742 backend.pull_heads(source, heads=['change-rebased'])
756
743
757 # update PR
744 # update PR
758 self.app.post(
745 self.app.post(
759 url(controller='pullrequests', action='update',
746 url(controller='pullrequests', action='update',
760 repo_name=target.repo_name,
747 repo_name=target.repo_name,
761 pull_request_id=str(pull_request_id)),
748 pull_request_id=str(pull_request_id)),
762 params={'update_commits': 'true', '_method': 'put',
749 params={'update_commits': 'true', '_method': 'put',
763 'csrf_token': csrf_token},
750 'csrf_token': csrf_token},
764 status=200)
751 status=200)
765
752
766 # Expect the target reference to be updated correctly
753 # Expect the target reference to be updated correctly
767 pull_request = PullRequest.get(pull_request_id)
754 pull_request = PullRequest.get(pull_request_id)
768 assert pull_request.revisions == [commit_ids['change-rebased']]
755 assert pull_request.revisions == [commit_ids['change-rebased']]
769 expected_target_ref = 'branch:{branch}:{commit_id}'.format(
756 expected_target_ref = 'branch:{branch}:{commit_id}'.format(
770 branch=backend.default_branch_name,
757 branch=backend.default_branch_name,
771 commit_id=commit_ids['ancestor-new'])
758 commit_id=commit_ids['ancestor-new'])
772 assert pull_request.target_ref == expected_target_ref
759 assert pull_request.target_ref == expected_target_ref
773
760
774 def test_remove_pull_request_branch(self, backend_git, csrf_token):
761 def test_remove_pull_request_branch(self, backend_git, csrf_token):
775 branch_name = 'development'
762 branch_name = 'development'
776 commits = [
763 commits = [
777 {'message': 'initial-commit'},
764 {'message': 'initial-commit'},
778 {'message': 'old-feature'},
765 {'message': 'old-feature'},
779 {'message': 'new-feature', 'branch': branch_name},
766 {'message': 'new-feature', 'branch': branch_name},
780 ]
767 ]
781 repo = backend_git.create_repo(commits)
768 repo = backend_git.create_repo(commits)
782 commit_ids = backend_git.commit_ids
769 commit_ids = backend_git.commit_ids
783
770
784 pull_request = PullRequest()
771 pull_request = PullRequest()
785 pull_request.source_repo = repo
772 pull_request.source_repo = repo
786 pull_request.target_repo = repo
773 pull_request.target_repo = repo
787 pull_request.source_ref = 'branch:{branch}:{commit_id}'.format(
774 pull_request.source_ref = 'branch:{branch}:{commit_id}'.format(
788 branch=branch_name, commit_id=commit_ids['new-feature'])
775 branch=branch_name, commit_id=commit_ids['new-feature'])
789 pull_request.target_ref = 'branch:{branch}:{commit_id}'.format(
776 pull_request.target_ref = 'branch:{branch}:{commit_id}'.format(
790 branch=backend_git.default_branch_name,
777 branch=backend_git.default_branch_name,
791 commit_id=commit_ids['old-feature'])
778 commit_id=commit_ids['old-feature'])
792 pull_request.revisions = [commit_ids['new-feature']]
779 pull_request.revisions = [commit_ids['new-feature']]
793 pull_request.title = u"Test"
780 pull_request.title = u"Test"
794 pull_request.description = u"Description"
781 pull_request.description = u"Description"
795 pull_request.author = UserModel().get_by_username(
782 pull_request.author = UserModel().get_by_username(
796 TEST_USER_ADMIN_LOGIN)
783 TEST_USER_ADMIN_LOGIN)
797 Session().add(pull_request)
784 Session().add(pull_request)
798 Session().commit()
785 Session().commit()
799
786
800 vcs = repo.scm_instance()
787 vcs = repo.scm_instance()
801 vcs.remove_ref('refs/heads/{}'.format(branch_name))
788 vcs.remove_ref('refs/heads/{}'.format(branch_name))
802
789
803 response = self.app.get(url(
790 response = self.app.get(url(
804 controller='pullrequests', action='show',
791 controller='pullrequests', action='show',
805 repo_name=repo.repo_name,
792 repo_name=repo.repo_name,
806 pull_request_id=str(pull_request.pull_request_id)))
793 pull_request_id=str(pull_request.pull_request_id)))
807
794
808 assert response.status_int == 200
795 assert response.status_int == 200
809 assert_response = AssertResponse(response)
796 assert_response = AssertResponse(response)
810 assert_response.element_contains(
797 assert_response.element_contains(
811 '#changeset_compare_view_content .alert strong',
798 '#changeset_compare_view_content .alert strong',
812 'Missing commits')
799 'Missing commits')
813 assert_response.element_contains(
800 assert_response.element_contains(
814 '#changeset_compare_view_content .alert',
801 '#changeset_compare_view_content .alert',
815 'This pull request cannot be displayed, because one or more'
802 'This pull request cannot be displayed, because one or more'
816 ' commits no longer exist in the source repository.')
803 ' commits no longer exist in the source repository.')
817
804
818 def test_strip_commits_from_pull_request(
805 def test_strip_commits_from_pull_request(
819 self, backend, pr_util, csrf_token):
806 self, backend, pr_util, csrf_token):
820 commits = [
807 commits = [
821 {'message': 'initial-commit'},
808 {'message': 'initial-commit'},
822 {'message': 'old-feature'},
809 {'message': 'old-feature'},
823 {'message': 'new-feature', 'parents': ['initial-commit']},
810 {'message': 'new-feature', 'parents': ['initial-commit']},
824 ]
811 ]
825 pull_request = pr_util.create_pull_request(
812 pull_request = pr_util.create_pull_request(
826 commits, target_head='initial-commit', source_head='new-feature',
813 commits, target_head='initial-commit', source_head='new-feature',
827 revisions=['new-feature'])
814 revisions=['new-feature'])
828
815
829 vcs = pr_util.source_repository.scm_instance()
816 vcs = pr_util.source_repository.scm_instance()
830 if backend.alias == 'git':
817 if backend.alias == 'git':
831 vcs.strip(pr_util.commit_ids['new-feature'], branch_name='master')
818 vcs.strip(pr_util.commit_ids['new-feature'], branch_name='master')
832 else:
819 else:
833 vcs.strip(pr_util.commit_ids['new-feature'])
820 vcs.strip(pr_util.commit_ids['new-feature'])
834
821
835 response = self.app.get(url(
822 response = self.app.get(url(
836 controller='pullrequests', action='show',
823 controller='pullrequests', action='show',
837 repo_name=pr_util.target_repository.repo_name,
824 repo_name=pr_util.target_repository.repo_name,
838 pull_request_id=str(pull_request.pull_request_id)))
825 pull_request_id=str(pull_request.pull_request_id)))
839
826
840 assert response.status_int == 200
827 assert response.status_int == 200
841 assert_response = AssertResponse(response)
828 assert_response = AssertResponse(response)
842 assert_response.element_contains(
829 assert_response.element_contains(
843 '#changeset_compare_view_content .alert strong',
830 '#changeset_compare_view_content .alert strong',
844 'Missing commits')
831 'Missing commits')
845 assert_response.element_contains(
832 assert_response.element_contains(
846 '#changeset_compare_view_content .alert',
833 '#changeset_compare_view_content .alert',
847 'This pull request cannot be displayed, because one or more'
834 'This pull request cannot be displayed, because one or more'
848 ' commits no longer exist in the source repository.')
835 ' commits no longer exist in the source repository.')
849 assert_response.element_contains(
836 assert_response.element_contains(
850 '#update_commits',
837 '#update_commits',
851 'Update commits')
838 'Update commits')
852
839
853 def test_strip_commits_and_update(
840 def test_strip_commits_and_update(
854 self, backend, pr_util, csrf_token):
841 self, backend, pr_util, csrf_token):
855 commits = [
842 commits = [
856 {'message': 'initial-commit'},
843 {'message': 'initial-commit'},
857 {'message': 'old-feature'},
844 {'message': 'old-feature'},
858 {'message': 'new-feature', 'parents': ['old-feature']},
845 {'message': 'new-feature', 'parents': ['old-feature']},
859 ]
846 ]
860 pull_request = pr_util.create_pull_request(
847 pull_request = pr_util.create_pull_request(
861 commits, target_head='old-feature', source_head='new-feature',
848 commits, target_head='old-feature', source_head='new-feature',
862 revisions=['new-feature'], mergeable=True)
849 revisions=['new-feature'], mergeable=True)
863
850
864 vcs = pr_util.source_repository.scm_instance()
851 vcs = pr_util.source_repository.scm_instance()
865 if backend.alias == 'git':
852 if backend.alias == 'git':
866 vcs.strip(pr_util.commit_ids['new-feature'], branch_name='master')
853 vcs.strip(pr_util.commit_ids['new-feature'], branch_name='master')
867 else:
854 else:
868 vcs.strip(pr_util.commit_ids['new-feature'])
855 vcs.strip(pr_util.commit_ids['new-feature'])
869
856
870 response = self.app.post(
857 response = self.app.post(
871 url(controller='pullrequests', action='update',
858 url(controller='pullrequests', action='update',
872 repo_name=pull_request.target_repo.repo_name,
859 repo_name=pull_request.target_repo.repo_name,
873 pull_request_id=str(pull_request.pull_request_id)),
860 pull_request_id=str(pull_request.pull_request_id)),
874 params={'update_commits': 'true', '_method': 'put',
861 params={'update_commits': 'true', '_method': 'put',
875 'csrf_token': csrf_token})
862 'csrf_token': csrf_token})
876
863
877 assert response.status_int == 200
864 assert response.status_int == 200
878 assert response.body == 'true'
865 assert response.body == 'true'
879
866
880 # Make sure that after update, it won't raise 500 errors
867 # Make sure that after update, it won't raise 500 errors
881 response = self.app.get(url(
868 response = self.app.get(url(
882 controller='pullrequests', action='show',
869 controller='pullrequests', action='show',
883 repo_name=pr_util.target_repository.repo_name,
870 repo_name=pr_util.target_repository.repo_name,
884 pull_request_id=str(pull_request.pull_request_id)))
871 pull_request_id=str(pull_request.pull_request_id)))
885
872
886 assert response.status_int == 200
873 assert response.status_int == 200
887 assert_response = AssertResponse(response)
874 assert_response = AssertResponse(response)
888 assert_response.element_contains(
875 assert_response.element_contains(
889 '#changeset_compare_view_content .alert strong',
876 '#changeset_compare_view_content .alert strong',
890 'Missing commits')
877 'Missing commits')
891
878
892 def test_branch_is_a_link(self, pr_util):
879 def test_branch_is_a_link(self, pr_util):
893 pull_request = pr_util.create_pull_request()
880 pull_request = pr_util.create_pull_request()
894 pull_request.source_ref = 'branch:origin:1234567890abcdef'
881 pull_request.source_ref = 'branch:origin:1234567890abcdef'
895 pull_request.target_ref = 'branch:target:abcdef1234567890'
882 pull_request.target_ref = 'branch:target:abcdef1234567890'
896 Session().add(pull_request)
883 Session().add(pull_request)
897 Session().commit()
884 Session().commit()
898
885
899 response = self.app.get(url(
886 response = self.app.get(url(
900 controller='pullrequests', action='show',
887 controller='pullrequests', action='show',
901 repo_name=pull_request.target_repo.scm_instance().name,
888 repo_name=pull_request.target_repo.scm_instance().name,
902 pull_request_id=str(pull_request.pull_request_id)))
889 pull_request_id=str(pull_request.pull_request_id)))
903 assert response.status_int == 200
890 assert response.status_int == 200
904 assert_response = AssertResponse(response)
891 assert_response = AssertResponse(response)
905
892
906 origin = assert_response.get_element('.pr-origininfo .tag')
893 origin = assert_response.get_element('.pr-origininfo .tag')
907 origin_children = origin.getchildren()
894 origin_children = origin.getchildren()
908 assert len(origin_children) == 1
895 assert len(origin_children) == 1
909 target = assert_response.get_element('.pr-targetinfo .tag')
896 target = assert_response.get_element('.pr-targetinfo .tag')
910 target_children = target.getchildren()
897 target_children = target.getchildren()
911 assert len(target_children) == 1
898 assert len(target_children) == 1
912
899
913 expected_origin_link = url(
900 expected_origin_link = url(
914 'changelog_home',
901 'changelog_home',
915 repo_name=pull_request.source_repo.scm_instance().name,
902 repo_name=pull_request.source_repo.scm_instance().name,
916 branch='origin')
903 branch='origin')
917 expected_target_link = url(
904 expected_target_link = url(
918 'changelog_home',
905 'changelog_home',
919 repo_name=pull_request.target_repo.scm_instance().name,
906 repo_name=pull_request.target_repo.scm_instance().name,
920 branch='target')
907 branch='target')
921 assert origin_children[0].attrib['href'] == expected_origin_link
908 assert origin_children[0].attrib['href'] == expected_origin_link
922 assert origin_children[0].text == 'branch: origin'
909 assert origin_children[0].text == 'branch: origin'
923 assert target_children[0].attrib['href'] == expected_target_link
910 assert target_children[0].attrib['href'] == expected_target_link
924 assert target_children[0].text == 'branch: target'
911 assert target_children[0].text == 'branch: target'
925
912
926 def test_bookmark_is_not_a_link(self, pr_util):
913 def test_bookmark_is_not_a_link(self, pr_util):
927 pull_request = pr_util.create_pull_request()
914 pull_request = pr_util.create_pull_request()
928 pull_request.source_ref = 'bookmark:origin:1234567890abcdef'
915 pull_request.source_ref = 'bookmark:origin:1234567890abcdef'
929 pull_request.target_ref = 'bookmark:target:abcdef1234567890'
916 pull_request.target_ref = 'bookmark:target:abcdef1234567890'
930 Session().add(pull_request)
917 Session().add(pull_request)
931 Session().commit()
918 Session().commit()
932
919
933 response = self.app.get(url(
920 response = self.app.get(url(
934 controller='pullrequests', action='show',
921 controller='pullrequests', action='show',
935 repo_name=pull_request.target_repo.scm_instance().name,
922 repo_name=pull_request.target_repo.scm_instance().name,
936 pull_request_id=str(pull_request.pull_request_id)))
923 pull_request_id=str(pull_request.pull_request_id)))
937 assert response.status_int == 200
924 assert response.status_int == 200
938 assert_response = AssertResponse(response)
925 assert_response = AssertResponse(response)
939
926
940 origin = assert_response.get_element('.pr-origininfo .tag')
927 origin = assert_response.get_element('.pr-origininfo .tag')
941 assert origin.text.strip() == 'bookmark: origin'
928 assert origin.text.strip() == 'bookmark: origin'
942 assert origin.getchildren() == []
929 assert origin.getchildren() == []
943
930
944 target = assert_response.get_element('.pr-targetinfo .tag')
931 target = assert_response.get_element('.pr-targetinfo .tag')
945 assert target.text.strip() == 'bookmark: target'
932 assert target.text.strip() == 'bookmark: target'
946 assert target.getchildren() == []
933 assert target.getchildren() == []
947
934
948 def test_tag_is_not_a_link(self, pr_util):
935 def test_tag_is_not_a_link(self, pr_util):
949 pull_request = pr_util.create_pull_request()
936 pull_request = pr_util.create_pull_request()
950 pull_request.source_ref = 'tag:origin:1234567890abcdef'
937 pull_request.source_ref = 'tag:origin:1234567890abcdef'
951 pull_request.target_ref = 'tag:target:abcdef1234567890'
938 pull_request.target_ref = 'tag:target:abcdef1234567890'
952 Session().add(pull_request)
939 Session().add(pull_request)
953 Session().commit()
940 Session().commit()
954
941
955 response = self.app.get(url(
942 response = self.app.get(url(
956 controller='pullrequests', action='show',
943 controller='pullrequests', action='show',
957 repo_name=pull_request.target_repo.scm_instance().name,
944 repo_name=pull_request.target_repo.scm_instance().name,
958 pull_request_id=str(pull_request.pull_request_id)))
945 pull_request_id=str(pull_request.pull_request_id)))
959 assert response.status_int == 200
946 assert response.status_int == 200
960 assert_response = AssertResponse(response)
947 assert_response = AssertResponse(response)
961
948
962 origin = assert_response.get_element('.pr-origininfo .tag')
949 origin = assert_response.get_element('.pr-origininfo .tag')
963 assert origin.text.strip() == 'tag: origin'
950 assert origin.text.strip() == 'tag: origin'
964 assert origin.getchildren() == []
951 assert origin.getchildren() == []
965
952
966 target = assert_response.get_element('.pr-targetinfo .tag')
953 target = assert_response.get_element('.pr-targetinfo .tag')
967 assert target.text.strip() == 'tag: target'
954 assert target.text.strip() == 'tag: target'
968 assert target.getchildren() == []
955 assert target.getchildren() == []
969
956
970 def test_description_is_escaped_on_index_page(self, backend, pr_util):
957 def test_description_is_escaped_on_index_page(self, backend, pr_util):
971 xss_description = "<script>alert('Hi!')</script>"
958 xss_description = "<script>alert('Hi!')</script>"
972 pull_request = pr_util.create_pull_request(description=xss_description)
959 pull_request = pr_util.create_pull_request(description=xss_description)
973 response = self.app.get(url(
960 response = self.app.get(url(
974 controller='pullrequests', action='show_all',
961 controller='pullrequests', action='show_all',
975 repo_name=pull_request.target_repo.repo_name))
962 repo_name=pull_request.target_repo.repo_name))
976 response.mustcontain(
963 response.mustcontain(
977 "&lt;script&gt;alert(&#39;Hi!&#39;)&lt;/script&gt;")
964 "&lt;script&gt;alert(&#39;Hi!&#39;)&lt;/script&gt;")
978
965
979 @pytest.mark.parametrize('mergeable', [True, False])
966 @pytest.mark.parametrize('mergeable', [True, False])
980 def test_shadow_repository_link(
967 def test_shadow_repository_link(
981 self, mergeable, pr_util, http_host_stub):
968 self, mergeable, pr_util, http_host_stub):
982 """
969 """
983 Check that the pull request summary page displays a link to the shadow
970 Check that the pull request summary page displays a link to the shadow
984 repository if the pull request is mergeable. If it is not mergeable
971 repository if the pull request is mergeable. If it is not mergeable
985 the link should not be displayed.
972 the link should not be displayed.
986 """
973 """
987 pull_request = pr_util.create_pull_request(
974 pull_request = pr_util.create_pull_request(
988 mergeable=mergeable, enable_notifications=False)
975 mergeable=mergeable, enable_notifications=False)
989 target_repo = pull_request.target_repo.scm_instance()
976 target_repo = pull_request.target_repo.scm_instance()
990 pr_id = pull_request.pull_request_id
977 pr_id = pull_request.pull_request_id
991 shadow_url = '{host}/{repo}/pull-request/{pr_id}/repository'.format(
978 shadow_url = '{host}/{repo}/pull-request/{pr_id}/repository'.format(
992 host=http_host_stub, repo=target_repo.name, pr_id=pr_id)
979 host=http_host_stub, repo=target_repo.name, pr_id=pr_id)
993
980
994 response = self.app.get(url(
981 response = self.app.get(url(
995 controller='pullrequests', action='show',
982 controller='pullrequests', action='show',
996 repo_name=target_repo.name,
983 repo_name=target_repo.name,
997 pull_request_id=str(pr_id)))
984 pull_request_id=str(pr_id)))
998
985
999 assertr = AssertResponse(response)
986 assertr = AssertResponse(response)
1000 if mergeable:
987 if mergeable:
1001 assertr.element_value_contains(
988 assertr.element_value_contains(
1002 'div.pr-mergeinfo input', shadow_url)
989 'div.pr-mergeinfo input', shadow_url)
1003 assertr.element_value_contains(
990 assertr.element_value_contains(
1004 'div.pr-mergeinfo input', 'pr-merge')
991 'div.pr-mergeinfo input', 'pr-merge')
1005 else:
992 else:
1006 assertr.no_element_exists('div.pr-mergeinfo')
993 assertr.no_element_exists('div.pr-mergeinfo')
1007
994
1008
995
1009 @pytest.mark.usefixtures('app')
996 @pytest.mark.usefixtures('app')
1010 @pytest.mark.backends("git", "hg")
997 @pytest.mark.backends("git", "hg")
1011 class TestPullrequestsControllerDelete(object):
998 class TestPullrequestsControllerDelete(object):
1012 def test_pull_request_delete_button_permissions_admin(
999 def test_pull_request_delete_button_permissions_admin(
1013 self, autologin_user, user_admin, pr_util):
1000 self, autologin_user, user_admin, pr_util):
1014 pull_request = pr_util.create_pull_request(
1001 pull_request = pr_util.create_pull_request(
1015 author=user_admin.username, enable_notifications=False)
1002 author=user_admin.username, enable_notifications=False)
1016
1003
1017 response = self.app.get(url(
1004 response = self.app.get(url(
1018 controller='pullrequests', action='show',
1005 controller='pullrequests', action='show',
1019 repo_name=pull_request.target_repo.scm_instance().name,
1006 repo_name=pull_request.target_repo.scm_instance().name,
1020 pull_request_id=str(pull_request.pull_request_id)))
1007 pull_request_id=str(pull_request.pull_request_id)))
1021
1008
1022 response.mustcontain('id="delete_pullrequest"')
1009 response.mustcontain('id="delete_pullrequest"')
1023 response.mustcontain('Confirm to delete this pull request')
1010 response.mustcontain('Confirm to delete this pull request')
1024
1011
1025 def test_pull_request_delete_button_permissions_owner(
1012 def test_pull_request_delete_button_permissions_owner(
1026 self, autologin_regular_user, user_regular, pr_util):
1013 self, autologin_regular_user, user_regular, pr_util):
1027 pull_request = pr_util.create_pull_request(
1014 pull_request = pr_util.create_pull_request(
1028 author=user_regular.username, enable_notifications=False)
1015 author=user_regular.username, enable_notifications=False)
1029
1016
1030 response = self.app.get(url(
1017 response = self.app.get(url(
1031 controller='pullrequests', action='show',
1018 controller='pullrequests', action='show',
1032 repo_name=pull_request.target_repo.scm_instance().name,
1019 repo_name=pull_request.target_repo.scm_instance().name,
1033 pull_request_id=str(pull_request.pull_request_id)))
1020 pull_request_id=str(pull_request.pull_request_id)))
1034
1021
1035 response.mustcontain('id="delete_pullrequest"')
1022 response.mustcontain('id="delete_pullrequest"')
1036 response.mustcontain('Confirm to delete this pull request')
1023 response.mustcontain('Confirm to delete this pull request')
1037
1024
1038 def test_pull_request_delete_button_permissions_forbidden(
1025 def test_pull_request_delete_button_permissions_forbidden(
1039 self, autologin_regular_user, user_regular, user_admin, pr_util):
1026 self, autologin_regular_user, user_regular, user_admin, pr_util):
1040 pull_request = pr_util.create_pull_request(
1027 pull_request = pr_util.create_pull_request(
1041 author=user_admin.username, enable_notifications=False)
1028 author=user_admin.username, enable_notifications=False)
1042
1029
1043 response = self.app.get(url(
1030 response = self.app.get(url(
1044 controller='pullrequests', action='show',
1031 controller='pullrequests', action='show',
1045 repo_name=pull_request.target_repo.scm_instance().name,
1032 repo_name=pull_request.target_repo.scm_instance().name,
1046 pull_request_id=str(pull_request.pull_request_id)))
1033 pull_request_id=str(pull_request.pull_request_id)))
1047 response.mustcontain(no=['id="delete_pullrequest"'])
1034 response.mustcontain(no=['id="delete_pullrequest"'])
1048 response.mustcontain(no=['Confirm to delete this pull request'])
1035 response.mustcontain(no=['Confirm to delete this pull request'])
1049
1036
1050 def test_pull_request_delete_button_permissions_can_update_cannot_delete(
1037 def test_pull_request_delete_button_permissions_can_update_cannot_delete(
1051 self, autologin_regular_user, user_regular, user_admin, pr_util,
1038 self, autologin_regular_user, user_regular, user_admin, pr_util,
1052 user_util):
1039 user_util):
1053
1040
1054 pull_request = pr_util.create_pull_request(
1041 pull_request = pr_util.create_pull_request(
1055 author=user_admin.username, enable_notifications=False)
1042 author=user_admin.username, enable_notifications=False)
1056
1043
1057 user_util.grant_user_permission_to_repo(
1044 user_util.grant_user_permission_to_repo(
1058 pull_request.target_repo, user_regular,
1045 pull_request.target_repo, user_regular,
1059 'repository.write')
1046 'repository.write')
1060
1047
1061 response = self.app.get(url(
1048 response = self.app.get(url(
1062 controller='pullrequests', action='show',
1049 controller='pullrequests', action='show',
1063 repo_name=pull_request.target_repo.scm_instance().name,
1050 repo_name=pull_request.target_repo.scm_instance().name,
1064 pull_request_id=str(pull_request.pull_request_id)))
1051 pull_request_id=str(pull_request.pull_request_id)))
1065
1052
1066 response.mustcontain('id="open_edit_pullrequest"')
1053 response.mustcontain('id="open_edit_pullrequest"')
1067 response.mustcontain('id="delete_pullrequest"')
1054 response.mustcontain('id="delete_pullrequest"')
1068 response.mustcontain(no=['Confirm to delete this pull request'])
1055 response.mustcontain(no=['Confirm to delete this pull request'])
1069
1056
1070
1057
1071 def assert_pull_request_status(pull_request, expected_status):
1058 def assert_pull_request_status(pull_request, expected_status):
1072 status = ChangesetStatusModel().calculated_review_status(
1059 status = ChangesetStatusModel().calculated_review_status(
1073 pull_request=pull_request)
1060 pull_request=pull_request)
1074 assert status == expected_status
1061 assert status == expected_status
1075
1062
1076
1063
1077 @pytest.mark.parametrize('action', ['show_all', 'index', 'create'])
1064 @pytest.mark.parametrize('action', ['show_all', 'index', 'create'])
1078 @pytest.mark.usefixtures("autologin_user")
1065 @pytest.mark.usefixtures("autologin_user")
1079 def test_redirects_to_repo_summary_for_svn_repositories(
1066 def test_redirects_to_repo_summary_for_svn_repositories(
1080 backend_svn, app, action):
1067 backend_svn, app, action):
1081 denied_actions = ['show_all', 'index', 'create']
1068 denied_actions = ['show_all', 'index', 'create']
1082 for action in denied_actions:
1069 for action in denied_actions:
1083 response = app.get(url(
1070 response = app.get(url(
1084 controller='pullrequests', action=action,
1071 controller='pullrequests', action=action,
1085 repo_name=backend_svn.repo_name))
1072 repo_name=backend_svn.repo_name))
1086 assert response.status_int == 302
1073 assert response.status_int == 302
1087
1074
1088 # Not allowed, redirect to the summary
1075 # Not allowed, redirect to the summary
1089 redirected = response.follow()
1076 redirected = response.follow()
1090 summary_url = url('summary_home', repo_name=backend_svn.repo_name)
1077 summary_url = url('summary_home', repo_name=backend_svn.repo_name)
1091
1078
1092 # URL adds leading slash and path doesn't have it
1079 # URL adds leading slash and path doesn't have it
1093 assert redirected.req.path == summary_url
1080 assert redirected.req.path == summary_url
1094
1081
1095
1082
1096 def test_delete_comment_returns_404_if_comment_does_not_exist(pylonsapp):
1083 def test_delete_comment_returns_404_if_comment_does_not_exist(pylonsapp):
1097 # TODO: johbo: Global import not possible because models.forms blows up
1084 # TODO: johbo: Global import not possible because models.forms blows up
1098 from rhodecode.controllers.pullrequests import PullrequestsController
1085 from rhodecode.controllers.pullrequests import PullrequestsController
1099 controller = PullrequestsController()
1086 controller = PullrequestsController()
1100 patcher = mock.patch(
1087 patcher = mock.patch(
1101 'rhodecode.model.db.BaseModel.get', return_value=None)
1088 'rhodecode.model.db.BaseModel.get', return_value=None)
1102 with pytest.raises(HTTPNotFound), patcher:
1089 with pytest.raises(HTTPNotFound), patcher:
1103 controller._delete_comment(1)
1090 controller._delete_comment(1)
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now