##// END OF EJS Templates
ux: fix bug where saving empty issue tracker form was causing...
dan -
r1000:dbebf42b default
parent child Browse files
Show More
@@ -1,796 +1,797 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2016 RhodeCode GmbH
3 # Copyright (C) 2010-2016 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21
21
22 """
22 """
23 settings controller for rhodecode admin
23 settings controller for rhodecode admin
24 """
24 """
25
25
26 import collections
26 import collections
27 import logging
27 import logging
28 import urllib2
28 import urllib2
29
29
30 import datetime
30 import datetime
31 import formencode
31 import formencode
32 from formencode import htmlfill
32 from formencode import htmlfill
33 import packaging.version
33 import packaging.version
34 from pylons import request, tmpl_context as c, url, config
34 from pylons import request, tmpl_context as c, url, config
35 from pylons.controllers.util import redirect
35 from pylons.controllers.util import redirect
36 from pylons.i18n.translation import _, lazy_ugettext
36 from pylons.i18n.translation import _, lazy_ugettext
37 from webob.exc import HTTPBadRequest
37 from webob.exc import HTTPBadRequest
38
38
39 import rhodecode
39 import rhodecode
40 from rhodecode.admin.navigation import navigation_list
40 from rhodecode.admin.navigation import navigation_list
41 from rhodecode.lib import auth
41 from rhodecode.lib import auth
42 from rhodecode.lib import helpers as h
42 from rhodecode.lib import helpers as h
43 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
43 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
44 from rhodecode.lib.base import BaseController, render
44 from rhodecode.lib.base import BaseController, render
45 from rhodecode.lib.celerylib import tasks, run_task
45 from rhodecode.lib.celerylib import tasks, run_task
46 from rhodecode.lib.utils import repo2db_mapper
46 from rhodecode.lib.utils import repo2db_mapper
47 from rhodecode.lib.utils2 import (
47 from rhodecode.lib.utils2 import (
48 str2bool, safe_unicode, AttributeDict, safe_int)
48 str2bool, safe_unicode, AttributeDict, safe_int)
49 from rhodecode.lib.compat import OrderedDict
49 from rhodecode.lib.compat import OrderedDict
50 from rhodecode.lib.ext_json import json
50 from rhodecode.lib.ext_json import json
51 from rhodecode.lib.utils import jsonify
51 from rhodecode.lib.utils import jsonify
52
52
53 from rhodecode.model.db import RhodeCodeUi, Repository
53 from rhodecode.model.db import RhodeCodeUi, Repository
54 from rhodecode.model.forms import ApplicationSettingsForm, \
54 from rhodecode.model.forms import ApplicationSettingsForm, \
55 ApplicationUiSettingsForm, ApplicationVisualisationForm, \
55 ApplicationUiSettingsForm, ApplicationVisualisationForm, \
56 LabsSettingsForm, IssueTrackerPatternsForm
56 LabsSettingsForm, IssueTrackerPatternsForm
57
57
58 from rhodecode.model.scm import ScmModel
58 from rhodecode.model.scm import ScmModel
59 from rhodecode.model.notification import EmailNotificationModel
59 from rhodecode.model.notification import EmailNotificationModel
60 from rhodecode.model.meta import Session
60 from rhodecode.model.meta import Session
61 from rhodecode.model.settings import (
61 from rhodecode.model.settings import (
62 IssueTrackerSettingsModel, VcsSettingsModel, SettingNotFound,
62 IssueTrackerSettingsModel, VcsSettingsModel, SettingNotFound,
63 SettingsModel)
63 SettingsModel)
64
64
65 from rhodecode.model.supervisor import SupervisorModel, SUPERVISOR_MASTER
65 from rhodecode.model.supervisor import SupervisorModel, SUPERVISOR_MASTER
66
66
67
67
68 log = logging.getLogger(__name__)
68 log = logging.getLogger(__name__)
69
69
70
70
71 class SettingsController(BaseController):
71 class SettingsController(BaseController):
72 """REST Controller styled on the Atom Publishing Protocol"""
72 """REST Controller styled on the Atom Publishing Protocol"""
73 # To properly map this controller, ensure your config/routing.py
73 # To properly map this controller, ensure your config/routing.py
74 # file has a resource setup:
74 # file has a resource setup:
75 # map.resource('setting', 'settings', controller='admin/settings',
75 # map.resource('setting', 'settings', controller='admin/settings',
76 # path_prefix='/admin', name_prefix='admin_')
76 # path_prefix='/admin', name_prefix='admin_')
77
77
78 @LoginRequired()
78 @LoginRequired()
79 def __before__(self):
79 def __before__(self):
80 super(SettingsController, self).__before__()
80 super(SettingsController, self).__before__()
81 c.labs_active = str2bool(
81 c.labs_active = str2bool(
82 rhodecode.CONFIG.get('labs_settings_active', 'true'))
82 rhodecode.CONFIG.get('labs_settings_active', 'true'))
83 c.navlist = navigation_list(request)
83 c.navlist = navigation_list(request)
84
84
85 def _get_hg_ui_settings(self):
85 def _get_hg_ui_settings(self):
86 ret = RhodeCodeUi.query().all()
86 ret = RhodeCodeUi.query().all()
87
87
88 if not ret:
88 if not ret:
89 raise Exception('Could not get application ui settings !')
89 raise Exception('Could not get application ui settings !')
90 settings = {}
90 settings = {}
91 for each in ret:
91 for each in ret:
92 k = each.ui_key
92 k = each.ui_key
93 v = each.ui_value
93 v = each.ui_value
94 if k == '/':
94 if k == '/':
95 k = 'root_path'
95 k = 'root_path'
96
96
97 if k in ['push_ssl', 'publish']:
97 if k in ['push_ssl', 'publish']:
98 v = str2bool(v)
98 v = str2bool(v)
99
99
100 if k.find('.') != -1:
100 if k.find('.') != -1:
101 k = k.replace('.', '_')
101 k = k.replace('.', '_')
102
102
103 if each.ui_section in ['hooks', 'extensions']:
103 if each.ui_section in ['hooks', 'extensions']:
104 v = each.ui_active
104 v = each.ui_active
105
105
106 settings[each.ui_section + '_' + k] = v
106 settings[each.ui_section + '_' + k] = v
107 return settings
107 return settings
108
108
109 @HasPermissionAllDecorator('hg.admin')
109 @HasPermissionAllDecorator('hg.admin')
110 @auth.CSRFRequired()
110 @auth.CSRFRequired()
111 @jsonify
111 @jsonify
112 def delete_svn_pattern(self):
112 def delete_svn_pattern(self):
113 if not request.is_xhr:
113 if not request.is_xhr:
114 raise HTTPBadRequest()
114 raise HTTPBadRequest()
115
115
116 delete_pattern_id = request.POST.get('delete_svn_pattern')
116 delete_pattern_id = request.POST.get('delete_svn_pattern')
117 model = VcsSettingsModel()
117 model = VcsSettingsModel()
118 try:
118 try:
119 model.delete_global_svn_pattern(delete_pattern_id)
119 model.delete_global_svn_pattern(delete_pattern_id)
120 except SettingNotFound:
120 except SettingNotFound:
121 raise HTTPBadRequest()
121 raise HTTPBadRequest()
122
122
123 Session().commit()
123 Session().commit()
124 return True
124 return True
125
125
126 @HasPermissionAllDecorator('hg.admin')
126 @HasPermissionAllDecorator('hg.admin')
127 @auth.CSRFRequired()
127 @auth.CSRFRequired()
128 def settings_vcs_update(self):
128 def settings_vcs_update(self):
129 """POST /admin/settings: All items in the collection"""
129 """POST /admin/settings: All items in the collection"""
130 # url('admin_settings_vcs')
130 # url('admin_settings_vcs')
131 c.active = 'vcs'
131 c.active = 'vcs'
132
132
133 model = VcsSettingsModel()
133 model = VcsSettingsModel()
134 c.svn_branch_patterns = model.get_global_svn_branch_patterns()
134 c.svn_branch_patterns = model.get_global_svn_branch_patterns()
135 c.svn_tag_patterns = model.get_global_svn_tag_patterns()
135 c.svn_tag_patterns = model.get_global_svn_tag_patterns()
136
136
137 application_form = ApplicationUiSettingsForm()()
137 application_form = ApplicationUiSettingsForm()()
138
138
139 try:
139 try:
140 form_result = application_form.to_python(dict(request.POST))
140 form_result = application_form.to_python(dict(request.POST))
141 except formencode.Invalid as errors:
141 except formencode.Invalid as errors:
142 h.flash(
142 h.flash(
143 _("Some form inputs contain invalid data."),
143 _("Some form inputs contain invalid data."),
144 category='error')
144 category='error')
145 return htmlfill.render(
145 return htmlfill.render(
146 render('admin/settings/settings.html'),
146 render('admin/settings/settings.html'),
147 defaults=errors.value,
147 defaults=errors.value,
148 errors=errors.error_dict or {},
148 errors=errors.error_dict or {},
149 prefix_error=False,
149 prefix_error=False,
150 encoding="UTF-8",
150 encoding="UTF-8",
151 force_defaults=False
151 force_defaults=False
152 )
152 )
153
153
154 try:
154 try:
155 if c.visual.allow_repo_location_change:
155 if c.visual.allow_repo_location_change:
156 model.update_global_path_setting(
156 model.update_global_path_setting(
157 form_result['paths_root_path'])
157 form_result['paths_root_path'])
158
158
159 model.update_global_ssl_setting(form_result['web_push_ssl'])
159 model.update_global_ssl_setting(form_result['web_push_ssl'])
160 model.update_global_hook_settings(form_result)
160 model.update_global_hook_settings(form_result)
161
161
162 model.create_or_update_global_svn_settings(form_result)
162 model.create_or_update_global_svn_settings(form_result)
163 model.create_or_update_global_hg_settings(form_result)
163 model.create_or_update_global_hg_settings(form_result)
164 model.create_or_update_global_pr_settings(form_result)
164 model.create_or_update_global_pr_settings(form_result)
165 except Exception:
165 except Exception:
166 log.exception("Exception while updating settings")
166 log.exception("Exception while updating settings")
167 h.flash(_('Error occurred during updating '
167 h.flash(_('Error occurred during updating '
168 'application settings'), category='error')
168 'application settings'), category='error')
169 else:
169 else:
170 Session().commit()
170 Session().commit()
171 h.flash(_('Updated VCS settings'), category='success')
171 h.flash(_('Updated VCS settings'), category='success')
172 return redirect(url('admin_settings_vcs'))
172 return redirect(url('admin_settings_vcs'))
173
173
174 return htmlfill.render(
174 return htmlfill.render(
175 render('admin/settings/settings.html'),
175 render('admin/settings/settings.html'),
176 defaults=self._form_defaults(),
176 defaults=self._form_defaults(),
177 encoding="UTF-8",
177 encoding="UTF-8",
178 force_defaults=False)
178 force_defaults=False)
179
179
180 @HasPermissionAllDecorator('hg.admin')
180 @HasPermissionAllDecorator('hg.admin')
181 def settings_vcs(self):
181 def settings_vcs(self):
182 """GET /admin/settings: All items in the collection"""
182 """GET /admin/settings: All items in the collection"""
183 # url('admin_settings_vcs')
183 # url('admin_settings_vcs')
184 c.active = 'vcs'
184 c.active = 'vcs'
185 model = VcsSettingsModel()
185 model = VcsSettingsModel()
186 c.svn_branch_patterns = model.get_global_svn_branch_patterns()
186 c.svn_branch_patterns = model.get_global_svn_branch_patterns()
187 c.svn_tag_patterns = model.get_global_svn_tag_patterns()
187 c.svn_tag_patterns = model.get_global_svn_tag_patterns()
188
188
189 return htmlfill.render(
189 return htmlfill.render(
190 render('admin/settings/settings.html'),
190 render('admin/settings/settings.html'),
191 defaults=self._form_defaults(),
191 defaults=self._form_defaults(),
192 encoding="UTF-8",
192 encoding="UTF-8",
193 force_defaults=False)
193 force_defaults=False)
194
194
195 @HasPermissionAllDecorator('hg.admin')
195 @HasPermissionAllDecorator('hg.admin')
196 @auth.CSRFRequired()
196 @auth.CSRFRequired()
197 def settings_mapping_update(self):
197 def settings_mapping_update(self):
198 """POST /admin/settings/mapping: All items in the collection"""
198 """POST /admin/settings/mapping: All items in the collection"""
199 # url('admin_settings_mapping')
199 # url('admin_settings_mapping')
200 c.active = 'mapping'
200 c.active = 'mapping'
201 rm_obsolete = request.POST.get('destroy', False)
201 rm_obsolete = request.POST.get('destroy', False)
202 invalidate_cache = request.POST.get('invalidate', False)
202 invalidate_cache = request.POST.get('invalidate', False)
203 log.debug(
203 log.debug(
204 'rescanning repo location with destroy obsolete=%s', rm_obsolete)
204 'rescanning repo location with destroy obsolete=%s', rm_obsolete)
205
205
206 if invalidate_cache:
206 if invalidate_cache:
207 log.debug('invalidating all repositories cache')
207 log.debug('invalidating all repositories cache')
208 for repo in Repository.get_all():
208 for repo in Repository.get_all():
209 ScmModel().mark_for_invalidation(repo.repo_name, delete=True)
209 ScmModel().mark_for_invalidation(repo.repo_name, delete=True)
210
210
211 filesystem_repos = ScmModel().repo_scan()
211 filesystem_repos = ScmModel().repo_scan()
212 added, removed = repo2db_mapper(filesystem_repos, rm_obsolete)
212 added, removed = repo2db_mapper(filesystem_repos, rm_obsolete)
213 _repr = lambda l: ', '.join(map(safe_unicode, l)) or '-'
213 _repr = lambda l: ', '.join(map(safe_unicode, l)) or '-'
214 h.flash(_('Repositories successfully '
214 h.flash(_('Repositories successfully '
215 'rescanned added: %s ; removed: %s') %
215 'rescanned added: %s ; removed: %s') %
216 (_repr(added), _repr(removed)),
216 (_repr(added), _repr(removed)),
217 category='success')
217 category='success')
218 return redirect(url('admin_settings_mapping'))
218 return redirect(url('admin_settings_mapping'))
219
219
220 @HasPermissionAllDecorator('hg.admin')
220 @HasPermissionAllDecorator('hg.admin')
221 def settings_mapping(self):
221 def settings_mapping(self):
222 """GET /admin/settings/mapping: All items in the collection"""
222 """GET /admin/settings/mapping: All items in the collection"""
223 # url('admin_settings_mapping')
223 # url('admin_settings_mapping')
224 c.active = 'mapping'
224 c.active = 'mapping'
225
225
226 return htmlfill.render(
226 return htmlfill.render(
227 render('admin/settings/settings.html'),
227 render('admin/settings/settings.html'),
228 defaults=self._form_defaults(),
228 defaults=self._form_defaults(),
229 encoding="UTF-8",
229 encoding="UTF-8",
230 force_defaults=False)
230 force_defaults=False)
231
231
232 @HasPermissionAllDecorator('hg.admin')
232 @HasPermissionAllDecorator('hg.admin')
233 @auth.CSRFRequired()
233 @auth.CSRFRequired()
234 def settings_global_update(self):
234 def settings_global_update(self):
235 """POST /admin/settings/global: All items in the collection"""
235 """POST /admin/settings/global: All items in the collection"""
236 # url('admin_settings_global')
236 # url('admin_settings_global')
237 c.active = 'global'
237 c.active = 'global'
238 application_form = ApplicationSettingsForm()()
238 application_form = ApplicationSettingsForm()()
239 try:
239 try:
240 form_result = application_form.to_python(dict(request.POST))
240 form_result = application_form.to_python(dict(request.POST))
241 except formencode.Invalid as errors:
241 except formencode.Invalid as errors:
242 return htmlfill.render(
242 return htmlfill.render(
243 render('admin/settings/settings.html'),
243 render('admin/settings/settings.html'),
244 defaults=errors.value,
244 defaults=errors.value,
245 errors=errors.error_dict or {},
245 errors=errors.error_dict or {},
246 prefix_error=False,
246 prefix_error=False,
247 encoding="UTF-8",
247 encoding="UTF-8",
248 force_defaults=False)
248 force_defaults=False)
249
249
250 try:
250 try:
251 settings = [
251 settings = [
252 ('title', 'rhodecode_title'),
252 ('title', 'rhodecode_title'),
253 ('realm', 'rhodecode_realm'),
253 ('realm', 'rhodecode_realm'),
254 ('pre_code', 'rhodecode_pre_code'),
254 ('pre_code', 'rhodecode_pre_code'),
255 ('post_code', 'rhodecode_post_code'),
255 ('post_code', 'rhodecode_post_code'),
256 ('captcha_public_key', 'rhodecode_captcha_public_key'),
256 ('captcha_public_key', 'rhodecode_captcha_public_key'),
257 ('captcha_private_key', 'rhodecode_captcha_private_key'),
257 ('captcha_private_key', 'rhodecode_captcha_private_key'),
258 ]
258 ]
259 for setting, form_key in settings:
259 for setting, form_key in settings:
260 sett = SettingsModel().create_or_update_setting(
260 sett = SettingsModel().create_or_update_setting(
261 setting, form_result[form_key])
261 setting, form_result[form_key])
262 Session().add(sett)
262 Session().add(sett)
263
263
264 Session().commit()
264 Session().commit()
265 SettingsModel().invalidate_settings_cache()
265 SettingsModel().invalidate_settings_cache()
266 h.flash(_('Updated application settings'), category='success')
266 h.flash(_('Updated application settings'), category='success')
267 except Exception:
267 except Exception:
268 log.exception("Exception while updating application settings")
268 log.exception("Exception while updating application settings")
269 h.flash(
269 h.flash(
270 _('Error occurred during updating application settings'),
270 _('Error occurred during updating application settings'),
271 category='error')
271 category='error')
272
272
273 return redirect(url('admin_settings_global'))
273 return redirect(url('admin_settings_global'))
274
274
275 @HasPermissionAllDecorator('hg.admin')
275 @HasPermissionAllDecorator('hg.admin')
276 def settings_global(self):
276 def settings_global(self):
277 """GET /admin/settings/global: All items in the collection"""
277 """GET /admin/settings/global: All items in the collection"""
278 # url('admin_settings_global')
278 # url('admin_settings_global')
279 c.active = 'global'
279 c.active = 'global'
280
280
281 return htmlfill.render(
281 return htmlfill.render(
282 render('admin/settings/settings.html'),
282 render('admin/settings/settings.html'),
283 defaults=self._form_defaults(),
283 defaults=self._form_defaults(),
284 encoding="UTF-8",
284 encoding="UTF-8",
285 force_defaults=False)
285 force_defaults=False)
286
286
287 @HasPermissionAllDecorator('hg.admin')
287 @HasPermissionAllDecorator('hg.admin')
288 @auth.CSRFRequired()
288 @auth.CSRFRequired()
289 def settings_visual_update(self):
289 def settings_visual_update(self):
290 """POST /admin/settings/visual: All items in the collection"""
290 """POST /admin/settings/visual: All items in the collection"""
291 # url('admin_settings_visual')
291 # url('admin_settings_visual')
292 c.active = 'visual'
292 c.active = 'visual'
293 application_form = ApplicationVisualisationForm()()
293 application_form = ApplicationVisualisationForm()()
294 try:
294 try:
295 form_result = application_form.to_python(dict(request.POST))
295 form_result = application_form.to_python(dict(request.POST))
296 except formencode.Invalid as errors:
296 except formencode.Invalid as errors:
297 return htmlfill.render(
297 return htmlfill.render(
298 render('admin/settings/settings.html'),
298 render('admin/settings/settings.html'),
299 defaults=errors.value,
299 defaults=errors.value,
300 errors=errors.error_dict or {},
300 errors=errors.error_dict or {},
301 prefix_error=False,
301 prefix_error=False,
302 encoding="UTF-8",
302 encoding="UTF-8",
303 force_defaults=False
303 force_defaults=False
304 )
304 )
305
305
306 try:
306 try:
307 settings = [
307 settings = [
308 ('show_public_icon', 'rhodecode_show_public_icon', 'bool'),
308 ('show_public_icon', 'rhodecode_show_public_icon', 'bool'),
309 ('show_private_icon', 'rhodecode_show_private_icon', 'bool'),
309 ('show_private_icon', 'rhodecode_show_private_icon', 'bool'),
310 ('stylify_metatags', 'rhodecode_stylify_metatags', 'bool'),
310 ('stylify_metatags', 'rhodecode_stylify_metatags', 'bool'),
311 ('repository_fields', 'rhodecode_repository_fields', 'bool'),
311 ('repository_fields', 'rhodecode_repository_fields', 'bool'),
312 ('dashboard_items', 'rhodecode_dashboard_items', 'int'),
312 ('dashboard_items', 'rhodecode_dashboard_items', 'int'),
313 ('admin_grid_items', 'rhodecode_admin_grid_items', 'int'),
313 ('admin_grid_items', 'rhodecode_admin_grid_items', 'int'),
314 ('show_version', 'rhodecode_show_version', 'bool'),
314 ('show_version', 'rhodecode_show_version', 'bool'),
315 ('use_gravatar', 'rhodecode_use_gravatar', 'bool'),
315 ('use_gravatar', 'rhodecode_use_gravatar', 'bool'),
316 ('markup_renderer', 'rhodecode_markup_renderer', 'unicode'),
316 ('markup_renderer', 'rhodecode_markup_renderer', 'unicode'),
317 ('gravatar_url', 'rhodecode_gravatar_url', 'unicode'),
317 ('gravatar_url', 'rhodecode_gravatar_url', 'unicode'),
318 ('clone_uri_tmpl', 'rhodecode_clone_uri_tmpl', 'unicode'),
318 ('clone_uri_tmpl', 'rhodecode_clone_uri_tmpl', 'unicode'),
319 ('support_url', 'rhodecode_support_url', 'unicode'),
319 ('support_url', 'rhodecode_support_url', 'unicode'),
320 ('show_revision_number', 'rhodecode_show_revision_number', 'bool'),
320 ('show_revision_number', 'rhodecode_show_revision_number', 'bool'),
321 ('show_sha_length', 'rhodecode_show_sha_length', 'int'),
321 ('show_sha_length', 'rhodecode_show_sha_length', 'int'),
322 ]
322 ]
323 for setting, form_key, type_ in settings:
323 for setting, form_key, type_ in settings:
324 sett = SettingsModel().create_or_update_setting(
324 sett = SettingsModel().create_or_update_setting(
325 setting, form_result[form_key], type_)
325 setting, form_result[form_key], type_)
326 Session().add(sett)
326 Session().add(sett)
327
327
328 Session().commit()
328 Session().commit()
329 SettingsModel().invalidate_settings_cache()
329 SettingsModel().invalidate_settings_cache()
330 h.flash(_('Updated visualisation settings'), category='success')
330 h.flash(_('Updated visualisation settings'), category='success')
331 except Exception:
331 except Exception:
332 log.exception("Exception updating visualization settings")
332 log.exception("Exception updating visualization settings")
333 h.flash(_('Error occurred during updating '
333 h.flash(_('Error occurred during updating '
334 'visualisation settings'),
334 'visualisation settings'),
335 category='error')
335 category='error')
336
336
337 return redirect(url('admin_settings_visual'))
337 return redirect(url('admin_settings_visual'))
338
338
339 @HasPermissionAllDecorator('hg.admin')
339 @HasPermissionAllDecorator('hg.admin')
340 def settings_visual(self):
340 def settings_visual(self):
341 """GET /admin/settings/visual: All items in the collection"""
341 """GET /admin/settings/visual: All items in the collection"""
342 # url('admin_settings_visual')
342 # url('admin_settings_visual')
343 c.active = 'visual'
343 c.active = 'visual'
344
344
345 return htmlfill.render(
345 return htmlfill.render(
346 render('admin/settings/settings.html'),
346 render('admin/settings/settings.html'),
347 defaults=self._form_defaults(),
347 defaults=self._form_defaults(),
348 encoding="UTF-8",
348 encoding="UTF-8",
349 force_defaults=False)
349 force_defaults=False)
350
350
351 @HasPermissionAllDecorator('hg.admin')
351 @HasPermissionAllDecorator('hg.admin')
352 @auth.CSRFRequired()
352 @auth.CSRFRequired()
353 def settings_issuetracker_test(self):
353 def settings_issuetracker_test(self):
354 if request.is_xhr:
354 if request.is_xhr:
355 return h.urlify_commit_message(
355 return h.urlify_commit_message(
356 request.POST.get('test_text', ''),
356 request.POST.get('test_text', ''),
357 'repo_group/test_repo1')
357 'repo_group/test_repo1')
358 else:
358 else:
359 raise HTTPBadRequest()
359 raise HTTPBadRequest()
360
360
361 @HasPermissionAllDecorator('hg.admin')
361 @HasPermissionAllDecorator('hg.admin')
362 @auth.CSRFRequired()
362 @auth.CSRFRequired()
363 def settings_issuetracker_delete(self):
363 def settings_issuetracker_delete(self):
364 uid = request.POST.get('uid')
364 uid = request.POST.get('uid')
365 IssueTrackerSettingsModel().delete_entries(uid)
365 IssueTrackerSettingsModel().delete_entries(uid)
366 h.flash(_('Removed issue tracker entry'), category='success')
366 h.flash(_('Removed issue tracker entry'), category='success')
367 return redirect(url('admin_settings_issuetracker'))
367 return redirect(url('admin_settings_issuetracker'))
368
368
369 @HasPermissionAllDecorator('hg.admin')
369 @HasPermissionAllDecorator('hg.admin')
370 def settings_issuetracker(self):
370 def settings_issuetracker(self):
371 """GET /admin/settings/issue-tracker: All items in the collection"""
371 """GET /admin/settings/issue-tracker: All items in the collection"""
372 # url('admin_settings_issuetracker')
372 # url('admin_settings_issuetracker')
373 c.active = 'issuetracker'
373 c.active = 'issuetracker'
374 defaults = SettingsModel().get_all_settings()
374 defaults = SettingsModel().get_all_settings()
375
375
376 entry_key = 'rhodecode_issuetracker_pat_'
376 entry_key = 'rhodecode_issuetracker_pat_'
377
377
378 c.issuetracker_entries = {}
378 c.issuetracker_entries = {}
379 for k, v in defaults.items():
379 for k, v in defaults.items():
380 if k.startswith(entry_key):
380 if k.startswith(entry_key):
381 uid = k[len(entry_key):]
381 uid = k[len(entry_key):]
382 c.issuetracker_entries[uid] = None
382 c.issuetracker_entries[uid] = None
383
383
384 for uid in c.issuetracker_entries:
384 for uid in c.issuetracker_entries:
385 c.issuetracker_entries[uid] = AttributeDict({
385 c.issuetracker_entries[uid] = AttributeDict({
386 'pat': defaults.get('rhodecode_issuetracker_pat_' + uid),
386 'pat': defaults.get('rhodecode_issuetracker_pat_' + uid),
387 'url': defaults.get('rhodecode_issuetracker_url_' + uid),
387 'url': defaults.get('rhodecode_issuetracker_url_' + uid),
388 'pref': defaults.get('rhodecode_issuetracker_pref_' + uid),
388 'pref': defaults.get('rhodecode_issuetracker_pref_' + uid),
389 'desc': defaults.get('rhodecode_issuetracker_desc_' + uid),
389 'desc': defaults.get('rhodecode_issuetracker_desc_' + uid),
390 })
390 })
391
391
392 return render('admin/settings/settings.html')
392 return render('admin/settings/settings.html')
393
393
394 @HasPermissionAllDecorator('hg.admin')
394 @HasPermissionAllDecorator('hg.admin')
395 @auth.CSRFRequired()
395 @auth.CSRFRequired()
396 def settings_issuetracker_save(self):
396 def settings_issuetracker_save(self):
397 settings_model = IssueTrackerSettingsModel()
397 settings_model = IssueTrackerSettingsModel()
398
398
399 form = IssueTrackerPatternsForm()().to_python(request.POST)
399 form = IssueTrackerPatternsForm()().to_python(request.POST)
400 for uid in form['delete_patterns']:
400 if form:
401 for uid in form.get('delete_patterns', []):
401 settings_model.delete_entries(uid)
402 settings_model.delete_entries(uid)
402
403
403 for pattern in form['patterns']:
404 for pattern in form.get('patterns', []):
404 for setting, value, type_ in pattern:
405 for setting, value, type_ in pattern:
405 sett = settings_model.create_or_update_setting(
406 sett = settings_model.create_or_update_setting(
406 setting, value, type_)
407 setting, value, type_)
407 Session().add(sett)
408 Session().add(sett)
408
409
409 Session().commit()
410 Session().commit()
410
411
411 SettingsModel().invalidate_settings_cache()
412 SettingsModel().invalidate_settings_cache()
412 h.flash(_('Updated issue tracker entries'), category='success')
413 h.flash(_('Updated issue tracker entries'), category='success')
413 return redirect(url('admin_settings_issuetracker'))
414 return redirect(url('admin_settings_issuetracker'))
414
415
415 @HasPermissionAllDecorator('hg.admin')
416 @HasPermissionAllDecorator('hg.admin')
416 @auth.CSRFRequired()
417 @auth.CSRFRequired()
417 def settings_email_update(self):
418 def settings_email_update(self):
418 """POST /admin/settings/email: All items in the collection"""
419 """POST /admin/settings/email: All items in the collection"""
419 # url('admin_settings_email')
420 # url('admin_settings_email')
420 c.active = 'email'
421 c.active = 'email'
421
422
422 test_email = request.POST.get('test_email')
423 test_email = request.POST.get('test_email')
423
424
424 if not test_email:
425 if not test_email:
425 h.flash(_('Please enter email address'), category='error')
426 h.flash(_('Please enter email address'), category='error')
426 return redirect(url('admin_settings_email'))
427 return redirect(url('admin_settings_email'))
427
428
428 email_kwargs = {
429 email_kwargs = {
429 'date': datetime.datetime.now(),
430 'date': datetime.datetime.now(),
430 'user': c.rhodecode_user,
431 'user': c.rhodecode_user,
431 'rhodecode_version': c.rhodecode_version
432 'rhodecode_version': c.rhodecode_version
432 }
433 }
433
434
434 (subject, headers, email_body,
435 (subject, headers, email_body,
435 email_body_plaintext) = EmailNotificationModel().render_email(
436 email_body_plaintext) = EmailNotificationModel().render_email(
436 EmailNotificationModel.TYPE_EMAIL_TEST, **email_kwargs)
437 EmailNotificationModel.TYPE_EMAIL_TEST, **email_kwargs)
437
438
438 recipients = [test_email] if test_email else None
439 recipients = [test_email] if test_email else None
439
440
440 run_task(tasks.send_email, recipients, subject,
441 run_task(tasks.send_email, recipients, subject,
441 email_body_plaintext, email_body)
442 email_body_plaintext, email_body)
442
443
443 h.flash(_('Send email task created'), category='success')
444 h.flash(_('Send email task created'), category='success')
444 return redirect(url('admin_settings_email'))
445 return redirect(url('admin_settings_email'))
445
446
446 @HasPermissionAllDecorator('hg.admin')
447 @HasPermissionAllDecorator('hg.admin')
447 def settings_email(self):
448 def settings_email(self):
448 """GET /admin/settings/email: All items in the collection"""
449 """GET /admin/settings/email: All items in the collection"""
449 # url('admin_settings_email')
450 # url('admin_settings_email')
450 c.active = 'email'
451 c.active = 'email'
451 c.rhodecode_ini = rhodecode.CONFIG
452 c.rhodecode_ini = rhodecode.CONFIG
452
453
453 return htmlfill.render(
454 return htmlfill.render(
454 render('admin/settings/settings.html'),
455 render('admin/settings/settings.html'),
455 defaults=self._form_defaults(),
456 defaults=self._form_defaults(),
456 encoding="UTF-8",
457 encoding="UTF-8",
457 force_defaults=False)
458 force_defaults=False)
458
459
459 @HasPermissionAllDecorator('hg.admin')
460 @HasPermissionAllDecorator('hg.admin')
460 @auth.CSRFRequired()
461 @auth.CSRFRequired()
461 def settings_hooks_update(self):
462 def settings_hooks_update(self):
462 """POST or DELETE /admin/settings/hooks: All items in the collection"""
463 """POST or DELETE /admin/settings/hooks: All items in the collection"""
463 # url('admin_settings_hooks')
464 # url('admin_settings_hooks')
464 c.active = 'hooks'
465 c.active = 'hooks'
465 if c.visual.allow_custom_hooks_settings:
466 if c.visual.allow_custom_hooks_settings:
466 ui_key = request.POST.get('new_hook_ui_key')
467 ui_key = request.POST.get('new_hook_ui_key')
467 ui_value = request.POST.get('new_hook_ui_value')
468 ui_value = request.POST.get('new_hook_ui_value')
468
469
469 hook_id = request.POST.get('hook_id')
470 hook_id = request.POST.get('hook_id')
470 new_hook = False
471 new_hook = False
471
472
472 model = SettingsModel()
473 model = SettingsModel()
473 try:
474 try:
474 if ui_value and ui_key:
475 if ui_value and ui_key:
475 model.create_or_update_hook(ui_key, ui_value)
476 model.create_or_update_hook(ui_key, ui_value)
476 h.flash(_('Added new hook'), category='success')
477 h.flash(_('Added new hook'), category='success')
477 new_hook = True
478 new_hook = True
478 elif hook_id:
479 elif hook_id:
479 RhodeCodeUi.delete(hook_id)
480 RhodeCodeUi.delete(hook_id)
480 Session().commit()
481 Session().commit()
481
482
482 # check for edits
483 # check for edits
483 update = False
484 update = False
484 _d = request.POST.dict_of_lists()
485 _d = request.POST.dict_of_lists()
485 for k, v in zip(_d.get('hook_ui_key', []),
486 for k, v in zip(_d.get('hook_ui_key', []),
486 _d.get('hook_ui_value_new', [])):
487 _d.get('hook_ui_value_new', [])):
487 model.create_or_update_hook(k, v)
488 model.create_or_update_hook(k, v)
488 update = True
489 update = True
489
490
490 if update and not new_hook:
491 if update and not new_hook:
491 h.flash(_('Updated hooks'), category='success')
492 h.flash(_('Updated hooks'), category='success')
492 Session().commit()
493 Session().commit()
493 except Exception:
494 except Exception:
494 log.exception("Exception during hook creation")
495 log.exception("Exception during hook creation")
495 h.flash(_('Error occurred during hook creation'),
496 h.flash(_('Error occurred during hook creation'),
496 category='error')
497 category='error')
497
498
498 return redirect(url('admin_settings_hooks'))
499 return redirect(url('admin_settings_hooks'))
499
500
500 @HasPermissionAllDecorator('hg.admin')
501 @HasPermissionAllDecorator('hg.admin')
501 def settings_hooks(self):
502 def settings_hooks(self):
502 """GET /admin/settings/hooks: All items in the collection"""
503 """GET /admin/settings/hooks: All items in the collection"""
503 # url('admin_settings_hooks')
504 # url('admin_settings_hooks')
504 c.active = 'hooks'
505 c.active = 'hooks'
505
506
506 model = SettingsModel()
507 model = SettingsModel()
507 c.hooks = model.get_builtin_hooks()
508 c.hooks = model.get_builtin_hooks()
508 c.custom_hooks = model.get_custom_hooks()
509 c.custom_hooks = model.get_custom_hooks()
509
510
510 return htmlfill.render(
511 return htmlfill.render(
511 render('admin/settings/settings.html'),
512 render('admin/settings/settings.html'),
512 defaults=self._form_defaults(),
513 defaults=self._form_defaults(),
513 encoding="UTF-8",
514 encoding="UTF-8",
514 force_defaults=False)
515 force_defaults=False)
515
516
516 @HasPermissionAllDecorator('hg.admin')
517 @HasPermissionAllDecorator('hg.admin')
517 def settings_search(self):
518 def settings_search(self):
518 """GET /admin/settings/search: All items in the collection"""
519 """GET /admin/settings/search: All items in the collection"""
519 # url('admin_settings_search')
520 # url('admin_settings_search')
520 c.active = 'search'
521 c.active = 'search'
521
522
522 from rhodecode.lib.index import searcher_from_config
523 from rhodecode.lib.index import searcher_from_config
523 searcher = searcher_from_config(config)
524 searcher = searcher_from_config(config)
524 c.statistics = searcher.statistics()
525 c.statistics = searcher.statistics()
525
526
526 return render('admin/settings/settings.html')
527 return render('admin/settings/settings.html')
527
528
528 @HasPermissionAllDecorator('hg.admin')
529 @HasPermissionAllDecorator('hg.admin')
529 def settings_system(self):
530 def settings_system(self):
530 """GET /admin/settings/system: All items in the collection"""
531 """GET /admin/settings/system: All items in the collection"""
531 # url('admin_settings_system')
532 # url('admin_settings_system')
532 snapshot = str2bool(request.GET.get('snapshot'))
533 snapshot = str2bool(request.GET.get('snapshot'))
533 c.active = 'system'
534 c.active = 'system'
534
535
535 defaults = self._form_defaults()
536 defaults = self._form_defaults()
536 c.rhodecode_ini = rhodecode.CONFIG
537 c.rhodecode_ini = rhodecode.CONFIG
537 c.rhodecode_update_url = defaults.get('rhodecode_update_url')
538 c.rhodecode_update_url = defaults.get('rhodecode_update_url')
538 server_info = ScmModel().get_server_info(request.environ)
539 server_info = ScmModel().get_server_info(request.environ)
539 for key, val in server_info.iteritems():
540 for key, val in server_info.iteritems():
540 setattr(c, key, val)
541 setattr(c, key, val)
541
542
542 if c.disk['percent'] > 90:
543 if c.disk['percent'] > 90:
543 h.flash(h.literal(_(
544 h.flash(h.literal(_(
544 'Critical: your disk space is very low <b>%s%%</b> used' %
545 'Critical: your disk space is very low <b>%s%%</b> used' %
545 c.disk['percent'])), 'error')
546 c.disk['percent'])), 'error')
546 elif c.disk['percent'] > 70:
547 elif c.disk['percent'] > 70:
547 h.flash(h.literal(_(
548 h.flash(h.literal(_(
548 'Warning: your disk space is running low <b>%s%%</b> used' %
549 'Warning: your disk space is running low <b>%s%%</b> used' %
549 c.disk['percent'])), 'warning')
550 c.disk['percent'])), 'warning')
550
551
551 try:
552 try:
552 c.uptime_age = h._age(
553 c.uptime_age = h._age(
553 h.time_to_datetime(c.boot_time), False, show_suffix=False)
554 h.time_to_datetime(c.boot_time), False, show_suffix=False)
554 except TypeError:
555 except TypeError:
555 c.uptime_age = c.boot_time
556 c.uptime_age = c.boot_time
556
557
557 try:
558 try:
558 c.system_memory = '%s/%s, %s%% (%s%%) used%s' % (
559 c.system_memory = '%s/%s, %s%% (%s%%) used%s' % (
559 h.format_byte_size_binary(c.memory['used']),
560 h.format_byte_size_binary(c.memory['used']),
560 h.format_byte_size_binary(c.memory['total']),
561 h.format_byte_size_binary(c.memory['total']),
561 c.memory['percent2'],
562 c.memory['percent2'],
562 c.memory['percent'],
563 c.memory['percent'],
563 ' %s' % c.memory['error'] if 'error' in c.memory else '')
564 ' %s' % c.memory['error'] if 'error' in c.memory else '')
564 except TypeError:
565 except TypeError:
565 c.system_memory = 'NOT AVAILABLE'
566 c.system_memory = 'NOT AVAILABLE'
566
567
567 rhodecode_ini_safe = rhodecode.CONFIG.copy()
568 rhodecode_ini_safe = rhodecode.CONFIG.copy()
568 blacklist = [
569 blacklist = [
569 'rhodecode_license_key',
570 'rhodecode_license_key',
570 'routes.map',
571 'routes.map',
571 'pylons.h',
572 'pylons.h',
572 'pylons.app_globals',
573 'pylons.app_globals',
573 'pylons.environ_config',
574 'pylons.environ_config',
574 'sqlalchemy.db1.url',
575 'sqlalchemy.db1.url',
575 ('app_conf', 'sqlalchemy.db1.url')
576 ('app_conf', 'sqlalchemy.db1.url')
576 ]
577 ]
577 for k in blacklist:
578 for k in blacklist:
578 if isinstance(k, tuple):
579 if isinstance(k, tuple):
579 section, key = k
580 section, key = k
580 if section in rhodecode_ini_safe:
581 if section in rhodecode_ini_safe:
581 rhodecode_ini_safe[section].pop(key, None)
582 rhodecode_ini_safe[section].pop(key, None)
582 else:
583 else:
583 rhodecode_ini_safe.pop(k, None)
584 rhodecode_ini_safe.pop(k, None)
584
585
585 c.rhodecode_ini_safe = rhodecode_ini_safe
586 c.rhodecode_ini_safe = rhodecode_ini_safe
586
587
587 # TODO: marcink, figure out how to allow only selected users to do this
588 # TODO: marcink, figure out how to allow only selected users to do this
588 c.allowed_to_snapshot = False
589 c.allowed_to_snapshot = False
589
590
590 if snapshot:
591 if snapshot:
591 if c.allowed_to_snapshot:
592 if c.allowed_to_snapshot:
592 return render('admin/settings/settings_system_snapshot.html')
593 return render('admin/settings/settings_system_snapshot.html')
593 else:
594 else:
594 h.flash('You are not allowed to do this', category='warning')
595 h.flash('You are not allowed to do this', category='warning')
595
596
596 return htmlfill.render(
597 return htmlfill.render(
597 render('admin/settings/settings.html'),
598 render('admin/settings/settings.html'),
598 defaults=defaults,
599 defaults=defaults,
599 encoding="UTF-8",
600 encoding="UTF-8",
600 force_defaults=False)
601 force_defaults=False)
601
602
602 @staticmethod
603 @staticmethod
603 def get_update_data(update_url):
604 def get_update_data(update_url):
604 """Return the JSON update data."""
605 """Return the JSON update data."""
605 ver = rhodecode.__version__
606 ver = rhodecode.__version__
606 log.debug('Checking for upgrade on `%s` server', update_url)
607 log.debug('Checking for upgrade on `%s` server', update_url)
607 opener = urllib2.build_opener()
608 opener = urllib2.build_opener()
608 opener.addheaders = [('User-agent', 'RhodeCode-SCM/%s' % ver)]
609 opener.addheaders = [('User-agent', 'RhodeCode-SCM/%s' % ver)]
609 response = opener.open(update_url)
610 response = opener.open(update_url)
610 response_data = response.read()
611 response_data = response.read()
611 data = json.loads(response_data)
612 data = json.loads(response_data)
612
613
613 return data
614 return data
614
615
615 @HasPermissionAllDecorator('hg.admin')
616 @HasPermissionAllDecorator('hg.admin')
616 def settings_system_update(self):
617 def settings_system_update(self):
617 """GET /admin/settings/system/updates: All items in the collection"""
618 """GET /admin/settings/system/updates: All items in the collection"""
618 # url('admin_settings_system_update')
619 # url('admin_settings_system_update')
619 defaults = self._form_defaults()
620 defaults = self._form_defaults()
620 update_url = defaults.get('rhodecode_update_url', '')
621 update_url = defaults.get('rhodecode_update_url', '')
621
622
622 _err = lambda s: '<div style="color:#ff8888; padding:4px 0px">%s</div>' % (s)
623 _err = lambda s: '<div style="color:#ff8888; padding:4px 0px">%s</div>' % (s)
623 try:
624 try:
624 data = self.get_update_data(update_url)
625 data = self.get_update_data(update_url)
625 except urllib2.URLError as e:
626 except urllib2.URLError as e:
626 log.exception("Exception contacting upgrade server")
627 log.exception("Exception contacting upgrade server")
627 return _err('Failed to contact upgrade server: %r' % e)
628 return _err('Failed to contact upgrade server: %r' % e)
628 except ValueError as e:
629 except ValueError as e:
629 log.exception("Bad data sent from update server")
630 log.exception("Bad data sent from update server")
630 return _err('Bad data sent from update server')
631 return _err('Bad data sent from update server')
631
632
632 latest = data['versions'][0]
633 latest = data['versions'][0]
633
634
634 c.update_url = update_url
635 c.update_url = update_url
635 c.latest_data = latest
636 c.latest_data = latest
636 c.latest_ver = latest['version']
637 c.latest_ver = latest['version']
637 c.cur_ver = rhodecode.__version__
638 c.cur_ver = rhodecode.__version__
638 c.should_upgrade = False
639 c.should_upgrade = False
639
640
640 if (packaging.version.Version(c.latest_ver) >
641 if (packaging.version.Version(c.latest_ver) >
641 packaging.version.Version(c.cur_ver)):
642 packaging.version.Version(c.cur_ver)):
642 c.should_upgrade = True
643 c.should_upgrade = True
643 c.important_notices = latest['general']
644 c.important_notices = latest['general']
644
645
645 return render('admin/settings/settings_system_update.html')
646 return render('admin/settings/settings_system_update.html')
646
647
647 @HasPermissionAllDecorator('hg.admin')
648 @HasPermissionAllDecorator('hg.admin')
648 def settings_supervisor(self):
649 def settings_supervisor(self):
649 c.rhodecode_ini = rhodecode.CONFIG
650 c.rhodecode_ini = rhodecode.CONFIG
650 c.active = 'supervisor'
651 c.active = 'supervisor'
651
652
652 c.supervisor_procs = OrderedDict([
653 c.supervisor_procs = OrderedDict([
653 (SUPERVISOR_MASTER, {}),
654 (SUPERVISOR_MASTER, {}),
654 ])
655 ])
655
656
656 c.log_size = 10240
657 c.log_size = 10240
657 supervisor = SupervisorModel()
658 supervisor = SupervisorModel()
658
659
659 _connection = supervisor.get_connection(
660 _connection = supervisor.get_connection(
660 c.rhodecode_ini.get('supervisor.uri'))
661 c.rhodecode_ini.get('supervisor.uri'))
661 c.connection_error = None
662 c.connection_error = None
662 try:
663 try:
663 _connection.supervisor.getAllProcessInfo()
664 _connection.supervisor.getAllProcessInfo()
664 except Exception as e:
665 except Exception as e:
665 c.connection_error = str(e)
666 c.connection_error = str(e)
666 log.exception("Exception reading supervisor data")
667 log.exception("Exception reading supervisor data")
667 return render('admin/settings/settings.html')
668 return render('admin/settings/settings.html')
668
669
669 groupid = c.rhodecode_ini.get('supervisor.group_id')
670 groupid = c.rhodecode_ini.get('supervisor.group_id')
670
671
671 # feed our group processes to the main
672 # feed our group processes to the main
672 for proc in supervisor.get_group_processes(_connection, groupid):
673 for proc in supervisor.get_group_processes(_connection, groupid):
673 c.supervisor_procs[proc['name']] = {}
674 c.supervisor_procs[proc['name']] = {}
674
675
675 for k in c.supervisor_procs.keys():
676 for k in c.supervisor_procs.keys():
676 try:
677 try:
677 # master process info
678 # master process info
678 if k == SUPERVISOR_MASTER:
679 if k == SUPERVISOR_MASTER:
679 _data = supervisor.get_master_state(_connection)
680 _data = supervisor.get_master_state(_connection)
680 _data['name'] = 'supervisor master'
681 _data['name'] = 'supervisor master'
681 _data['description'] = 'pid %s, id: %s, ver: %s' % (
682 _data['description'] = 'pid %s, id: %s, ver: %s' % (
682 _data['pid'], _data['id'], _data['ver'])
683 _data['pid'], _data['id'], _data['ver'])
683 c.supervisor_procs[k] = _data
684 c.supervisor_procs[k] = _data
684 else:
685 else:
685 procid = groupid + ":" + k
686 procid = groupid + ":" + k
686 c.supervisor_procs[k] = supervisor.get_process_info(_connection, procid)
687 c.supervisor_procs[k] = supervisor.get_process_info(_connection, procid)
687 except Exception as e:
688 except Exception as e:
688 log.exception("Exception reading supervisor data")
689 log.exception("Exception reading supervisor data")
689 c.supervisor_procs[k] = {'_rhodecode_error': str(e)}
690 c.supervisor_procs[k] = {'_rhodecode_error': str(e)}
690
691
691 return render('admin/settings/settings.html')
692 return render('admin/settings/settings.html')
692
693
693 @HasPermissionAllDecorator('hg.admin')
694 @HasPermissionAllDecorator('hg.admin')
694 def settings_supervisor_log(self, procid):
695 def settings_supervisor_log(self, procid):
695 import rhodecode
696 import rhodecode
696 c.rhodecode_ini = rhodecode.CONFIG
697 c.rhodecode_ini = rhodecode.CONFIG
697 c.active = 'supervisor_tail'
698 c.active = 'supervisor_tail'
698
699
699 supervisor = SupervisorModel()
700 supervisor = SupervisorModel()
700 _connection = supervisor.get_connection(c.rhodecode_ini.get('supervisor.uri'))
701 _connection = supervisor.get_connection(c.rhodecode_ini.get('supervisor.uri'))
701 groupid = c.rhodecode_ini.get('supervisor.group_id')
702 groupid = c.rhodecode_ini.get('supervisor.group_id')
702 procid = groupid + ":" + procid if procid != SUPERVISOR_MASTER else procid
703 procid = groupid + ":" + procid if procid != SUPERVISOR_MASTER else procid
703
704
704 c.log_size = 10240
705 c.log_size = 10240
705 offset = abs(safe_int(request.GET.get('offset', c.log_size))) * -1
706 offset = abs(safe_int(request.GET.get('offset', c.log_size))) * -1
706 c.log = supervisor.read_process_log(_connection, procid, offset, 0)
707 c.log = supervisor.read_process_log(_connection, procid, offset, 0)
707
708
708 return render('admin/settings/settings.html')
709 return render('admin/settings/settings.html')
709
710
710 @HasPermissionAllDecorator('hg.admin')
711 @HasPermissionAllDecorator('hg.admin')
711 @auth.CSRFRequired()
712 @auth.CSRFRequired()
712 def settings_labs_update(self):
713 def settings_labs_update(self):
713 """POST /admin/settings/labs: All items in the collection"""
714 """POST /admin/settings/labs: All items in the collection"""
714 # url('admin_settings/labs', method={'POST'})
715 # url('admin_settings/labs', method={'POST'})
715 c.active = 'labs'
716 c.active = 'labs'
716
717
717 application_form = LabsSettingsForm()()
718 application_form = LabsSettingsForm()()
718 try:
719 try:
719 form_result = application_form.to_python(dict(request.POST))
720 form_result = application_form.to_python(dict(request.POST))
720 except formencode.Invalid as errors:
721 except formencode.Invalid as errors:
721 h.flash(
722 h.flash(
722 _('Some form inputs contain invalid data.'),
723 _('Some form inputs contain invalid data.'),
723 category='error')
724 category='error')
724 return htmlfill.render(
725 return htmlfill.render(
725 render('admin/settings/settings.html'),
726 render('admin/settings/settings.html'),
726 defaults=errors.value,
727 defaults=errors.value,
727 errors=errors.error_dict or {},
728 errors=errors.error_dict or {},
728 prefix_error=False,
729 prefix_error=False,
729 encoding='UTF-8',
730 encoding='UTF-8',
730 force_defaults=False
731 force_defaults=False
731 )
732 )
732
733
733 try:
734 try:
734 session = Session()
735 session = Session()
735 for setting in _LAB_SETTINGS:
736 for setting in _LAB_SETTINGS:
736 setting_name = setting.key[len('rhodecode_'):]
737 setting_name = setting.key[len('rhodecode_'):]
737 sett = SettingsModel().create_or_update_setting(
738 sett = SettingsModel().create_or_update_setting(
738 setting_name, form_result[setting.key], setting.type)
739 setting_name, form_result[setting.key], setting.type)
739 session.add(sett)
740 session.add(sett)
740
741
741 except Exception:
742 except Exception:
742 log.exception('Exception while updating lab settings')
743 log.exception('Exception while updating lab settings')
743 h.flash(_('Error occurred during updating labs settings'),
744 h.flash(_('Error occurred during updating labs settings'),
744 category='error')
745 category='error')
745 else:
746 else:
746 Session().commit()
747 Session().commit()
747 SettingsModel().invalidate_settings_cache()
748 SettingsModel().invalidate_settings_cache()
748 h.flash(_('Updated Labs settings'), category='success')
749 h.flash(_('Updated Labs settings'), category='success')
749 return redirect(url('admin_settings_labs'))
750 return redirect(url('admin_settings_labs'))
750
751
751 return htmlfill.render(
752 return htmlfill.render(
752 render('admin/settings/settings.html'),
753 render('admin/settings/settings.html'),
753 defaults=self._form_defaults(),
754 defaults=self._form_defaults(),
754 encoding='UTF-8',
755 encoding='UTF-8',
755 force_defaults=False)
756 force_defaults=False)
756
757
757 @HasPermissionAllDecorator('hg.admin')
758 @HasPermissionAllDecorator('hg.admin')
758 def settings_labs(self):
759 def settings_labs(self):
759 """GET /admin/settings/labs: All items in the collection"""
760 """GET /admin/settings/labs: All items in the collection"""
760 # url('admin_settings_labs')
761 # url('admin_settings_labs')
761 if not c.labs_active:
762 if not c.labs_active:
762 redirect(url('admin_settings'))
763 redirect(url('admin_settings'))
763
764
764 c.active = 'labs'
765 c.active = 'labs'
765 c.lab_settings = _LAB_SETTINGS
766 c.lab_settings = _LAB_SETTINGS
766
767
767 return htmlfill.render(
768 return htmlfill.render(
768 render('admin/settings/settings.html'),
769 render('admin/settings/settings.html'),
769 defaults=self._form_defaults(),
770 defaults=self._form_defaults(),
770 encoding='UTF-8',
771 encoding='UTF-8',
771 force_defaults=False)
772 force_defaults=False)
772
773
773 def _form_defaults(self):
774 def _form_defaults(self):
774 defaults = SettingsModel().get_all_settings()
775 defaults = SettingsModel().get_all_settings()
775 defaults.update(self._get_hg_ui_settings())
776 defaults.update(self._get_hg_ui_settings())
776 defaults.update({
777 defaults.update({
777 'new_svn_branch': '',
778 'new_svn_branch': '',
778 'new_svn_tag': '',
779 'new_svn_tag': '',
779 })
780 })
780 return defaults
781 return defaults
781
782
782
783
783 # :param key: name of the setting including the 'rhodecode_' prefix
784 # :param key: name of the setting including the 'rhodecode_' prefix
784 # :param type: the RhodeCodeSetting type to use.
785 # :param type: the RhodeCodeSetting type to use.
785 # :param group: the i18ned group in which we should dispaly this setting
786 # :param group: the i18ned group in which we should dispaly this setting
786 # :param label: the i18ned label we should display for this setting
787 # :param label: the i18ned label we should display for this setting
787 # :param help: the i18ned help we should dispaly for this setting
788 # :param help: the i18ned help we should dispaly for this setting
788 LabSetting = collections.namedtuple(
789 LabSetting = collections.namedtuple(
789 'LabSetting', ('key', 'type', 'group', 'label', 'help'))
790 'LabSetting', ('key', 'type', 'group', 'label', 'help'))
790
791
791
792
792 # This list has to be kept in sync with the form
793 # This list has to be kept in sync with the form
793 # rhodecode.model.forms.LabsSettingsForm.
794 # rhodecode.model.forms.LabsSettingsForm.
794 _LAB_SETTINGS = [
795 _LAB_SETTINGS = [
795
796
796 ]
797 ]
@@ -1,611 +1,619 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2016 RhodeCode GmbH
3 # Copyright (C) 2010-2016 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.config.routing import ADMIN_PREFIX
25 from rhodecode.config.routing 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 url, assert_session_flash
30 from rhodecode.tests import url, 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 = (
34 UPDATE_DATA_QUALNAME = (
35 'rhodecode.controllers.admin.settings.SettingsController.get_update_data')
35 'rhodecode.controllers.admin.settings.SettingsController.get_update_data')
36
36
37
37
38 @pytest.mark.usefixtures('autologin_user', 'app')
38 @pytest.mark.usefixtures('autologin_user', 'app')
39 class TestAdminSettingsController:
39 class TestAdminSettingsController:
40
40
41 @pytest.mark.parametrize('urlname', [
41 @pytest.mark.parametrize('urlname', [
42 'admin_settings_vcs',
42 'admin_settings_vcs',
43 'admin_settings_mapping',
43 'admin_settings_mapping',
44 'admin_settings_global',
44 'admin_settings_global',
45 'admin_settings_visual',
45 'admin_settings_visual',
46 'admin_settings_email',
46 'admin_settings_email',
47 'admin_settings_hooks',
47 'admin_settings_hooks',
48 'admin_settings_search',
48 'admin_settings_search',
49 'admin_settings_system',
49 'admin_settings_system',
50 ])
50 ])
51 def test_simple_get(self, urlname, app):
51 def test_simple_get(self, urlname, app):
52 app.get(url(urlname))
52 app.get(url(urlname))
53
53
54 def test_create_custom_hook(self, csrf_token):
54 def test_create_custom_hook(self, csrf_token):
55 response = self.app.post(
55 response = self.app.post(
56 url('admin_settings_hooks'),
56 url('admin_settings_hooks'),
57 params={
57 params={
58 'new_hook_ui_key': 'test_hooks_1',
58 'new_hook_ui_key': 'test_hooks_1',
59 'new_hook_ui_value': 'cd /tmp',
59 'new_hook_ui_value': 'cd /tmp',
60 'csrf_token': csrf_token})
60 'csrf_token': csrf_token})
61
61
62 response = response.follow()
62 response = response.follow()
63 response.mustcontain('test_hooks_1')
63 response.mustcontain('test_hooks_1')
64 response.mustcontain('cd /tmp')
64 response.mustcontain('cd /tmp')
65
65
66 def test_create_custom_hook_delete(self, csrf_token):
66 def test_create_custom_hook_delete(self, csrf_token):
67 response = self.app.post(
67 response = self.app.post(
68 url('admin_settings_hooks'),
68 url('admin_settings_hooks'),
69 params={
69 params={
70 'new_hook_ui_key': 'test_hooks_2',
70 'new_hook_ui_key': 'test_hooks_2',
71 'new_hook_ui_value': 'cd /tmp2',
71 'new_hook_ui_value': 'cd /tmp2',
72 'csrf_token': csrf_token})
72 'csrf_token': csrf_token})
73
73
74 response = response.follow()
74 response = response.follow()
75 response.mustcontain('test_hooks_2')
75 response.mustcontain('test_hooks_2')
76 response.mustcontain('cd /tmp2')
76 response.mustcontain('cd /tmp2')
77
77
78 hook_id = SettingsModel().get_ui_by_key('test_hooks_2').ui_id
78 hook_id = SettingsModel().get_ui_by_key('test_hooks_2').ui_id
79
79
80 # delete
80 # delete
81 self.app.post(
81 self.app.post(
82 url('admin_settings_hooks'),
82 url('admin_settings_hooks'),
83 params={'hook_id': hook_id, 'csrf_token': csrf_token})
83 params={'hook_id': hook_id, 'csrf_token': csrf_token})
84 response = self.app.get(url('admin_settings_hooks'))
84 response = self.app.get(url('admin_settings_hooks'))
85 response.mustcontain(no=['test_hooks_2'])
85 response.mustcontain(no=['test_hooks_2'])
86 response.mustcontain(no=['cd /tmp2'])
86 response.mustcontain(no=['cd /tmp2'])
87
87
88 def test_system_update_new_version(self):
88 def test_system_update_new_version(self):
89 update_data = {
89 update_data = {
90 'versions': [
90 'versions': [
91 {
91 {
92 'version': '100.3.1415926535',
92 'version': '100.3.1415926535',
93 'general': 'The latest version we are ever going to ship'
93 'general': 'The latest version we are ever going to ship'
94 },
94 },
95 {
95 {
96 'version': '0.0.0',
96 'version': '0.0.0',
97 'general': 'The first version we ever shipped'
97 'general': 'The first version we ever shipped'
98 }
98 }
99 ]
99 ]
100 }
100 }
101 with mock.patch(UPDATE_DATA_QUALNAME, return_value=update_data):
101 with mock.patch(UPDATE_DATA_QUALNAME, return_value=update_data):
102 response = self.app.get(url('admin_settings_system_update'))
102 response = self.app.get(url('admin_settings_system_update'))
103 response.mustcontain('A <b>new version</b> is available')
103 response.mustcontain('A <b>new version</b> is available')
104
104
105 def test_system_update_nothing_new(self):
105 def test_system_update_nothing_new(self):
106 update_data = {
106 update_data = {
107 'versions': [
107 'versions': [
108 {
108 {
109 'version': '0.0.0',
109 'version': '0.0.0',
110 'general': 'The first version we ever shipped'
110 'general': 'The first version we ever shipped'
111 }
111 }
112 ]
112 ]
113 }
113 }
114 with mock.patch(UPDATE_DATA_QUALNAME, return_value=update_data):
114 with mock.patch(UPDATE_DATA_QUALNAME, return_value=update_data):
115 response = self.app.get(url('admin_settings_system_update'))
115 response = self.app.get(url('admin_settings_system_update'))
116 response.mustcontain(
116 response.mustcontain(
117 'You already have the <b>latest</b> stable version.')
117 'You already have the <b>latest</b> stable version.')
118
118
119 def test_system_update_bad_response(self):
119 def test_system_update_bad_response(self):
120 with mock.patch(UPDATE_DATA_QUALNAME, side_effect=ValueError('foo')):
120 with mock.patch(UPDATE_DATA_QUALNAME, side_effect=ValueError('foo')):
121 response = self.app.get(url('admin_settings_system_update'))
121 response = self.app.get(url('admin_settings_system_update'))
122 response.mustcontain(
122 response.mustcontain(
123 'Bad data sent from update server')
123 'Bad data sent from update server')
124
124
125
125
126 @pytest.mark.usefixtures('autologin_user', 'app')
126 @pytest.mark.usefixtures('autologin_user', 'app')
127 class TestAdminSettingsGlobal:
127 class TestAdminSettingsGlobal:
128
128
129 def test_pre_post_code_code_active(self, csrf_token):
129 def test_pre_post_code_code_active(self, csrf_token):
130 pre_code = 'rc-pre-code-187652122'
130 pre_code = 'rc-pre-code-187652122'
131 post_code = 'rc-postcode-98165231'
131 post_code = 'rc-postcode-98165231'
132
132
133 response = self.post_and_verify_settings({
133 response = self.post_and_verify_settings({
134 'rhodecode_pre_code': pre_code,
134 'rhodecode_pre_code': pre_code,
135 'rhodecode_post_code': post_code,
135 'rhodecode_post_code': post_code,
136 'csrf_token': csrf_token,
136 'csrf_token': csrf_token,
137 })
137 })
138
138
139 response = response.follow()
139 response = response.follow()
140 response.mustcontain(pre_code, post_code)
140 response.mustcontain(pre_code, post_code)
141
141
142 def test_pre_post_code_code_inactive(self, csrf_token):
142 def test_pre_post_code_code_inactive(self, csrf_token):
143 pre_code = 'rc-pre-code-187652122'
143 pre_code = 'rc-pre-code-187652122'
144 post_code = 'rc-postcode-98165231'
144 post_code = 'rc-postcode-98165231'
145 response = self.post_and_verify_settings({
145 response = self.post_and_verify_settings({
146 'rhodecode_pre_code': '',
146 'rhodecode_pre_code': '',
147 'rhodecode_post_code': '',
147 'rhodecode_post_code': '',
148 'csrf_token': csrf_token,
148 'csrf_token': csrf_token,
149 })
149 })
150
150
151 response = response.follow()
151 response = response.follow()
152 response.mustcontain(no=[pre_code, post_code])
152 response.mustcontain(no=[pre_code, post_code])
153
153
154 def test_captcha_activate(self, csrf_token):
154 def test_captcha_activate(self, csrf_token):
155 self.post_and_verify_settings({
155 self.post_and_verify_settings({
156 'rhodecode_captcha_private_key': '1234567890',
156 'rhodecode_captcha_private_key': '1234567890',
157 'rhodecode_captcha_public_key': '1234567890',
157 'rhodecode_captcha_public_key': '1234567890',
158 'csrf_token': csrf_token,
158 'csrf_token': csrf_token,
159 })
159 })
160
160
161 response = self.app.get(ADMIN_PREFIX + '/register')
161 response = self.app.get(ADMIN_PREFIX + '/register')
162 response.mustcontain('captcha')
162 response.mustcontain('captcha')
163
163
164 def test_captcha_deactivate(self, csrf_token):
164 def test_captcha_deactivate(self, csrf_token):
165 self.post_and_verify_settings({
165 self.post_and_verify_settings({
166 'rhodecode_captcha_private_key': '',
166 'rhodecode_captcha_private_key': '',
167 'rhodecode_captcha_public_key': '1234567890',
167 'rhodecode_captcha_public_key': '1234567890',
168 'csrf_token': csrf_token,
168 'csrf_token': csrf_token,
169 })
169 })
170
170
171 response = self.app.get(ADMIN_PREFIX + '/register')
171 response = self.app.get(ADMIN_PREFIX + '/register')
172 response.mustcontain(no=['captcha'])
172 response.mustcontain(no=['captcha'])
173
173
174 def test_title_change(self, csrf_token):
174 def test_title_change(self, csrf_token):
175 old_title = 'RhodeCode'
175 old_title = 'RhodeCode'
176 new_title = old_title + '_changed'
176 new_title = old_title + '_changed'
177
177
178 for new_title in ['Changed', 'Ε»Γ³Ε‚wik', old_title]:
178 for new_title in ['Changed', 'Ε»Γ³Ε‚wik', old_title]:
179 response = self.post_and_verify_settings({
179 response = self.post_and_verify_settings({
180 'rhodecode_title': new_title,
180 'rhodecode_title': new_title,
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(
185 response.mustcontain(
186 """<div class="branding">- %s</div>""" % new_title)
186 """<div class="branding">- %s</div>""" % new_title)
187
187
188 def post_and_verify_settings(self, settings):
188 def post_and_verify_settings(self, settings):
189 old_title = 'RhodeCode'
189 old_title = 'RhodeCode'
190 old_realm = 'RhodeCode authentication'
190 old_realm = 'RhodeCode authentication'
191 params = {
191 params = {
192 'rhodecode_title': old_title,
192 'rhodecode_title': old_title,
193 'rhodecode_realm': old_realm,
193 'rhodecode_realm': old_realm,
194 'rhodecode_pre_code': '',
194 'rhodecode_pre_code': '',
195 'rhodecode_post_code': '',
195 'rhodecode_post_code': '',
196 'rhodecode_captcha_private_key': '',
196 'rhodecode_captcha_private_key': '',
197 'rhodecode_captcha_public_key': '',
197 'rhodecode_captcha_public_key': '',
198 }
198 }
199 params.update(settings)
199 params.update(settings)
200 response = self.app.post(url('admin_settings_global'), params=params)
200 response = self.app.post(url('admin_settings_global'), params=params)
201
201
202 assert_session_flash(response, 'Updated application settings')
202 assert_session_flash(response, 'Updated application settings')
203 app_settings = SettingsModel().get_all_settings()
203 app_settings = SettingsModel().get_all_settings()
204 del settings['csrf_token']
204 del settings['csrf_token']
205 for key, value in settings.iteritems():
205 for key, value in settings.iteritems():
206 assert app_settings[key] == value.decode('utf-8')
206 assert app_settings[key] == value.decode('utf-8')
207
207
208 return response
208 return response
209
209
210
210
211 @pytest.mark.usefixtures('autologin_user', 'app')
211 @pytest.mark.usefixtures('autologin_user', 'app')
212 class TestAdminSettingsVcs:
212 class TestAdminSettingsVcs:
213
213
214 def test_contains_svn_default_patterns(self, app):
214 def test_contains_svn_default_patterns(self, app):
215 response = app.get(url('admin_settings_vcs'))
215 response = app.get(url('admin_settings_vcs'))
216 expected_patterns = [
216 expected_patterns = [
217 '/trunk',
217 '/trunk',
218 '/branches/*',
218 '/branches/*',
219 '/tags/*',
219 '/tags/*',
220 ]
220 ]
221 for pattern in expected_patterns:
221 for pattern in expected_patterns:
222 response.mustcontain(pattern)
222 response.mustcontain(pattern)
223
223
224 def test_add_new_svn_branch_and_tag_pattern(
224 def test_add_new_svn_branch_and_tag_pattern(
225 self, app, backend_svn, form_defaults, disable_sql_cache,
225 self, app, backend_svn, form_defaults, disable_sql_cache,
226 csrf_token):
226 csrf_token):
227 form_defaults.update({
227 form_defaults.update({
228 'new_svn_branch': '/exp/branches/*',
228 'new_svn_branch': '/exp/branches/*',
229 'new_svn_tag': '/important_tags/*',
229 'new_svn_tag': '/important_tags/*',
230 'csrf_token': csrf_token,
230 'csrf_token': csrf_token,
231 })
231 })
232
232
233 response = app.post(
233 response = app.post(
234 url('admin_settings_vcs'), params=form_defaults, status=302)
234 url('admin_settings_vcs'), params=form_defaults, status=302)
235 response = response.follow()
235 response = response.follow()
236
236
237 # Expect to find the new values on the page
237 # Expect to find the new values on the page
238 response.mustcontain('/exp/branches/*')
238 response.mustcontain('/exp/branches/*')
239 response.mustcontain('/important_tags/*')
239 response.mustcontain('/important_tags/*')
240
240
241 # Expect that those patterns are used to match branches and tags now
241 # Expect that those patterns are used to match branches and tags now
242 repo = backend_svn['svn-simple-layout'].scm_instance()
242 repo = backend_svn['svn-simple-layout'].scm_instance()
243 assert 'exp/branches/exp-sphinx-docs' in repo.branches
243 assert 'exp/branches/exp-sphinx-docs' in repo.branches
244 assert 'important_tags/v0.5' in repo.tags
244 assert 'important_tags/v0.5' in repo.tags
245
245
246 def test_add_same_svn_value_twice_shows_an_error_message(
246 def test_add_same_svn_value_twice_shows_an_error_message(
247 self, app, form_defaults, csrf_token, settings_util):
247 self, app, form_defaults, csrf_token, settings_util):
248 settings_util.create_rhodecode_ui('vcs_svn_branch', '/test')
248 settings_util.create_rhodecode_ui('vcs_svn_branch', '/test')
249 settings_util.create_rhodecode_ui('vcs_svn_tag', '/test')
249 settings_util.create_rhodecode_ui('vcs_svn_tag', '/test')
250
250
251 response = app.post(
251 response = app.post(
252 url('admin_settings_vcs'),
252 url('admin_settings_vcs'),
253 params={
253 params={
254 'paths_root_path': form_defaults['paths_root_path'],
254 'paths_root_path': form_defaults['paths_root_path'],
255 'new_svn_branch': '/test',
255 'new_svn_branch': '/test',
256 'new_svn_tag': '/test',
256 'new_svn_tag': '/test',
257 'csrf_token': csrf_token,
257 'csrf_token': csrf_token,
258 },
258 },
259 status=200)
259 status=200)
260
260
261 response.mustcontain("Pattern already exists")
261 response.mustcontain("Pattern already exists")
262 response.mustcontain("Some form inputs contain invalid data.")
262 response.mustcontain("Some form inputs contain invalid data.")
263
263
264 @pytest.mark.parametrize('section', [
264 @pytest.mark.parametrize('section', [
265 'vcs_svn_branch',
265 'vcs_svn_branch',
266 'vcs_svn_tag',
266 'vcs_svn_tag',
267 ])
267 ])
268 def test_delete_svn_patterns(
268 def test_delete_svn_patterns(
269 self, section, app, csrf_token, settings_util):
269 self, section, app, csrf_token, settings_util):
270 setting = settings_util.create_rhodecode_ui(
270 setting = settings_util.create_rhodecode_ui(
271 section, '/test_delete', cleanup=False)
271 section, '/test_delete', cleanup=False)
272
272
273 app.post(
273 app.post(
274 url('admin_settings_vcs'),
274 url('admin_settings_vcs'),
275 params={
275 params={
276 '_method': 'delete',
276 '_method': 'delete',
277 'delete_svn_pattern': setting.ui_id,
277 'delete_svn_pattern': setting.ui_id,
278 'csrf_token': csrf_token},
278 'csrf_token': csrf_token},
279 headers={'X-REQUESTED-WITH': 'XMLHttpRequest'})
279 headers={'X-REQUESTED-WITH': 'XMLHttpRequest'})
280
280
281 @pytest.mark.parametrize('section', [
281 @pytest.mark.parametrize('section', [
282 'vcs_svn_branch',
282 'vcs_svn_branch',
283 'vcs_svn_tag',
283 'vcs_svn_tag',
284 ])
284 ])
285 def test_delete_svn_patterns_raises_400_when_no_xhr(
285 def test_delete_svn_patterns_raises_400_when_no_xhr(
286 self, section, app, csrf_token, settings_util):
286 self, section, app, csrf_token, settings_util):
287 setting = settings_util.create_rhodecode_ui(section, '/test_delete')
287 setting = settings_util.create_rhodecode_ui(section, '/test_delete')
288
288
289 app.post(
289 app.post(
290 url('admin_settings_vcs'),
290 url('admin_settings_vcs'),
291 params={
291 params={
292 '_method': 'delete',
292 '_method': 'delete',
293 'delete_svn_pattern': setting.ui_id,
293 'delete_svn_pattern': setting.ui_id,
294 'csrf_token': csrf_token},
294 'csrf_token': csrf_token},
295 status=400)
295 status=400)
296
296
297 def test_extensions_hgsubversion(self, app, form_defaults, csrf_token):
297 def test_extensions_hgsubversion(self, app, form_defaults, csrf_token):
298 form_defaults.update({
298 form_defaults.update({
299 'csrf_token': csrf_token,
299 'csrf_token': csrf_token,
300 'extensions_hgsubversion': 'True',
300 'extensions_hgsubversion': 'True',
301 })
301 })
302 response = app.post(
302 response = app.post(
303 url('admin_settings_vcs'),
303 url('admin_settings_vcs'),
304 params=form_defaults,
304 params=form_defaults,
305 status=302)
305 status=302)
306
306
307 response = response.follow()
307 response = response.follow()
308 extensions_input = (
308 extensions_input = (
309 '<input id="extensions_hgsubversion" '
309 '<input id="extensions_hgsubversion" '
310 'name="extensions_hgsubversion" type="checkbox" '
310 'name="extensions_hgsubversion" type="checkbox" '
311 'value="True" checked="checked" />')
311 'value="True" checked="checked" />')
312 response.mustcontain(extensions_input)
312 response.mustcontain(extensions_input)
313
313
314 def test_has_a_section_for_pull_request_settings(self, app):
314 def test_has_a_section_for_pull_request_settings(self, app):
315 response = app.get(url('admin_settings_vcs'))
315 response = app.get(url('admin_settings_vcs'))
316 response.mustcontain('Pull Request Settings')
316 response.mustcontain('Pull Request Settings')
317
317
318 def test_has_an_input_for_invalidation_of_inline_comments(
318 def test_has_an_input_for_invalidation_of_inline_comments(
319 self, app):
319 self, app):
320 response = app.get(url('admin_settings_vcs'))
320 response = app.get(url('admin_settings_vcs'))
321 assert_response = AssertResponse(response)
321 assert_response = AssertResponse(response)
322 assert_response.one_element_exists(
322 assert_response.one_element_exists(
323 '[name=rhodecode_use_outdated_comments]')
323 '[name=rhodecode_use_outdated_comments]')
324
324
325 @pytest.mark.parametrize('new_value', [True, False])
325 @pytest.mark.parametrize('new_value', [True, False])
326 def test_allows_to_change_invalidation_of_inline_comments(
326 def test_allows_to_change_invalidation_of_inline_comments(
327 self, app, form_defaults, csrf_token, new_value):
327 self, app, form_defaults, csrf_token, new_value):
328 setting_key = 'use_outdated_comments'
328 setting_key = 'use_outdated_comments'
329 setting = SettingsModel().create_or_update_setting(
329 setting = SettingsModel().create_or_update_setting(
330 setting_key, not new_value, 'bool')
330 setting_key, not new_value, 'bool')
331 Session().add(setting)
331 Session().add(setting)
332 Session().commit()
332 Session().commit()
333
333
334 form_defaults.update({
334 form_defaults.update({
335 'csrf_token': csrf_token,
335 'csrf_token': csrf_token,
336 'rhodecode_use_outdated_comments': str(new_value),
336 'rhodecode_use_outdated_comments': str(new_value),
337 })
337 })
338 response = app.post(
338 response = app.post(
339 url('admin_settings_vcs'),
339 url('admin_settings_vcs'),
340 params=form_defaults,
340 params=form_defaults,
341 status=302)
341 status=302)
342 response = response.follow()
342 response = response.follow()
343 setting = SettingsModel().get_setting_by_name(setting_key)
343 setting = SettingsModel().get_setting_by_name(setting_key)
344 assert setting.app_settings_value is new_value
344 assert setting.app_settings_value is new_value
345
345
346 def test_has_a_section_for_labs_settings_if_enabled(self, app):
346 def test_has_a_section_for_labs_settings_if_enabled(self, app):
347 with mock.patch.dict(
347 with mock.patch.dict(
348 rhodecode.CONFIG, {'labs_settings_active': 'true'}):
348 rhodecode.CONFIG, {'labs_settings_active': 'true'}):
349 response = self.app.get(url('admin_settings_vcs'))
349 response = self.app.get(url('admin_settings_vcs'))
350 response.mustcontain('Labs Settings')
350 response.mustcontain('Labs Settings')
351
351
352 def test_has_not_a_section_for_labs_settings_if_disables(self, app):
352 def test_has_not_a_section_for_labs_settings_if_disables(self, app):
353 with mock.patch.dict(
353 with mock.patch.dict(
354 rhodecode.CONFIG, {'labs_settings_active': 'false'}):
354 rhodecode.CONFIG, {'labs_settings_active': 'false'}):
355 response = self.app.get(url('admin_settings_vcs'))
355 response = self.app.get(url('admin_settings_vcs'))
356 response.mustcontain(no='Labs Settings')
356 response.mustcontain(no='Labs Settings')
357
357
358 @pytest.mark.parametrize('new_value', [True, False])
358 @pytest.mark.parametrize('new_value', [True, False])
359 def test_allows_to_change_hg_rebase_merge_strategy(
359 def test_allows_to_change_hg_rebase_merge_strategy(
360 self, app, form_defaults, csrf_token, new_value):
360 self, app, form_defaults, csrf_token, new_value):
361 setting_key = 'hg_use_rebase_for_merging'
361 setting_key = 'hg_use_rebase_for_merging'
362
362
363 form_defaults.update({
363 form_defaults.update({
364 'csrf_token': csrf_token,
364 'csrf_token': csrf_token,
365 'rhodecode_' + setting_key: str(new_value),
365 'rhodecode_' + setting_key: str(new_value),
366 })
366 })
367
367
368 with mock.patch.dict(
368 with mock.patch.dict(
369 rhodecode.CONFIG, {'labs_settings_active': 'true'}):
369 rhodecode.CONFIG, {'labs_settings_active': 'true'}):
370 app.post(
370 app.post(
371 url('admin_settings_vcs'),
371 url('admin_settings_vcs'),
372 params=form_defaults,
372 params=form_defaults,
373 status=302)
373 status=302)
374
374
375 setting = SettingsModel().get_setting_by_name(setting_key)
375 setting = SettingsModel().get_setting_by_name(setting_key)
376 assert setting.app_settings_value is new_value
376 assert setting.app_settings_value is new_value
377
377
378 @pytest.fixture
378 @pytest.fixture
379 def disable_sql_cache(self, request):
379 def disable_sql_cache(self, request):
380 patcher = mock.patch(
380 patcher = mock.patch(
381 'rhodecode.lib.caching_query.FromCache.process_query')
381 'rhodecode.lib.caching_query.FromCache.process_query')
382 request.addfinalizer(patcher.stop)
382 request.addfinalizer(patcher.stop)
383 patcher.start()
383 patcher.start()
384
384
385 @pytest.fixture
385 @pytest.fixture
386 def form_defaults(self):
386 def form_defaults(self):
387 from rhodecode.controllers.admin.settings import SettingsController
387 from rhodecode.controllers.admin.settings import SettingsController
388 controller = SettingsController()
388 controller = SettingsController()
389 return controller._form_defaults()
389 return controller._form_defaults()
390
390
391 # TODO: johbo: What we really want is to checkpoint before a test run and
391 # TODO: johbo: What we really want is to checkpoint before a test run and
392 # reset the session afterwards.
392 # reset the session afterwards.
393 @pytest.fixture(scope='class', autouse=True)
393 @pytest.fixture(scope='class', autouse=True)
394 def cleanup_settings(self, request, pylonsapp):
394 def cleanup_settings(self, request, pylonsapp):
395 ui_id = RhodeCodeUi.ui_id
395 ui_id = RhodeCodeUi.ui_id
396 original_ids = list(
396 original_ids = list(
397 r.ui_id for r in RhodeCodeUi.query().values(ui_id))
397 r.ui_id for r in RhodeCodeUi.query().values(ui_id))
398
398
399 @request.addfinalizer
399 @request.addfinalizer
400 def cleanup():
400 def cleanup():
401 RhodeCodeUi.query().filter(
401 RhodeCodeUi.query().filter(
402 ui_id.notin_(original_ids)).delete(False)
402 ui_id.notin_(original_ids)).delete(False)
403
403
404
404
405 @pytest.mark.usefixtures('autologin_user', 'app')
405 @pytest.mark.usefixtures('autologin_user', 'app')
406 class TestLabsSettings(object):
406 class TestLabsSettings(object):
407 def test_get_settings_page_disabled(self):
407 def test_get_settings_page_disabled(self):
408 with mock.patch.dict(rhodecode.CONFIG,
408 with mock.patch.dict(rhodecode.CONFIG,
409 {'labs_settings_active': 'false'}):
409 {'labs_settings_active': 'false'}):
410 response = self.app.get(url('admin_settings_labs'), status=302)
410 response = self.app.get(url('admin_settings_labs'), status=302)
411
411
412 assert response.location.endswith(url('admin_settings'))
412 assert response.location.endswith(url('admin_settings'))
413
413
414 def test_get_settings_page_enabled(self):
414 def test_get_settings_page_enabled(self):
415 from rhodecode.controllers.admin import settings
415 from rhodecode.controllers.admin import settings
416 lab_settings = [
416 lab_settings = [
417 settings.LabSetting(
417 settings.LabSetting(
418 key='rhodecode_bool',
418 key='rhodecode_bool',
419 type='bool',
419 type='bool',
420 group='bool group',
420 group='bool group',
421 label='bool label',
421 label='bool label',
422 help='bool help'
422 help='bool help'
423 ),
423 ),
424 settings.LabSetting(
424 settings.LabSetting(
425 key='rhodecode_text',
425 key='rhodecode_text',
426 type='unicode',
426 type='unicode',
427 group='text group',
427 group='text group',
428 label='text label',
428 label='text label',
429 help='text help'
429 help='text help'
430 ),
430 ),
431 ]
431 ]
432 with mock.patch.dict(rhodecode.CONFIG,
432 with mock.patch.dict(rhodecode.CONFIG,
433 {'labs_settings_active': 'true'}):
433 {'labs_settings_active': 'true'}):
434 with mock.patch.object(settings, '_LAB_SETTINGS', lab_settings):
434 with mock.patch.object(settings, '_LAB_SETTINGS', lab_settings):
435 response = self.app.get(url('admin_settings_labs'))
435 response = self.app.get(url('admin_settings_labs'))
436
436
437 assert '<label>bool group:</label>' in response
437 assert '<label>bool group:</label>' in response
438 assert '<label for="rhodecode_bool">bool label</label>' in response
438 assert '<label for="rhodecode_bool">bool label</label>' in response
439 assert '<p class="help-block">bool help</p>' in response
439 assert '<p class="help-block">bool help</p>' in response
440 assert 'name="rhodecode_bool" type="checkbox"' in response
440 assert 'name="rhodecode_bool" type="checkbox"' in response
441
441
442 assert '<label>text group:</label>' in response
442 assert '<label>text group:</label>' in response
443 assert '<label for="rhodecode_text">text label</label>' in response
443 assert '<label for="rhodecode_text">text label</label>' in response
444 assert '<p class="help-block">text help</p>' in response
444 assert '<p class="help-block">text help</p>' in response
445 assert 'name="rhodecode_text" size="60" type="text"' in response
445 assert 'name="rhodecode_text" size="60" type="text"' in response
446
446
447
447
448 @pytest.mark.usefixtures('app')
448 @pytest.mark.usefixtures('app')
449 class TestOpenSourceLicenses(object):
449 class TestOpenSourceLicenses(object):
450
450
451 def _get_url(self):
451 def _get_url(self):
452 return ADMIN_PREFIX + '/settings/open_source'
452 return ADMIN_PREFIX + '/settings/open_source'
453
453
454 def test_records_are_displayed(self, autologin_user):
454 def test_records_are_displayed(self, autologin_user):
455 sample_licenses = {
455 sample_licenses = {
456 "python2.7-pytest-2.7.1": {
456 "python2.7-pytest-2.7.1": {
457 "UNKNOWN": None
457 "UNKNOWN": None
458 },
458 },
459 "python2.7-Markdown-2.6.2": {
459 "python2.7-Markdown-2.6.2": {
460 "BSD-3-Clause": "http://spdx.org/licenses/BSD-3-Clause"
460 "BSD-3-Clause": "http://spdx.org/licenses/BSD-3-Clause"
461 }
461 }
462 }
462 }
463 read_licenses_patch = mock.patch(
463 read_licenses_patch = mock.patch(
464 'rhodecode.admin.views.read_opensource_licenses',
464 'rhodecode.admin.views.read_opensource_licenses',
465 return_value=sample_licenses)
465 return_value=sample_licenses)
466 with read_licenses_patch:
466 with read_licenses_patch:
467 response = self.app.get(self._get_url(), status=200)
467 response = self.app.get(self._get_url(), status=200)
468
468
469 assert_response = AssertResponse(response)
469 assert_response = AssertResponse(response)
470 assert_response.element_contains(
470 assert_response.element_contains(
471 '.panel-heading', 'Licenses of Third Party Packages')
471 '.panel-heading', 'Licenses of Third Party Packages')
472 for name in sample_licenses:
472 for name in sample_licenses:
473 response.mustcontain(name)
473 response.mustcontain(name)
474 for license in sample_licenses[name]:
474 for license in sample_licenses[name]:
475 assert_response.element_contains('.panel-body', license)
475 assert_response.element_contains('.panel-body', license)
476
476
477 def test_records_can_be_read(self, autologin_user):
477 def test_records_can_be_read(self, autologin_user):
478 response = self.app.get(self._get_url(), status=200)
478 response = self.app.get(self._get_url(), status=200)
479 assert_response = AssertResponse(response)
479 assert_response = AssertResponse(response)
480 assert_response.element_contains(
480 assert_response.element_contains(
481 '.panel-heading', 'Licenses of Third Party Packages')
481 '.panel-heading', 'Licenses of Third Party Packages')
482
482
483 def test_forbidden_when_normal_user(self, autologin_regular_user):
483 def test_forbidden_when_normal_user(self, autologin_regular_user):
484 self.app.get(self._get_url(), status=403)
484 self.app.get(self._get_url(), status=403)
485
485
486
486
487 @pytest.mark.usefixtures("app")
487 @pytest.mark.usefixtures("app")
488 class TestAdminSettingsIssueTracker:
488 class TestAdminSettingsIssueTracker:
489 RC_PREFIX = 'rhodecode_'
489 RC_PREFIX = 'rhodecode_'
490 SHORT_PATTERN_KEY = 'issuetracker_pat_'
490 SHORT_PATTERN_KEY = 'issuetracker_pat_'
491 PATTERN_KEY = RC_PREFIX + SHORT_PATTERN_KEY
491 PATTERN_KEY = RC_PREFIX + SHORT_PATTERN_KEY
492
492
493 def test_issuetracker_index(self, autologin_user):
493 def test_issuetracker_index(self, autologin_user):
494 response = self.app.get(url('admin_settings_issuetracker'))
494 response = self.app.get(url('admin_settings_issuetracker'))
495 assert response.status_code == 200
495 assert response.status_code == 200
496
496
497 def test_add_empty_issuetracker_pattern(
498 self, request, autologin_user, csrf_token):
499 post_url = url('admin_settings_issuetracker_save')
500 post_data = {
501 'csrf_token': csrf_token
502 }
503 self.app.post(post_url, post_data, status=302)
504
497 def test_add_issuetracker_pattern(
505 def test_add_issuetracker_pattern(
498 self, request, autologin_user, csrf_token):
506 self, request, autologin_user, csrf_token):
499 pattern = 'issuetracker_pat'
507 pattern = 'issuetracker_pat'
500 another_pattern = pattern+'1'
508 another_pattern = pattern+'1'
501 post_url = url('admin_settings_issuetracker_save')
509 post_url = url('admin_settings_issuetracker_save')
502 post_data = {
510 post_data = {
503 'new_pattern_pattern_0': pattern,
511 'new_pattern_pattern_0': pattern,
504 'new_pattern_url_0': 'url',
512 'new_pattern_url_0': 'url',
505 'new_pattern_prefix_0': 'prefix',
513 'new_pattern_prefix_0': 'prefix',
506 'new_pattern_description_0': 'description',
514 'new_pattern_description_0': 'description',
507 'new_pattern_pattern_1': another_pattern,
515 'new_pattern_pattern_1': another_pattern,
508 'new_pattern_url_1': 'url1',
516 'new_pattern_url_1': 'url1',
509 'new_pattern_prefix_1': 'prefix1',
517 'new_pattern_prefix_1': 'prefix1',
510 'new_pattern_description_1': 'description1',
518 'new_pattern_description_1': 'description1',
511 'csrf_token': csrf_token
519 'csrf_token': csrf_token
512 }
520 }
513 self.app.post(post_url, post_data, status=302)
521 self.app.post(post_url, post_data, status=302)
514 settings = SettingsModel().get_all_settings()
522 settings = SettingsModel().get_all_settings()
515 self.uid = md5(pattern)
523 self.uid = md5(pattern)
516 assert settings[self.PATTERN_KEY+self.uid] == pattern
524 assert settings[self.PATTERN_KEY+self.uid] == pattern
517 self.another_uid = md5(another_pattern)
525 self.another_uid = md5(another_pattern)
518 assert settings[self.PATTERN_KEY+self.another_uid] == another_pattern
526 assert settings[self.PATTERN_KEY+self.another_uid] == another_pattern
519
527
520 @request.addfinalizer
528 @request.addfinalizer
521 def cleanup():
529 def cleanup():
522 defaults = SettingsModel().get_all_settings()
530 defaults = SettingsModel().get_all_settings()
523
531
524 entries = [name for name in defaults if (
532 entries = [name for name in defaults if (
525 (self.uid in name) or (self.another_uid) in name)]
533 (self.uid in name) or (self.another_uid) in name)]
526 start = len(self.RC_PREFIX)
534 start = len(self.RC_PREFIX)
527 for del_key in entries:
535 for del_key in entries:
528 # TODO: anderson: get_by_name needs name without prefix
536 # TODO: anderson: get_by_name needs name without prefix
529 entry = SettingsModel().get_setting_by_name(del_key[start:])
537 entry = SettingsModel().get_setting_by_name(del_key[start:])
530 Session().delete(entry)
538 Session().delete(entry)
531
539
532 Session().commit()
540 Session().commit()
533
541
534 def test_edit_issuetracker_pattern(
542 def test_edit_issuetracker_pattern(
535 self, autologin_user, backend, csrf_token, request):
543 self, autologin_user, backend, csrf_token, request):
536 old_pattern = 'issuetracker_pat'
544 old_pattern = 'issuetracker_pat'
537 old_uid = md5(old_pattern)
545 old_uid = md5(old_pattern)
538 pattern = 'issuetracker_pat_new'
546 pattern = 'issuetracker_pat_new'
539 self.new_uid = md5(pattern)
547 self.new_uid = md5(pattern)
540
548
541 SettingsModel().create_or_update_setting(
549 SettingsModel().create_or_update_setting(
542 self.SHORT_PATTERN_KEY+old_uid, old_pattern, 'unicode')
550 self.SHORT_PATTERN_KEY+old_uid, old_pattern, 'unicode')
543
551
544 post_url = url('admin_settings_issuetracker_save')
552 post_url = url('admin_settings_issuetracker_save')
545 post_data = {
553 post_data = {
546 'new_pattern_pattern_0': pattern,
554 'new_pattern_pattern_0': pattern,
547 'new_pattern_url_0': 'url',
555 'new_pattern_url_0': 'url',
548 'new_pattern_prefix_0': 'prefix',
556 'new_pattern_prefix_0': 'prefix',
549 'new_pattern_description_0': 'description',
557 'new_pattern_description_0': 'description',
550 'uid': old_uid,
558 'uid': old_uid,
551 'csrf_token': csrf_token
559 'csrf_token': csrf_token
552 }
560 }
553 self.app.post(post_url, post_data, status=302)
561 self.app.post(post_url, post_data, status=302)
554 settings = SettingsModel().get_all_settings()
562 settings = SettingsModel().get_all_settings()
555 assert settings[self.PATTERN_KEY+self.new_uid] == pattern
563 assert settings[self.PATTERN_KEY+self.new_uid] == pattern
556 assert self.PATTERN_KEY+old_uid not in settings
564 assert self.PATTERN_KEY+old_uid not in settings
557
565
558 @request.addfinalizer
566 @request.addfinalizer
559 def cleanup():
567 def cleanup():
560 IssueTrackerSettingsModel().delete_entries(self.new_uid)
568 IssueTrackerSettingsModel().delete_entries(self.new_uid)
561
569
562 def test_replace_issuetracker_pattern_description(
570 def test_replace_issuetracker_pattern_description(
563 self, autologin_user, csrf_token, request, settings_util):
571 self, autologin_user, csrf_token, request, settings_util):
564 prefix = 'issuetracker'
572 prefix = 'issuetracker'
565 pattern = 'issuetracker_pat'
573 pattern = 'issuetracker_pat'
566 self.uid = md5(pattern)
574 self.uid = md5(pattern)
567 pattern_key = '_'.join([prefix, 'pat', self.uid])
575 pattern_key = '_'.join([prefix, 'pat', self.uid])
568 rc_pattern_key = '_'.join(['rhodecode', pattern_key])
576 rc_pattern_key = '_'.join(['rhodecode', pattern_key])
569 desc_key = '_'.join([prefix, 'desc', self.uid])
577 desc_key = '_'.join([prefix, 'desc', self.uid])
570 rc_desc_key = '_'.join(['rhodecode', desc_key])
578 rc_desc_key = '_'.join(['rhodecode', desc_key])
571 new_description = 'new_description'
579 new_description = 'new_description'
572
580
573 settings_util.create_rhodecode_setting(
581 settings_util.create_rhodecode_setting(
574 pattern_key, pattern, 'unicode', cleanup=False)
582 pattern_key, pattern, 'unicode', cleanup=False)
575 settings_util.create_rhodecode_setting(
583 settings_util.create_rhodecode_setting(
576 desc_key, 'old description', 'unicode', cleanup=False)
584 desc_key, 'old description', 'unicode', cleanup=False)
577
585
578 post_url = url('admin_settings_issuetracker_save')
586 post_url = url('admin_settings_issuetracker_save')
579 post_data = {
587 post_data = {
580 'new_pattern_pattern_0': pattern,
588 'new_pattern_pattern_0': pattern,
581 'new_pattern_url_0': 'url',
589 'new_pattern_url_0': 'url',
582 'new_pattern_prefix_0': 'prefix',
590 'new_pattern_prefix_0': 'prefix',
583 'new_pattern_description_0': new_description,
591 'new_pattern_description_0': new_description,
584 'uid': self.uid,
592 'uid': self.uid,
585 'csrf_token': csrf_token
593 'csrf_token': csrf_token
586 }
594 }
587 self.app.post(post_url, post_data, status=302)
595 self.app.post(post_url, post_data, status=302)
588 settings = SettingsModel().get_all_settings()
596 settings = SettingsModel().get_all_settings()
589 assert settings[rc_pattern_key] == pattern
597 assert settings[rc_pattern_key] == pattern
590 assert settings[rc_desc_key] == new_description
598 assert settings[rc_desc_key] == new_description
591
599
592 @request.addfinalizer
600 @request.addfinalizer
593 def cleanup():
601 def cleanup():
594 IssueTrackerSettingsModel().delete_entries(self.uid)
602 IssueTrackerSettingsModel().delete_entries(self.uid)
595
603
596 def test_delete_issuetracker_pattern(
604 def test_delete_issuetracker_pattern(
597 self, autologin_user, backend, csrf_token, settings_util):
605 self, autologin_user, backend, csrf_token, settings_util):
598 pattern = 'issuetracker_pat'
606 pattern = 'issuetracker_pat'
599 uid = md5(pattern)
607 uid = md5(pattern)
600 settings_util.create_rhodecode_setting(
608 settings_util.create_rhodecode_setting(
601 self.SHORT_PATTERN_KEY+uid, pattern, 'unicode', cleanup=False)
609 self.SHORT_PATTERN_KEY+uid, pattern, 'unicode', cleanup=False)
602
610
603 post_url = url('admin_issuetracker_delete')
611 post_url = url('admin_issuetracker_delete')
604 post_data = {
612 post_data = {
605 '_method': 'delete',
613 '_method': 'delete',
606 'uid': uid,
614 'uid': uid,
607 'csrf_token': csrf_token
615 'csrf_token': csrf_token
608 }
616 }
609 self.app.post(post_url, post_data, status=302)
617 self.app.post(post_url, post_data, status=302)
610 settings = SettingsModel().get_all_settings()
618 settings = SettingsModel().get_all_settings()
611 assert 'rhodecode_%s%s' % (self.SHORT_PATTERN_KEY, uid) not in settings
619 assert 'rhodecode_%s%s' % (self.SHORT_PATTERN_KEY, uid) not in settings
General Comments 0
You need to be logged in to leave comments. Login now