##// END OF EJS Templates
settings: fixed some issuetracker settings tests
super-admin -
r4835:39136da6 default
parent child Browse files
Show More
@@ -1,742 +1,767 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2020 RhodeCode GmbH
3 # Copyright (C) 2010-2020 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
23
24 import rhodecode
24 import rhodecode
25 from rhodecode.apps._base import ADMIN_PREFIX
25 from rhodecode.apps._base import ADMIN_PREFIX
26 from rhodecode.lib.utils2 import md5
26 from rhodecode.lib.utils2 import md5
27 from rhodecode.model.db import RhodeCodeUi
27 from rhodecode.model.db import RhodeCodeUi
28 from rhodecode.model.meta import Session
28 from rhodecode.model.meta import Session
29 from rhodecode.model.settings import SettingsModel, IssueTrackerSettingsModel
29 from rhodecode.model.settings import SettingsModel, IssueTrackerSettingsModel
30 from rhodecode.tests import assert_session_flash
30 from rhodecode.tests import assert_session_flash
31 from rhodecode.tests.utils import AssertResponse
31 from rhodecode.tests.utils import AssertResponse
32
32
33
33
34 UPDATE_DATA_QUALNAME = 'rhodecode.model.update.UpdateModel.get_update_data'
34 UPDATE_DATA_QUALNAME = 'rhodecode.model.update.UpdateModel.get_update_data'
35
35
36
36
37 def route_path(name, params=None, **kwargs):
37 def route_path(name, params=None, **kwargs):
38 import urllib
38 import urllib
39 from rhodecode.apps._base import ADMIN_PREFIX
39 from rhodecode.apps._base import ADMIN_PREFIX
40
40
41 base_url = {
41 base_url = {
42
42
43 'admin_settings':
43 'admin_settings':
44 ADMIN_PREFIX +'/settings',
44 ADMIN_PREFIX +'/settings',
45 'admin_settings_update':
45 'admin_settings_update':
46 ADMIN_PREFIX + '/settings/update',
46 ADMIN_PREFIX + '/settings/update',
47 'admin_settings_global':
47 'admin_settings_global':
48 ADMIN_PREFIX + '/settings/global',
48 ADMIN_PREFIX + '/settings/global',
49 'admin_settings_global_update':
49 'admin_settings_global_update':
50 ADMIN_PREFIX + '/settings/global/update',
50 ADMIN_PREFIX + '/settings/global/update',
51 'admin_settings_vcs':
51 'admin_settings_vcs':
52 ADMIN_PREFIX + '/settings/vcs',
52 ADMIN_PREFIX + '/settings/vcs',
53 'admin_settings_vcs_update':
53 'admin_settings_vcs_update':
54 ADMIN_PREFIX + '/settings/vcs/update',
54 ADMIN_PREFIX + '/settings/vcs/update',
55 'admin_settings_vcs_svn_pattern_delete':
55 'admin_settings_vcs_svn_pattern_delete':
56 ADMIN_PREFIX + '/settings/vcs/svn_pattern_delete',
56 ADMIN_PREFIX + '/settings/vcs/svn_pattern_delete',
57 'admin_settings_mapping':
57 'admin_settings_mapping':
58 ADMIN_PREFIX + '/settings/mapping',
58 ADMIN_PREFIX + '/settings/mapping',
59 'admin_settings_mapping_update':
59 'admin_settings_mapping_update':
60 ADMIN_PREFIX + '/settings/mapping/update',
60 ADMIN_PREFIX + '/settings/mapping/update',
61 'admin_settings_visual':
61 'admin_settings_visual':
62 ADMIN_PREFIX + '/settings/visual',
62 ADMIN_PREFIX + '/settings/visual',
63 'admin_settings_visual_update':
63 'admin_settings_visual_update':
64 ADMIN_PREFIX + '/settings/visual/update',
64 ADMIN_PREFIX + '/settings/visual/update',
65 'admin_settings_issuetracker':
65 'admin_settings_issuetracker':
66 ADMIN_PREFIX + '/settings/issue-tracker',
66 ADMIN_PREFIX + '/settings/issue-tracker',
67 'admin_settings_issuetracker_update':
67 'admin_settings_issuetracker_update':
68 ADMIN_PREFIX + '/settings/issue-tracker/update',
68 ADMIN_PREFIX + '/settings/issue-tracker/update',
69 'admin_settings_issuetracker_test':
69 'admin_settings_issuetracker_test':
70 ADMIN_PREFIX + '/settings/issue-tracker/test',
70 ADMIN_PREFIX + '/settings/issue-tracker/test',
71 'admin_settings_issuetracker_delete':
71 'admin_settings_issuetracker_delete':
72 ADMIN_PREFIX + '/settings/issue-tracker/delete',
72 ADMIN_PREFIX + '/settings/issue-tracker/delete',
73 'admin_settings_email':
73 'admin_settings_email':
74 ADMIN_PREFIX + '/settings/email',
74 ADMIN_PREFIX + '/settings/email',
75 'admin_settings_email_update':
75 'admin_settings_email_update':
76 ADMIN_PREFIX + '/settings/email/update',
76 ADMIN_PREFIX + '/settings/email/update',
77 'admin_settings_hooks':
77 'admin_settings_hooks':
78 ADMIN_PREFIX + '/settings/hooks',
78 ADMIN_PREFIX + '/settings/hooks',
79 'admin_settings_hooks_update':
79 'admin_settings_hooks_update':
80 ADMIN_PREFIX + '/settings/hooks/update',
80 ADMIN_PREFIX + '/settings/hooks/update',
81 'admin_settings_hooks_delete':
81 'admin_settings_hooks_delete':
82 ADMIN_PREFIX + '/settings/hooks/delete',
82 ADMIN_PREFIX + '/settings/hooks/delete',
83 'admin_settings_search':
83 'admin_settings_search':
84 ADMIN_PREFIX + '/settings/search',
84 ADMIN_PREFIX + '/settings/search',
85 'admin_settings_labs':
85 'admin_settings_labs':
86 ADMIN_PREFIX + '/settings/labs',
86 ADMIN_PREFIX + '/settings/labs',
87 'admin_settings_labs_update':
87 'admin_settings_labs_update':
88 ADMIN_PREFIX + '/settings/labs/update',
88 ADMIN_PREFIX + '/settings/labs/update',
89
89
90 'admin_settings_sessions':
90 'admin_settings_sessions':
91 ADMIN_PREFIX + '/settings/sessions',
91 ADMIN_PREFIX + '/settings/sessions',
92 'admin_settings_sessions_cleanup':
92 'admin_settings_sessions_cleanup':
93 ADMIN_PREFIX + '/settings/sessions/cleanup',
93 ADMIN_PREFIX + '/settings/sessions/cleanup',
94 'admin_settings_system':
94 'admin_settings_system':
95 ADMIN_PREFIX + '/settings/system',
95 ADMIN_PREFIX + '/settings/system',
96 'admin_settings_system_update':
96 'admin_settings_system_update':
97 ADMIN_PREFIX + '/settings/system/updates',
97 ADMIN_PREFIX + '/settings/system/updates',
98 'admin_settings_open_source':
98 'admin_settings_open_source':
99 ADMIN_PREFIX + '/settings/open_source',
99 ADMIN_PREFIX + '/settings/open_source',
100
100
101
101
102 }[name].format(**kwargs)
102 }[name].format(**kwargs)
103
103
104 if params:
104 if params:
105 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
105 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
106 return base_url
106 return base_url
107
107
108
108
109 @pytest.mark.usefixtures('autologin_user', 'app')
109 @pytest.mark.usefixtures('autologin_user', 'app')
110 class TestAdminSettingsController(object):
110 class TestAdminSettingsController(object):
111
111
112 @pytest.mark.parametrize('urlname', [
112 @pytest.mark.parametrize('urlname', [
113 'admin_settings_vcs',
113 'admin_settings_vcs',
114 'admin_settings_mapping',
114 'admin_settings_mapping',
115 'admin_settings_global',
115 'admin_settings_global',
116 'admin_settings_visual',
116 'admin_settings_visual',
117 'admin_settings_email',
117 'admin_settings_email',
118 'admin_settings_hooks',
118 'admin_settings_hooks',
119 'admin_settings_search',
119 'admin_settings_search',
120 ])
120 ])
121 def test_simple_get(self, urlname):
121 def test_simple_get(self, urlname):
122 self.app.get(route_path(urlname))
122 self.app.get(route_path(urlname))
123
123
124 def test_create_custom_hook(self, csrf_token):
124 def test_create_custom_hook(self, csrf_token):
125 response = self.app.post(
125 response = self.app.post(
126 route_path('admin_settings_hooks_update'),
126 route_path('admin_settings_hooks_update'),
127 params={
127 params={
128 'new_hook_ui_key': 'test_hooks_1',
128 'new_hook_ui_key': 'test_hooks_1',
129 'new_hook_ui_value': 'cd /tmp',
129 'new_hook_ui_value': 'cd /tmp',
130 'csrf_token': csrf_token})
130 'csrf_token': csrf_token})
131
131
132 response = response.follow()
132 response = response.follow()
133 response.mustcontain('test_hooks_1')
133 response.mustcontain('test_hooks_1')
134 response.mustcontain('cd /tmp')
134 response.mustcontain('cd /tmp')
135
135
136 def test_create_custom_hook_delete(self, csrf_token):
136 def test_create_custom_hook_delete(self, csrf_token):
137 response = self.app.post(
137 response = self.app.post(
138 route_path('admin_settings_hooks_update'),
138 route_path('admin_settings_hooks_update'),
139 params={
139 params={
140 'new_hook_ui_key': 'test_hooks_2',
140 'new_hook_ui_key': 'test_hooks_2',
141 'new_hook_ui_value': 'cd /tmp2',
141 'new_hook_ui_value': 'cd /tmp2',
142 'csrf_token': csrf_token})
142 'csrf_token': csrf_token})
143
143
144 response = response.follow()
144 response = response.follow()
145 response.mustcontain('test_hooks_2')
145 response.mustcontain('test_hooks_2')
146 response.mustcontain('cd /tmp2')
146 response.mustcontain('cd /tmp2')
147
147
148 hook_id = SettingsModel().get_ui_by_key('test_hooks_2').ui_id
148 hook_id = SettingsModel().get_ui_by_key('test_hooks_2').ui_id
149
149
150 # delete
150 # delete
151 self.app.post(
151 self.app.post(
152 route_path('admin_settings_hooks_delete'),
152 route_path('admin_settings_hooks_delete'),
153 params={'hook_id': hook_id, 'csrf_token': csrf_token})
153 params={'hook_id': hook_id, 'csrf_token': csrf_token})
154 response = self.app.get(route_path('admin_settings_hooks'))
154 response = self.app.get(route_path('admin_settings_hooks'))
155 response.mustcontain(no=['test_hooks_2'])
155 response.mustcontain(no=['test_hooks_2'])
156 response.mustcontain(no=['cd /tmp2'])
156 response.mustcontain(no=['cd /tmp2'])
157
157
158
158
159 @pytest.mark.usefixtures('autologin_user', 'app')
159 @pytest.mark.usefixtures('autologin_user', 'app')
160 class TestAdminSettingsGlobal(object):
160 class TestAdminSettingsGlobal(object):
161
161
162 def test_pre_post_code_code_active(self, csrf_token):
162 def test_pre_post_code_code_active(self, csrf_token):
163 pre_code = 'rc-pre-code-187652122'
163 pre_code = 'rc-pre-code-187652122'
164 post_code = 'rc-postcode-98165231'
164 post_code = 'rc-postcode-98165231'
165
165
166 response = self.post_and_verify_settings({
166 response = self.post_and_verify_settings({
167 'rhodecode_pre_code': pre_code,
167 'rhodecode_pre_code': pre_code,
168 'rhodecode_post_code': post_code,
168 'rhodecode_post_code': post_code,
169 'csrf_token': csrf_token,
169 'csrf_token': csrf_token,
170 })
170 })
171
171
172 response = response.follow()
172 response = response.follow()
173 response.mustcontain(pre_code, post_code)
173 response.mustcontain(pre_code, post_code)
174
174
175 def test_pre_post_code_code_inactive(self, csrf_token):
175 def test_pre_post_code_code_inactive(self, csrf_token):
176 pre_code = 'rc-pre-code-187652122'
176 pre_code = 'rc-pre-code-187652122'
177 post_code = 'rc-postcode-98165231'
177 post_code = 'rc-postcode-98165231'
178 response = self.post_and_verify_settings({
178 response = self.post_and_verify_settings({
179 'rhodecode_pre_code': '',
179 'rhodecode_pre_code': '',
180 'rhodecode_post_code': '',
180 'rhodecode_post_code': '',
181 'csrf_token': csrf_token,
181 'csrf_token': csrf_token,
182 })
182 })
183
183
184 response = response.follow()
184 response = response.follow()
185 response.mustcontain(no=[pre_code, post_code])
185 response.mustcontain(no=[pre_code, post_code])
186
186
187 def test_captcha_activate(self, csrf_token):
187 def test_captcha_activate(self, csrf_token):
188 self.post_and_verify_settings({
188 self.post_and_verify_settings({
189 'rhodecode_captcha_private_key': '1234567890',
189 'rhodecode_captcha_private_key': '1234567890',
190 'rhodecode_captcha_public_key': '1234567890',
190 'rhodecode_captcha_public_key': '1234567890',
191 'csrf_token': csrf_token,
191 'csrf_token': csrf_token,
192 })
192 })
193
193
194 response = self.app.get(ADMIN_PREFIX + '/register')
194 response = self.app.get(ADMIN_PREFIX + '/register')
195 response.mustcontain('captcha')
195 response.mustcontain('captcha')
196
196
197 def test_captcha_deactivate(self, csrf_token):
197 def test_captcha_deactivate(self, csrf_token):
198 self.post_and_verify_settings({
198 self.post_and_verify_settings({
199 'rhodecode_captcha_private_key': '',
199 'rhodecode_captcha_private_key': '',
200 'rhodecode_captcha_public_key': '1234567890',
200 'rhodecode_captcha_public_key': '1234567890',
201 'csrf_token': csrf_token,
201 'csrf_token': csrf_token,
202 })
202 })
203
203
204 response = self.app.get(ADMIN_PREFIX + '/register')
204 response = self.app.get(ADMIN_PREFIX + '/register')
205 response.mustcontain(no=['captcha'])
205 response.mustcontain(no=['captcha'])
206
206
207 def test_title_change(self, csrf_token):
207 def test_title_change(self, csrf_token):
208 old_title = 'RhodeCode'
208 old_title = 'RhodeCode'
209
209
210 for new_title in ['Changed', 'Ε»Γ³Ε‚wik', old_title]:
210 for new_title in ['Changed', 'Ε»Γ³Ε‚wik', old_title]:
211 response = self.post_and_verify_settings({
211 response = self.post_and_verify_settings({
212 'rhodecode_title': new_title,
212 'rhodecode_title': new_title,
213 'csrf_token': csrf_token,
213 'csrf_token': csrf_token,
214 })
214 })
215
215
216 response = response.follow()
216 response = response.follow()
217 response.mustcontain(new_title)
217 response.mustcontain(new_title)
218
218
219 def post_and_verify_settings(self, settings):
219 def post_and_verify_settings(self, settings):
220 old_title = 'RhodeCode'
220 old_title = 'RhodeCode'
221 old_realm = 'RhodeCode authentication'
221 old_realm = 'RhodeCode authentication'
222 params = {
222 params = {
223 'rhodecode_title': old_title,
223 'rhodecode_title': old_title,
224 'rhodecode_realm': old_realm,
224 'rhodecode_realm': old_realm,
225 'rhodecode_pre_code': '',
225 'rhodecode_pre_code': '',
226 'rhodecode_post_code': '',
226 'rhodecode_post_code': '',
227 'rhodecode_captcha_private_key': '',
227 'rhodecode_captcha_private_key': '',
228 'rhodecode_captcha_public_key': '',
228 'rhodecode_captcha_public_key': '',
229 'rhodecode_create_personal_repo_group': False,
229 'rhodecode_create_personal_repo_group': False,
230 'rhodecode_personal_repo_group_pattern': '${username}',
230 'rhodecode_personal_repo_group_pattern': '${username}',
231 }
231 }
232 params.update(settings)
232 params.update(settings)
233 response = self.app.post(
233 response = self.app.post(
234 route_path('admin_settings_global_update'), params=params)
234 route_path('admin_settings_global_update'), params=params)
235
235
236 assert_session_flash(response, 'Updated application settings')
236 assert_session_flash(response, 'Updated application settings')
237 app_settings = SettingsModel().get_all_settings()
237 app_settings = SettingsModel().get_all_settings()
238 del settings['csrf_token']
238 del settings['csrf_token']
239 for key, value in settings.iteritems():
239 for key, value in settings.iteritems():
240 assert app_settings[key] == value.decode('utf-8')
240 assert app_settings[key] == value.decode('utf-8')
241
241
242 return response
242 return response
243
243
244
244
245 @pytest.mark.usefixtures('autologin_user', 'app')
245 @pytest.mark.usefixtures('autologin_user', 'app')
246 class TestAdminSettingsVcs(object):
246 class TestAdminSettingsVcs(object):
247
247
248 def test_contains_svn_default_patterns(self):
248 def test_contains_svn_default_patterns(self):
249 response = self.app.get(route_path('admin_settings_vcs'))
249 response = self.app.get(route_path('admin_settings_vcs'))
250 expected_patterns = [
250 expected_patterns = [
251 '/trunk',
251 '/trunk',
252 '/branches/*',
252 '/branches/*',
253 '/tags/*',
253 '/tags/*',
254 ]
254 ]
255 for pattern in expected_patterns:
255 for pattern in expected_patterns:
256 response.mustcontain(pattern)
256 response.mustcontain(pattern)
257
257
258 def test_add_new_svn_branch_and_tag_pattern(
258 def test_add_new_svn_branch_and_tag_pattern(
259 self, backend_svn, form_defaults, disable_sql_cache,
259 self, backend_svn, form_defaults, disable_sql_cache,
260 csrf_token):
260 csrf_token):
261 form_defaults.update({
261 form_defaults.update({
262 'new_svn_branch': '/exp/branches/*',
262 'new_svn_branch': '/exp/branches/*',
263 'new_svn_tag': '/important_tags/*',
263 'new_svn_tag': '/important_tags/*',
264 'csrf_token': csrf_token,
264 'csrf_token': csrf_token,
265 })
265 })
266
266
267 response = self.app.post(
267 response = self.app.post(
268 route_path('admin_settings_vcs_update'),
268 route_path('admin_settings_vcs_update'),
269 params=form_defaults, status=302)
269 params=form_defaults, status=302)
270 response = response.follow()
270 response = response.follow()
271
271
272 # Expect to find the new values on the page
272 # Expect to find the new values on the page
273 response.mustcontain('/exp/branches/*')
273 response.mustcontain('/exp/branches/*')
274 response.mustcontain('/important_tags/*')
274 response.mustcontain('/important_tags/*')
275
275
276 # Expect that those patterns are used to match branches and tags now
276 # Expect that those patterns are used to match branches and tags now
277 repo = backend_svn['svn-simple-layout'].scm_instance()
277 repo = backend_svn['svn-simple-layout'].scm_instance()
278 assert 'exp/branches/exp-sphinx-docs' in repo.branches
278 assert 'exp/branches/exp-sphinx-docs' in repo.branches
279 assert 'important_tags/v0.5' in repo.tags
279 assert 'important_tags/v0.5' in repo.tags
280
280
281 def test_add_same_svn_value_twice_shows_an_error_message(
281 def test_add_same_svn_value_twice_shows_an_error_message(
282 self, form_defaults, csrf_token, settings_util):
282 self, form_defaults, csrf_token, settings_util):
283 settings_util.create_rhodecode_ui('vcs_svn_branch', '/test')
283 settings_util.create_rhodecode_ui('vcs_svn_branch', '/test')
284 settings_util.create_rhodecode_ui('vcs_svn_tag', '/test')
284 settings_util.create_rhodecode_ui('vcs_svn_tag', '/test')
285
285
286 response = self.app.post(
286 response = self.app.post(
287 route_path('admin_settings_vcs_update'),
287 route_path('admin_settings_vcs_update'),
288 params={
288 params={
289 'paths_root_path': form_defaults['paths_root_path'],
289 'paths_root_path': form_defaults['paths_root_path'],
290 'new_svn_branch': '/test',
290 'new_svn_branch': '/test',
291 'new_svn_tag': '/test',
291 'new_svn_tag': '/test',
292 'csrf_token': csrf_token,
292 'csrf_token': csrf_token,
293 },
293 },
294 status=200)
294 status=200)
295
295
296 response.mustcontain("Pattern already exists")
296 response.mustcontain("Pattern already exists")
297 response.mustcontain("Some form inputs contain invalid data.")
297 response.mustcontain("Some form inputs contain invalid data.")
298
298
299 @pytest.mark.parametrize('section', [
299 @pytest.mark.parametrize('section', [
300 'vcs_svn_branch',
300 'vcs_svn_branch',
301 'vcs_svn_tag',
301 'vcs_svn_tag',
302 ])
302 ])
303 def test_delete_svn_patterns(
303 def test_delete_svn_patterns(
304 self, section, csrf_token, settings_util):
304 self, section, csrf_token, settings_util):
305 setting = settings_util.create_rhodecode_ui(
305 setting = settings_util.create_rhodecode_ui(
306 section, '/test_delete', cleanup=False)
306 section, '/test_delete', cleanup=False)
307
307
308 self.app.post(
308 self.app.post(
309 route_path('admin_settings_vcs_svn_pattern_delete'),
309 route_path('admin_settings_vcs_svn_pattern_delete'),
310 params={
310 params={
311 'delete_svn_pattern': setting.ui_id,
311 'delete_svn_pattern': setting.ui_id,
312 'csrf_token': csrf_token},
312 'csrf_token': csrf_token},
313 headers={'X-REQUESTED-WITH': 'XMLHttpRequest'})
313 headers={'X-REQUESTED-WITH': 'XMLHttpRequest'})
314
314
315 @pytest.mark.parametrize('section', [
315 @pytest.mark.parametrize('section', [
316 'vcs_svn_branch',
316 'vcs_svn_branch',
317 'vcs_svn_tag',
317 'vcs_svn_tag',
318 ])
318 ])
319 def test_delete_svn_patterns_raises_404_when_no_xhr(
319 def test_delete_svn_patterns_raises_404_when_no_xhr(
320 self, section, csrf_token, settings_util):
320 self, section, csrf_token, settings_util):
321 setting = settings_util.create_rhodecode_ui(section, '/test_delete')
321 setting = settings_util.create_rhodecode_ui(section, '/test_delete')
322
322
323 self.app.post(
323 self.app.post(
324 route_path('admin_settings_vcs_svn_pattern_delete'),
324 route_path('admin_settings_vcs_svn_pattern_delete'),
325 params={
325 params={
326 'delete_svn_pattern': setting.ui_id,
326 'delete_svn_pattern': setting.ui_id,
327 'csrf_token': csrf_token},
327 'csrf_token': csrf_token},
328 status=404)
328 status=404)
329
329
330 def test_extensions_hgsubversion(self, form_defaults, csrf_token):
330 def test_extensions_hgsubversion(self, form_defaults, csrf_token):
331 form_defaults.update({
331 form_defaults.update({
332 'csrf_token': csrf_token,
332 'csrf_token': csrf_token,
333 'extensions_hgsubversion': 'True',
333 'extensions_hgsubversion': 'True',
334 })
334 })
335 response = self.app.post(
335 response = self.app.post(
336 route_path('admin_settings_vcs_update'),
336 route_path('admin_settings_vcs_update'),
337 params=form_defaults,
337 params=form_defaults,
338 status=302)
338 status=302)
339
339
340 response = response.follow()
340 response = response.follow()
341 extensions_input = (
341 extensions_input = (
342 '<input id="extensions_hgsubversion" '
342 '<input id="extensions_hgsubversion" '
343 'name="extensions_hgsubversion" type="checkbox" '
343 'name="extensions_hgsubversion" type="checkbox" '
344 'value="True" checked="checked" />')
344 'value="True" checked="checked" />')
345 response.mustcontain(extensions_input)
345 response.mustcontain(extensions_input)
346
346
347 def test_extensions_hgevolve(self, form_defaults, csrf_token):
347 def test_extensions_hgevolve(self, form_defaults, csrf_token):
348 form_defaults.update({
348 form_defaults.update({
349 'csrf_token': csrf_token,
349 'csrf_token': csrf_token,
350 'extensions_evolve': 'True',
350 'extensions_evolve': 'True',
351 })
351 })
352 response = self.app.post(
352 response = self.app.post(
353 route_path('admin_settings_vcs_update'),
353 route_path('admin_settings_vcs_update'),
354 params=form_defaults,
354 params=form_defaults,
355 status=302)
355 status=302)
356
356
357 response = response.follow()
357 response = response.follow()
358 extensions_input = (
358 extensions_input = (
359 '<input id="extensions_evolve" '
359 '<input id="extensions_evolve" '
360 'name="extensions_evolve" type="checkbox" '
360 'name="extensions_evolve" type="checkbox" '
361 'value="True" checked="checked" />')
361 'value="True" checked="checked" />')
362 response.mustcontain(extensions_input)
362 response.mustcontain(extensions_input)
363
363
364 def test_has_a_section_for_pull_request_settings(self):
364 def test_has_a_section_for_pull_request_settings(self):
365 response = self.app.get(route_path('admin_settings_vcs'))
365 response = self.app.get(route_path('admin_settings_vcs'))
366 response.mustcontain('Pull Request Settings')
366 response.mustcontain('Pull Request Settings')
367
367
368 def test_has_an_input_for_invalidation_of_inline_comments(self):
368 def test_has_an_input_for_invalidation_of_inline_comments(self):
369 response = self.app.get(route_path('admin_settings_vcs'))
369 response = self.app.get(route_path('admin_settings_vcs'))
370 assert_response = response.assert_response()
370 assert_response = response.assert_response()
371 assert_response.one_element_exists(
371 assert_response.one_element_exists(
372 '[name=rhodecode_use_outdated_comments]')
372 '[name=rhodecode_use_outdated_comments]')
373
373
374 @pytest.mark.parametrize('new_value', [True, False])
374 @pytest.mark.parametrize('new_value', [True, False])
375 def test_allows_to_change_invalidation_of_inline_comments(
375 def test_allows_to_change_invalidation_of_inline_comments(
376 self, form_defaults, csrf_token, new_value):
376 self, form_defaults, csrf_token, new_value):
377 setting_key = 'use_outdated_comments'
377 setting_key = 'use_outdated_comments'
378 setting = SettingsModel().create_or_update_setting(
378 setting = SettingsModel().create_or_update_setting(
379 setting_key, not new_value, 'bool')
379 setting_key, not new_value, 'bool')
380 Session().add(setting)
380 Session().add(setting)
381 Session().commit()
381 Session().commit()
382
382
383 form_defaults.update({
383 form_defaults.update({
384 'csrf_token': csrf_token,
384 'csrf_token': csrf_token,
385 'rhodecode_use_outdated_comments': str(new_value),
385 'rhodecode_use_outdated_comments': str(new_value),
386 })
386 })
387 response = self.app.post(
387 response = self.app.post(
388 route_path('admin_settings_vcs_update'),
388 route_path('admin_settings_vcs_update'),
389 params=form_defaults,
389 params=form_defaults,
390 status=302)
390 status=302)
391 response = response.follow()
391 response = response.follow()
392 setting = SettingsModel().get_setting_by_name(setting_key)
392 setting = SettingsModel().get_setting_by_name(setting_key)
393 assert setting.app_settings_value is new_value
393 assert setting.app_settings_value is new_value
394
394
395 @pytest.mark.parametrize('new_value', [True, False])
395 @pytest.mark.parametrize('new_value', [True, False])
396 def test_allows_to_change_hg_rebase_merge_strategy(
396 def test_allows_to_change_hg_rebase_merge_strategy(
397 self, form_defaults, csrf_token, new_value):
397 self, form_defaults, csrf_token, new_value):
398 setting_key = 'hg_use_rebase_for_merging'
398 setting_key = 'hg_use_rebase_for_merging'
399
399
400 form_defaults.update({
400 form_defaults.update({
401 'csrf_token': csrf_token,
401 'csrf_token': csrf_token,
402 'rhodecode_' + setting_key: str(new_value),
402 'rhodecode_' + setting_key: str(new_value),
403 })
403 })
404
404
405 with mock.patch.dict(
405 with mock.patch.dict(
406 rhodecode.CONFIG, {'labs_settings_active': 'true'}):
406 rhodecode.CONFIG, {'labs_settings_active': 'true'}):
407 self.app.post(
407 self.app.post(
408 route_path('admin_settings_vcs_update'),
408 route_path('admin_settings_vcs_update'),
409 params=form_defaults,
409 params=form_defaults,
410 status=302)
410 status=302)
411
411
412 setting = SettingsModel().get_setting_by_name(setting_key)
412 setting = SettingsModel().get_setting_by_name(setting_key)
413 assert setting.app_settings_value is new_value
413 assert setting.app_settings_value is new_value
414
414
415 @pytest.fixture()
415 @pytest.fixture()
416 def disable_sql_cache(self, request):
416 def disable_sql_cache(self, request):
417 patcher = mock.patch(
417 patcher = mock.patch(
418 'rhodecode.lib.caching_query.FromCache.process_query')
418 'rhodecode.lib.caching_query.FromCache.process_query')
419 request.addfinalizer(patcher.stop)
419 request.addfinalizer(patcher.stop)
420 patcher.start()
420 patcher.start()
421
421
422 @pytest.fixture()
422 @pytest.fixture()
423 def form_defaults(self):
423 def form_defaults(self):
424 from rhodecode.apps.admin.views.settings import AdminSettingsView
424 from rhodecode.apps.admin.views.settings import AdminSettingsView
425 return AdminSettingsView._form_defaults()
425 return AdminSettingsView._form_defaults()
426
426
427 # TODO: johbo: What we really want is to checkpoint before a test run and
427 # TODO: johbo: What we really want is to checkpoint before a test run and
428 # reset the session afterwards.
428 # reset the session afterwards.
429 @pytest.fixture(scope='class', autouse=True)
429 @pytest.fixture(scope='class', autouse=True)
430 def cleanup_settings(self, request, baseapp):
430 def cleanup_settings(self, request, baseapp):
431 ui_id = RhodeCodeUi.ui_id
431 ui_id = RhodeCodeUi.ui_id
432 original_ids = list(
432 original_ids = list(
433 r.ui_id for r in RhodeCodeUi.query().values(ui_id))
433 r.ui_id for r in RhodeCodeUi.query().values(ui_id))
434
434
435 @request.addfinalizer
435 @request.addfinalizer
436 def cleanup():
436 def cleanup():
437 RhodeCodeUi.query().filter(
437 RhodeCodeUi.query().filter(
438 ui_id.notin_(original_ids)).delete(False)
438 ui_id.notin_(original_ids)).delete(False)
439
439
440
440
441 @pytest.mark.usefixtures('autologin_user', 'app')
441 @pytest.mark.usefixtures('autologin_user', 'app')
442 class TestLabsSettings(object):
442 class TestLabsSettings(object):
443 def test_get_settings_page_disabled(self):
443 def test_get_settings_page_disabled(self):
444 with mock.patch.dict(
444 with mock.patch.dict(
445 rhodecode.CONFIG, {'labs_settings_active': 'false'}):
445 rhodecode.CONFIG, {'labs_settings_active': 'false'}):
446
446
447 response = self.app.get(
447 response = self.app.get(
448 route_path('admin_settings_labs'), status=302)
448 route_path('admin_settings_labs'), status=302)
449
449
450 assert response.location.endswith(route_path('admin_settings'))
450 assert response.location.endswith(route_path('admin_settings'))
451
451
452 def test_get_settings_page_enabled(self):
452 def test_get_settings_page_enabled(self):
453 from rhodecode.apps.admin.views import settings
453 from rhodecode.apps.admin.views import settings
454 lab_settings = [
454 lab_settings = [
455 settings.LabSetting(
455 settings.LabSetting(
456 key='rhodecode_bool',
456 key='rhodecode_bool',
457 type='bool',
457 type='bool',
458 group='bool group',
458 group='bool group',
459 label='bool label',
459 label='bool label',
460 help='bool help'
460 help='bool help'
461 ),
461 ),
462 settings.LabSetting(
462 settings.LabSetting(
463 key='rhodecode_text',
463 key='rhodecode_text',
464 type='unicode',
464 type='unicode',
465 group='text group',
465 group='text group',
466 label='text label',
466 label='text label',
467 help='text help'
467 help='text help'
468 ),
468 ),
469 ]
469 ]
470 with mock.patch.dict(rhodecode.CONFIG,
470 with mock.patch.dict(rhodecode.CONFIG,
471 {'labs_settings_active': 'true'}):
471 {'labs_settings_active': 'true'}):
472 with mock.patch.object(settings, '_LAB_SETTINGS', lab_settings):
472 with mock.patch.object(settings, '_LAB_SETTINGS', lab_settings):
473 response = self.app.get(route_path('admin_settings_labs'))
473 response = self.app.get(route_path('admin_settings_labs'))
474
474
475 assert '<label>bool group:</label>' in response
475 assert '<label>bool group:</label>' in response
476 assert '<label for="rhodecode_bool">bool label</label>' in response
476 assert '<label for="rhodecode_bool">bool label</label>' in response
477 assert '<p class="help-block">bool help</p>' in response
477 assert '<p class="help-block">bool help</p>' in response
478 assert 'name="rhodecode_bool" type="checkbox"' in response
478 assert 'name="rhodecode_bool" type="checkbox"' in response
479
479
480 assert '<label>text group:</label>' in response
480 assert '<label>text group:</label>' in response
481 assert '<label for="rhodecode_text">text label</label>' in response
481 assert '<label for="rhodecode_text">text label</label>' in response
482 assert '<p class="help-block">text help</p>' in response
482 assert '<p class="help-block">text help</p>' in response
483 assert 'name="rhodecode_text" size="60" type="text"' in response
483 assert 'name="rhodecode_text" size="60" type="text"' in response
484
484
485
485
486 @pytest.mark.usefixtures('app')
486 @pytest.mark.usefixtures('app')
487 class TestOpenSourceLicenses(object):
487 class TestOpenSourceLicenses(object):
488
488
489 def test_records_are_displayed(self, autologin_user):
489 def test_records_are_displayed(self, autologin_user):
490 sample_licenses = [
490 sample_licenses = [
491 {
491 {
492 "license": [
492 "license": [
493 {
493 {
494 "fullName": "BSD 4-clause \"Original\" or \"Old\" License",
494 "fullName": "BSD 4-clause \"Original\" or \"Old\" License",
495 "shortName": "bsdOriginal",
495 "shortName": "bsdOriginal",
496 "spdxId": "BSD-4-Clause",
496 "spdxId": "BSD-4-Clause",
497 "url": "http://spdx.org/licenses/BSD-4-Clause.html"
497 "url": "http://spdx.org/licenses/BSD-4-Clause.html"
498 }
498 }
499 ],
499 ],
500 "name": "python2.7-coverage-3.7.1"
500 "name": "python2.7-coverage-3.7.1"
501 },
501 },
502 {
502 {
503 "license": [
503 "license": [
504 {
504 {
505 "fullName": "MIT License",
505 "fullName": "MIT License",
506 "shortName": "mit",
506 "shortName": "mit",
507 "spdxId": "MIT",
507 "spdxId": "MIT",
508 "url": "http://spdx.org/licenses/MIT.html"
508 "url": "http://spdx.org/licenses/MIT.html"
509 }
509 }
510 ],
510 ],
511 "name": "python2.7-bootstrapped-pip-9.0.1"
511 "name": "python2.7-bootstrapped-pip-9.0.1"
512 },
512 },
513 ]
513 ]
514 read_licenses_patch = mock.patch(
514 read_licenses_patch = mock.patch(
515 'rhodecode.apps.admin.views.open_source_licenses.read_opensource_licenses',
515 'rhodecode.apps.admin.views.open_source_licenses.read_opensource_licenses',
516 return_value=sample_licenses)
516 return_value=sample_licenses)
517 with read_licenses_patch:
517 with read_licenses_patch:
518 response = self.app.get(
518 response = self.app.get(
519 route_path('admin_settings_open_source'), status=200)
519 route_path('admin_settings_open_source'), status=200)
520
520
521 assert_response = response.assert_response()
521 assert_response = response.assert_response()
522 assert_response.element_contains(
522 assert_response.element_contains(
523 '.panel-heading', 'Licenses of Third Party Packages')
523 '.panel-heading', 'Licenses of Third Party Packages')
524 for license_data in sample_licenses:
524 for license_data in sample_licenses:
525 response.mustcontain(license_data["license"][0]["spdxId"])
525 response.mustcontain(license_data["license"][0]["spdxId"])
526 assert_response.element_contains('.panel-body', license_data["name"])
526 assert_response.element_contains('.panel-body', license_data["name"])
527
527
528 def test_records_can_be_read(self, autologin_user):
528 def test_records_can_be_read(self, autologin_user):
529 response = self.app.get(
529 response = self.app.get(
530 route_path('admin_settings_open_source'), status=200)
530 route_path('admin_settings_open_source'), status=200)
531 assert_response = response.assert_response()
531 assert_response = response.assert_response()
532 assert_response.element_contains(
532 assert_response.element_contains(
533 '.panel-heading', 'Licenses of Third Party Packages')
533 '.panel-heading', 'Licenses of Third Party Packages')
534
534
535 def test_forbidden_when_normal_user(self, autologin_regular_user):
535 def test_forbidden_when_normal_user(self, autologin_regular_user):
536 self.app.get(
536 self.app.get(
537 route_path('admin_settings_open_source'), status=404)
537 route_path('admin_settings_open_source'), status=404)
538
538
539
539
540 @pytest.mark.usefixtures('app')
540 @pytest.mark.usefixtures('app')
541 class TestUserSessions(object):
541 class TestUserSessions(object):
542
542
543 def test_forbidden_when_normal_user(self, autologin_regular_user):
543 def test_forbidden_when_normal_user(self, autologin_regular_user):
544 self.app.get(route_path('admin_settings_sessions'), status=404)
544 self.app.get(route_path('admin_settings_sessions'), status=404)
545
545
546 def test_show_sessions_page(self, autologin_user):
546 def test_show_sessions_page(self, autologin_user):
547 response = self.app.get(route_path('admin_settings_sessions'), status=200)
547 response = self.app.get(route_path('admin_settings_sessions'), status=200)
548 response.mustcontain('file')
548 response.mustcontain('file')
549
549
550 def test_cleanup_old_sessions(self, autologin_user, csrf_token):
550 def test_cleanup_old_sessions(self, autologin_user, csrf_token):
551
551
552 post_data = {
552 post_data = {
553 'csrf_token': csrf_token,
553 'csrf_token': csrf_token,
554 'expire_days': '60'
554 'expire_days': '60'
555 }
555 }
556 response = self.app.post(
556 response = self.app.post(
557 route_path('admin_settings_sessions_cleanup'), params=post_data,
557 route_path('admin_settings_sessions_cleanup'), params=post_data,
558 status=302)
558 status=302)
559 assert_session_flash(response, 'Cleaned up old sessions')
559 assert_session_flash(response, 'Cleaned up old sessions')
560
560
561
561
562 @pytest.mark.usefixtures('app')
562 @pytest.mark.usefixtures('app')
563 class TestAdminSystemInfo(object):
563 class TestAdminSystemInfo(object):
564
564
565 def test_forbidden_when_normal_user(self, autologin_regular_user):
565 def test_forbidden_when_normal_user(self, autologin_regular_user):
566 self.app.get(route_path('admin_settings_system'), status=404)
566 self.app.get(route_path('admin_settings_system'), status=404)
567
567
568 def test_system_info_page(self, autologin_user):
568 def test_system_info_page(self, autologin_user):
569 response = self.app.get(route_path('admin_settings_system'))
569 response = self.app.get(route_path('admin_settings_system'))
570 response.mustcontain('RhodeCode Community Edition, version {}'.format(
570 response.mustcontain('RhodeCode Community Edition, version {}'.format(
571 rhodecode.__version__))
571 rhodecode.__version__))
572
572
573 def test_system_update_new_version(self, autologin_user):
573 def test_system_update_new_version(self, autologin_user):
574 update_data = {
574 update_data = {
575 'versions': [
575 'versions': [
576 {
576 {
577 'version': '100.3.1415926535',
577 'version': '100.3.1415926535',
578 'general': 'The latest version we are ever going to ship'
578 'general': 'The latest version we are ever going to ship'
579 },
579 },
580 {
580 {
581 'version': '0.0.0',
581 'version': '0.0.0',
582 'general': 'The first version we ever shipped'
582 'general': 'The first version we ever shipped'
583 }
583 }
584 ]
584 ]
585 }
585 }
586 with mock.patch(UPDATE_DATA_QUALNAME, return_value=update_data):
586 with mock.patch(UPDATE_DATA_QUALNAME, return_value=update_data):
587 response = self.app.get(route_path('admin_settings_system_update'))
587 response = self.app.get(route_path('admin_settings_system_update'))
588 response.mustcontain('A <b>new version</b> is available')
588 response.mustcontain('A <b>new version</b> is available')
589
589
590 def test_system_update_nothing_new(self, autologin_user):
590 def test_system_update_nothing_new(self, autologin_user):
591 update_data = {
591 update_data = {
592 'versions': [
592 'versions': [
593 {
593 {
594 'version': '0.0.0',
594 'version': '0.0.0',
595 'general': 'The first version we ever shipped'
595 'general': 'The first version we ever shipped'
596 }
596 }
597 ]
597 ]
598 }
598 }
599 with mock.patch(UPDATE_DATA_QUALNAME, return_value=update_data):
599 with mock.patch(UPDATE_DATA_QUALNAME, return_value=update_data):
600 response = self.app.get(route_path('admin_settings_system_update'))
600 response = self.app.get(route_path('admin_settings_system_update'))
601 response.mustcontain(
601 response.mustcontain(
602 'This instance is already running the <b>latest</b> stable version')
602 'This instance is already running the <b>latest</b> stable version')
603
603
604 def test_system_update_bad_response(self, autologin_user):
604 def test_system_update_bad_response(self, autologin_user):
605 with mock.patch(UPDATE_DATA_QUALNAME, side_effect=ValueError('foo')):
605 with mock.patch(UPDATE_DATA_QUALNAME, side_effect=ValueError('foo')):
606 response = self.app.get(route_path('admin_settings_system_update'))
606 response = self.app.get(route_path('admin_settings_system_update'))
607 response.mustcontain(
607 response.mustcontain(
608 'Bad data sent from update server')
608 'Bad data sent from update server')
609
609
610
610
611 @pytest.mark.usefixtures("app")
611 @pytest.mark.usefixtures("app")
612 class TestAdminSettingsIssueTracker(object):
612 class TestAdminSettingsIssueTracker(object):
613 RC_PREFIX = 'rhodecode_'
613 RC_PREFIX = 'rhodecode_'
614 SHORT_PATTERN_KEY = 'issuetracker_pat_'
614 SHORT_PATTERN_KEY = 'issuetracker_pat_'
615 PATTERN_KEY = RC_PREFIX + SHORT_PATTERN_KEY
615 PATTERN_KEY = RC_PREFIX + SHORT_PATTERN_KEY
616 DESC_KEY = RC_PREFIX + 'issuetracker_desc_'
616
617
617 def test_issuetracker_index(self, autologin_user):
618 def test_issuetracker_index(self, autologin_user):
618 response = self.app.get(route_path('admin_settings_issuetracker'))
619 response = self.app.get(route_path('admin_settings_issuetracker'))
619 assert response.status_code == 200
620 assert response.status_code == 200
620
621
621 def test_add_empty_issuetracker_pattern(
622 def test_add_empty_issuetracker_pattern(
622 self, request, autologin_user, csrf_token):
623 self, request, autologin_user, csrf_token):
623 post_url = route_path('admin_settings_issuetracker_update')
624 post_url = route_path('admin_settings_issuetracker_update')
624 post_data = {
625 post_data = {
625 'csrf_token': csrf_token
626 'csrf_token': csrf_token
626 }
627 }
627 self.app.post(post_url, post_data, status=302)
628 self.app.post(post_url, post_data, status=302)
628
629
629 def test_add_issuetracker_pattern(
630 def test_add_issuetracker_pattern(
630 self, request, autologin_user, csrf_token):
631 self, request, autologin_user, csrf_token):
631 pattern = 'issuetracker_pat'
632 pattern = 'issuetracker_pat'
632 another_pattern = pattern+'1'
633 another_pattern = pattern+'1'
633 post_url = route_path('admin_settings_issuetracker_update')
634 post_url = route_path('admin_settings_issuetracker_update')
634 post_data = {
635 post_data = {
635 'new_pattern_pattern_0': pattern,
636 'new_pattern_pattern_0': pattern,
636 'new_pattern_url_0': 'http://url',
637 'new_pattern_url_0': 'http://url',
637 'new_pattern_prefix_0': 'prefix',
638 'new_pattern_prefix_0': 'prefix',
638 'new_pattern_description_0': 'description',
639 'new_pattern_description_0': 'description',
639 'new_pattern_pattern_1': another_pattern,
640 'new_pattern_pattern_1': another_pattern,
640 'new_pattern_url_1': 'https://url1',
641 'new_pattern_url_1': 'https://url1',
641 'new_pattern_prefix_1': 'prefix1',
642 'new_pattern_prefix_1': 'prefix1',
642 'new_pattern_description_1': 'description1',
643 'new_pattern_description_1': 'description1',
643 'csrf_token': csrf_token
644 'csrf_token': csrf_token
644 }
645 }
645 self.app.post(post_url, post_data, status=302)
646 self.app.post(post_url, post_data, status=302)
646 settings = SettingsModel().get_all_settings()
647 settings = SettingsModel().get_all_settings()
647 self.uid = md5(pattern)
648 self.uid = md5(pattern)
648 assert settings[self.PATTERN_KEY+self.uid] == pattern
649 assert settings[self.PATTERN_KEY+self.uid] == pattern
649 self.another_uid = md5(another_pattern)
650 self.another_uid = md5(another_pattern)
650 assert settings[self.PATTERN_KEY+self.another_uid] == another_pattern
651 assert settings[self.PATTERN_KEY+self.another_uid] == another_pattern
651
652
652 @request.addfinalizer
653 @request.addfinalizer
653 def cleanup():
654 def cleanup():
654 defaults = SettingsModel().get_all_settings()
655 defaults = SettingsModel().get_all_settings()
655
656
656 entries = [name for name in defaults if (
657 entries = [name for name in defaults if (
657 (self.uid in name) or (self.another_uid) in name)]
658 (self.uid in name) or (self.another_uid) in name)]
658 start = len(self.RC_PREFIX)
659 start = len(self.RC_PREFIX)
659 for del_key in entries:
660 for del_key in entries:
660 # TODO: anderson: get_by_name needs name without prefix
661 # TODO: anderson: get_by_name needs name without prefix
661 entry = SettingsModel().get_setting_by_name(del_key[start:])
662 entry = SettingsModel().get_setting_by_name(del_key[start:])
662 Session().delete(entry)
663 Session().delete(entry)
663
664
664 Session().commit()
665 Session().commit()
665
666
666 def test_edit_issuetracker_pattern(
667 def test_edit_issuetracker_pattern(
667 self, autologin_user, backend, csrf_token, request):
668 self, autologin_user, backend, csrf_token, request):
668 old_pattern = 'issuetracker_pat'
669
670 old_pattern = 'issuetracker_pat1'
669 old_uid = md5(old_pattern)
671 old_uid = md5(old_pattern)
670 pattern = 'issuetracker_pat_new'
671 self.new_uid = md5(pattern)
672
673 SettingsModel().create_or_update_setting(
674 self.SHORT_PATTERN_KEY+old_uid, old_pattern, 'unicode')
675
672
676 post_url = route_path('admin_settings_issuetracker_update')
673 post_url = route_path('admin_settings_issuetracker_update')
677 post_data = {
674 post_data = {
678 'new_pattern_pattern_0': pattern,
675 'new_pattern_pattern_0': old_pattern,
679 'new_pattern_url_0': 'https://url',
676 'new_pattern_url_0': 'http://url',
680 'new_pattern_prefix_0': 'prefix',
677 'new_pattern_prefix_0': 'prefix',
681 'new_pattern_description_0': 'description',
678 'new_pattern_description_0': 'description',
679
680 'csrf_token': csrf_token
681 }
682 self.app.post(post_url, post_data, status=302)
683
684 new_pattern = 'issuetracker_pat1_edited'
685 self.new_uid = md5(new_pattern)
686
687 post_url = route_path('admin_settings_issuetracker_update')
688 post_data = {
689 'new_pattern_pattern_{}'.format(old_uid): new_pattern,
690 'new_pattern_url_{}'.format(old_uid): 'https://url_edited',
691 'new_pattern_prefix_{}'.format(old_uid): 'prefix_edited',
692 'new_pattern_description_{}'.format(old_uid): 'description_edited',
682 'uid': old_uid,
693 'uid': old_uid,
683 'csrf_token': csrf_token
694 'csrf_token': csrf_token
684 }
695 }
685 self.app.post(post_url, post_data, status=302)
696 self.app.post(post_url, post_data, status=302)
697
686 settings = SettingsModel().get_all_settings()
698 settings = SettingsModel().get_all_settings()
687 assert settings[self.PATTERN_KEY+self.new_uid] == pattern
699 assert settings[self.PATTERN_KEY+self.new_uid] == new_pattern
700 assert settings[self.DESC_KEY + self.new_uid] == 'description_edited'
688 assert self.PATTERN_KEY+old_uid not in settings
701 assert self.PATTERN_KEY+old_uid not in settings
689
702
690 @request.addfinalizer
703 @request.addfinalizer
691 def cleanup():
704 def cleanup():
705 IssueTrackerSettingsModel().delete_entries(old_uid)
692 IssueTrackerSettingsModel().delete_entries(self.new_uid)
706 IssueTrackerSettingsModel().delete_entries(self.new_uid)
693
707
694 def test_replace_issuetracker_pattern_description(
708 def test_replace_issuetracker_pattern_description(
695 self, autologin_user, csrf_token, request, settings_util):
709 self, autologin_user, csrf_token, request, settings_util):
696 prefix = 'issuetracker'
710 prefix = 'issuetracker'
697 pattern = 'issuetracker_pat'
711 pattern = 'issuetracker_pat'
698 self.uid = md5(pattern)
712 self.uid = md5(pattern)
699 pattern_key = '_'.join([prefix, 'pat', self.uid])
713 pattern_key = '_'.join([prefix, 'pat', self.uid])
700 rc_pattern_key = '_'.join(['rhodecode', pattern_key])
714 rc_pattern_key = '_'.join(['rhodecode', pattern_key])
701 desc_key = '_'.join([prefix, 'desc', self.uid])
715 desc_key = '_'.join([prefix, 'desc', self.uid])
702 rc_desc_key = '_'.join(['rhodecode', desc_key])
716 rc_desc_key = '_'.join(['rhodecode', desc_key])
703 new_description = 'new_description'
717 new_description = 'new_description'
704
718
705 settings_util.create_rhodecode_setting(
719 settings_util.create_rhodecode_setting(
706 pattern_key, pattern, 'unicode', cleanup=False)
720 pattern_key, pattern, 'unicode', cleanup=False)
707 settings_util.create_rhodecode_setting(
721 settings_util.create_rhodecode_setting(
708 desc_key, 'old description', 'unicode', cleanup=False)
722 desc_key, 'old description', 'unicode', cleanup=False)
709
723
710 post_url = route_path('admin_settings_issuetracker_update')
724 post_url = route_path('admin_settings_issuetracker_update')
711 post_data = {
725 post_data = {
712 'new_pattern_pattern_0': pattern,
726 'new_pattern_pattern_0': pattern,
713 'new_pattern_url_0': 'https://url',
727 'new_pattern_url_0': 'https://url',
714 'new_pattern_prefix_0': 'prefix',
728 'new_pattern_prefix_0': 'prefix',
715 'new_pattern_description_0': new_description,
729 'new_pattern_description_0': new_description,
716 'uid': self.uid,
730 'uid': self.uid,
717 'csrf_token': csrf_token
731 'csrf_token': csrf_token
718 }
732 }
719 self.app.post(post_url, post_data, status=302)
733 self.app.post(post_url, post_data, status=302)
720 settings = SettingsModel().get_all_settings()
734 settings = SettingsModel().get_all_settings()
721 assert settings[rc_pattern_key] == pattern
735 assert settings[rc_pattern_key] == pattern
722 assert settings[rc_desc_key] == new_description
736 assert settings[rc_desc_key] == new_description
723
737
724 @request.addfinalizer
738 @request.addfinalizer
725 def cleanup():
739 def cleanup():
726 IssueTrackerSettingsModel().delete_entries(self.uid)
740 IssueTrackerSettingsModel().delete_entries(self.uid)
727
741
728 def test_delete_issuetracker_pattern(
742 def test_delete_issuetracker_pattern(
729 self, autologin_user, backend, csrf_token, settings_util, xhr_header):
743 self, autologin_user, backend, csrf_token, settings_util, xhr_header):
730 pattern = 'issuetracker_pat'
744
731 uid = md5(pattern)
745 old_pattern = 'issuetracker_pat_deleted'
732 settings_util.create_rhodecode_setting(
746 old_uid = md5(old_pattern)
733 self.SHORT_PATTERN_KEY+uid, pattern, 'unicode', cleanup=False)
747
748 post_url = route_path('admin_settings_issuetracker_update')
749 post_data = {
750 'new_pattern_pattern_0': old_pattern,
751 'new_pattern_url_0': 'http://url',
752 'new_pattern_prefix_0': 'prefix',
753 'new_pattern_description_0': 'description',
754
755 'csrf_token': csrf_token
756 }
757 self.app.post(post_url, post_data, status=302)
734
758
735 post_url = route_path('admin_settings_issuetracker_delete')
759 post_url = route_path('admin_settings_issuetracker_delete')
736 post_data = {
760 post_data = {
737 'uid': uid,
761 'uid': old_uid,
738 'csrf_token': csrf_token
762 'csrf_token': csrf_token
739 }
763 }
740 self.app.post(post_url, post_data, extra_environ=xhr_header, status=200)
764 self.app.post(post_url, post_data, extra_environ=xhr_header, status=200)
741 settings = SettingsModel().get_all_settings()
765 settings = SettingsModel().get_all_settings()
742 assert 'rhodecode_%s%s' % (self.SHORT_PATTERN_KEY, uid) not in settings
766 assert self.PATTERN_KEY+old_uid not in settings
767 assert self.DESC_KEY + old_uid not in settings
@@ -1,924 +1,924 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2020 RhodeCode GmbH
3 # Copyright (C) 2010-2020 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 os
21 import os
22 import re
22 import re
23 import hashlib
23 import hashlib
24 import logging
24 import logging
25 import time
25 import time
26 from collections import namedtuple
26 from collections import namedtuple
27 from functools import wraps
27 from functools import wraps
28 import bleach
28 import bleach
29 from pyramid.threadlocal import get_current_request, get_current_registry
29 from pyramid.threadlocal import get_current_request, get_current_registry
30
30
31 from rhodecode.lib import rc_cache
31 from rhodecode.lib import rc_cache
32 from rhodecode.lib.utils2 import (
32 from rhodecode.lib.utils2 import (
33 Optional, AttributeDict, safe_str, remove_prefix, str2bool)
33 Optional, AttributeDict, safe_str, remove_prefix, str2bool)
34 from rhodecode.lib.vcs.backends import base
34 from rhodecode.lib.vcs.backends import base
35 from rhodecode.lib.statsd_client import StatsdClient
35 from rhodecode.lib.statsd_client import StatsdClient
36 from rhodecode.model import BaseModel
36 from rhodecode.model import BaseModel
37 from rhodecode.model.db import (
37 from rhodecode.model.db import (
38 RepoRhodeCodeUi, RepoRhodeCodeSetting, RhodeCodeUi, RhodeCodeSetting)
38 RepoRhodeCodeUi, RepoRhodeCodeSetting, RhodeCodeUi, RhodeCodeSetting)
39 from rhodecode.model.meta import Session
39 from rhodecode.model.meta import Session
40
40
41
41
42 log = logging.getLogger(__name__)
42 log = logging.getLogger(__name__)
43
43
44
44
45 UiSetting = namedtuple(
45 UiSetting = namedtuple(
46 'UiSetting', ['section', 'key', 'value', 'active'])
46 'UiSetting', ['section', 'key', 'value', 'active'])
47
47
48 SOCIAL_PLUGINS_LIST = ['github', 'bitbucket', 'twitter', 'google']
48 SOCIAL_PLUGINS_LIST = ['github', 'bitbucket', 'twitter', 'google']
49
49
50
50
51 class SettingNotFound(Exception):
51 class SettingNotFound(Exception):
52 def __init__(self, setting_id):
52 def __init__(self, setting_id):
53 msg = 'Setting `{}` is not found'.format(setting_id)
53 msg = 'Setting `{}` is not found'.format(setting_id)
54 super(SettingNotFound, self).__init__(msg)
54 super(SettingNotFound, self).__init__(msg)
55
55
56
56
57 class SettingsModel(BaseModel):
57 class SettingsModel(BaseModel):
58 BUILTIN_HOOKS = (
58 BUILTIN_HOOKS = (
59 RhodeCodeUi.HOOK_REPO_SIZE, RhodeCodeUi.HOOK_PUSH,
59 RhodeCodeUi.HOOK_REPO_SIZE, RhodeCodeUi.HOOK_PUSH,
60 RhodeCodeUi.HOOK_PRE_PUSH, RhodeCodeUi.HOOK_PRETX_PUSH,
60 RhodeCodeUi.HOOK_PRE_PUSH, RhodeCodeUi.HOOK_PRETX_PUSH,
61 RhodeCodeUi.HOOK_PULL, RhodeCodeUi.HOOK_PRE_PULL,
61 RhodeCodeUi.HOOK_PULL, RhodeCodeUi.HOOK_PRE_PULL,
62 RhodeCodeUi.HOOK_PUSH_KEY,)
62 RhodeCodeUi.HOOK_PUSH_KEY,)
63 HOOKS_SECTION = 'hooks'
63 HOOKS_SECTION = 'hooks'
64
64
65 def __init__(self, sa=None, repo=None):
65 def __init__(self, sa=None, repo=None):
66 self.repo = repo
66 self.repo = repo
67 self.UiDbModel = RepoRhodeCodeUi if repo else RhodeCodeUi
67 self.UiDbModel = RepoRhodeCodeUi if repo else RhodeCodeUi
68 self.SettingsDbModel = (
68 self.SettingsDbModel = (
69 RepoRhodeCodeSetting if repo else RhodeCodeSetting)
69 RepoRhodeCodeSetting if repo else RhodeCodeSetting)
70 super(SettingsModel, self).__init__(sa)
70 super(SettingsModel, self).__init__(sa)
71
71
72 def get_ui_by_key(self, key):
72 def get_ui_by_key(self, key):
73 q = self.UiDbModel.query()
73 q = self.UiDbModel.query()
74 q = q.filter(self.UiDbModel.ui_key == key)
74 q = q.filter(self.UiDbModel.ui_key == key)
75 q = self._filter_by_repo(RepoRhodeCodeUi, q)
75 q = self._filter_by_repo(RepoRhodeCodeUi, q)
76 return q.scalar()
76 return q.scalar()
77
77
78 def get_ui_by_section(self, section):
78 def get_ui_by_section(self, section):
79 q = self.UiDbModel.query()
79 q = self.UiDbModel.query()
80 q = q.filter(self.UiDbModel.ui_section == section)
80 q = q.filter(self.UiDbModel.ui_section == section)
81 q = self._filter_by_repo(RepoRhodeCodeUi, q)
81 q = self._filter_by_repo(RepoRhodeCodeUi, q)
82 return q.all()
82 return q.all()
83
83
84 def get_ui_by_section_and_key(self, section, key):
84 def get_ui_by_section_and_key(self, section, key):
85 q = self.UiDbModel.query()
85 q = self.UiDbModel.query()
86 q = q.filter(self.UiDbModel.ui_section == section)
86 q = q.filter(self.UiDbModel.ui_section == section)
87 q = q.filter(self.UiDbModel.ui_key == key)
87 q = q.filter(self.UiDbModel.ui_key == key)
88 q = self._filter_by_repo(RepoRhodeCodeUi, q)
88 q = self._filter_by_repo(RepoRhodeCodeUi, q)
89 return q.scalar()
89 return q.scalar()
90
90
91 def get_ui(self, section=None, key=None):
91 def get_ui(self, section=None, key=None):
92 q = self.UiDbModel.query()
92 q = self.UiDbModel.query()
93 q = self._filter_by_repo(RepoRhodeCodeUi, q)
93 q = self._filter_by_repo(RepoRhodeCodeUi, q)
94
94
95 if section:
95 if section:
96 q = q.filter(self.UiDbModel.ui_section == section)
96 q = q.filter(self.UiDbModel.ui_section == section)
97 if key:
97 if key:
98 q = q.filter(self.UiDbModel.ui_key == key)
98 q = q.filter(self.UiDbModel.ui_key == key)
99
99
100 # TODO: mikhail: add caching
100 # TODO: mikhail: add caching
101 result = [
101 result = [
102 UiSetting(
102 UiSetting(
103 section=safe_str(r.ui_section), key=safe_str(r.ui_key),
103 section=safe_str(r.ui_section), key=safe_str(r.ui_key),
104 value=safe_str(r.ui_value), active=r.ui_active
104 value=safe_str(r.ui_value), active=r.ui_active
105 )
105 )
106 for r in q.all()
106 for r in q.all()
107 ]
107 ]
108 return result
108 return result
109
109
110 def get_builtin_hooks(self):
110 def get_builtin_hooks(self):
111 q = self.UiDbModel.query()
111 q = self.UiDbModel.query()
112 q = q.filter(self.UiDbModel.ui_key.in_(self.BUILTIN_HOOKS))
112 q = q.filter(self.UiDbModel.ui_key.in_(self.BUILTIN_HOOKS))
113 return self._get_hooks(q)
113 return self._get_hooks(q)
114
114
115 def get_custom_hooks(self):
115 def get_custom_hooks(self):
116 q = self.UiDbModel.query()
116 q = self.UiDbModel.query()
117 q = q.filter(~self.UiDbModel.ui_key.in_(self.BUILTIN_HOOKS))
117 q = q.filter(~self.UiDbModel.ui_key.in_(self.BUILTIN_HOOKS))
118 return self._get_hooks(q)
118 return self._get_hooks(q)
119
119
120 def create_ui_section_value(self, section, val, key=None, active=True):
120 def create_ui_section_value(self, section, val, key=None, active=True):
121 new_ui = self.UiDbModel()
121 new_ui = self.UiDbModel()
122 new_ui.ui_section = section
122 new_ui.ui_section = section
123 new_ui.ui_value = val
123 new_ui.ui_value = val
124 new_ui.ui_active = active
124 new_ui.ui_active = active
125
125
126 repository_id = ''
126 repository_id = ''
127 if self.repo:
127 if self.repo:
128 repo = self._get_repo(self.repo)
128 repo = self._get_repo(self.repo)
129 repository_id = repo.repo_id
129 repository_id = repo.repo_id
130 new_ui.repository_id = repository_id
130 new_ui.repository_id = repository_id
131
131
132 if not key:
132 if not key:
133 # keys are unique so they need appended info
133 # keys are unique so they need appended info
134 if self.repo:
134 if self.repo:
135 key = hashlib.sha1(
135 key = hashlib.sha1(
136 '{}{}{}'.format(section, val, repository_id)).hexdigest()
136 '{}{}{}'.format(section, val, repository_id)).hexdigest()
137 else:
137 else:
138 key = hashlib.sha1('{}{}'.format(section, val)).hexdigest()
138 key = hashlib.sha1('{}{}'.format(section, val)).hexdigest()
139
139
140 new_ui.ui_key = key
140 new_ui.ui_key = key
141
141
142 Session().add(new_ui)
142 Session().add(new_ui)
143 return new_ui
143 return new_ui
144
144
145 def create_or_update_hook(self, key, value):
145 def create_or_update_hook(self, key, value):
146 ui = (
146 ui = (
147 self.get_ui_by_section_and_key(self.HOOKS_SECTION, key) or
147 self.get_ui_by_section_and_key(self.HOOKS_SECTION, key) or
148 self.UiDbModel())
148 self.UiDbModel())
149 ui.ui_section = self.HOOKS_SECTION
149 ui.ui_section = self.HOOKS_SECTION
150 ui.ui_active = True
150 ui.ui_active = True
151 ui.ui_key = key
151 ui.ui_key = key
152 ui.ui_value = value
152 ui.ui_value = value
153
153
154 if self.repo:
154 if self.repo:
155 repo = self._get_repo(self.repo)
155 repo = self._get_repo(self.repo)
156 repository_id = repo.repo_id
156 repository_id = repo.repo_id
157 ui.repository_id = repository_id
157 ui.repository_id = repository_id
158
158
159 Session().add(ui)
159 Session().add(ui)
160 return ui
160 return ui
161
161
162 def delete_ui(self, id_):
162 def delete_ui(self, id_):
163 ui = self.UiDbModel.get(id_)
163 ui = self.UiDbModel.get(id_)
164 if not ui:
164 if not ui:
165 raise SettingNotFound(id_)
165 raise SettingNotFound(id_)
166 Session().delete(ui)
166 Session().delete(ui)
167
167
168 def get_setting_by_name(self, name):
168 def get_setting_by_name(self, name):
169 q = self._get_settings_query()
169 q = self._get_settings_query()
170 q = q.filter(self.SettingsDbModel.app_settings_name == name)
170 q = q.filter(self.SettingsDbModel.app_settings_name == name)
171 return q.scalar()
171 return q.scalar()
172
172
173 def create_or_update_setting(
173 def create_or_update_setting(
174 self, name, val=Optional(''), type_=Optional('unicode')):
174 self, name, val=Optional(''), type_=Optional('unicode')):
175 """
175 """
176 Creates or updates RhodeCode setting. If updates is triggered it will
176 Creates or updates RhodeCode setting. If updates is triggered it will
177 only update parameters that are explicityl set Optional instance will
177 only update parameters that are explicitly set Optional instance will
178 be skipped
178 be skipped
179
179
180 :param name:
180 :param name:
181 :param val:
181 :param val:
182 :param type_:
182 :param type_:
183 :return:
183 :return:
184 """
184 """
185
185
186 res = self.get_setting_by_name(name)
186 res = self.get_setting_by_name(name)
187 repo = self._get_repo(self.repo) if self.repo else None
187 repo = self._get_repo(self.repo) if self.repo else None
188
188
189 if not res:
189 if not res:
190 val = Optional.extract(val)
190 val = Optional.extract(val)
191 type_ = Optional.extract(type_)
191 type_ = Optional.extract(type_)
192
192
193 args = (
193 args = (
194 (repo.repo_id, name, val, type_)
194 (repo.repo_id, name, val, type_)
195 if repo else (name, val, type_))
195 if repo else (name, val, type_))
196 res = self.SettingsDbModel(*args)
196 res = self.SettingsDbModel(*args)
197
197
198 else:
198 else:
199 if self.repo:
199 if self.repo:
200 res.repository_id = repo.repo_id
200 res.repository_id = repo.repo_id
201
201
202 res.app_settings_name = name
202 res.app_settings_name = name
203 if not isinstance(type_, Optional):
203 if not isinstance(type_, Optional):
204 # update if set
204 # update if set
205 res.app_settings_type = type_
205 res.app_settings_type = type_
206 if not isinstance(val, Optional):
206 if not isinstance(val, Optional):
207 # update if set
207 # update if set
208 res.app_settings_value = val
208 res.app_settings_value = val
209
209
210 Session().add(res)
210 Session().add(res)
211 return res
211 return res
212
212
213 def get_cache_region(self):
213 def get_cache_region(self):
214 repo = self._get_repo(self.repo) if self.repo else None
214 repo = self._get_repo(self.repo) if self.repo else None
215 cache_key = "repo.{}".format(repo.repo_id) if repo else "general_settings"
215 cache_key = "repo.{}".format(repo.repo_id) if repo else "general_settings"
216 cache_namespace_uid = 'cache_settings.{}'.format(cache_key)
216 cache_namespace_uid = 'cache_settings.{}'.format(cache_key)
217 region = rc_cache.get_or_create_region('cache_general', cache_namespace_uid)
217 region = rc_cache.get_or_create_region('cache_general', cache_namespace_uid)
218 return region, cache_key
218 return region, cache_key
219
219
220 def invalidate_settings_cache(self):
220 def invalidate_settings_cache(self):
221 from rhodecode.authentication.base import get_authn_registry
221 from rhodecode.authentication.base import get_authn_registry
222
222
223 region, cache_key = self.get_cache_region()
223 region, cache_key = self.get_cache_region()
224 log.debug('Invalidation cache region %s for cache_key: %s', region, cache_key)
224 log.debug('Invalidation cache region %s for cache_key: %s', region, cache_key)
225 region.invalidate()
225 region.invalidate()
226 registry = get_current_registry()
226 registry = get_current_registry()
227 if registry:
227 if registry:
228 authn_registry = get_authn_registry(registry)
228 authn_registry = get_authn_registry(registry)
229 if authn_registry:
229 if authn_registry:
230 authn_registry.invalidate_plugins_for_auth()
230 authn_registry.invalidate_plugins_for_auth()
231
231
232 def get_all_settings(self, cache=False, from_request=True):
232 def get_all_settings(self, cache=False, from_request=True):
233 # defines if we use GLOBAL, or PER_REPO
233 # defines if we use GLOBAL, or PER_REPO
234 repo = self._get_repo(self.repo) if self.repo else None
234 repo = self._get_repo(self.repo) if self.repo else None
235
235
236 # initially try the requests context, this is the fastest
236 # initially try the requests context, this is the fastest
237 # we only fetch global config
237 # we only fetch global config
238 if from_request:
238 if from_request:
239 request = get_current_request()
239 request = get_current_request()
240
240
241 if request and not repo and hasattr(request, 'call_context') and hasattr(request.call_context, 'rc_config'):
241 if request and not repo and hasattr(request, 'call_context') and hasattr(request.call_context, 'rc_config'):
242 rc_config = request.call_context.rc_config
242 rc_config = request.call_context.rc_config
243 if rc_config:
243 if rc_config:
244 return rc_config
244 return rc_config
245
245
246 region, cache_key = self.get_cache_region()
246 region, cache_key = self.get_cache_region()
247
247
248 @region.conditional_cache_on_arguments(condition=cache)
248 @region.conditional_cache_on_arguments(condition=cache)
249 def _get_all_settings(name, key):
249 def _get_all_settings(name, key):
250 q = self._get_settings_query()
250 q = self._get_settings_query()
251 if not q:
251 if not q:
252 raise Exception('Could not get application settings !')
252 raise Exception('Could not get application settings !')
253
253
254 settings = {
254 settings = {
255 'rhodecode_' + res.app_settings_name: res.app_settings_value
255 'rhodecode_' + res.app_settings_name: res.app_settings_value
256 for res in q
256 for res in q
257 }
257 }
258 return settings
258 return settings
259
259
260 start = time.time()
260 start = time.time()
261 result = _get_all_settings('rhodecode_settings', cache_key)
261 result = _get_all_settings('rhodecode_settings', cache_key)
262 compute_time = time.time() - start
262 compute_time = time.time() - start
263
263
264 statsd = StatsdClient.statsd
264 statsd = StatsdClient.statsd
265 if statsd:
265 if statsd:
266 elapsed_time_ms = round(1000.0 * compute_time) # use ms only
266 elapsed_time_ms = round(1000.0 * compute_time) # use ms only
267 statsd.timing("rhodecode_settings_timing.histogram", elapsed_time_ms,
267 statsd.timing("rhodecode_settings_timing.histogram", elapsed_time_ms,
268 use_decimals=False)
268 use_decimals=False)
269
269
270 log.debug('Fetching app settings for key: %s took: %.4fs', cache_key, compute_time)
270 log.debug('Fetching app settings for key: %s took: %.4fs: cache: %s', cache_key, compute_time, cache)
271
271
272 return result
272 return result
273
273
274 def get_auth_settings(self):
274 def get_auth_settings(self):
275 q = self._get_settings_query()
275 q = self._get_settings_query()
276 q = q.filter(
276 q = q.filter(
277 self.SettingsDbModel.app_settings_name.startswith('auth_'))
277 self.SettingsDbModel.app_settings_name.startswith('auth_'))
278 rows = q.all()
278 rows = q.all()
279 auth_settings = {
279 auth_settings = {
280 row.app_settings_name: row.app_settings_value for row in rows}
280 row.app_settings_name: row.app_settings_value for row in rows}
281 return auth_settings
281 return auth_settings
282
282
283 def get_auth_plugins(self):
283 def get_auth_plugins(self):
284 auth_plugins = self.get_setting_by_name("auth_plugins")
284 auth_plugins = self.get_setting_by_name("auth_plugins")
285 return auth_plugins.app_settings_value
285 return auth_plugins.app_settings_value
286
286
287 def get_default_repo_settings(self, strip_prefix=False):
287 def get_default_repo_settings(self, strip_prefix=False):
288 q = self._get_settings_query()
288 q = self._get_settings_query()
289 q = q.filter(
289 q = q.filter(
290 self.SettingsDbModel.app_settings_name.startswith('default_'))
290 self.SettingsDbModel.app_settings_name.startswith('default_'))
291 rows = q.all()
291 rows = q.all()
292
292
293 result = {}
293 result = {}
294 for row in rows:
294 for row in rows:
295 key = row.app_settings_name
295 key = row.app_settings_name
296 if strip_prefix:
296 if strip_prefix:
297 key = remove_prefix(key, prefix='default_')
297 key = remove_prefix(key, prefix='default_')
298 result.update({key: row.app_settings_value})
298 result.update({key: row.app_settings_value})
299 return result
299 return result
300
300
301 def get_repo(self):
301 def get_repo(self):
302 repo = self._get_repo(self.repo)
302 repo = self._get_repo(self.repo)
303 if not repo:
303 if not repo:
304 raise Exception(
304 raise Exception(
305 'Repository `{}` cannot be found inside the database'.format(
305 'Repository `{}` cannot be found inside the database'.format(
306 self.repo))
306 self.repo))
307 return repo
307 return repo
308
308
309 def _filter_by_repo(self, model, query):
309 def _filter_by_repo(self, model, query):
310 if self.repo:
310 if self.repo:
311 repo = self.get_repo()
311 repo = self.get_repo()
312 query = query.filter(model.repository_id == repo.repo_id)
312 query = query.filter(model.repository_id == repo.repo_id)
313 return query
313 return query
314
314
315 def _get_hooks(self, query):
315 def _get_hooks(self, query):
316 query = query.filter(self.UiDbModel.ui_section == self.HOOKS_SECTION)
316 query = query.filter(self.UiDbModel.ui_section == self.HOOKS_SECTION)
317 query = self._filter_by_repo(RepoRhodeCodeUi, query)
317 query = self._filter_by_repo(RepoRhodeCodeUi, query)
318 return query.all()
318 return query.all()
319
319
320 def _get_settings_query(self):
320 def _get_settings_query(self):
321 q = self.SettingsDbModel.query()
321 q = self.SettingsDbModel.query()
322 return self._filter_by_repo(RepoRhodeCodeSetting, q)
322 return self._filter_by_repo(RepoRhodeCodeSetting, q)
323
323
324 def list_enabled_social_plugins(self, settings):
324 def list_enabled_social_plugins(self, settings):
325 enabled = []
325 enabled = []
326 for plug in SOCIAL_PLUGINS_LIST:
326 for plug in SOCIAL_PLUGINS_LIST:
327 if str2bool(settings.get('rhodecode_auth_{}_enabled'.format(plug)
327 if str2bool(settings.get('rhodecode_auth_{}_enabled'.format(plug)
328 )):
328 )):
329 enabled.append(plug)
329 enabled.append(plug)
330 return enabled
330 return enabled
331
331
332
332
333 def assert_repo_settings(func):
333 def assert_repo_settings(func):
334 @wraps(func)
334 @wraps(func)
335 def _wrapper(self, *args, **kwargs):
335 def _wrapper(self, *args, **kwargs):
336 if not self.repo_settings:
336 if not self.repo_settings:
337 raise Exception('Repository is not specified')
337 raise Exception('Repository is not specified')
338 return func(self, *args, **kwargs)
338 return func(self, *args, **kwargs)
339 return _wrapper
339 return _wrapper
340
340
341
341
342 class IssueTrackerSettingsModel(object):
342 class IssueTrackerSettingsModel(object):
343 INHERIT_SETTINGS = 'inherit_issue_tracker_settings'
343 INHERIT_SETTINGS = 'inherit_issue_tracker_settings'
344 SETTINGS_PREFIX = 'issuetracker_'
344 SETTINGS_PREFIX = 'issuetracker_'
345
345
346 def __init__(self, sa=None, repo=None):
346 def __init__(self, sa=None, repo=None):
347 self.global_settings = SettingsModel(sa=sa)
347 self.global_settings = SettingsModel(sa=sa)
348 self.repo_settings = SettingsModel(sa=sa, repo=repo) if repo else None
348 self.repo_settings = SettingsModel(sa=sa, repo=repo) if repo else None
349
349
350 @property
350 @property
351 def inherit_global_settings(self):
351 def inherit_global_settings(self):
352 if not self.repo_settings:
352 if not self.repo_settings:
353 return True
353 return True
354 setting = self.repo_settings.get_setting_by_name(self.INHERIT_SETTINGS)
354 setting = self.repo_settings.get_setting_by_name(self.INHERIT_SETTINGS)
355 return setting.app_settings_value if setting else True
355 return setting.app_settings_value if setting else True
356
356
357 @inherit_global_settings.setter
357 @inherit_global_settings.setter
358 def inherit_global_settings(self, value):
358 def inherit_global_settings(self, value):
359 if self.repo_settings:
359 if self.repo_settings:
360 settings = self.repo_settings.create_or_update_setting(
360 settings = self.repo_settings.create_or_update_setting(
361 self.INHERIT_SETTINGS, value, type_='bool')
361 self.INHERIT_SETTINGS, value, type_='bool')
362 Session().add(settings)
362 Session().add(settings)
363
363
364 def _get_keyname(self, key, uid, prefix=''):
364 def _get_keyname(self, key, uid, prefix=''):
365 return '{0}{1}{2}_{3}'.format(
365 return '{0}{1}{2}_{3}'.format(
366 prefix, self.SETTINGS_PREFIX, key, uid)
366 prefix, self.SETTINGS_PREFIX, key, uid)
367
367
368 def _make_dict_for_settings(self, qs):
368 def _make_dict_for_settings(self, qs):
369 prefix_match = self._get_keyname('pat', '', 'rhodecode_')
369 prefix_match = self._get_keyname('pat', '', 'rhodecode_')
370
370
371 issuetracker_entries = {}
371 issuetracker_entries = {}
372 # create keys
372 # create keys
373 for k, v in qs.items():
373 for k, v in qs.items():
374 if k.startswith(prefix_match):
374 if k.startswith(prefix_match):
375 uid = k[len(prefix_match):]
375 uid = k[len(prefix_match):]
376 issuetracker_entries[uid] = None
376 issuetracker_entries[uid] = None
377
377
378 def url_cleaner(input_str):
378 def url_cleaner(input_str):
379 input_str = input_str.replace('"', '').replace("'", '')
379 input_str = input_str.replace('"', '').replace("'", '')
380 input_str = bleach.clean(input_str, strip=True)
380 input_str = bleach.clean(input_str, strip=True)
381 return input_str
381 return input_str
382
382
383 # populate
383 # populate
384 for uid in issuetracker_entries:
384 for uid in issuetracker_entries:
385 url_data = qs.get(self._get_keyname('url', uid, 'rhodecode_'))
385 url_data = qs.get(self._get_keyname('url', uid, 'rhodecode_'))
386
386
387 pat = qs.get(self._get_keyname('pat', uid, 'rhodecode_'))
387 pat = qs.get(self._get_keyname('pat', uid, 'rhodecode_'))
388 try:
388 try:
389 pat_compiled = re.compile(r'%s' % pat)
389 pat_compiled = re.compile(r'%s' % pat)
390 except re.error:
390 except re.error:
391 pat_compiled = None
391 pat_compiled = None
392
392
393 issuetracker_entries[uid] = AttributeDict({
393 issuetracker_entries[uid] = AttributeDict({
394 'pat': pat,
394 'pat': pat,
395 'pat_compiled': pat_compiled,
395 'pat_compiled': pat_compiled,
396 'url': url_cleaner(
396 'url': url_cleaner(
397 qs.get(self._get_keyname('url', uid, 'rhodecode_')) or ''),
397 qs.get(self._get_keyname('url', uid, 'rhodecode_')) or ''),
398 'pref': bleach.clean(
398 'pref': bleach.clean(
399 qs.get(self._get_keyname('pref', uid, 'rhodecode_')) or ''),
399 qs.get(self._get_keyname('pref', uid, 'rhodecode_')) or ''),
400 'desc': qs.get(
400 'desc': qs.get(
401 self._get_keyname('desc', uid, 'rhodecode_')),
401 self._get_keyname('desc', uid, 'rhodecode_')),
402 })
402 })
403
403
404 return issuetracker_entries
404 return issuetracker_entries
405
405
406 def get_global_settings(self, cache=False):
406 def get_global_settings(self, cache=False):
407 """
407 """
408 Returns list of global issue tracker settings
408 Returns list of global issue tracker settings
409 """
409 """
410 defaults = self.global_settings.get_all_settings(cache=cache)
410 defaults = self.global_settings.get_all_settings(cache=cache)
411 settings = self._make_dict_for_settings(defaults)
411 settings = self._make_dict_for_settings(defaults)
412 return settings
412 return settings
413
413
414 def get_repo_settings(self, cache=False):
414 def get_repo_settings(self, cache=False):
415 """
415 """
416 Returns list of issue tracker settings per repository
416 Returns list of issue tracker settings per repository
417 """
417 """
418 if not self.repo_settings:
418 if not self.repo_settings:
419 raise Exception('Repository is not specified')
419 raise Exception('Repository is not specified')
420 all_settings = self.repo_settings.get_all_settings(cache=cache)
420 all_settings = self.repo_settings.get_all_settings(cache=cache)
421 settings = self._make_dict_for_settings(all_settings)
421 settings = self._make_dict_for_settings(all_settings)
422 return settings
422 return settings
423
423
424 def get_settings(self, cache=False):
424 def get_settings(self, cache=False):
425 if self.inherit_global_settings:
425 if self.inherit_global_settings:
426 return self.get_global_settings(cache=cache)
426 return self.get_global_settings(cache=cache)
427 else:
427 else:
428 return self.get_repo_settings(cache=cache)
428 return self.get_repo_settings(cache=cache)
429
429
430 def delete_entries(self, uid):
430 def delete_entries(self, uid):
431 if self.repo_settings:
431 if self.repo_settings:
432 all_patterns = self.get_repo_settings()
432 all_patterns = self.get_repo_settings()
433 settings_model = self.repo_settings
433 settings_model = self.repo_settings
434 else:
434 else:
435 all_patterns = self.get_global_settings()
435 all_patterns = self.get_global_settings()
436 settings_model = self.global_settings
436 settings_model = self.global_settings
437 entries = all_patterns.get(uid, [])
437 entries = all_patterns.get(uid, [])
438
438
439 for del_key in entries:
439 for del_key in entries:
440 setting_name = self._get_keyname(del_key, uid)
440 setting_name = self._get_keyname(del_key, uid)
441 entry = settings_model.get_setting_by_name(setting_name)
441 entry = settings_model.get_setting_by_name(setting_name)
442 if entry:
442 if entry:
443 Session().delete(entry)
443 Session().delete(entry)
444
444
445 Session().commit()
445 Session().commit()
446
446
447 def create_or_update_setting(
447 def create_or_update_setting(
448 self, name, val=Optional(''), type_=Optional('unicode')):
448 self, name, val=Optional(''), type_=Optional('unicode')):
449 if self.repo_settings:
449 if self.repo_settings:
450 setting = self.repo_settings.create_or_update_setting(
450 setting = self.repo_settings.create_or_update_setting(
451 name, val, type_)
451 name, val, type_)
452 else:
452 else:
453 setting = self.global_settings.create_or_update_setting(
453 setting = self.global_settings.create_or_update_setting(
454 name, val, type_)
454 name, val, type_)
455 return setting
455 return setting
456
456
457
457
458 class VcsSettingsModel(object):
458 class VcsSettingsModel(object):
459
459
460 INHERIT_SETTINGS = 'inherit_vcs_settings'
460 INHERIT_SETTINGS = 'inherit_vcs_settings'
461 GENERAL_SETTINGS = (
461 GENERAL_SETTINGS = (
462 'use_outdated_comments',
462 'use_outdated_comments',
463 'pr_merge_enabled',
463 'pr_merge_enabled',
464 'hg_use_rebase_for_merging',
464 'hg_use_rebase_for_merging',
465 'hg_close_branch_before_merging',
465 'hg_close_branch_before_merging',
466 'git_use_rebase_for_merging',
466 'git_use_rebase_for_merging',
467 'git_close_branch_before_merging',
467 'git_close_branch_before_merging',
468 'diff_cache',
468 'diff_cache',
469 )
469 )
470
470
471 HOOKS_SETTINGS = (
471 HOOKS_SETTINGS = (
472 ('hooks', 'changegroup.repo_size'),
472 ('hooks', 'changegroup.repo_size'),
473 ('hooks', 'changegroup.push_logger'),
473 ('hooks', 'changegroup.push_logger'),
474 ('hooks', 'outgoing.pull_logger'),
474 ('hooks', 'outgoing.pull_logger'),
475 )
475 )
476 HG_SETTINGS = (
476 HG_SETTINGS = (
477 ('extensions', 'largefiles'),
477 ('extensions', 'largefiles'),
478 ('phases', 'publish'),
478 ('phases', 'publish'),
479 ('extensions', 'evolve'),
479 ('extensions', 'evolve'),
480 ('extensions', 'topic'),
480 ('extensions', 'topic'),
481 ('experimental', 'evolution'),
481 ('experimental', 'evolution'),
482 ('experimental', 'evolution.exchange'),
482 ('experimental', 'evolution.exchange'),
483 )
483 )
484 GIT_SETTINGS = (
484 GIT_SETTINGS = (
485 ('vcs_git_lfs', 'enabled'),
485 ('vcs_git_lfs', 'enabled'),
486 )
486 )
487 GLOBAL_HG_SETTINGS = (
487 GLOBAL_HG_SETTINGS = (
488 ('extensions', 'largefiles'),
488 ('extensions', 'largefiles'),
489 ('largefiles', 'usercache'),
489 ('largefiles', 'usercache'),
490 ('phases', 'publish'),
490 ('phases', 'publish'),
491 ('extensions', 'hgsubversion'),
491 ('extensions', 'hgsubversion'),
492 ('extensions', 'evolve'),
492 ('extensions', 'evolve'),
493 ('extensions', 'topic'),
493 ('extensions', 'topic'),
494 ('experimental', 'evolution'),
494 ('experimental', 'evolution'),
495 ('experimental', 'evolution.exchange'),
495 ('experimental', 'evolution.exchange'),
496 )
496 )
497
497
498 GLOBAL_GIT_SETTINGS = (
498 GLOBAL_GIT_SETTINGS = (
499 ('vcs_git_lfs', 'enabled'),
499 ('vcs_git_lfs', 'enabled'),
500 ('vcs_git_lfs', 'store_location')
500 ('vcs_git_lfs', 'store_location')
501 )
501 )
502
502
503 GLOBAL_SVN_SETTINGS = (
503 GLOBAL_SVN_SETTINGS = (
504 ('vcs_svn_proxy', 'http_requests_enabled'),
504 ('vcs_svn_proxy', 'http_requests_enabled'),
505 ('vcs_svn_proxy', 'http_server_url')
505 ('vcs_svn_proxy', 'http_server_url')
506 )
506 )
507
507
508 SVN_BRANCH_SECTION = 'vcs_svn_branch'
508 SVN_BRANCH_SECTION = 'vcs_svn_branch'
509 SVN_TAG_SECTION = 'vcs_svn_tag'
509 SVN_TAG_SECTION = 'vcs_svn_tag'
510 SSL_SETTING = ('web', 'push_ssl')
510 SSL_SETTING = ('web', 'push_ssl')
511 PATH_SETTING = ('paths', '/')
511 PATH_SETTING = ('paths', '/')
512
512
513 def __init__(self, sa=None, repo=None):
513 def __init__(self, sa=None, repo=None):
514 self.global_settings = SettingsModel(sa=sa)
514 self.global_settings = SettingsModel(sa=sa)
515 self.repo_settings = SettingsModel(sa=sa, repo=repo) if repo else None
515 self.repo_settings = SettingsModel(sa=sa, repo=repo) if repo else None
516 self._ui_settings = (
516 self._ui_settings = (
517 self.HG_SETTINGS + self.GIT_SETTINGS + self.HOOKS_SETTINGS)
517 self.HG_SETTINGS + self.GIT_SETTINGS + self.HOOKS_SETTINGS)
518 self._svn_sections = (self.SVN_BRANCH_SECTION, self.SVN_TAG_SECTION)
518 self._svn_sections = (self.SVN_BRANCH_SECTION, self.SVN_TAG_SECTION)
519
519
520 @property
520 @property
521 @assert_repo_settings
521 @assert_repo_settings
522 def inherit_global_settings(self):
522 def inherit_global_settings(self):
523 setting = self.repo_settings.get_setting_by_name(self.INHERIT_SETTINGS)
523 setting = self.repo_settings.get_setting_by_name(self.INHERIT_SETTINGS)
524 return setting.app_settings_value if setting else True
524 return setting.app_settings_value if setting else True
525
525
526 @inherit_global_settings.setter
526 @inherit_global_settings.setter
527 @assert_repo_settings
527 @assert_repo_settings
528 def inherit_global_settings(self, value):
528 def inherit_global_settings(self, value):
529 self.repo_settings.create_or_update_setting(
529 self.repo_settings.create_or_update_setting(
530 self.INHERIT_SETTINGS, value, type_='bool')
530 self.INHERIT_SETTINGS, value, type_='bool')
531
531
532 def get_global_svn_branch_patterns(self):
532 def get_global_svn_branch_patterns(self):
533 return self.global_settings.get_ui_by_section(self.SVN_BRANCH_SECTION)
533 return self.global_settings.get_ui_by_section(self.SVN_BRANCH_SECTION)
534
534
535 @assert_repo_settings
535 @assert_repo_settings
536 def get_repo_svn_branch_patterns(self):
536 def get_repo_svn_branch_patterns(self):
537 return self.repo_settings.get_ui_by_section(self.SVN_BRANCH_SECTION)
537 return self.repo_settings.get_ui_by_section(self.SVN_BRANCH_SECTION)
538
538
539 def get_global_svn_tag_patterns(self):
539 def get_global_svn_tag_patterns(self):
540 return self.global_settings.get_ui_by_section(self.SVN_TAG_SECTION)
540 return self.global_settings.get_ui_by_section(self.SVN_TAG_SECTION)
541
541
542 @assert_repo_settings
542 @assert_repo_settings
543 def get_repo_svn_tag_patterns(self):
543 def get_repo_svn_tag_patterns(self):
544 return self.repo_settings.get_ui_by_section(self.SVN_TAG_SECTION)
544 return self.repo_settings.get_ui_by_section(self.SVN_TAG_SECTION)
545
545
546 def get_global_settings(self):
546 def get_global_settings(self):
547 return self._collect_all_settings(global_=True)
547 return self._collect_all_settings(global_=True)
548
548
549 @assert_repo_settings
549 @assert_repo_settings
550 def get_repo_settings(self):
550 def get_repo_settings(self):
551 return self._collect_all_settings(global_=False)
551 return self._collect_all_settings(global_=False)
552
552
553 @assert_repo_settings
553 @assert_repo_settings
554 def get_repo_settings_inherited(self):
554 def get_repo_settings_inherited(self):
555 global_settings = self.get_global_settings()
555 global_settings = self.get_global_settings()
556 global_settings.update(self.get_repo_settings())
556 global_settings.update(self.get_repo_settings())
557 return global_settings
557 return global_settings
558
558
559 @assert_repo_settings
559 @assert_repo_settings
560 def create_or_update_repo_settings(
560 def create_or_update_repo_settings(
561 self, data, inherit_global_settings=False):
561 self, data, inherit_global_settings=False):
562 from rhodecode.model.scm import ScmModel
562 from rhodecode.model.scm import ScmModel
563
563
564 self.inherit_global_settings = inherit_global_settings
564 self.inherit_global_settings = inherit_global_settings
565
565
566 repo = self.repo_settings.get_repo()
566 repo = self.repo_settings.get_repo()
567 if not inherit_global_settings:
567 if not inherit_global_settings:
568 if repo.repo_type == 'svn':
568 if repo.repo_type == 'svn':
569 self.create_repo_svn_settings(data)
569 self.create_repo_svn_settings(data)
570 else:
570 else:
571 self.create_or_update_repo_hook_settings(data)
571 self.create_or_update_repo_hook_settings(data)
572 self.create_or_update_repo_pr_settings(data)
572 self.create_or_update_repo_pr_settings(data)
573
573
574 if repo.repo_type == 'hg':
574 if repo.repo_type == 'hg':
575 self.create_or_update_repo_hg_settings(data)
575 self.create_or_update_repo_hg_settings(data)
576
576
577 if repo.repo_type == 'git':
577 if repo.repo_type == 'git':
578 self.create_or_update_repo_git_settings(data)
578 self.create_or_update_repo_git_settings(data)
579
579
580 ScmModel().mark_for_invalidation(repo.repo_name, delete=True)
580 ScmModel().mark_for_invalidation(repo.repo_name, delete=True)
581
581
582 @assert_repo_settings
582 @assert_repo_settings
583 def create_or_update_repo_hook_settings(self, data):
583 def create_or_update_repo_hook_settings(self, data):
584 for section, key in self.HOOKS_SETTINGS:
584 for section, key in self.HOOKS_SETTINGS:
585 data_key = self._get_form_ui_key(section, key)
585 data_key = self._get_form_ui_key(section, key)
586 if data_key not in data:
586 if data_key not in data:
587 raise ValueError(
587 raise ValueError(
588 'The given data does not contain {} key'.format(data_key))
588 'The given data does not contain {} key'.format(data_key))
589
589
590 active = data.get(data_key)
590 active = data.get(data_key)
591 repo_setting = self.repo_settings.get_ui_by_section_and_key(
591 repo_setting = self.repo_settings.get_ui_by_section_and_key(
592 section, key)
592 section, key)
593 if not repo_setting:
593 if not repo_setting:
594 global_setting = self.global_settings.\
594 global_setting = self.global_settings.\
595 get_ui_by_section_and_key(section, key)
595 get_ui_by_section_and_key(section, key)
596 self.repo_settings.create_ui_section_value(
596 self.repo_settings.create_ui_section_value(
597 section, global_setting.ui_value, key=key, active=active)
597 section, global_setting.ui_value, key=key, active=active)
598 else:
598 else:
599 repo_setting.ui_active = active
599 repo_setting.ui_active = active
600 Session().add(repo_setting)
600 Session().add(repo_setting)
601
601
602 def update_global_hook_settings(self, data):
602 def update_global_hook_settings(self, data):
603 for section, key in self.HOOKS_SETTINGS:
603 for section, key in self.HOOKS_SETTINGS:
604 data_key = self._get_form_ui_key(section, key)
604 data_key = self._get_form_ui_key(section, key)
605 if data_key not in data:
605 if data_key not in data:
606 raise ValueError(
606 raise ValueError(
607 'The given data does not contain {} key'.format(data_key))
607 'The given data does not contain {} key'.format(data_key))
608 active = data.get(data_key)
608 active = data.get(data_key)
609 repo_setting = self.global_settings.get_ui_by_section_and_key(
609 repo_setting = self.global_settings.get_ui_by_section_and_key(
610 section, key)
610 section, key)
611 repo_setting.ui_active = active
611 repo_setting.ui_active = active
612 Session().add(repo_setting)
612 Session().add(repo_setting)
613
613
614 @assert_repo_settings
614 @assert_repo_settings
615 def create_or_update_repo_pr_settings(self, data):
615 def create_or_update_repo_pr_settings(self, data):
616 return self._create_or_update_general_settings(
616 return self._create_or_update_general_settings(
617 self.repo_settings, data)
617 self.repo_settings, data)
618
618
619 def create_or_update_global_pr_settings(self, data):
619 def create_or_update_global_pr_settings(self, data):
620 return self._create_or_update_general_settings(
620 return self._create_or_update_general_settings(
621 self.global_settings, data)
621 self.global_settings, data)
622
622
623 @assert_repo_settings
623 @assert_repo_settings
624 def create_repo_svn_settings(self, data):
624 def create_repo_svn_settings(self, data):
625 return self._create_svn_settings(self.repo_settings, data)
625 return self._create_svn_settings(self.repo_settings, data)
626
626
627 def _set_evolution(self, settings, is_enabled):
627 def _set_evolution(self, settings, is_enabled):
628 if is_enabled:
628 if is_enabled:
629 # if evolve is active set evolution=all
629 # if evolve is active set evolution=all
630
630
631 self._create_or_update_ui(
631 self._create_or_update_ui(
632 settings, *('experimental', 'evolution'), value='all',
632 settings, *('experimental', 'evolution'), value='all',
633 active=True)
633 active=True)
634 self._create_or_update_ui(
634 self._create_or_update_ui(
635 settings, *('experimental', 'evolution.exchange'), value='yes',
635 settings, *('experimental', 'evolution.exchange'), value='yes',
636 active=True)
636 active=True)
637 # if evolve is active set topics server support
637 # if evolve is active set topics server support
638 self._create_or_update_ui(
638 self._create_or_update_ui(
639 settings, *('extensions', 'topic'), value='',
639 settings, *('extensions', 'topic'), value='',
640 active=True)
640 active=True)
641
641
642 else:
642 else:
643 self._create_or_update_ui(
643 self._create_or_update_ui(
644 settings, *('experimental', 'evolution'), value='',
644 settings, *('experimental', 'evolution'), value='',
645 active=False)
645 active=False)
646 self._create_or_update_ui(
646 self._create_or_update_ui(
647 settings, *('experimental', 'evolution.exchange'), value='no',
647 settings, *('experimental', 'evolution.exchange'), value='no',
648 active=False)
648 active=False)
649 self._create_or_update_ui(
649 self._create_or_update_ui(
650 settings, *('extensions', 'topic'), value='',
650 settings, *('extensions', 'topic'), value='',
651 active=False)
651 active=False)
652
652
653 @assert_repo_settings
653 @assert_repo_settings
654 def create_or_update_repo_hg_settings(self, data):
654 def create_or_update_repo_hg_settings(self, data):
655 largefiles, phases, evolve = \
655 largefiles, phases, evolve = \
656 self.HG_SETTINGS[:3]
656 self.HG_SETTINGS[:3]
657 largefiles_key, phases_key, evolve_key = \
657 largefiles_key, phases_key, evolve_key = \
658 self._get_settings_keys(self.HG_SETTINGS[:3], data)
658 self._get_settings_keys(self.HG_SETTINGS[:3], data)
659
659
660 self._create_or_update_ui(
660 self._create_or_update_ui(
661 self.repo_settings, *largefiles, value='',
661 self.repo_settings, *largefiles, value='',
662 active=data[largefiles_key])
662 active=data[largefiles_key])
663 self._create_or_update_ui(
663 self._create_or_update_ui(
664 self.repo_settings, *evolve, value='',
664 self.repo_settings, *evolve, value='',
665 active=data[evolve_key])
665 active=data[evolve_key])
666 self._set_evolution(self.repo_settings, is_enabled=data[evolve_key])
666 self._set_evolution(self.repo_settings, is_enabled=data[evolve_key])
667
667
668 self._create_or_update_ui(
668 self._create_or_update_ui(
669 self.repo_settings, *phases, value=safe_str(data[phases_key]))
669 self.repo_settings, *phases, value=safe_str(data[phases_key]))
670
670
671 def create_or_update_global_hg_settings(self, data):
671 def create_or_update_global_hg_settings(self, data):
672 largefiles, largefiles_store, phases, hgsubversion, evolve \
672 largefiles, largefiles_store, phases, hgsubversion, evolve \
673 = self.GLOBAL_HG_SETTINGS[:5]
673 = self.GLOBAL_HG_SETTINGS[:5]
674 largefiles_key, largefiles_store_key, phases_key, subversion_key, evolve_key \
674 largefiles_key, largefiles_store_key, phases_key, subversion_key, evolve_key \
675 = self._get_settings_keys(self.GLOBAL_HG_SETTINGS[:5], data)
675 = self._get_settings_keys(self.GLOBAL_HG_SETTINGS[:5], data)
676
676
677 self._create_or_update_ui(
677 self._create_or_update_ui(
678 self.global_settings, *largefiles, value='',
678 self.global_settings, *largefiles, value='',
679 active=data[largefiles_key])
679 active=data[largefiles_key])
680 self._create_or_update_ui(
680 self._create_or_update_ui(
681 self.global_settings, *largefiles_store, value=data[largefiles_store_key])
681 self.global_settings, *largefiles_store, value=data[largefiles_store_key])
682 self._create_or_update_ui(
682 self._create_or_update_ui(
683 self.global_settings, *phases, value=safe_str(data[phases_key]))
683 self.global_settings, *phases, value=safe_str(data[phases_key]))
684 self._create_or_update_ui(
684 self._create_or_update_ui(
685 self.global_settings, *hgsubversion, active=data[subversion_key])
685 self.global_settings, *hgsubversion, active=data[subversion_key])
686 self._create_or_update_ui(
686 self._create_or_update_ui(
687 self.global_settings, *evolve, value='',
687 self.global_settings, *evolve, value='',
688 active=data[evolve_key])
688 active=data[evolve_key])
689 self._set_evolution(self.global_settings, is_enabled=data[evolve_key])
689 self._set_evolution(self.global_settings, is_enabled=data[evolve_key])
690
690
691 def create_or_update_repo_git_settings(self, data):
691 def create_or_update_repo_git_settings(self, data):
692 # NOTE(marcink): # comma makes unpack work properly
692 # NOTE(marcink): # comma makes unpack work properly
693 lfs_enabled, \
693 lfs_enabled, \
694 = self.GIT_SETTINGS
694 = self.GIT_SETTINGS
695
695
696 lfs_enabled_key, \
696 lfs_enabled_key, \
697 = self._get_settings_keys(self.GIT_SETTINGS, data)
697 = self._get_settings_keys(self.GIT_SETTINGS, data)
698
698
699 self._create_or_update_ui(
699 self._create_or_update_ui(
700 self.repo_settings, *lfs_enabled, value=data[lfs_enabled_key],
700 self.repo_settings, *lfs_enabled, value=data[lfs_enabled_key],
701 active=data[lfs_enabled_key])
701 active=data[lfs_enabled_key])
702
702
703 def create_or_update_global_git_settings(self, data):
703 def create_or_update_global_git_settings(self, data):
704 lfs_enabled, lfs_store_location \
704 lfs_enabled, lfs_store_location \
705 = self.GLOBAL_GIT_SETTINGS
705 = self.GLOBAL_GIT_SETTINGS
706 lfs_enabled_key, lfs_store_location_key \
706 lfs_enabled_key, lfs_store_location_key \
707 = self._get_settings_keys(self.GLOBAL_GIT_SETTINGS, data)
707 = self._get_settings_keys(self.GLOBAL_GIT_SETTINGS, data)
708
708
709 self._create_or_update_ui(
709 self._create_or_update_ui(
710 self.global_settings, *lfs_enabled, value=data[lfs_enabled_key],
710 self.global_settings, *lfs_enabled, value=data[lfs_enabled_key],
711 active=data[lfs_enabled_key])
711 active=data[lfs_enabled_key])
712 self._create_or_update_ui(
712 self._create_or_update_ui(
713 self.global_settings, *lfs_store_location,
713 self.global_settings, *lfs_store_location,
714 value=data[lfs_store_location_key])
714 value=data[lfs_store_location_key])
715
715
716 def create_or_update_global_svn_settings(self, data):
716 def create_or_update_global_svn_settings(self, data):
717 # branch/tags patterns
717 # branch/tags patterns
718 self._create_svn_settings(self.global_settings, data)
718 self._create_svn_settings(self.global_settings, data)
719
719
720 http_requests_enabled, http_server_url = self.GLOBAL_SVN_SETTINGS
720 http_requests_enabled, http_server_url = self.GLOBAL_SVN_SETTINGS
721 http_requests_enabled_key, http_server_url_key = self._get_settings_keys(
721 http_requests_enabled_key, http_server_url_key = self._get_settings_keys(
722 self.GLOBAL_SVN_SETTINGS, data)
722 self.GLOBAL_SVN_SETTINGS, data)
723
723
724 self._create_or_update_ui(
724 self._create_or_update_ui(
725 self.global_settings, *http_requests_enabled,
725 self.global_settings, *http_requests_enabled,
726 value=safe_str(data[http_requests_enabled_key]))
726 value=safe_str(data[http_requests_enabled_key]))
727 self._create_or_update_ui(
727 self._create_or_update_ui(
728 self.global_settings, *http_server_url,
728 self.global_settings, *http_server_url,
729 value=data[http_server_url_key])
729 value=data[http_server_url_key])
730
730
731 def update_global_ssl_setting(self, value):
731 def update_global_ssl_setting(self, value):
732 self._create_or_update_ui(
732 self._create_or_update_ui(
733 self.global_settings, *self.SSL_SETTING, value=value)
733 self.global_settings, *self.SSL_SETTING, value=value)
734
734
735 def update_global_path_setting(self, value):
735 def update_global_path_setting(self, value):
736 self._create_or_update_ui(
736 self._create_or_update_ui(
737 self.global_settings, *self.PATH_SETTING, value=value)
737 self.global_settings, *self.PATH_SETTING, value=value)
738
738
739 @assert_repo_settings
739 @assert_repo_settings
740 def delete_repo_svn_pattern(self, id_):
740 def delete_repo_svn_pattern(self, id_):
741 ui = self.repo_settings.UiDbModel.get(id_)
741 ui = self.repo_settings.UiDbModel.get(id_)
742 if ui and ui.repository.repo_name == self.repo_settings.repo:
742 if ui and ui.repository.repo_name == self.repo_settings.repo:
743 # only delete if it's the same repo as initialized settings
743 # only delete if it's the same repo as initialized settings
744 self.repo_settings.delete_ui(id_)
744 self.repo_settings.delete_ui(id_)
745 else:
745 else:
746 # raise error as if we wouldn't find this option
746 # raise error as if we wouldn't find this option
747 self.repo_settings.delete_ui(-1)
747 self.repo_settings.delete_ui(-1)
748
748
749 def delete_global_svn_pattern(self, id_):
749 def delete_global_svn_pattern(self, id_):
750 self.global_settings.delete_ui(id_)
750 self.global_settings.delete_ui(id_)
751
751
752 @assert_repo_settings
752 @assert_repo_settings
753 def get_repo_ui_settings(self, section=None, key=None):
753 def get_repo_ui_settings(self, section=None, key=None):
754 global_uis = self.global_settings.get_ui(section, key)
754 global_uis = self.global_settings.get_ui(section, key)
755 repo_uis = self.repo_settings.get_ui(section, key)
755 repo_uis = self.repo_settings.get_ui(section, key)
756
756
757 filtered_repo_uis = self._filter_ui_settings(repo_uis)
757 filtered_repo_uis = self._filter_ui_settings(repo_uis)
758 filtered_repo_uis_keys = [
758 filtered_repo_uis_keys = [
759 (s.section, s.key) for s in filtered_repo_uis]
759 (s.section, s.key) for s in filtered_repo_uis]
760
760
761 def _is_global_ui_filtered(ui):
761 def _is_global_ui_filtered(ui):
762 return (
762 return (
763 (ui.section, ui.key) in filtered_repo_uis_keys
763 (ui.section, ui.key) in filtered_repo_uis_keys
764 or ui.section in self._svn_sections)
764 or ui.section in self._svn_sections)
765
765
766 filtered_global_uis = [
766 filtered_global_uis = [
767 ui for ui in global_uis if not _is_global_ui_filtered(ui)]
767 ui for ui in global_uis if not _is_global_ui_filtered(ui)]
768
768
769 return filtered_global_uis + filtered_repo_uis
769 return filtered_global_uis + filtered_repo_uis
770
770
771 def get_global_ui_settings(self, section=None, key=None):
771 def get_global_ui_settings(self, section=None, key=None):
772 return self.global_settings.get_ui(section, key)
772 return self.global_settings.get_ui(section, key)
773
773
774 def get_ui_settings_as_config_obj(self, section=None, key=None):
774 def get_ui_settings_as_config_obj(self, section=None, key=None):
775 config = base.Config()
775 config = base.Config()
776
776
777 ui_settings = self.get_ui_settings(section=section, key=key)
777 ui_settings = self.get_ui_settings(section=section, key=key)
778
778
779 for entry in ui_settings:
779 for entry in ui_settings:
780 config.set(entry.section, entry.key, entry.value)
780 config.set(entry.section, entry.key, entry.value)
781
781
782 return config
782 return config
783
783
784 def get_ui_settings(self, section=None, key=None):
784 def get_ui_settings(self, section=None, key=None):
785 if not self.repo_settings or self.inherit_global_settings:
785 if not self.repo_settings or self.inherit_global_settings:
786 return self.get_global_ui_settings(section, key)
786 return self.get_global_ui_settings(section, key)
787 else:
787 else:
788 return self.get_repo_ui_settings(section, key)
788 return self.get_repo_ui_settings(section, key)
789
789
790 def get_svn_patterns(self, section=None):
790 def get_svn_patterns(self, section=None):
791 if not self.repo_settings:
791 if not self.repo_settings:
792 return self.get_global_ui_settings(section)
792 return self.get_global_ui_settings(section)
793 else:
793 else:
794 return self.get_repo_ui_settings(section)
794 return self.get_repo_ui_settings(section)
795
795
796 @assert_repo_settings
796 @assert_repo_settings
797 def get_repo_general_settings(self):
797 def get_repo_general_settings(self):
798 global_settings = self.global_settings.get_all_settings()
798 global_settings = self.global_settings.get_all_settings()
799 repo_settings = self.repo_settings.get_all_settings()
799 repo_settings = self.repo_settings.get_all_settings()
800 filtered_repo_settings = self._filter_general_settings(repo_settings)
800 filtered_repo_settings = self._filter_general_settings(repo_settings)
801 global_settings.update(filtered_repo_settings)
801 global_settings.update(filtered_repo_settings)
802 return global_settings
802 return global_settings
803
803
804 def get_global_general_settings(self):
804 def get_global_general_settings(self):
805 return self.global_settings.get_all_settings()
805 return self.global_settings.get_all_settings()
806
806
807 def get_general_settings(self):
807 def get_general_settings(self):
808 if not self.repo_settings or self.inherit_global_settings:
808 if not self.repo_settings or self.inherit_global_settings:
809 return self.get_global_general_settings()
809 return self.get_global_general_settings()
810 else:
810 else:
811 return self.get_repo_general_settings()
811 return self.get_repo_general_settings()
812
812
813 def get_repos_location(self):
813 def get_repos_location(self):
814 return self.global_settings.get_ui_by_key('/').ui_value
814 return self.global_settings.get_ui_by_key('/').ui_value
815
815
816 def _filter_ui_settings(self, settings):
816 def _filter_ui_settings(self, settings):
817 filtered_settings = [
817 filtered_settings = [
818 s for s in settings if self._should_keep_setting(s)]
818 s for s in settings if self._should_keep_setting(s)]
819 return filtered_settings
819 return filtered_settings
820
820
821 def _should_keep_setting(self, setting):
821 def _should_keep_setting(self, setting):
822 keep = (
822 keep = (
823 (setting.section, setting.key) in self._ui_settings or
823 (setting.section, setting.key) in self._ui_settings or
824 setting.section in self._svn_sections)
824 setting.section in self._svn_sections)
825 return keep
825 return keep
826
826
827 def _filter_general_settings(self, settings):
827 def _filter_general_settings(self, settings):
828 keys = ['rhodecode_{}'.format(key) for key in self.GENERAL_SETTINGS]
828 keys = ['rhodecode_{}'.format(key) for key in self.GENERAL_SETTINGS]
829 return {
829 return {
830 k: settings[k]
830 k: settings[k]
831 for k in settings if k in keys}
831 for k in settings if k in keys}
832
832
833 def _collect_all_settings(self, global_=False):
833 def _collect_all_settings(self, global_=False):
834 settings = self.global_settings if global_ else self.repo_settings
834 settings = self.global_settings if global_ else self.repo_settings
835 result = {}
835 result = {}
836
836
837 for section, key in self._ui_settings:
837 for section, key in self._ui_settings:
838 ui = settings.get_ui_by_section_and_key(section, key)
838 ui = settings.get_ui_by_section_and_key(section, key)
839 result_key = self._get_form_ui_key(section, key)
839 result_key = self._get_form_ui_key(section, key)
840
840
841 if ui:
841 if ui:
842 if section in ('hooks', 'extensions'):
842 if section in ('hooks', 'extensions'):
843 result[result_key] = ui.ui_active
843 result[result_key] = ui.ui_active
844 elif result_key in ['vcs_git_lfs_enabled']:
844 elif result_key in ['vcs_git_lfs_enabled']:
845 result[result_key] = ui.ui_active
845 result[result_key] = ui.ui_active
846 else:
846 else:
847 result[result_key] = ui.ui_value
847 result[result_key] = ui.ui_value
848
848
849 for name in self.GENERAL_SETTINGS:
849 for name in self.GENERAL_SETTINGS:
850 setting = settings.get_setting_by_name(name)
850 setting = settings.get_setting_by_name(name)
851 if setting:
851 if setting:
852 result_key = 'rhodecode_{}'.format(name)
852 result_key = 'rhodecode_{}'.format(name)
853 result[result_key] = setting.app_settings_value
853 result[result_key] = setting.app_settings_value
854
854
855 return result
855 return result
856
856
857 def _get_form_ui_key(self, section, key):
857 def _get_form_ui_key(self, section, key):
858 return '{section}_{key}'.format(
858 return '{section}_{key}'.format(
859 section=section, key=key.replace('.', '_'))
859 section=section, key=key.replace('.', '_'))
860
860
861 def _create_or_update_ui(
861 def _create_or_update_ui(
862 self, settings, section, key, value=None, active=None):
862 self, settings, section, key, value=None, active=None):
863 ui = settings.get_ui_by_section_and_key(section, key)
863 ui = settings.get_ui_by_section_and_key(section, key)
864 if not ui:
864 if not ui:
865 active = True if active is None else active
865 active = True if active is None else active
866 settings.create_ui_section_value(
866 settings.create_ui_section_value(
867 section, value, key=key, active=active)
867 section, value, key=key, active=active)
868 else:
868 else:
869 if active is not None:
869 if active is not None:
870 ui.ui_active = active
870 ui.ui_active = active
871 if value is not None:
871 if value is not None:
872 ui.ui_value = value
872 ui.ui_value = value
873 Session().add(ui)
873 Session().add(ui)
874
874
875 def _create_svn_settings(self, settings, data):
875 def _create_svn_settings(self, settings, data):
876 svn_settings = {
876 svn_settings = {
877 'new_svn_branch': self.SVN_BRANCH_SECTION,
877 'new_svn_branch': self.SVN_BRANCH_SECTION,
878 'new_svn_tag': self.SVN_TAG_SECTION
878 'new_svn_tag': self.SVN_TAG_SECTION
879 }
879 }
880 for key in svn_settings:
880 for key in svn_settings:
881 if data.get(key):
881 if data.get(key):
882 settings.create_ui_section_value(svn_settings[key], data[key])
882 settings.create_ui_section_value(svn_settings[key], data[key])
883
883
884 def _create_or_update_general_settings(self, settings, data):
884 def _create_or_update_general_settings(self, settings, data):
885 for name in self.GENERAL_SETTINGS:
885 for name in self.GENERAL_SETTINGS:
886 data_key = 'rhodecode_{}'.format(name)
886 data_key = 'rhodecode_{}'.format(name)
887 if data_key not in data:
887 if data_key not in data:
888 raise ValueError(
888 raise ValueError(
889 'The given data does not contain {} key'.format(data_key))
889 'The given data does not contain {} key'.format(data_key))
890 setting = settings.create_or_update_setting(
890 setting = settings.create_or_update_setting(
891 name, data[data_key], 'bool')
891 name, data[data_key], 'bool')
892 Session().add(setting)
892 Session().add(setting)
893
893
894 def _get_settings_keys(self, settings, data):
894 def _get_settings_keys(self, settings, data):
895 data_keys = [self._get_form_ui_key(*s) for s in settings]
895 data_keys = [self._get_form_ui_key(*s) for s in settings]
896 for data_key in data_keys:
896 for data_key in data_keys:
897 if data_key not in data:
897 if data_key not in data:
898 raise ValueError(
898 raise ValueError(
899 'The given data does not contain {} key'.format(data_key))
899 'The given data does not contain {} key'.format(data_key))
900 return data_keys
900 return data_keys
901
901
902 def create_largeobjects_dirs_if_needed(self, repo_store_path):
902 def create_largeobjects_dirs_if_needed(self, repo_store_path):
903 """
903 """
904 This is subscribed to the `pyramid.events.ApplicationCreated` event. It
904 This is subscribed to the `pyramid.events.ApplicationCreated` event. It
905 does a repository scan if enabled in the settings.
905 does a repository scan if enabled in the settings.
906 """
906 """
907
907
908 from rhodecode.lib.vcs.backends.hg import largefiles_store
908 from rhodecode.lib.vcs.backends.hg import largefiles_store
909 from rhodecode.lib.vcs.backends.git import lfs_store
909 from rhodecode.lib.vcs.backends.git import lfs_store
910
910
911 paths = [
911 paths = [
912 largefiles_store(repo_store_path),
912 largefiles_store(repo_store_path),
913 lfs_store(repo_store_path)]
913 lfs_store(repo_store_path)]
914
914
915 for path in paths:
915 for path in paths:
916 if os.path.isdir(path):
916 if os.path.isdir(path):
917 continue
917 continue
918 if os.path.isfile(path):
918 if os.path.isfile(path):
919 continue
919 continue
920 # not a file nor dir, we try to create it
920 # not a file nor dir, we try to create it
921 try:
921 try:
922 os.makedirs(path)
922 os.makedirs(path)
923 except Exception:
923 except Exception:
924 log.warning('Failed to create largefiles dir:%s', path)
924 log.warning('Failed to create largefiles dir:%s', path)
General Comments 0
You need to be logged in to leave comments. Login now