Show More
@@ -0,0 +1,71 b'' | |||||
|
1 | # -*- coding: utf-8 -*- | |||
|
2 | ||||
|
3 | # Copyright (C) 2013-2017 RhodeCode GmbH | |||
|
4 | # | |||
|
5 | # This program is free software: you can redistribute it and/or modify | |||
|
6 | # it under the terms of the GNU Affero General Public License, version 3 | |||
|
7 | # (only), as published by the Free Software Foundation. | |||
|
8 | # | |||
|
9 | # This program is distributed in the hope that it will be useful, | |||
|
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
|
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
|
12 | # GNU General Public License for more details. | |||
|
13 | # | |||
|
14 | # You should have received a copy of the GNU Affero General Public License | |||
|
15 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
|
16 | # | |||
|
17 | # This program is dual-licensed. If you wish to learn more about the | |||
|
18 | # RhodeCode Enterprise Edition, including its added features, Support services, | |||
|
19 | # and proprietary license terms, please see https://rhodecode.com/licenses/ | |||
|
20 | ||||
|
21 | import logging | |||
|
22 | import urllib2 | |||
|
23 | from packaging import version | |||
|
24 | ||||
|
25 | import rhodecode | |||
|
26 | from rhodecode.lib.ext_json import json | |||
|
27 | from rhodecode.model import BaseModel | |||
|
28 | from rhodecode.model.meta import Session | |||
|
29 | from rhodecode.model.settings import SettingsModel | |||
|
30 | ||||
|
31 | ||||
|
32 | log = logging.getLogger(__name__) | |||
|
33 | ||||
|
34 | ||||
|
35 | class UpdateModel(BaseModel): | |||
|
36 | UPDATE_SETTINGS_KEY = 'update_version' | |||
|
37 | UPDATE_URL_SETTINGS_KEY = 'rhodecode_update_url' | |||
|
38 | ||||
|
39 | @staticmethod | |||
|
40 | def get_update_data(update_url): | |||
|
41 | """Return the JSON update data.""" | |||
|
42 | ver = rhodecode.__version__ | |||
|
43 | log.debug('Checking for upgrade on `%s` server', update_url) | |||
|
44 | opener = urllib2.build_opener() | |||
|
45 | opener.addheaders = [('User-agent', 'RhodeCode-SCM/%s' % ver)] | |||
|
46 | response = opener.open(update_url) | |||
|
47 | response_data = response.read() | |||
|
48 | data = json.loads(response_data) | |||
|
49 | log.debug('update server returned data') | |||
|
50 | return data | |||
|
51 | ||||
|
52 | def get_update_url(self): | |||
|
53 | settings = SettingsModel().get_all_settings() | |||
|
54 | return settings.get(self.UPDATE_URL_SETTINGS_KEY) | |||
|
55 | ||||
|
56 | def store_version(self, version): | |||
|
57 | log.debug('Storing version %s into settings', version) | |||
|
58 | setting = SettingsModel().create_or_update_setting( | |||
|
59 | self.UPDATE_SETTINGS_KEY, version) | |||
|
60 | Session().add(setting) | |||
|
61 | Session().commit() | |||
|
62 | ||||
|
63 | def get_stored_version(self): | |||
|
64 | obj = SettingsModel().get_setting_by_name(self.UPDATE_SETTINGS_KEY) | |||
|
65 | if obj: | |||
|
66 | return obj.app_settings_value | |||
|
67 | return '0.0.0' | |||
|
68 | ||||
|
69 | def is_outdated(self, cur_version, latest_version=None): | |||
|
70 | latest_version = latest_version or self.get_stored_version() | |||
|
71 | return version.Version(latest_version) > version.Version(cur_version) |
@@ -1,730 +1,729 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 |
|
2 | |||
3 | # Copyright (C) 2010-2017 RhodeCode GmbH |
|
3 | # Copyright (C) 2010-2017 RhodeCode GmbH | |
4 | # |
|
4 | # | |
5 | # This program is free software: you can redistribute it and/or modify |
|
5 | # This program is free software: you can redistribute it and/or modify | |
6 | # it under the terms of the GNU Affero General Public License, version 3 |
|
6 | # it under the terms of the GNU Affero General Public License, version 3 | |
7 | # (only), as published by the Free Software Foundation. |
|
7 | # (only), as published by the Free Software Foundation. | |
8 | # |
|
8 | # | |
9 | # This program is distributed in the hope that it will be useful, |
|
9 | # This program is distributed in the hope that it will be useful, | |
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | # GNU General Public License for more details. |
|
12 | # GNU General Public License for more details. | |
13 | # |
|
13 | # | |
14 | # You should have received a copy of the GNU Affero General Public License |
|
14 | # You should have received a copy of the GNU Affero General Public License | |
15 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
15 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
16 | # |
|
16 | # | |
17 | # This program is dual-licensed. If you wish to learn more about the |
|
17 | # This program is dual-licensed. If you wish to learn more about the | |
18 | # RhodeCode Enterprise Edition, including its added features, Support services, |
|
18 | # RhodeCode Enterprise Edition, including its added features, Support services, | |
19 | # and proprietary license terms, please see https://rhodecode.com/licenses/ |
|
19 | # and proprietary license terms, please see https://rhodecode.com/licenses/ | |
20 |
|
20 | |||
21 | import mock |
|
21 | import mock | |
22 | import pytest |
|
22 | import pytest | |
23 |
|
23 | |||
24 | import rhodecode |
|
24 | import rhodecode | |
25 | from rhodecode.apps._base import ADMIN_PREFIX |
|
25 | from rhodecode.apps._base import ADMIN_PREFIX | |
26 | from rhodecode.lib.utils2 import md5 |
|
26 | from rhodecode.lib.utils2 import md5 | |
27 | from rhodecode.model.db import RhodeCodeUi |
|
27 | from rhodecode.model.db import RhodeCodeUi | |
28 | from rhodecode.model.meta import Session |
|
28 | from rhodecode.model.meta import Session | |
29 | from rhodecode.model.settings import SettingsModel, IssueTrackerSettingsModel |
|
29 | from rhodecode.model.settings import SettingsModel, IssueTrackerSettingsModel | |
30 | from rhodecode.tests import assert_session_flash |
|
30 | from rhodecode.tests import assert_session_flash | |
31 | from rhodecode.tests.utils import AssertResponse |
|
31 | from rhodecode.tests.utils import AssertResponse | |
32 |
|
32 | |||
33 |
|
33 | |||
34 | UPDATE_DATA_QUALNAME = ( |
|
34 | UPDATE_DATA_QUALNAME = 'rhodecode.model.update.UpdateModel.get_update_data' | |
35 | 'rhodecode.apps.admin.views.system_info.AdminSystemInfoSettingsView.get_update_data') |
|
|||
36 |
|
35 | |||
37 |
|
36 | |||
38 | def route_path(name, params=None, **kwargs): |
|
37 | def route_path(name, params=None, **kwargs): | |
39 | import urllib |
|
38 | import urllib | |
40 | from rhodecode.apps._base import ADMIN_PREFIX |
|
39 | from rhodecode.apps._base import ADMIN_PREFIX | |
41 |
|
40 | |||
42 | base_url = { |
|
41 | base_url = { | |
43 |
|
42 | |||
44 | 'admin_settings': |
|
43 | 'admin_settings': | |
45 | ADMIN_PREFIX +'/settings', |
|
44 | ADMIN_PREFIX +'/settings', | |
46 | 'admin_settings_update': |
|
45 | 'admin_settings_update': | |
47 | ADMIN_PREFIX + '/settings/update', |
|
46 | ADMIN_PREFIX + '/settings/update', | |
48 | 'admin_settings_global': |
|
47 | 'admin_settings_global': | |
49 | ADMIN_PREFIX + '/settings/global', |
|
48 | ADMIN_PREFIX + '/settings/global', | |
50 | 'admin_settings_global_update': |
|
49 | 'admin_settings_global_update': | |
51 | ADMIN_PREFIX + '/settings/global/update', |
|
50 | ADMIN_PREFIX + '/settings/global/update', | |
52 | 'admin_settings_vcs': |
|
51 | 'admin_settings_vcs': | |
53 | ADMIN_PREFIX + '/settings/vcs', |
|
52 | ADMIN_PREFIX + '/settings/vcs', | |
54 | 'admin_settings_vcs_update': |
|
53 | 'admin_settings_vcs_update': | |
55 | ADMIN_PREFIX + '/settings/vcs/update', |
|
54 | ADMIN_PREFIX + '/settings/vcs/update', | |
56 | 'admin_settings_vcs_svn_pattern_delete': |
|
55 | 'admin_settings_vcs_svn_pattern_delete': | |
57 | ADMIN_PREFIX + '/settings/vcs/svn_pattern_delete', |
|
56 | ADMIN_PREFIX + '/settings/vcs/svn_pattern_delete', | |
58 | 'admin_settings_mapping': |
|
57 | 'admin_settings_mapping': | |
59 | ADMIN_PREFIX + '/settings/mapping', |
|
58 | ADMIN_PREFIX + '/settings/mapping', | |
60 | 'admin_settings_mapping_update': |
|
59 | 'admin_settings_mapping_update': | |
61 | ADMIN_PREFIX + '/settings/mapping/update', |
|
60 | ADMIN_PREFIX + '/settings/mapping/update', | |
62 | 'admin_settings_visual': |
|
61 | 'admin_settings_visual': | |
63 | ADMIN_PREFIX + '/settings/visual', |
|
62 | ADMIN_PREFIX + '/settings/visual', | |
64 | 'admin_settings_visual_update': |
|
63 | 'admin_settings_visual_update': | |
65 | ADMIN_PREFIX + '/settings/visual/update', |
|
64 | ADMIN_PREFIX + '/settings/visual/update', | |
66 | 'admin_settings_issuetracker': |
|
65 | 'admin_settings_issuetracker': | |
67 | ADMIN_PREFIX + '/settings/issue-tracker', |
|
66 | ADMIN_PREFIX + '/settings/issue-tracker', | |
68 | 'admin_settings_issuetracker_update': |
|
67 | 'admin_settings_issuetracker_update': | |
69 | ADMIN_PREFIX + '/settings/issue-tracker/update', |
|
68 | ADMIN_PREFIX + '/settings/issue-tracker/update', | |
70 | 'admin_settings_issuetracker_test': |
|
69 | 'admin_settings_issuetracker_test': | |
71 | ADMIN_PREFIX + '/settings/issue-tracker/test', |
|
70 | ADMIN_PREFIX + '/settings/issue-tracker/test', | |
72 | 'admin_settings_issuetracker_delete': |
|
71 | 'admin_settings_issuetracker_delete': | |
73 | ADMIN_PREFIX + '/settings/issue-tracker/delete', |
|
72 | ADMIN_PREFIX + '/settings/issue-tracker/delete', | |
74 | 'admin_settings_email': |
|
73 | 'admin_settings_email': | |
75 | ADMIN_PREFIX + '/settings/email', |
|
74 | ADMIN_PREFIX + '/settings/email', | |
76 | 'admin_settings_email_update': |
|
75 | 'admin_settings_email_update': | |
77 | ADMIN_PREFIX + '/settings/email/update', |
|
76 | ADMIN_PREFIX + '/settings/email/update', | |
78 | 'admin_settings_hooks': |
|
77 | 'admin_settings_hooks': | |
79 | ADMIN_PREFIX + '/settings/hooks', |
|
78 | ADMIN_PREFIX + '/settings/hooks', | |
80 | 'admin_settings_hooks_update': |
|
79 | 'admin_settings_hooks_update': | |
81 | ADMIN_PREFIX + '/settings/hooks/update', |
|
80 | ADMIN_PREFIX + '/settings/hooks/update', | |
82 | 'admin_settings_hooks_delete': |
|
81 | 'admin_settings_hooks_delete': | |
83 | ADMIN_PREFIX + '/settings/hooks/delete', |
|
82 | ADMIN_PREFIX + '/settings/hooks/delete', | |
84 | 'admin_settings_search': |
|
83 | 'admin_settings_search': | |
85 | ADMIN_PREFIX + '/settings/search', |
|
84 | ADMIN_PREFIX + '/settings/search', | |
86 | 'admin_settings_labs': |
|
85 | 'admin_settings_labs': | |
87 | ADMIN_PREFIX + '/settings/labs', |
|
86 | ADMIN_PREFIX + '/settings/labs', | |
88 | 'admin_settings_labs_update': |
|
87 | 'admin_settings_labs_update': | |
89 | ADMIN_PREFIX + '/settings/labs/update', |
|
88 | ADMIN_PREFIX + '/settings/labs/update', | |
90 |
|
89 | |||
91 | 'admin_settings_sessions': |
|
90 | 'admin_settings_sessions': | |
92 | ADMIN_PREFIX + '/settings/sessions', |
|
91 | ADMIN_PREFIX + '/settings/sessions', | |
93 | 'admin_settings_sessions_cleanup': |
|
92 | 'admin_settings_sessions_cleanup': | |
94 | ADMIN_PREFIX + '/settings/sessions/cleanup', |
|
93 | ADMIN_PREFIX + '/settings/sessions/cleanup', | |
95 | 'admin_settings_system': |
|
94 | 'admin_settings_system': | |
96 | ADMIN_PREFIX + '/settings/system', |
|
95 | ADMIN_PREFIX + '/settings/system', | |
97 | 'admin_settings_system_update': |
|
96 | 'admin_settings_system_update': | |
98 | ADMIN_PREFIX + '/settings/system/updates', |
|
97 | ADMIN_PREFIX + '/settings/system/updates', | |
99 | 'admin_settings_open_source': |
|
98 | 'admin_settings_open_source': | |
100 | ADMIN_PREFIX + '/settings/open_source', |
|
99 | ADMIN_PREFIX + '/settings/open_source', | |
101 |
|
100 | |||
102 |
|
101 | |||
103 | }[name].format(**kwargs) |
|
102 | }[name].format(**kwargs) | |
104 |
|
103 | |||
105 | if params: |
|
104 | if params: | |
106 | base_url = '{}?{}'.format(base_url, urllib.urlencode(params)) |
|
105 | base_url = '{}?{}'.format(base_url, urllib.urlencode(params)) | |
107 | return base_url |
|
106 | return base_url | |
108 |
|
107 | |||
109 |
|
108 | |||
110 | @pytest.mark.usefixtures('autologin_user', 'app') |
|
109 | @pytest.mark.usefixtures('autologin_user', 'app') | |
111 | class TestAdminSettingsController(object): |
|
110 | class TestAdminSettingsController(object): | |
112 |
|
111 | |||
113 | @pytest.mark.parametrize('urlname', [ |
|
112 | @pytest.mark.parametrize('urlname', [ | |
114 | 'admin_settings_vcs', |
|
113 | 'admin_settings_vcs', | |
115 | 'admin_settings_mapping', |
|
114 | 'admin_settings_mapping', | |
116 | 'admin_settings_global', |
|
115 | 'admin_settings_global', | |
117 | 'admin_settings_visual', |
|
116 | 'admin_settings_visual', | |
118 | 'admin_settings_email', |
|
117 | 'admin_settings_email', | |
119 | 'admin_settings_hooks', |
|
118 | 'admin_settings_hooks', | |
120 | 'admin_settings_search', |
|
119 | 'admin_settings_search', | |
121 | ]) |
|
120 | ]) | |
122 | def test_simple_get(self, urlname): |
|
121 | def test_simple_get(self, urlname): | |
123 | self.app.get(route_path(urlname)) |
|
122 | self.app.get(route_path(urlname)) | |
124 |
|
123 | |||
125 | def test_create_custom_hook(self, csrf_token): |
|
124 | def test_create_custom_hook(self, csrf_token): | |
126 | response = self.app.post( |
|
125 | response = self.app.post( | |
127 | route_path('admin_settings_hooks_update'), |
|
126 | route_path('admin_settings_hooks_update'), | |
128 | params={ |
|
127 | params={ | |
129 | 'new_hook_ui_key': 'test_hooks_1', |
|
128 | 'new_hook_ui_key': 'test_hooks_1', | |
130 | 'new_hook_ui_value': 'cd /tmp', |
|
129 | 'new_hook_ui_value': 'cd /tmp', | |
131 | 'csrf_token': csrf_token}) |
|
130 | 'csrf_token': csrf_token}) | |
132 |
|
131 | |||
133 | response = response.follow() |
|
132 | response = response.follow() | |
134 | response.mustcontain('test_hooks_1') |
|
133 | response.mustcontain('test_hooks_1') | |
135 | response.mustcontain('cd /tmp') |
|
134 | response.mustcontain('cd /tmp') | |
136 |
|
135 | |||
137 | def test_create_custom_hook_delete(self, csrf_token): |
|
136 | def test_create_custom_hook_delete(self, csrf_token): | |
138 | response = self.app.post( |
|
137 | response = self.app.post( | |
139 | route_path('admin_settings_hooks_update'), |
|
138 | route_path('admin_settings_hooks_update'), | |
140 | params={ |
|
139 | params={ | |
141 | 'new_hook_ui_key': 'test_hooks_2', |
|
140 | 'new_hook_ui_key': 'test_hooks_2', | |
142 | 'new_hook_ui_value': 'cd /tmp2', |
|
141 | 'new_hook_ui_value': 'cd /tmp2', | |
143 | 'csrf_token': csrf_token}) |
|
142 | 'csrf_token': csrf_token}) | |
144 |
|
143 | |||
145 | response = response.follow() |
|
144 | response = response.follow() | |
146 | response.mustcontain('test_hooks_2') |
|
145 | response.mustcontain('test_hooks_2') | |
147 | response.mustcontain('cd /tmp2') |
|
146 | response.mustcontain('cd /tmp2') | |
148 |
|
147 | |||
149 | hook_id = SettingsModel().get_ui_by_key('test_hooks_2').ui_id |
|
148 | hook_id = SettingsModel().get_ui_by_key('test_hooks_2').ui_id | |
150 |
|
149 | |||
151 | # delete |
|
150 | # delete | |
152 | self.app.post( |
|
151 | self.app.post( | |
153 | route_path('admin_settings_hooks_delete'), |
|
152 | route_path('admin_settings_hooks_delete'), | |
154 | params={'hook_id': hook_id, 'csrf_token': csrf_token}) |
|
153 | params={'hook_id': hook_id, 'csrf_token': csrf_token}) | |
155 | response = self.app.get(route_path('admin_settings_hooks')) |
|
154 | response = self.app.get(route_path('admin_settings_hooks')) | |
156 | response.mustcontain(no=['test_hooks_2']) |
|
155 | response.mustcontain(no=['test_hooks_2']) | |
157 | response.mustcontain(no=['cd /tmp2']) |
|
156 | response.mustcontain(no=['cd /tmp2']) | |
158 |
|
157 | |||
159 |
|
158 | |||
160 | @pytest.mark.usefixtures('autologin_user', 'app') |
|
159 | @pytest.mark.usefixtures('autologin_user', 'app') | |
161 | class TestAdminSettingsGlobal(object): |
|
160 | class TestAdminSettingsGlobal(object): | |
162 |
|
161 | |||
163 | def test_pre_post_code_code_active(self, csrf_token): |
|
162 | def test_pre_post_code_code_active(self, csrf_token): | |
164 | pre_code = 'rc-pre-code-187652122' |
|
163 | pre_code = 'rc-pre-code-187652122' | |
165 | post_code = 'rc-postcode-98165231' |
|
164 | post_code = 'rc-postcode-98165231' | |
166 |
|
165 | |||
167 | response = self.post_and_verify_settings({ |
|
166 | response = self.post_and_verify_settings({ | |
168 | 'rhodecode_pre_code': pre_code, |
|
167 | 'rhodecode_pre_code': pre_code, | |
169 | 'rhodecode_post_code': post_code, |
|
168 | 'rhodecode_post_code': post_code, | |
170 | 'csrf_token': csrf_token, |
|
169 | 'csrf_token': csrf_token, | |
171 | }) |
|
170 | }) | |
172 |
|
171 | |||
173 | response = response.follow() |
|
172 | response = response.follow() | |
174 | response.mustcontain(pre_code, post_code) |
|
173 | response.mustcontain(pre_code, post_code) | |
175 |
|
174 | |||
176 | def test_pre_post_code_code_inactive(self, csrf_token): |
|
175 | def test_pre_post_code_code_inactive(self, csrf_token): | |
177 | pre_code = 'rc-pre-code-187652122' |
|
176 | pre_code = 'rc-pre-code-187652122' | |
178 | post_code = 'rc-postcode-98165231' |
|
177 | post_code = 'rc-postcode-98165231' | |
179 | response = self.post_and_verify_settings({ |
|
178 | response = self.post_and_verify_settings({ | |
180 | 'rhodecode_pre_code': '', |
|
179 | 'rhodecode_pre_code': '', | |
181 | 'rhodecode_post_code': '', |
|
180 | 'rhodecode_post_code': '', | |
182 | 'csrf_token': csrf_token, |
|
181 | 'csrf_token': csrf_token, | |
183 | }) |
|
182 | }) | |
184 |
|
183 | |||
185 | response = response.follow() |
|
184 | response = response.follow() | |
186 | response.mustcontain(no=[pre_code, post_code]) |
|
185 | response.mustcontain(no=[pre_code, post_code]) | |
187 |
|
186 | |||
188 | def test_captcha_activate(self, csrf_token): |
|
187 | def test_captcha_activate(self, csrf_token): | |
189 | self.post_and_verify_settings({ |
|
188 | self.post_and_verify_settings({ | |
190 | 'rhodecode_captcha_private_key': '1234567890', |
|
189 | 'rhodecode_captcha_private_key': '1234567890', | |
191 | 'rhodecode_captcha_public_key': '1234567890', |
|
190 | 'rhodecode_captcha_public_key': '1234567890', | |
192 | 'csrf_token': csrf_token, |
|
191 | 'csrf_token': csrf_token, | |
193 | }) |
|
192 | }) | |
194 |
|
193 | |||
195 | response = self.app.get(ADMIN_PREFIX + '/register') |
|
194 | response = self.app.get(ADMIN_PREFIX + '/register') | |
196 | response.mustcontain('captcha') |
|
195 | response.mustcontain('captcha') | |
197 |
|
196 | |||
198 | def test_captcha_deactivate(self, csrf_token): |
|
197 | def test_captcha_deactivate(self, csrf_token): | |
199 | self.post_and_verify_settings({ |
|
198 | self.post_and_verify_settings({ | |
200 | 'rhodecode_captcha_private_key': '', |
|
199 | 'rhodecode_captcha_private_key': '', | |
201 | 'rhodecode_captcha_public_key': '1234567890', |
|
200 | 'rhodecode_captcha_public_key': '1234567890', | |
202 | 'csrf_token': csrf_token, |
|
201 | 'csrf_token': csrf_token, | |
203 | }) |
|
202 | }) | |
204 |
|
203 | |||
205 | response = self.app.get(ADMIN_PREFIX + '/register') |
|
204 | response = self.app.get(ADMIN_PREFIX + '/register') | |
206 | response.mustcontain(no=['captcha']) |
|
205 | response.mustcontain(no=['captcha']) | |
207 |
|
206 | |||
208 | def test_title_change(self, csrf_token): |
|
207 | def test_title_change(self, csrf_token): | |
209 | old_title = 'RhodeCode' |
|
208 | old_title = 'RhodeCode' | |
210 |
|
209 | |||
211 | for new_title in ['Changed', 'Ε»Γ³Εwik', old_title]: |
|
210 | for new_title in ['Changed', 'Ε»Γ³Εwik', old_title]: | |
212 | response = self.post_and_verify_settings({ |
|
211 | response = self.post_and_verify_settings({ | |
213 | 'rhodecode_title': new_title, |
|
212 | 'rhodecode_title': new_title, | |
214 | 'csrf_token': csrf_token, |
|
213 | 'csrf_token': csrf_token, | |
215 | }) |
|
214 | }) | |
216 |
|
215 | |||
217 | response = response.follow() |
|
216 | response = response.follow() | |
218 | response.mustcontain( |
|
217 | response.mustcontain( | |
219 | """<div class="branding">- %s</div>""" % new_title) |
|
218 | """<div class="branding">- %s</div>""" % new_title) | |
220 |
|
219 | |||
221 | def post_and_verify_settings(self, settings): |
|
220 | def post_and_verify_settings(self, settings): | |
222 | old_title = 'RhodeCode' |
|
221 | old_title = 'RhodeCode' | |
223 | old_realm = 'RhodeCode authentication' |
|
222 | old_realm = 'RhodeCode authentication' | |
224 | params = { |
|
223 | params = { | |
225 | 'rhodecode_title': old_title, |
|
224 | 'rhodecode_title': old_title, | |
226 | 'rhodecode_realm': old_realm, |
|
225 | 'rhodecode_realm': old_realm, | |
227 | 'rhodecode_pre_code': '', |
|
226 | 'rhodecode_pre_code': '', | |
228 | 'rhodecode_post_code': '', |
|
227 | 'rhodecode_post_code': '', | |
229 | 'rhodecode_captcha_private_key': '', |
|
228 | 'rhodecode_captcha_private_key': '', | |
230 | 'rhodecode_captcha_public_key': '', |
|
229 | 'rhodecode_captcha_public_key': '', | |
231 | 'rhodecode_create_personal_repo_group': False, |
|
230 | 'rhodecode_create_personal_repo_group': False, | |
232 | 'rhodecode_personal_repo_group_pattern': '${username}', |
|
231 | 'rhodecode_personal_repo_group_pattern': '${username}', | |
233 | } |
|
232 | } | |
234 | params.update(settings) |
|
233 | params.update(settings) | |
235 | response = self.app.post( |
|
234 | response = self.app.post( | |
236 | route_path('admin_settings_global_update'), params=params) |
|
235 | route_path('admin_settings_global_update'), params=params) | |
237 |
|
236 | |||
238 | assert_session_flash(response, 'Updated application settings') |
|
237 | assert_session_flash(response, 'Updated application settings') | |
239 | app_settings = SettingsModel().get_all_settings() |
|
238 | app_settings = SettingsModel().get_all_settings() | |
240 | del settings['csrf_token'] |
|
239 | del settings['csrf_token'] | |
241 | for key, value in settings.iteritems(): |
|
240 | for key, value in settings.iteritems(): | |
242 | assert app_settings[key] == value.decode('utf-8') |
|
241 | assert app_settings[key] == value.decode('utf-8') | |
243 |
|
242 | |||
244 | return response |
|
243 | return response | |
245 |
|
244 | |||
246 |
|
245 | |||
247 | @pytest.mark.usefixtures('autologin_user', 'app') |
|
246 | @pytest.mark.usefixtures('autologin_user', 'app') | |
248 | class TestAdminSettingsVcs(object): |
|
247 | class TestAdminSettingsVcs(object): | |
249 |
|
248 | |||
250 | def test_contains_svn_default_patterns(self): |
|
249 | def test_contains_svn_default_patterns(self): | |
251 | response = self.app.get(route_path('admin_settings_vcs')) |
|
250 | response = self.app.get(route_path('admin_settings_vcs')) | |
252 | expected_patterns = [ |
|
251 | expected_patterns = [ | |
253 | '/trunk', |
|
252 | '/trunk', | |
254 | '/branches/*', |
|
253 | '/branches/*', | |
255 | '/tags/*', |
|
254 | '/tags/*', | |
256 | ] |
|
255 | ] | |
257 | for pattern in expected_patterns: |
|
256 | for pattern in expected_patterns: | |
258 | response.mustcontain(pattern) |
|
257 | response.mustcontain(pattern) | |
259 |
|
258 | |||
260 | def test_add_new_svn_branch_and_tag_pattern( |
|
259 | def test_add_new_svn_branch_and_tag_pattern( | |
261 | self, backend_svn, form_defaults, disable_sql_cache, |
|
260 | self, backend_svn, form_defaults, disable_sql_cache, | |
262 | csrf_token): |
|
261 | csrf_token): | |
263 | form_defaults.update({ |
|
262 | form_defaults.update({ | |
264 | 'new_svn_branch': '/exp/branches/*', |
|
263 | 'new_svn_branch': '/exp/branches/*', | |
265 | 'new_svn_tag': '/important_tags/*', |
|
264 | 'new_svn_tag': '/important_tags/*', | |
266 | 'csrf_token': csrf_token, |
|
265 | 'csrf_token': csrf_token, | |
267 | }) |
|
266 | }) | |
268 |
|
267 | |||
269 | response = self.app.post( |
|
268 | response = self.app.post( | |
270 | route_path('admin_settings_vcs_update'), |
|
269 | route_path('admin_settings_vcs_update'), | |
271 | params=form_defaults, status=302) |
|
270 | params=form_defaults, status=302) | |
272 | response = response.follow() |
|
271 | response = response.follow() | |
273 |
|
272 | |||
274 | # Expect to find the new values on the page |
|
273 | # Expect to find the new values on the page | |
275 | response.mustcontain('/exp/branches/*') |
|
274 | response.mustcontain('/exp/branches/*') | |
276 | response.mustcontain('/important_tags/*') |
|
275 | response.mustcontain('/important_tags/*') | |
277 |
|
276 | |||
278 | # Expect that those patterns are used to match branches and tags now |
|
277 | # Expect that those patterns are used to match branches and tags now | |
279 | repo = backend_svn['svn-simple-layout'].scm_instance() |
|
278 | repo = backend_svn['svn-simple-layout'].scm_instance() | |
280 | assert 'exp/branches/exp-sphinx-docs' in repo.branches |
|
279 | assert 'exp/branches/exp-sphinx-docs' in repo.branches | |
281 | assert 'important_tags/v0.5' in repo.tags |
|
280 | assert 'important_tags/v0.5' in repo.tags | |
282 |
|
281 | |||
283 | def test_add_same_svn_value_twice_shows_an_error_message( |
|
282 | def test_add_same_svn_value_twice_shows_an_error_message( | |
284 | self, form_defaults, csrf_token, settings_util): |
|
283 | self, form_defaults, csrf_token, settings_util): | |
285 | settings_util.create_rhodecode_ui('vcs_svn_branch', '/test') |
|
284 | settings_util.create_rhodecode_ui('vcs_svn_branch', '/test') | |
286 | settings_util.create_rhodecode_ui('vcs_svn_tag', '/test') |
|
285 | settings_util.create_rhodecode_ui('vcs_svn_tag', '/test') | |
287 |
|
286 | |||
288 | response = self.app.post( |
|
287 | response = self.app.post( | |
289 | route_path('admin_settings_vcs_update'), |
|
288 | route_path('admin_settings_vcs_update'), | |
290 | params={ |
|
289 | params={ | |
291 | 'paths_root_path': form_defaults['paths_root_path'], |
|
290 | 'paths_root_path': form_defaults['paths_root_path'], | |
292 | 'new_svn_branch': '/test', |
|
291 | 'new_svn_branch': '/test', | |
293 | 'new_svn_tag': '/test', |
|
292 | 'new_svn_tag': '/test', | |
294 | 'csrf_token': csrf_token, |
|
293 | 'csrf_token': csrf_token, | |
295 | }, |
|
294 | }, | |
296 | status=200) |
|
295 | status=200) | |
297 |
|
296 | |||
298 | response.mustcontain("Pattern already exists") |
|
297 | response.mustcontain("Pattern already exists") | |
299 | response.mustcontain("Some form inputs contain invalid data.") |
|
298 | response.mustcontain("Some form inputs contain invalid data.") | |
300 |
|
299 | |||
301 | @pytest.mark.parametrize('section', [ |
|
300 | @pytest.mark.parametrize('section', [ | |
302 | 'vcs_svn_branch', |
|
301 | 'vcs_svn_branch', | |
303 | 'vcs_svn_tag', |
|
302 | 'vcs_svn_tag', | |
304 | ]) |
|
303 | ]) | |
305 | def test_delete_svn_patterns( |
|
304 | def test_delete_svn_patterns( | |
306 | self, section, csrf_token, settings_util): |
|
305 | self, section, csrf_token, settings_util): | |
307 | setting = settings_util.create_rhodecode_ui( |
|
306 | setting = settings_util.create_rhodecode_ui( | |
308 | section, '/test_delete', cleanup=False) |
|
307 | section, '/test_delete', cleanup=False) | |
309 |
|
308 | |||
310 | self.app.post( |
|
309 | self.app.post( | |
311 | route_path('admin_settings_vcs_svn_pattern_delete'), |
|
310 | route_path('admin_settings_vcs_svn_pattern_delete'), | |
312 | params={ |
|
311 | params={ | |
313 | 'delete_svn_pattern': setting.ui_id, |
|
312 | 'delete_svn_pattern': setting.ui_id, | |
314 | 'csrf_token': csrf_token}, |
|
313 | 'csrf_token': csrf_token}, | |
315 | headers={'X-REQUESTED-WITH': 'XMLHttpRequest'}) |
|
314 | headers={'X-REQUESTED-WITH': 'XMLHttpRequest'}) | |
316 |
|
315 | |||
317 | @pytest.mark.parametrize('section', [ |
|
316 | @pytest.mark.parametrize('section', [ | |
318 | 'vcs_svn_branch', |
|
317 | 'vcs_svn_branch', | |
319 | 'vcs_svn_tag', |
|
318 | 'vcs_svn_tag', | |
320 | ]) |
|
319 | ]) | |
321 | def test_delete_svn_patterns_raises_404_when_no_xhr( |
|
320 | def test_delete_svn_patterns_raises_404_when_no_xhr( | |
322 | self, section, csrf_token, settings_util): |
|
321 | self, section, csrf_token, settings_util): | |
323 | setting = settings_util.create_rhodecode_ui(section, '/test_delete') |
|
322 | setting = settings_util.create_rhodecode_ui(section, '/test_delete') | |
324 |
|
323 | |||
325 | self.app.post( |
|
324 | self.app.post( | |
326 | route_path('admin_settings_vcs_svn_pattern_delete'), |
|
325 | route_path('admin_settings_vcs_svn_pattern_delete'), | |
327 | params={ |
|
326 | params={ | |
328 | 'delete_svn_pattern': setting.ui_id, |
|
327 | 'delete_svn_pattern': setting.ui_id, | |
329 | 'csrf_token': csrf_token}, |
|
328 | 'csrf_token': csrf_token}, | |
330 | status=404) |
|
329 | status=404) | |
331 |
|
330 | |||
332 | def test_extensions_hgsubversion(self, form_defaults, csrf_token): |
|
331 | def test_extensions_hgsubversion(self, form_defaults, csrf_token): | |
333 | form_defaults.update({ |
|
332 | form_defaults.update({ | |
334 | 'csrf_token': csrf_token, |
|
333 | 'csrf_token': csrf_token, | |
335 | 'extensions_hgsubversion': 'True', |
|
334 | 'extensions_hgsubversion': 'True', | |
336 | }) |
|
335 | }) | |
337 | response = self.app.post( |
|
336 | response = self.app.post( | |
338 | route_path('admin_settings_vcs_update'), |
|
337 | route_path('admin_settings_vcs_update'), | |
339 | params=form_defaults, |
|
338 | params=form_defaults, | |
340 | status=302) |
|
339 | status=302) | |
341 |
|
340 | |||
342 | response = response.follow() |
|
341 | response = response.follow() | |
343 | extensions_input = ( |
|
342 | extensions_input = ( | |
344 | '<input id="extensions_hgsubversion" ' |
|
343 | '<input id="extensions_hgsubversion" ' | |
345 | 'name="extensions_hgsubversion" type="checkbox" ' |
|
344 | 'name="extensions_hgsubversion" type="checkbox" ' | |
346 | 'value="True" checked="checked" />') |
|
345 | 'value="True" checked="checked" />') | |
347 | response.mustcontain(extensions_input) |
|
346 | response.mustcontain(extensions_input) | |
348 |
|
347 | |||
349 | def test_extensions_hgevolve(self, form_defaults, csrf_token): |
|
348 | def test_extensions_hgevolve(self, form_defaults, csrf_token): | |
350 | form_defaults.update({ |
|
349 | form_defaults.update({ | |
351 | 'csrf_token': csrf_token, |
|
350 | 'csrf_token': csrf_token, | |
352 | 'extensions_evolve': 'True', |
|
351 | 'extensions_evolve': 'True', | |
353 | }) |
|
352 | }) | |
354 | response = self.app.post( |
|
353 | response = self.app.post( | |
355 | route_path('admin_settings_vcs_update'), |
|
354 | route_path('admin_settings_vcs_update'), | |
356 | params=form_defaults, |
|
355 | params=form_defaults, | |
357 | status=302) |
|
356 | status=302) | |
358 |
|
357 | |||
359 | response = response.follow() |
|
358 | response = response.follow() | |
360 | extensions_input = ( |
|
359 | extensions_input = ( | |
361 | '<input id="extensions_evolve" ' |
|
360 | '<input id="extensions_evolve" ' | |
362 | 'name="extensions_evolve" type="checkbox" ' |
|
361 | 'name="extensions_evolve" type="checkbox" ' | |
363 | 'value="True" checked="checked" />') |
|
362 | 'value="True" checked="checked" />') | |
364 | response.mustcontain(extensions_input) |
|
363 | response.mustcontain(extensions_input) | |
365 |
|
364 | |||
366 | def test_has_a_section_for_pull_request_settings(self): |
|
365 | def test_has_a_section_for_pull_request_settings(self): | |
367 | response = self.app.get(route_path('admin_settings_vcs')) |
|
366 | response = self.app.get(route_path('admin_settings_vcs')) | |
368 | response.mustcontain('Pull Request Settings') |
|
367 | response.mustcontain('Pull Request Settings') | |
369 |
|
368 | |||
370 | def test_has_an_input_for_invalidation_of_inline_comments(self): |
|
369 | def test_has_an_input_for_invalidation_of_inline_comments(self): | |
371 | response = self.app.get(route_path('admin_settings_vcs')) |
|
370 | response = self.app.get(route_path('admin_settings_vcs')) | |
372 | assert_response = AssertResponse(response) |
|
371 | assert_response = AssertResponse(response) | |
373 | assert_response.one_element_exists( |
|
372 | assert_response.one_element_exists( | |
374 | '[name=rhodecode_use_outdated_comments]') |
|
373 | '[name=rhodecode_use_outdated_comments]') | |
375 |
|
374 | |||
376 | @pytest.mark.parametrize('new_value', [True, False]) |
|
375 | @pytest.mark.parametrize('new_value', [True, False]) | |
377 | def test_allows_to_change_invalidation_of_inline_comments( |
|
376 | def test_allows_to_change_invalidation_of_inline_comments( | |
378 | self, form_defaults, csrf_token, new_value): |
|
377 | self, form_defaults, csrf_token, new_value): | |
379 | setting_key = 'use_outdated_comments' |
|
378 | setting_key = 'use_outdated_comments' | |
380 | setting = SettingsModel().create_or_update_setting( |
|
379 | setting = SettingsModel().create_or_update_setting( | |
381 | setting_key, not new_value, 'bool') |
|
380 | setting_key, not new_value, 'bool') | |
382 | Session().add(setting) |
|
381 | Session().add(setting) | |
383 | Session().commit() |
|
382 | Session().commit() | |
384 |
|
383 | |||
385 | form_defaults.update({ |
|
384 | form_defaults.update({ | |
386 | 'csrf_token': csrf_token, |
|
385 | 'csrf_token': csrf_token, | |
387 | 'rhodecode_use_outdated_comments': str(new_value), |
|
386 | 'rhodecode_use_outdated_comments': str(new_value), | |
388 | }) |
|
387 | }) | |
389 | response = self.app.post( |
|
388 | response = self.app.post( | |
390 | route_path('admin_settings_vcs_update'), |
|
389 | route_path('admin_settings_vcs_update'), | |
391 | params=form_defaults, |
|
390 | params=form_defaults, | |
392 | status=302) |
|
391 | status=302) | |
393 | response = response.follow() |
|
392 | response = response.follow() | |
394 | setting = SettingsModel().get_setting_by_name(setting_key) |
|
393 | setting = SettingsModel().get_setting_by_name(setting_key) | |
395 | assert setting.app_settings_value is new_value |
|
394 | assert setting.app_settings_value is new_value | |
396 |
|
395 | |||
397 | @pytest.mark.parametrize('new_value', [True, False]) |
|
396 | @pytest.mark.parametrize('new_value', [True, False]) | |
398 | def test_allows_to_change_hg_rebase_merge_strategy( |
|
397 | def test_allows_to_change_hg_rebase_merge_strategy( | |
399 | self, form_defaults, csrf_token, new_value): |
|
398 | self, form_defaults, csrf_token, new_value): | |
400 | setting_key = 'hg_use_rebase_for_merging' |
|
399 | setting_key = 'hg_use_rebase_for_merging' | |
401 |
|
400 | |||
402 | form_defaults.update({ |
|
401 | form_defaults.update({ | |
403 | 'csrf_token': csrf_token, |
|
402 | 'csrf_token': csrf_token, | |
404 | 'rhodecode_' + setting_key: str(new_value), |
|
403 | 'rhodecode_' + setting_key: str(new_value), | |
405 | }) |
|
404 | }) | |
406 |
|
405 | |||
407 | with mock.patch.dict( |
|
406 | with mock.patch.dict( | |
408 | rhodecode.CONFIG, {'labs_settings_active': 'true'}): |
|
407 | rhodecode.CONFIG, {'labs_settings_active': 'true'}): | |
409 | self.app.post( |
|
408 | self.app.post( | |
410 | route_path('admin_settings_vcs_update'), |
|
409 | route_path('admin_settings_vcs_update'), | |
411 | params=form_defaults, |
|
410 | params=form_defaults, | |
412 | status=302) |
|
411 | status=302) | |
413 |
|
412 | |||
414 | setting = SettingsModel().get_setting_by_name(setting_key) |
|
413 | setting = SettingsModel().get_setting_by_name(setting_key) | |
415 | assert setting.app_settings_value is new_value |
|
414 | assert setting.app_settings_value is new_value | |
416 |
|
415 | |||
417 | @pytest.fixture |
|
416 | @pytest.fixture | |
418 | def disable_sql_cache(self, request): |
|
417 | def disable_sql_cache(self, request): | |
419 | patcher = mock.patch( |
|
418 | patcher = mock.patch( | |
420 | 'rhodecode.lib.caching_query.FromCache.process_query') |
|
419 | 'rhodecode.lib.caching_query.FromCache.process_query') | |
421 | request.addfinalizer(patcher.stop) |
|
420 | request.addfinalizer(patcher.stop) | |
422 | patcher.start() |
|
421 | patcher.start() | |
423 |
|
422 | |||
424 | @pytest.fixture |
|
423 | @pytest.fixture | |
425 | def form_defaults(self): |
|
424 | def form_defaults(self): | |
426 | from rhodecode.apps.admin.views.settings import AdminSettingsView |
|
425 | from rhodecode.apps.admin.views.settings import AdminSettingsView | |
427 | return AdminSettingsView._form_defaults() |
|
426 | return AdminSettingsView._form_defaults() | |
428 |
|
427 | |||
429 | # TODO: johbo: What we really want is to checkpoint before a test run and |
|
428 | # TODO: johbo: What we really want is to checkpoint before a test run and | |
430 | # reset the session afterwards. |
|
429 | # reset the session afterwards. | |
431 | @pytest.fixture(scope='class', autouse=True) |
|
430 | @pytest.fixture(scope='class', autouse=True) | |
432 | def cleanup_settings(self, request, baseapp): |
|
431 | def cleanup_settings(self, request, baseapp): | |
433 | ui_id = RhodeCodeUi.ui_id |
|
432 | ui_id = RhodeCodeUi.ui_id | |
434 | original_ids = list( |
|
433 | original_ids = list( | |
435 | r.ui_id for r in RhodeCodeUi.query().values(ui_id)) |
|
434 | r.ui_id for r in RhodeCodeUi.query().values(ui_id)) | |
436 |
|
435 | |||
437 | @request.addfinalizer |
|
436 | @request.addfinalizer | |
438 | def cleanup(): |
|
437 | def cleanup(): | |
439 | RhodeCodeUi.query().filter( |
|
438 | RhodeCodeUi.query().filter( | |
440 | ui_id.notin_(original_ids)).delete(False) |
|
439 | ui_id.notin_(original_ids)).delete(False) | |
441 |
|
440 | |||
442 |
|
441 | |||
443 | @pytest.mark.usefixtures('autologin_user', 'app') |
|
442 | @pytest.mark.usefixtures('autologin_user', 'app') | |
444 | class TestLabsSettings(object): |
|
443 | class TestLabsSettings(object): | |
445 | def test_get_settings_page_disabled(self): |
|
444 | def test_get_settings_page_disabled(self): | |
446 | with mock.patch.dict( |
|
445 | with mock.patch.dict( | |
447 | rhodecode.CONFIG, {'labs_settings_active': 'false'}): |
|
446 | rhodecode.CONFIG, {'labs_settings_active': 'false'}): | |
448 |
|
447 | |||
449 | response = self.app.get( |
|
448 | response = self.app.get( | |
450 | route_path('admin_settings_labs'), status=302) |
|
449 | route_path('admin_settings_labs'), status=302) | |
451 |
|
450 | |||
452 | assert response.location.endswith(route_path('admin_settings')) |
|
451 | assert response.location.endswith(route_path('admin_settings')) | |
453 |
|
452 | |||
454 | def test_get_settings_page_enabled(self): |
|
453 | def test_get_settings_page_enabled(self): | |
455 | from rhodecode.apps.admin.views import settings |
|
454 | from rhodecode.apps.admin.views import settings | |
456 | lab_settings = [ |
|
455 | lab_settings = [ | |
457 | settings.LabSetting( |
|
456 | settings.LabSetting( | |
458 | key='rhodecode_bool', |
|
457 | key='rhodecode_bool', | |
459 | type='bool', |
|
458 | type='bool', | |
460 | group='bool group', |
|
459 | group='bool group', | |
461 | label='bool label', |
|
460 | label='bool label', | |
462 | help='bool help' |
|
461 | help='bool help' | |
463 | ), |
|
462 | ), | |
464 | settings.LabSetting( |
|
463 | settings.LabSetting( | |
465 | key='rhodecode_text', |
|
464 | key='rhodecode_text', | |
466 | type='unicode', |
|
465 | type='unicode', | |
467 | group='text group', |
|
466 | group='text group', | |
468 | label='text label', |
|
467 | label='text label', | |
469 | help='text help' |
|
468 | help='text help' | |
470 | ), |
|
469 | ), | |
471 | ] |
|
470 | ] | |
472 | with mock.patch.dict(rhodecode.CONFIG, |
|
471 | with mock.patch.dict(rhodecode.CONFIG, | |
473 | {'labs_settings_active': 'true'}): |
|
472 | {'labs_settings_active': 'true'}): | |
474 | with mock.patch.object(settings, '_LAB_SETTINGS', lab_settings): |
|
473 | with mock.patch.object(settings, '_LAB_SETTINGS', lab_settings): | |
475 | response = self.app.get(route_path('admin_settings_labs')) |
|
474 | response = self.app.get(route_path('admin_settings_labs')) | |
476 |
|
475 | |||
477 | assert '<label>bool group:</label>' in response |
|
476 | assert '<label>bool group:</label>' in response | |
478 | assert '<label for="rhodecode_bool">bool label</label>' in response |
|
477 | assert '<label for="rhodecode_bool">bool label</label>' in response | |
479 | assert '<p class="help-block">bool help</p>' in response |
|
478 | assert '<p class="help-block">bool help</p>' in response | |
480 | assert 'name="rhodecode_bool" type="checkbox"' in response |
|
479 | assert 'name="rhodecode_bool" type="checkbox"' in response | |
481 |
|
480 | |||
482 | assert '<label>text group:</label>' in response |
|
481 | assert '<label>text group:</label>' in response | |
483 | assert '<label for="rhodecode_text">text label</label>' in response |
|
482 | assert '<label for="rhodecode_text">text label</label>' in response | |
484 | assert '<p class="help-block">text help</p>' in response |
|
483 | assert '<p class="help-block">text help</p>' in response | |
485 | assert 'name="rhodecode_text" size="60" type="text"' in response |
|
484 | assert 'name="rhodecode_text" size="60" type="text"' in response | |
486 |
|
485 | |||
487 |
|
486 | |||
488 | @pytest.mark.usefixtures('app') |
|
487 | @pytest.mark.usefixtures('app') | |
489 | class TestOpenSourceLicenses(object): |
|
488 | class TestOpenSourceLicenses(object): | |
490 |
|
489 | |||
491 | def test_records_are_displayed(self, autologin_user): |
|
490 | def test_records_are_displayed(self, autologin_user): | |
492 | sample_licenses = { |
|
491 | sample_licenses = { | |
493 | "python2.7-pytest-2.7.1": { |
|
492 | "python2.7-pytest-2.7.1": { | |
494 | "UNKNOWN": None |
|
493 | "UNKNOWN": None | |
495 | }, |
|
494 | }, | |
496 | "python2.7-Markdown-2.6.2": { |
|
495 | "python2.7-Markdown-2.6.2": { | |
497 | "BSD-3-Clause": "http://spdx.org/licenses/BSD-3-Clause" |
|
496 | "BSD-3-Clause": "http://spdx.org/licenses/BSD-3-Clause" | |
498 | } |
|
497 | } | |
499 | } |
|
498 | } | |
500 | read_licenses_patch = mock.patch( |
|
499 | read_licenses_patch = mock.patch( | |
501 | 'rhodecode.apps.admin.views.open_source_licenses.read_opensource_licenses', |
|
500 | 'rhodecode.apps.admin.views.open_source_licenses.read_opensource_licenses', | |
502 | return_value=sample_licenses) |
|
501 | return_value=sample_licenses) | |
503 | with read_licenses_patch: |
|
502 | with read_licenses_patch: | |
504 | response = self.app.get( |
|
503 | response = self.app.get( | |
505 | route_path('admin_settings_open_source'), status=200) |
|
504 | route_path('admin_settings_open_source'), status=200) | |
506 |
|
505 | |||
507 | assert_response = AssertResponse(response) |
|
506 | assert_response = AssertResponse(response) | |
508 | assert_response.element_contains( |
|
507 | assert_response.element_contains( | |
509 | '.panel-heading', 'Licenses of Third Party Packages') |
|
508 | '.panel-heading', 'Licenses of Third Party Packages') | |
510 | for name in sample_licenses: |
|
509 | for name in sample_licenses: | |
511 | response.mustcontain(name) |
|
510 | response.mustcontain(name) | |
512 | for license in sample_licenses[name]: |
|
511 | for license in sample_licenses[name]: | |
513 | assert_response.element_contains('.panel-body', license) |
|
512 | assert_response.element_contains('.panel-body', license) | |
514 |
|
513 | |||
515 | def test_records_can_be_read(self, autologin_user): |
|
514 | def test_records_can_be_read(self, autologin_user): | |
516 | response = self.app.get( |
|
515 | response = self.app.get( | |
517 | route_path('admin_settings_open_source'), status=200) |
|
516 | route_path('admin_settings_open_source'), status=200) | |
518 | assert_response = AssertResponse(response) |
|
517 | assert_response = AssertResponse(response) | |
519 | assert_response.element_contains( |
|
518 | assert_response.element_contains( | |
520 | '.panel-heading', 'Licenses of Third Party Packages') |
|
519 | '.panel-heading', 'Licenses of Third Party Packages') | |
521 |
|
520 | |||
522 | def test_forbidden_when_normal_user(self, autologin_regular_user): |
|
521 | def test_forbidden_when_normal_user(self, autologin_regular_user): | |
523 | self.app.get( |
|
522 | self.app.get( | |
524 | route_path('admin_settings_open_source'), status=404) |
|
523 | route_path('admin_settings_open_source'), status=404) | |
525 |
|
524 | |||
526 |
|
525 | |||
527 | @pytest.mark.usefixtures('app') |
|
526 | @pytest.mark.usefixtures('app') | |
528 | class TestUserSessions(object): |
|
527 | class TestUserSessions(object): | |
529 |
|
528 | |||
530 | def test_forbidden_when_normal_user(self, autologin_regular_user): |
|
529 | def test_forbidden_when_normal_user(self, autologin_regular_user): | |
531 | self.app.get(route_path('admin_settings_sessions'), status=404) |
|
530 | self.app.get(route_path('admin_settings_sessions'), status=404) | |
532 |
|
531 | |||
533 | def test_show_sessions_page(self, autologin_user): |
|
532 | def test_show_sessions_page(self, autologin_user): | |
534 | response = self.app.get(route_path('admin_settings_sessions'), status=200) |
|
533 | response = self.app.get(route_path('admin_settings_sessions'), status=200) | |
535 | response.mustcontain('file') |
|
534 | response.mustcontain('file') | |
536 |
|
535 | |||
537 | def test_cleanup_old_sessions(self, autologin_user, csrf_token): |
|
536 | def test_cleanup_old_sessions(self, autologin_user, csrf_token): | |
538 |
|
537 | |||
539 | post_data = { |
|
538 | post_data = { | |
540 | 'csrf_token': csrf_token, |
|
539 | 'csrf_token': csrf_token, | |
541 | 'expire_days': '60' |
|
540 | 'expire_days': '60' | |
542 | } |
|
541 | } | |
543 | response = self.app.post( |
|
542 | response = self.app.post( | |
544 | route_path('admin_settings_sessions_cleanup'), params=post_data, |
|
543 | route_path('admin_settings_sessions_cleanup'), params=post_data, | |
545 | status=302) |
|
544 | status=302) | |
546 | assert_session_flash(response, 'Cleaned up old sessions') |
|
545 | assert_session_flash(response, 'Cleaned up old sessions') | |
547 |
|
546 | |||
548 |
|
547 | |||
549 | @pytest.mark.usefixtures('app') |
|
548 | @pytest.mark.usefixtures('app') | |
550 | class TestAdminSystemInfo(object): |
|
549 | class TestAdminSystemInfo(object): | |
551 |
|
550 | |||
552 | def test_forbidden_when_normal_user(self, autologin_regular_user): |
|
551 | def test_forbidden_when_normal_user(self, autologin_regular_user): | |
553 | self.app.get(route_path('admin_settings_system'), status=404) |
|
552 | self.app.get(route_path('admin_settings_system'), status=404) | |
554 |
|
553 | |||
555 | def test_system_info_page(self, autologin_user): |
|
554 | def test_system_info_page(self, autologin_user): | |
556 | response = self.app.get(route_path('admin_settings_system')) |
|
555 | response = self.app.get(route_path('admin_settings_system')) | |
557 | response.mustcontain('RhodeCode Community Edition, version {}'.format( |
|
556 | response.mustcontain('RhodeCode Community Edition, version {}'.format( | |
558 | rhodecode.__version__)) |
|
557 | rhodecode.__version__)) | |
559 |
|
558 | |||
560 | def test_system_update_new_version(self, autologin_user): |
|
559 | def test_system_update_new_version(self, autologin_user): | |
561 | update_data = { |
|
560 | update_data = { | |
562 | 'versions': [ |
|
561 | 'versions': [ | |
563 | { |
|
562 | { | |
564 | 'version': '100.3.1415926535', |
|
563 | 'version': '100.3.1415926535', | |
565 | 'general': 'The latest version we are ever going to ship' |
|
564 | 'general': 'The latest version we are ever going to ship' | |
566 | }, |
|
565 | }, | |
567 | { |
|
566 | { | |
568 | 'version': '0.0.0', |
|
567 | 'version': '0.0.0', | |
569 | 'general': 'The first version we ever shipped' |
|
568 | 'general': 'The first version we ever shipped' | |
570 | } |
|
569 | } | |
571 | ] |
|
570 | ] | |
572 | } |
|
571 | } | |
573 | with mock.patch(UPDATE_DATA_QUALNAME, return_value=update_data): |
|
572 | with mock.patch(UPDATE_DATA_QUALNAME, return_value=update_data): | |
574 | response = self.app.get(route_path('admin_settings_system_update')) |
|
573 | response = self.app.get(route_path('admin_settings_system_update')) | |
575 | response.mustcontain('A <b>new version</b> is available') |
|
574 | response.mustcontain('A <b>new version</b> is available') | |
576 |
|
575 | |||
577 | def test_system_update_nothing_new(self, autologin_user): |
|
576 | def test_system_update_nothing_new(self, autologin_user): | |
578 | update_data = { |
|
577 | update_data = { | |
579 | 'versions': [ |
|
578 | 'versions': [ | |
580 | { |
|
579 | { | |
581 | 'version': '0.0.0', |
|
580 | 'version': '0.0.0', | |
582 | 'general': 'The first version we ever shipped' |
|
581 | 'general': 'The first version we ever shipped' | |
583 | } |
|
582 | } | |
584 | ] |
|
583 | ] | |
585 | } |
|
584 | } | |
586 | with mock.patch(UPDATE_DATA_QUALNAME, return_value=update_data): |
|
585 | with mock.patch(UPDATE_DATA_QUALNAME, return_value=update_data): | |
587 | response = self.app.get(route_path('admin_settings_system_update')) |
|
586 | response = self.app.get(route_path('admin_settings_system_update')) | |
588 | response.mustcontain( |
|
587 | response.mustcontain( | |
589 |
' |
|
588 | 'This instance is already running the <b>latest</b> stable version') | |
590 |
|
589 | |||
591 | def test_system_update_bad_response(self, autologin_user): |
|
590 | def test_system_update_bad_response(self, autologin_user): | |
592 | with mock.patch(UPDATE_DATA_QUALNAME, side_effect=ValueError('foo')): |
|
591 | with mock.patch(UPDATE_DATA_QUALNAME, side_effect=ValueError('foo')): | |
593 | response = self.app.get(route_path('admin_settings_system_update')) |
|
592 | response = self.app.get(route_path('admin_settings_system_update')) | |
594 | response.mustcontain( |
|
593 | response.mustcontain( | |
595 | 'Bad data sent from update server') |
|
594 | 'Bad data sent from update server') | |
596 |
|
595 | |||
597 |
|
596 | |||
598 | @pytest.mark.usefixtures("app") |
|
597 | @pytest.mark.usefixtures("app") | |
599 | class TestAdminSettingsIssueTracker(object): |
|
598 | class TestAdminSettingsIssueTracker(object): | |
600 | RC_PREFIX = 'rhodecode_' |
|
599 | RC_PREFIX = 'rhodecode_' | |
601 | SHORT_PATTERN_KEY = 'issuetracker_pat_' |
|
600 | SHORT_PATTERN_KEY = 'issuetracker_pat_' | |
602 | PATTERN_KEY = RC_PREFIX + SHORT_PATTERN_KEY |
|
601 | PATTERN_KEY = RC_PREFIX + SHORT_PATTERN_KEY | |
603 |
|
602 | |||
604 | def test_issuetracker_index(self, autologin_user): |
|
603 | def test_issuetracker_index(self, autologin_user): | |
605 | response = self.app.get(route_path('admin_settings_issuetracker')) |
|
604 | response = self.app.get(route_path('admin_settings_issuetracker')) | |
606 | assert response.status_code == 200 |
|
605 | assert response.status_code == 200 | |
607 |
|
606 | |||
608 | def test_add_empty_issuetracker_pattern( |
|
607 | def test_add_empty_issuetracker_pattern( | |
609 | self, request, autologin_user, csrf_token): |
|
608 | self, request, autologin_user, csrf_token): | |
610 | post_url = route_path('admin_settings_issuetracker_update') |
|
609 | post_url = route_path('admin_settings_issuetracker_update') | |
611 | post_data = { |
|
610 | post_data = { | |
612 | 'csrf_token': csrf_token |
|
611 | 'csrf_token': csrf_token | |
613 | } |
|
612 | } | |
614 | self.app.post(post_url, post_data, status=302) |
|
613 | self.app.post(post_url, post_data, status=302) | |
615 |
|
614 | |||
616 | def test_add_issuetracker_pattern( |
|
615 | def test_add_issuetracker_pattern( | |
617 | self, request, autologin_user, csrf_token): |
|
616 | self, request, autologin_user, csrf_token): | |
618 | pattern = 'issuetracker_pat' |
|
617 | pattern = 'issuetracker_pat' | |
619 | another_pattern = pattern+'1' |
|
618 | another_pattern = pattern+'1' | |
620 | post_url = route_path('admin_settings_issuetracker_update') |
|
619 | post_url = route_path('admin_settings_issuetracker_update') | |
621 | post_data = { |
|
620 | post_data = { | |
622 | 'new_pattern_pattern_0': pattern, |
|
621 | 'new_pattern_pattern_0': pattern, | |
623 | 'new_pattern_url_0': 'http://url', |
|
622 | 'new_pattern_url_0': 'http://url', | |
624 | 'new_pattern_prefix_0': 'prefix', |
|
623 | 'new_pattern_prefix_0': 'prefix', | |
625 | 'new_pattern_description_0': 'description', |
|
624 | 'new_pattern_description_0': 'description', | |
626 | 'new_pattern_pattern_1': another_pattern, |
|
625 | 'new_pattern_pattern_1': another_pattern, | |
627 | 'new_pattern_url_1': 'https://url1', |
|
626 | 'new_pattern_url_1': 'https://url1', | |
628 | 'new_pattern_prefix_1': 'prefix1', |
|
627 | 'new_pattern_prefix_1': 'prefix1', | |
629 | 'new_pattern_description_1': 'description1', |
|
628 | 'new_pattern_description_1': 'description1', | |
630 | 'csrf_token': csrf_token |
|
629 | 'csrf_token': csrf_token | |
631 | } |
|
630 | } | |
632 | self.app.post(post_url, post_data, status=302) |
|
631 | self.app.post(post_url, post_data, status=302) | |
633 | settings = SettingsModel().get_all_settings() |
|
632 | settings = SettingsModel().get_all_settings() | |
634 | self.uid = md5(pattern) |
|
633 | self.uid = md5(pattern) | |
635 | assert settings[self.PATTERN_KEY+self.uid] == pattern |
|
634 | assert settings[self.PATTERN_KEY+self.uid] == pattern | |
636 | self.another_uid = md5(another_pattern) |
|
635 | self.another_uid = md5(another_pattern) | |
637 | assert settings[self.PATTERN_KEY+self.another_uid] == another_pattern |
|
636 | assert settings[self.PATTERN_KEY+self.another_uid] == another_pattern | |
638 |
|
637 | |||
639 | @request.addfinalizer |
|
638 | @request.addfinalizer | |
640 | def cleanup(): |
|
639 | def cleanup(): | |
641 | defaults = SettingsModel().get_all_settings() |
|
640 | defaults = SettingsModel().get_all_settings() | |
642 |
|
641 | |||
643 | entries = [name for name in defaults if ( |
|
642 | entries = [name for name in defaults if ( | |
644 | (self.uid in name) or (self.another_uid) in name)] |
|
643 | (self.uid in name) or (self.another_uid) in name)] | |
645 | start = len(self.RC_PREFIX) |
|
644 | start = len(self.RC_PREFIX) | |
646 | for del_key in entries: |
|
645 | for del_key in entries: | |
647 | # TODO: anderson: get_by_name needs name without prefix |
|
646 | # TODO: anderson: get_by_name needs name without prefix | |
648 | entry = SettingsModel().get_setting_by_name(del_key[start:]) |
|
647 | entry = SettingsModel().get_setting_by_name(del_key[start:]) | |
649 | Session().delete(entry) |
|
648 | Session().delete(entry) | |
650 |
|
649 | |||
651 | Session().commit() |
|
650 | Session().commit() | |
652 |
|
651 | |||
653 | def test_edit_issuetracker_pattern( |
|
652 | def test_edit_issuetracker_pattern( | |
654 | self, autologin_user, backend, csrf_token, request): |
|
653 | self, autologin_user, backend, csrf_token, request): | |
655 | old_pattern = 'issuetracker_pat' |
|
654 | old_pattern = 'issuetracker_pat' | |
656 | old_uid = md5(old_pattern) |
|
655 | old_uid = md5(old_pattern) | |
657 | pattern = 'issuetracker_pat_new' |
|
656 | pattern = 'issuetracker_pat_new' | |
658 | self.new_uid = md5(pattern) |
|
657 | self.new_uid = md5(pattern) | |
659 |
|
658 | |||
660 | SettingsModel().create_or_update_setting( |
|
659 | SettingsModel().create_or_update_setting( | |
661 | self.SHORT_PATTERN_KEY+old_uid, old_pattern, 'unicode') |
|
660 | self.SHORT_PATTERN_KEY+old_uid, old_pattern, 'unicode') | |
662 |
|
661 | |||
663 | post_url = route_path('admin_settings_issuetracker_update') |
|
662 | post_url = route_path('admin_settings_issuetracker_update') | |
664 | post_data = { |
|
663 | post_data = { | |
665 | 'new_pattern_pattern_0': pattern, |
|
664 | 'new_pattern_pattern_0': pattern, | |
666 | 'new_pattern_url_0': 'https://url', |
|
665 | 'new_pattern_url_0': 'https://url', | |
667 | 'new_pattern_prefix_0': 'prefix', |
|
666 | 'new_pattern_prefix_0': 'prefix', | |
668 | 'new_pattern_description_0': 'description', |
|
667 | 'new_pattern_description_0': 'description', | |
669 | 'uid': old_uid, |
|
668 | 'uid': old_uid, | |
670 | 'csrf_token': csrf_token |
|
669 | 'csrf_token': csrf_token | |
671 | } |
|
670 | } | |
672 | self.app.post(post_url, post_data, status=302) |
|
671 | self.app.post(post_url, post_data, status=302) | |
673 | settings = SettingsModel().get_all_settings() |
|
672 | settings = SettingsModel().get_all_settings() | |
674 | assert settings[self.PATTERN_KEY+self.new_uid] == pattern |
|
673 | assert settings[self.PATTERN_KEY+self.new_uid] == pattern | |
675 | assert self.PATTERN_KEY+old_uid not in settings |
|
674 | assert self.PATTERN_KEY+old_uid not in settings | |
676 |
|
675 | |||
677 | @request.addfinalizer |
|
676 | @request.addfinalizer | |
678 | def cleanup(): |
|
677 | def cleanup(): | |
679 | IssueTrackerSettingsModel().delete_entries(self.new_uid) |
|
678 | IssueTrackerSettingsModel().delete_entries(self.new_uid) | |
680 |
|
679 | |||
681 | def test_replace_issuetracker_pattern_description( |
|
680 | def test_replace_issuetracker_pattern_description( | |
682 | self, autologin_user, csrf_token, request, settings_util): |
|
681 | self, autologin_user, csrf_token, request, settings_util): | |
683 | prefix = 'issuetracker' |
|
682 | prefix = 'issuetracker' | |
684 | pattern = 'issuetracker_pat' |
|
683 | pattern = 'issuetracker_pat' | |
685 | self.uid = md5(pattern) |
|
684 | self.uid = md5(pattern) | |
686 | pattern_key = '_'.join([prefix, 'pat', self.uid]) |
|
685 | pattern_key = '_'.join([prefix, 'pat', self.uid]) | |
687 | rc_pattern_key = '_'.join(['rhodecode', pattern_key]) |
|
686 | rc_pattern_key = '_'.join(['rhodecode', pattern_key]) | |
688 | desc_key = '_'.join([prefix, 'desc', self.uid]) |
|
687 | desc_key = '_'.join([prefix, 'desc', self.uid]) | |
689 | rc_desc_key = '_'.join(['rhodecode', desc_key]) |
|
688 | rc_desc_key = '_'.join(['rhodecode', desc_key]) | |
690 | new_description = 'new_description' |
|
689 | new_description = 'new_description' | |
691 |
|
690 | |||
692 | settings_util.create_rhodecode_setting( |
|
691 | settings_util.create_rhodecode_setting( | |
693 | pattern_key, pattern, 'unicode', cleanup=False) |
|
692 | pattern_key, pattern, 'unicode', cleanup=False) | |
694 | settings_util.create_rhodecode_setting( |
|
693 | settings_util.create_rhodecode_setting( | |
695 | desc_key, 'old description', 'unicode', cleanup=False) |
|
694 | desc_key, 'old description', 'unicode', cleanup=False) | |
696 |
|
695 | |||
697 | post_url = route_path('admin_settings_issuetracker_update') |
|
696 | post_url = route_path('admin_settings_issuetracker_update') | |
698 | post_data = { |
|
697 | post_data = { | |
699 | 'new_pattern_pattern_0': pattern, |
|
698 | 'new_pattern_pattern_0': pattern, | |
700 | 'new_pattern_url_0': 'https://url', |
|
699 | 'new_pattern_url_0': 'https://url', | |
701 | 'new_pattern_prefix_0': 'prefix', |
|
700 | 'new_pattern_prefix_0': 'prefix', | |
702 | 'new_pattern_description_0': new_description, |
|
701 | 'new_pattern_description_0': new_description, | |
703 | 'uid': self.uid, |
|
702 | 'uid': self.uid, | |
704 | 'csrf_token': csrf_token |
|
703 | 'csrf_token': csrf_token | |
705 | } |
|
704 | } | |
706 | self.app.post(post_url, post_data, status=302) |
|
705 | self.app.post(post_url, post_data, status=302) | |
707 | settings = SettingsModel().get_all_settings() |
|
706 | settings = SettingsModel().get_all_settings() | |
708 | assert settings[rc_pattern_key] == pattern |
|
707 | assert settings[rc_pattern_key] == pattern | |
709 | assert settings[rc_desc_key] == new_description |
|
708 | assert settings[rc_desc_key] == new_description | |
710 |
|
709 | |||
711 | @request.addfinalizer |
|
710 | @request.addfinalizer | |
712 | def cleanup(): |
|
711 | def cleanup(): | |
713 | IssueTrackerSettingsModel().delete_entries(self.uid) |
|
712 | IssueTrackerSettingsModel().delete_entries(self.uid) | |
714 |
|
713 | |||
715 | def test_delete_issuetracker_pattern( |
|
714 | def test_delete_issuetracker_pattern( | |
716 | self, autologin_user, backend, csrf_token, settings_util): |
|
715 | self, autologin_user, backend, csrf_token, settings_util): | |
717 | pattern = 'issuetracker_pat' |
|
716 | pattern = 'issuetracker_pat' | |
718 | uid = md5(pattern) |
|
717 | uid = md5(pattern) | |
719 | settings_util.create_rhodecode_setting( |
|
718 | settings_util.create_rhodecode_setting( | |
720 | self.SHORT_PATTERN_KEY+uid, pattern, 'unicode', cleanup=False) |
|
719 | self.SHORT_PATTERN_KEY+uid, pattern, 'unicode', cleanup=False) | |
721 |
|
720 | |||
722 | post_url = route_path('admin_settings_issuetracker_delete') |
|
721 | post_url = route_path('admin_settings_issuetracker_delete') | |
723 | post_data = { |
|
722 | post_data = { | |
724 | '_method': 'delete', |
|
723 | '_method': 'delete', | |
725 | 'uid': uid, |
|
724 | 'uid': uid, | |
726 | 'csrf_token': csrf_token |
|
725 | 'csrf_token': csrf_token | |
727 | } |
|
726 | } | |
728 | self.app.post(post_url, post_data, status=302) |
|
727 | self.app.post(post_url, post_data, status=302) | |
729 | settings = SettingsModel().get_all_settings() |
|
728 | settings = SettingsModel().get_all_settings() | |
730 | assert 'rhodecode_%s%s' % (self.SHORT_PATTERN_KEY, uid) not in settings |
|
729 | assert 'rhodecode_%s%s' % (self.SHORT_PATTERN_KEY, uid) not in settings |
@@ -1,208 +1,197 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 |
|
2 | |||
3 | # Copyright (C) 2016-2017 RhodeCode GmbH |
|
3 | # Copyright (C) 2016-2017 RhodeCode GmbH | |
4 | # |
|
4 | # | |
5 | # This program is free software: you can redistribute it and/or modify |
|
5 | # This program is free software: you can redistribute it and/or modify | |
6 | # it under the terms of the GNU Affero General Public License, version 3 |
|
6 | # it under the terms of the GNU Affero General Public License, version 3 | |
7 | # (only), as published by the Free Software Foundation. |
|
7 | # (only), as published by the Free Software Foundation. | |
8 | # |
|
8 | # | |
9 | # This program is distributed in the hope that it will be useful, |
|
9 | # This program is distributed in the hope that it will be useful, | |
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | # GNU General Public License for more details. |
|
12 | # GNU General Public License for more details. | |
13 | # |
|
13 | # | |
14 | # You should have received a copy of the GNU Affero General Public License |
|
14 | # You should have received a copy of the GNU Affero General Public License | |
15 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
15 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
16 | # |
|
16 | # | |
17 | # This program is dual-licensed. If you wish to learn more about the |
|
17 | # This program is dual-licensed. If you wish to learn more about the | |
18 | # RhodeCode Enterprise Edition, including its added features, Support services, |
|
18 | # RhodeCode Enterprise Edition, including its added features, Support services, | |
19 | # and proprietary license terms, please see https://rhodecode.com/licenses/ |
|
19 | # and proprietary license terms, please see https://rhodecode.com/licenses/ | |
20 |
|
20 | |||
21 | import logging |
|
21 | import logging | |
22 | import urllib2 |
|
22 | import urllib2 | |
23 | import packaging.version |
|
|||
24 |
|
23 | |||
25 | from pyramid.view import view_config |
|
24 | from pyramid.view import view_config | |
26 |
|
25 | |||
27 | import rhodecode |
|
26 | import rhodecode | |
28 | from rhodecode.apps._base import BaseAppView |
|
27 | from rhodecode.apps._base import BaseAppView | |
29 | from rhodecode.apps.admin.navigation import navigation_list |
|
28 | from rhodecode.apps.admin.navigation import navigation_list | |
30 | from rhodecode.lib import helpers as h |
|
29 | from rhodecode.lib import helpers as h | |
31 | from rhodecode.lib.auth import (LoginRequired, HasPermissionAllDecorator) |
|
30 | from rhodecode.lib.auth import (LoginRequired, HasPermissionAllDecorator) | |
32 | from rhodecode.lib.utils2 import str2bool |
|
31 | from rhodecode.lib.utils2 import str2bool | |
33 | from rhodecode.lib import system_info |
|
32 | from rhodecode.lib import system_info | |
34 |
from rhodecode.l |
|
33 | from rhodecode.model.update import UpdateModel | |
35 | from rhodecode.model.settings import SettingsModel |
|
|||
36 |
|
34 | |||
37 | log = logging.getLogger(__name__) |
|
35 | log = logging.getLogger(__name__) | |
38 |
|
36 | |||
39 |
|
37 | |||
40 | class AdminSystemInfoSettingsView(BaseAppView): |
|
38 | class AdminSystemInfoSettingsView(BaseAppView): | |
41 | def load_default_context(self): |
|
39 | def load_default_context(self): | |
42 | c = self._get_local_tmpl_context() |
|
40 | c = self._get_local_tmpl_context() | |
43 |
|
||||
44 | return c |
|
41 | return c | |
45 |
|
42 | |||
46 | @staticmethod |
|
|||
47 | def get_update_data(update_url): |
|
|||
48 | """Return the JSON update data.""" |
|
|||
49 | ver = rhodecode.__version__ |
|
|||
50 | log.debug('Checking for upgrade on `%s` server', update_url) |
|
|||
51 | opener = urllib2.build_opener() |
|
|||
52 | opener.addheaders = [('User-agent', 'RhodeCode-SCM/%s' % ver)] |
|
|||
53 | response = opener.open(update_url) |
|
|||
54 | response_data = response.read() |
|
|||
55 | data = json.loads(response_data) |
|
|||
56 |
|
||||
57 | return data |
|
|||
58 |
|
||||
59 | def get_update_url(self): |
|
|||
60 | settings = SettingsModel().get_all_settings() |
|
|||
61 | return settings.get('rhodecode_update_url') |
|
|||
62 |
|
||||
63 | @LoginRequired() |
|
43 | @LoginRequired() | |
64 | @HasPermissionAllDecorator('hg.admin') |
|
44 | @HasPermissionAllDecorator('hg.admin') | |
65 | @view_config( |
|
45 | @view_config( | |
66 | route_name='admin_settings_system', request_method='GET', |
|
46 | route_name='admin_settings_system', request_method='GET', | |
67 | renderer='rhodecode:templates/admin/settings/settings.mako') |
|
47 | renderer='rhodecode:templates/admin/settings/settings.mako') | |
68 | def settings_system_info(self): |
|
48 | def settings_system_info(self): | |
69 | _ = self.request.translate |
|
49 | _ = self.request.translate | |
70 | c = self.load_default_context() |
|
50 | c = self.load_default_context() | |
71 |
|
51 | |||
72 | c.active = 'system' |
|
52 | c.active = 'system' | |
73 | c.navlist = navigation_list(self.request) |
|
53 | c.navlist = navigation_list(self.request) | |
74 |
|
54 | |||
75 | # TODO(marcink), figure out how to allow only selected users to do this |
|
55 | # TODO(marcink), figure out how to allow only selected users to do this | |
76 | c.allowed_to_snapshot = self._rhodecode_user.admin |
|
56 | c.allowed_to_snapshot = self._rhodecode_user.admin | |
77 |
|
57 | |||
78 | snapshot = str2bool(self.request.params.get('snapshot')) |
|
58 | snapshot = str2bool(self.request.params.get('snapshot')) | |
79 |
|
59 | |||
80 |
c.rhodecode_update_url = |
|
60 | c.rhodecode_update_url = UpdateModel().get_update_url() | |
81 | server_info = system_info.get_system_info(self.request.environ) |
|
61 | server_info = system_info.get_system_info(self.request.environ) | |
82 |
|
62 | |||
83 | for key, val in server_info.items(): |
|
63 | for key, val in server_info.items(): | |
84 | setattr(c, key, val) |
|
64 | setattr(c, key, val) | |
85 |
|
65 | |||
86 | def val(name, subkey='human_value'): |
|
66 | def val(name, subkey='human_value'): | |
87 | return server_info[name][subkey] |
|
67 | return server_info[name][subkey] | |
88 |
|
68 | |||
89 | def state(name): |
|
69 | def state(name): | |
90 | return server_info[name]['state'] |
|
70 | return server_info[name]['state'] | |
91 |
|
71 | |||
92 | def val2(name): |
|
72 | def val2(name): | |
93 | val = server_info[name]['human_value'] |
|
73 | val = server_info[name]['human_value'] | |
94 | state = server_info[name]['state'] |
|
74 | state = server_info[name]['state'] | |
95 | return val, state |
|
75 | return val, state | |
96 |
|
76 | |||
97 | update_info_msg = _('Note: please make sure this server can ' |
|
77 | update_info_msg = _('Note: please make sure this server can ' | |
98 | 'access `${url}` for the update link to work', |
|
78 | 'access `${url}` for the update link to work', | |
99 | mapping=dict(url=c.rhodecode_update_url)) |
|
79 | mapping=dict(url=c.rhodecode_update_url)) | |
|
80 | version = UpdateModel().get_stored_version() | |||
|
81 | is_outdated = UpdateModel().is_outdated( | |||
|
82 | rhodecode.__version__, version) | |||
|
83 | update_state = { | |||
|
84 | 'type': 'warning', | |||
|
85 | 'message': 'New version available: {}'.format(version) | |||
|
86 | } \ | |||
|
87 | if is_outdated else {} | |||
100 | c.data_items = [ |
|
88 | c.data_items = [ | |
101 | # update info |
|
89 | # update info | |
102 | (_('Update info'), h.literal( |
|
90 | (_('Update info'), h.literal( | |
103 | '<span class="link" id="check_for_update" >%s.</span>' % ( |
|
91 | '<span class="link" id="check_for_update" >%s.</span>' % ( | |
104 | _('Check for updates')) + |
|
92 | _('Check for updates')) + | |
105 | '<br/> <span >%s.</span>' % (update_info_msg) |
|
93 | '<br/> <span >%s.</span>' % (update_info_msg) | |
106 | ), ''), |
|
94 | ), ''), | |
107 |
|
95 | |||
108 | # RhodeCode specific |
|
96 | # RhodeCode specific | |
109 | (_('RhodeCode Version'), val('rhodecode_app')['text'], state('rhodecode_app')), |
|
97 | (_('RhodeCode Version'), val('rhodecode_app')['text'], state('rhodecode_app')), | |
|
98 | (_('Latest version'), version, update_state), | |||
110 | (_('RhodeCode Server IP'), val('server')['server_ip'], state('server')), |
|
99 | (_('RhodeCode Server IP'), val('server')['server_ip'], state('server')), | |
111 | (_('RhodeCode Server ID'), val('server')['server_id'], state('server')), |
|
100 | (_('RhodeCode Server ID'), val('server')['server_id'], state('server')), | |
112 | (_('RhodeCode Configuration'), val('rhodecode_config')['path'], state('rhodecode_config')), |
|
101 | (_('RhodeCode Configuration'), val('rhodecode_config')['path'], state('rhodecode_config')), | |
113 | (_('RhodeCode Certificate'), val('rhodecode_config')['cert_path'], state('rhodecode_config')), |
|
102 | (_('RhodeCode Certificate'), val('rhodecode_config')['cert_path'], state('rhodecode_config')), | |
114 | (_('Workers'), val('rhodecode_config')['config']['server:main'].get('workers', '?'), state('rhodecode_config')), |
|
103 | (_('Workers'), val('rhodecode_config')['config']['server:main'].get('workers', '?'), state('rhodecode_config')), | |
115 | (_('Worker Type'), val('rhodecode_config')['config']['server:main'].get('worker_class', 'sync'), state('rhodecode_config')), |
|
104 | (_('Worker Type'), val('rhodecode_config')['config']['server:main'].get('worker_class', 'sync'), state('rhodecode_config')), | |
116 | ('', '', ''), # spacer |
|
105 | ('', '', ''), # spacer | |
117 |
|
106 | |||
118 | # Database |
|
107 | # Database | |
119 | (_('Database'), val('database')['url'], state('database')), |
|
108 | (_('Database'), val('database')['url'], state('database')), | |
120 | (_('Database version'), val('database')['version'], state('database')), |
|
109 | (_('Database version'), val('database')['version'], state('database')), | |
121 | ('', '', ''), # spacer |
|
110 | ('', '', ''), # spacer | |
122 |
|
111 | |||
123 | # Platform/Python |
|
112 | # Platform/Python | |
124 | (_('Platform'), val('platform')['name'], state('platform')), |
|
113 | (_('Platform'), val('platform')['name'], state('platform')), | |
125 | (_('Platform UUID'), val('platform')['uuid'], state('platform')), |
|
114 | (_('Platform UUID'), val('platform')['uuid'], state('platform')), | |
126 | (_('Python version'), val('python')['version'], state('python')), |
|
115 | (_('Python version'), val('python')['version'], state('python')), | |
127 | (_('Python path'), val('python')['executable'], state('python')), |
|
116 | (_('Python path'), val('python')['executable'], state('python')), | |
128 | ('', '', ''), # spacer |
|
117 | ('', '', ''), # spacer | |
129 |
|
118 | |||
130 | # Systems stats |
|
119 | # Systems stats | |
131 | (_('CPU'), val('cpu')['text'], state('cpu')), |
|
120 | (_('CPU'), val('cpu')['text'], state('cpu')), | |
132 | (_('Load'), val('load')['text'], state('load')), |
|
121 | (_('Load'), val('load')['text'], state('load')), | |
133 | (_('Memory'), val('memory')['text'], state('memory')), |
|
122 | (_('Memory'), val('memory')['text'], state('memory')), | |
134 | (_('Uptime'), val('uptime')['text'], state('uptime')), |
|
123 | (_('Uptime'), val('uptime')['text'], state('uptime')), | |
135 | ('', '', ''), # spacer |
|
124 | ('', '', ''), # spacer | |
136 |
|
125 | |||
137 | # Repo storage |
|
126 | # Repo storage | |
138 | (_('Storage location'), val('storage')['path'], state('storage')), |
|
127 | (_('Storage location'), val('storage')['path'], state('storage')), | |
139 | (_('Storage info'), val('storage')['text'], state('storage')), |
|
128 | (_('Storage info'), val('storage')['text'], state('storage')), | |
140 | (_('Storage inodes'), val('storage_inodes')['text'], state('storage_inodes')), |
|
129 | (_('Storage inodes'), val('storage_inodes')['text'], state('storage_inodes')), | |
141 |
|
130 | |||
142 | (_('Gist storage location'), val('storage_gist')['path'], state('storage_gist')), |
|
131 | (_('Gist storage location'), val('storage_gist')['path'], state('storage_gist')), | |
143 | (_('Gist storage info'), val('storage_gist')['text'], state('storage_gist')), |
|
132 | (_('Gist storage info'), val('storage_gist')['text'], state('storage_gist')), | |
144 |
|
133 | |||
145 | (_('Archive cache storage location'), val('storage_archive')['path'], state('storage_archive')), |
|
134 | (_('Archive cache storage location'), val('storage_archive')['path'], state('storage_archive')), | |
146 | (_('Archive cache info'), val('storage_archive')['text'], state('storage_archive')), |
|
135 | (_('Archive cache info'), val('storage_archive')['text'], state('storage_archive')), | |
147 |
|
136 | |||
148 | (_('Temp storage location'), val('storage_temp')['path'], state('storage_temp')), |
|
137 | (_('Temp storage location'), val('storage_temp')['path'], state('storage_temp')), | |
149 | (_('Temp storage info'), val('storage_temp')['text'], state('storage_temp')), |
|
138 | (_('Temp storage info'), val('storage_temp')['text'], state('storage_temp')), | |
150 |
|
139 | |||
151 | (_('Search info'), val('search')['text'], state('search')), |
|
140 | (_('Search info'), val('search')['text'], state('search')), | |
152 | (_('Search location'), val('search')['location'], state('search')), |
|
141 | (_('Search location'), val('search')['location'], state('search')), | |
153 | ('', '', ''), # spacer |
|
142 | ('', '', ''), # spacer | |
154 |
|
143 | |||
155 | # VCS specific |
|
144 | # VCS specific | |
156 | (_('VCS Backends'), val('vcs_backends'), state('vcs_backends')), |
|
145 | (_('VCS Backends'), val('vcs_backends'), state('vcs_backends')), | |
157 | (_('VCS Server'), val('vcs_server')['text'], state('vcs_server')), |
|
146 | (_('VCS Server'), val('vcs_server')['text'], state('vcs_server')), | |
158 | (_('GIT'), val('git'), state('git')), |
|
147 | (_('GIT'), val('git'), state('git')), | |
159 | (_('HG'), val('hg'), state('hg')), |
|
148 | (_('HG'), val('hg'), state('hg')), | |
160 | (_('SVN'), val('svn'), state('svn')), |
|
149 | (_('SVN'), val('svn'), state('svn')), | |
161 |
|
150 | |||
162 | ] |
|
151 | ] | |
163 |
|
152 | |||
164 | if snapshot: |
|
153 | if snapshot: | |
165 | if c.allowed_to_snapshot: |
|
154 | if c.allowed_to_snapshot: | |
166 | c.data_items.pop(0) # remove server info |
|
155 | c.data_items.pop(0) # remove server info | |
167 | self.request.override_renderer = 'admin/settings/settings_system_snapshot.mako' |
|
156 | self.request.override_renderer = 'admin/settings/settings_system_snapshot.mako' | |
168 | else: |
|
157 | else: | |
169 | h.flash('You are not allowed to do this', category='warning') |
|
158 | h.flash('You are not allowed to do this', category='warning') | |
170 | return self._get_template_context(c) |
|
159 | return self._get_template_context(c) | |
171 |
|
160 | |||
172 | @LoginRequired() |
|
161 | @LoginRequired() | |
173 | @HasPermissionAllDecorator('hg.admin') |
|
162 | @HasPermissionAllDecorator('hg.admin') | |
174 | @view_config( |
|
163 | @view_config( | |
175 | route_name='admin_settings_system_update', request_method='GET', |
|
164 | route_name='admin_settings_system_update', request_method='GET', | |
176 | renderer='rhodecode:templates/admin/settings/settings_system_update.mako') |
|
165 | renderer='rhodecode:templates/admin/settings/settings_system_update.mako') | |
177 | def settings_system_info_check_update(self): |
|
166 | def settings_system_info_check_update(self): | |
178 | _ = self.request.translate |
|
167 | _ = self.request.translate | |
179 | c = self.load_default_context() |
|
168 | c = self.load_default_context() | |
180 |
|
169 | |||
181 |
update_url = |
|
170 | update_url = UpdateModel().get_update_url() | |
182 |
|
171 | |||
183 | _err = lambda s: '<div style="color:#ff8888; padding:4px 0px">{}</div>'.format(s) |
|
172 | _err = lambda s: '<div style="color:#ff8888; padding:4px 0px">{}</div>'.format(s) | |
184 | try: |
|
173 | try: | |
185 |
data = |
|
174 | data = UpdateModel().get_update_data(update_url) | |
186 | except urllib2.URLError as e: |
|
175 | except urllib2.URLError as e: | |
187 | log.exception("Exception contacting upgrade server") |
|
176 | log.exception("Exception contacting upgrade server") | |
188 | self.request.override_renderer = 'string' |
|
177 | self.request.override_renderer = 'string' | |
189 | return _err('Failed to contact upgrade server: %r' % e) |
|
178 | return _err('Failed to contact upgrade server: %r' % e) | |
190 | except ValueError as e: |
|
179 | except ValueError as e: | |
191 | log.exception("Bad data sent from update server") |
|
180 | log.exception("Bad data sent from update server") | |
192 | self.request.override_renderer = 'string' |
|
181 | self.request.override_renderer = 'string' | |
193 | return _err('Bad data sent from update server') |
|
182 | return _err('Bad data sent from update server') | |
194 |
|
183 | |||
195 | latest = data['versions'][0] |
|
184 | latest = data['versions'][0] | |
196 |
|
185 | |||
197 | c.update_url = update_url |
|
186 | c.update_url = update_url | |
198 | c.latest_data = latest |
|
187 | c.latest_data = latest | |
199 | c.latest_ver = latest['version'] |
|
188 | c.latest_ver = latest['version'] | |
200 | c.cur_ver = rhodecode.__version__ |
|
189 | c.cur_ver = rhodecode.__version__ | |
201 | c.should_upgrade = False |
|
190 | c.should_upgrade = False | |
202 |
|
191 | |||
203 | if (packaging.version.Version(c.latest_ver) > |
|
192 | is_oudated = UpdateModel().is_outdated(c.cur_ver, c.latest_ver) | |
204 | packaging.version.Version(c.cur_ver)): |
|
193 | if is_oudated: | |
205 | c.should_upgrade = True |
|
194 | c.should_upgrade = True | |
206 | c.important_notices = latest['general'] |
|
195 | c.important_notices = latest['general'] | |
207 |
|
196 | UpdateModel().store_version(latest['version']) | ||
208 | return self._get_template_context(c) |
|
197 | return self._get_template_context(c) |
@@ -1,285 +1,299 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 |
|
2 | |||
3 | # Copyright (C) 2012-2017 RhodeCode GmbH |
|
3 | # Copyright (C) 2012-2017 RhodeCode GmbH | |
4 | # |
|
4 | # | |
5 | # This program is free software: you can redistribute it and/or modify |
|
5 | # This program is free software: you can redistribute it and/or modify | |
6 | # it under the terms of the GNU Affero General Public License, version 3 |
|
6 | # it under the terms of the GNU Affero General Public License, version 3 | |
7 | # (only), as published by the Free Software Foundation. |
|
7 | # (only), as published by the Free Software Foundation. | |
8 | # |
|
8 | # | |
9 | # This program is distributed in the hope that it will be useful, |
|
9 | # This program is distributed in the hope that it will be useful, | |
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | # GNU General Public License for more details. |
|
12 | # GNU General Public License for more details. | |
13 | # |
|
13 | # | |
14 | # You should have received a copy of the GNU Affero General Public License |
|
14 | # You should have received a copy of the GNU Affero General Public License | |
15 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
15 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
16 | # |
|
16 | # | |
17 | # This program is dual-licensed. If you wish to learn more about the |
|
17 | # This program is dual-licensed. If you wish to learn more about the | |
18 | # RhodeCode Enterprise Edition, including its added features, Support services, |
|
18 | # RhodeCode Enterprise Edition, including its added features, Support services, | |
19 | # and proprietary license terms, please see https://rhodecode.com/licenses/ |
|
19 | # and proprietary license terms, please see https://rhodecode.com/licenses/ | |
20 |
|
20 | |||
21 | """ |
|
21 | """ | |
22 | RhodeCode task modules, containing all task that suppose to be run |
|
22 | RhodeCode task modules, containing all task that suppose to be run | |
23 | by celery daemon |
|
23 | by celery daemon | |
24 | """ |
|
24 | """ | |
25 |
|
25 | |||
26 | import os |
|
26 | import os | |
27 | import time |
|
27 | import time | |
28 |
|
28 | |||
29 | import rhodecode |
|
29 | import rhodecode | |
30 | from rhodecode.lib import audit_logger |
|
30 | from rhodecode.lib import audit_logger | |
31 | from rhodecode.lib.celerylib import get_logger, async_task, RequestContextTask |
|
31 | from rhodecode.lib.celerylib import get_logger, async_task, RequestContextTask | |
32 | from rhodecode.lib.hooks_base import log_create_repository |
|
32 | from rhodecode.lib.hooks_base import log_create_repository | |
33 | from rhodecode.lib.rcmail.smtp_mailer import SmtpMailer |
|
33 | from rhodecode.lib.rcmail.smtp_mailer import SmtpMailer | |
34 | from rhodecode.lib.utils2 import safe_int, str2bool |
|
34 | from rhodecode.lib.utils2 import safe_int, str2bool | |
35 | from rhodecode.model.db import Session, IntegrityError, Repository, User |
|
35 | from rhodecode.model.db import Session, IntegrityError, Repository, User | |
36 |
|
36 | |||
37 |
|
37 | |||
38 | @async_task(ignore_result=True, base=RequestContextTask) |
|
38 | @async_task(ignore_result=True, base=RequestContextTask) | |
39 | def send_email(recipients, subject, body='', html_body='', email_config=None): |
|
39 | def send_email(recipients, subject, body='', html_body='', email_config=None): | |
40 | """ |
|
40 | """ | |
41 | Sends an email with defined parameters from the .ini files. |
|
41 | Sends an email with defined parameters from the .ini files. | |
42 |
|
42 | |||
43 | :param recipients: list of recipients, it this is empty the defined email |
|
43 | :param recipients: list of recipients, it this is empty the defined email | |
44 | address from field 'email_to' is used instead |
|
44 | address from field 'email_to' is used instead | |
45 | :param subject: subject of the mail |
|
45 | :param subject: subject of the mail | |
46 | :param body: body of the mail |
|
46 | :param body: body of the mail | |
47 | :param html_body: html version of body |
|
47 | :param html_body: html version of body | |
48 | """ |
|
48 | """ | |
49 | log = get_logger(send_email) |
|
49 | log = get_logger(send_email) | |
50 |
|
50 | |||
51 | email_config = email_config or rhodecode.CONFIG |
|
51 | email_config = email_config or rhodecode.CONFIG | |
52 | subject = "%s %s" % (email_config.get('email_prefix', ''), subject) |
|
52 | subject = "%s %s" % (email_config.get('email_prefix', ''), subject) | |
53 | if not recipients: |
|
53 | if not recipients: | |
54 | # if recipients are not defined we send to email_config + all admins |
|
54 | # if recipients are not defined we send to email_config + all admins | |
55 | admins = [ |
|
55 | admins = [ | |
56 | u.email for u in User.query().filter(User.admin == True).all()] |
|
56 | u.email for u in User.query().filter(User.admin == True).all()] | |
57 | recipients = [email_config.get('email_to')] + admins |
|
57 | recipients = [email_config.get('email_to')] + admins | |
58 |
|
58 | |||
59 | mail_server = email_config.get('smtp_server') or None |
|
59 | mail_server = email_config.get('smtp_server') or None | |
60 | if mail_server is None: |
|
60 | if mail_server is None: | |
61 | log.error("SMTP server information missing. Sending email failed. " |
|
61 | log.error("SMTP server information missing. Sending email failed. " | |
62 | "Make sure that `smtp_server` variable is configured " |
|
62 | "Make sure that `smtp_server` variable is configured " | |
63 | "inside the .ini file") |
|
63 | "inside the .ini file") | |
64 | return False |
|
64 | return False | |
65 |
|
65 | |||
66 | mail_from = email_config.get('app_email_from', 'RhodeCode') |
|
66 | mail_from = email_config.get('app_email_from', 'RhodeCode') | |
67 | user = email_config.get('smtp_username') |
|
67 | user = email_config.get('smtp_username') | |
68 | passwd = email_config.get('smtp_password') |
|
68 | passwd = email_config.get('smtp_password') | |
69 | mail_port = email_config.get('smtp_port') |
|
69 | mail_port = email_config.get('smtp_port') | |
70 | tls = str2bool(email_config.get('smtp_use_tls')) |
|
70 | tls = str2bool(email_config.get('smtp_use_tls')) | |
71 | ssl = str2bool(email_config.get('smtp_use_ssl')) |
|
71 | ssl = str2bool(email_config.get('smtp_use_ssl')) | |
72 | debug = str2bool(email_config.get('debug')) |
|
72 | debug = str2bool(email_config.get('debug')) | |
73 | smtp_auth = email_config.get('smtp_auth') |
|
73 | smtp_auth = email_config.get('smtp_auth') | |
74 |
|
74 | |||
75 | try: |
|
75 | try: | |
76 | m = SmtpMailer(mail_from, user, passwd, mail_server, smtp_auth, |
|
76 | m = SmtpMailer(mail_from, user, passwd, mail_server, smtp_auth, | |
77 | mail_port, ssl, tls, debug=debug) |
|
77 | mail_port, ssl, tls, debug=debug) | |
78 | m.send(recipients, subject, body, html_body) |
|
78 | m.send(recipients, subject, body, html_body) | |
79 | except Exception: |
|
79 | except Exception: | |
80 | log.exception('Mail sending failed') |
|
80 | log.exception('Mail sending failed') | |
81 | return False |
|
81 | return False | |
82 | return True |
|
82 | return True | |
83 |
|
83 | |||
84 |
|
84 | |||
85 | @async_task(ignore_result=True, base=RequestContextTask) |
|
85 | @async_task(ignore_result=True, base=RequestContextTask) | |
86 | def create_repo(form_data, cur_user): |
|
86 | def create_repo(form_data, cur_user): | |
87 | from rhodecode.model.repo import RepoModel |
|
87 | from rhodecode.model.repo import RepoModel | |
88 | from rhodecode.model.user import UserModel |
|
88 | from rhodecode.model.user import UserModel | |
89 | from rhodecode.model.settings import SettingsModel |
|
89 | from rhodecode.model.settings import SettingsModel | |
90 |
|
90 | |||
91 | log = get_logger(create_repo) |
|
91 | log = get_logger(create_repo) | |
92 |
|
92 | |||
93 | cur_user = UserModel()._get_user(cur_user) |
|
93 | cur_user = UserModel()._get_user(cur_user) | |
94 | owner = cur_user |
|
94 | owner = cur_user | |
95 |
|
95 | |||
96 | repo_name = form_data['repo_name'] |
|
96 | repo_name = form_data['repo_name'] | |
97 | repo_name_full = form_data['repo_name_full'] |
|
97 | repo_name_full = form_data['repo_name_full'] | |
98 | repo_type = form_data['repo_type'] |
|
98 | repo_type = form_data['repo_type'] | |
99 | description = form_data['repo_description'] |
|
99 | description = form_data['repo_description'] | |
100 | private = form_data['repo_private'] |
|
100 | private = form_data['repo_private'] | |
101 | clone_uri = form_data.get('clone_uri') |
|
101 | clone_uri = form_data.get('clone_uri') | |
102 | repo_group = safe_int(form_data['repo_group']) |
|
102 | repo_group = safe_int(form_data['repo_group']) | |
103 | landing_rev = form_data['repo_landing_rev'] |
|
103 | landing_rev = form_data['repo_landing_rev'] | |
104 | copy_fork_permissions = form_data.get('copy_permissions') |
|
104 | copy_fork_permissions = form_data.get('copy_permissions') | |
105 | copy_group_permissions = form_data.get('repo_copy_permissions') |
|
105 | copy_group_permissions = form_data.get('repo_copy_permissions') | |
106 | fork_of = form_data.get('fork_parent_id') |
|
106 | fork_of = form_data.get('fork_parent_id') | |
107 | state = form_data.get('repo_state', Repository.STATE_PENDING) |
|
107 | state = form_data.get('repo_state', Repository.STATE_PENDING) | |
108 |
|
108 | |||
109 | # repo creation defaults, private and repo_type are filled in form |
|
109 | # repo creation defaults, private and repo_type are filled in form | |
110 | defs = SettingsModel().get_default_repo_settings(strip_prefix=True) |
|
110 | defs = SettingsModel().get_default_repo_settings(strip_prefix=True) | |
111 | enable_statistics = form_data.get( |
|
111 | enable_statistics = form_data.get( | |
112 | 'enable_statistics', defs.get('repo_enable_statistics')) |
|
112 | 'enable_statistics', defs.get('repo_enable_statistics')) | |
113 | enable_locking = form_data.get( |
|
113 | enable_locking = form_data.get( | |
114 | 'enable_locking', defs.get('repo_enable_locking')) |
|
114 | 'enable_locking', defs.get('repo_enable_locking')) | |
115 | enable_downloads = form_data.get( |
|
115 | enable_downloads = form_data.get( | |
116 | 'enable_downloads', defs.get('repo_enable_downloads')) |
|
116 | 'enable_downloads', defs.get('repo_enable_downloads')) | |
117 |
|
117 | |||
118 | try: |
|
118 | try: | |
119 | repo = RepoModel()._create_repo( |
|
119 | repo = RepoModel()._create_repo( | |
120 | repo_name=repo_name_full, |
|
120 | repo_name=repo_name_full, | |
121 | repo_type=repo_type, |
|
121 | repo_type=repo_type, | |
122 | description=description, |
|
122 | description=description, | |
123 | owner=owner, |
|
123 | owner=owner, | |
124 | private=private, |
|
124 | private=private, | |
125 | clone_uri=clone_uri, |
|
125 | clone_uri=clone_uri, | |
126 | repo_group=repo_group, |
|
126 | repo_group=repo_group, | |
127 | landing_rev=landing_rev, |
|
127 | landing_rev=landing_rev, | |
128 | fork_of=fork_of, |
|
128 | fork_of=fork_of, | |
129 | copy_fork_permissions=copy_fork_permissions, |
|
129 | copy_fork_permissions=copy_fork_permissions, | |
130 | copy_group_permissions=copy_group_permissions, |
|
130 | copy_group_permissions=copy_group_permissions, | |
131 | enable_statistics=enable_statistics, |
|
131 | enable_statistics=enable_statistics, | |
132 | enable_locking=enable_locking, |
|
132 | enable_locking=enable_locking, | |
133 | enable_downloads=enable_downloads, |
|
133 | enable_downloads=enable_downloads, | |
134 | state=state |
|
134 | state=state | |
135 | ) |
|
135 | ) | |
136 | Session().commit() |
|
136 | Session().commit() | |
137 |
|
137 | |||
138 | # now create this repo on Filesystem |
|
138 | # now create this repo on Filesystem | |
139 | RepoModel()._create_filesystem_repo( |
|
139 | RepoModel()._create_filesystem_repo( | |
140 | repo_name=repo_name, |
|
140 | repo_name=repo_name, | |
141 | repo_type=repo_type, |
|
141 | repo_type=repo_type, | |
142 | repo_group=RepoModel()._get_repo_group(repo_group), |
|
142 | repo_group=RepoModel()._get_repo_group(repo_group), | |
143 | clone_uri=clone_uri, |
|
143 | clone_uri=clone_uri, | |
144 | ) |
|
144 | ) | |
145 | repo = Repository.get_by_repo_name(repo_name_full) |
|
145 | repo = Repository.get_by_repo_name(repo_name_full) | |
146 | log_create_repository(created_by=owner.username, **repo.get_dict()) |
|
146 | log_create_repository(created_by=owner.username, **repo.get_dict()) | |
147 |
|
147 | |||
148 | # update repo commit caches initially |
|
148 | # update repo commit caches initially | |
149 | repo.update_commit_cache() |
|
149 | repo.update_commit_cache() | |
150 |
|
150 | |||
151 | # set new created state |
|
151 | # set new created state | |
152 | repo.set_state(Repository.STATE_CREATED) |
|
152 | repo.set_state(Repository.STATE_CREATED) | |
153 | repo_id = repo.repo_id |
|
153 | repo_id = repo.repo_id | |
154 | repo_data = repo.get_api_data() |
|
154 | repo_data = repo.get_api_data() | |
155 |
|
155 | |||
156 | audit_logger.store( |
|
156 | audit_logger.store( | |
157 | 'repo.create', action_data={'data': repo_data}, |
|
157 | 'repo.create', action_data={'data': repo_data}, | |
158 | user=cur_user, |
|
158 | user=cur_user, | |
159 | repo=audit_logger.RepoWrap(repo_name=repo_name, repo_id=repo_id)) |
|
159 | repo=audit_logger.RepoWrap(repo_name=repo_name, repo_id=repo_id)) | |
160 |
|
160 | |||
161 | Session().commit() |
|
161 | Session().commit() | |
162 | except Exception as e: |
|
162 | except Exception as e: | |
163 | log.warning('Exception occurred when creating repository, ' |
|
163 | log.warning('Exception occurred when creating repository, ' | |
164 | 'doing cleanup...', exc_info=True) |
|
164 | 'doing cleanup...', exc_info=True) | |
165 | if isinstance(e, IntegrityError): |
|
165 | if isinstance(e, IntegrityError): | |
166 | Session().rollback() |
|
166 | Session().rollback() | |
167 |
|
167 | |||
168 | # rollback things manually ! |
|
168 | # rollback things manually ! | |
169 | repo = Repository.get_by_repo_name(repo_name_full) |
|
169 | repo = Repository.get_by_repo_name(repo_name_full) | |
170 | if repo: |
|
170 | if repo: | |
171 | Repository.delete(repo.repo_id) |
|
171 | Repository.delete(repo.repo_id) | |
172 | Session().commit() |
|
172 | Session().commit() | |
173 | RepoModel()._delete_filesystem_repo(repo) |
|
173 | RepoModel()._delete_filesystem_repo(repo) | |
174 | log.info('Cleanup of repo %s finished', repo_name_full) |
|
174 | log.info('Cleanup of repo %s finished', repo_name_full) | |
175 | raise |
|
175 | raise | |
176 |
|
176 | |||
177 | return True |
|
177 | return True | |
178 |
|
178 | |||
179 |
|
179 | |||
180 | @async_task(ignore_result=True, base=RequestContextTask) |
|
180 | @async_task(ignore_result=True, base=RequestContextTask) | |
181 | def create_repo_fork(form_data, cur_user): |
|
181 | def create_repo_fork(form_data, cur_user): | |
182 | """ |
|
182 | """ | |
183 | Creates a fork of repository using internal VCS methods |
|
183 | Creates a fork of repository using internal VCS methods | |
184 | """ |
|
184 | """ | |
185 | from rhodecode.model.repo import RepoModel |
|
185 | from rhodecode.model.repo import RepoModel | |
186 | from rhodecode.model.user import UserModel |
|
186 | from rhodecode.model.user import UserModel | |
187 |
|
187 | |||
188 | log = get_logger(create_repo_fork) |
|
188 | log = get_logger(create_repo_fork) | |
189 |
|
189 | |||
190 | cur_user = UserModel()._get_user(cur_user) |
|
190 | cur_user = UserModel()._get_user(cur_user) | |
191 | owner = cur_user |
|
191 | owner = cur_user | |
192 |
|
192 | |||
193 | repo_name = form_data['repo_name'] # fork in this case |
|
193 | repo_name = form_data['repo_name'] # fork in this case | |
194 | repo_name_full = form_data['repo_name_full'] |
|
194 | repo_name_full = form_data['repo_name_full'] | |
195 | repo_type = form_data['repo_type'] |
|
195 | repo_type = form_data['repo_type'] | |
196 | description = form_data['description'] |
|
196 | description = form_data['description'] | |
197 | private = form_data['private'] |
|
197 | private = form_data['private'] | |
198 | clone_uri = form_data.get('clone_uri') |
|
198 | clone_uri = form_data.get('clone_uri') | |
199 | repo_group = safe_int(form_data['repo_group']) |
|
199 | repo_group = safe_int(form_data['repo_group']) | |
200 | landing_rev = form_data['landing_rev'] |
|
200 | landing_rev = form_data['landing_rev'] | |
201 | copy_fork_permissions = form_data.get('copy_permissions') |
|
201 | copy_fork_permissions = form_data.get('copy_permissions') | |
202 | fork_id = safe_int(form_data.get('fork_parent_id')) |
|
202 | fork_id = safe_int(form_data.get('fork_parent_id')) | |
203 |
|
203 | |||
204 | try: |
|
204 | try: | |
205 | fork_of = RepoModel()._get_repo(fork_id) |
|
205 | fork_of = RepoModel()._get_repo(fork_id) | |
206 | RepoModel()._create_repo( |
|
206 | RepoModel()._create_repo( | |
207 | repo_name=repo_name_full, |
|
207 | repo_name=repo_name_full, | |
208 | repo_type=repo_type, |
|
208 | repo_type=repo_type, | |
209 | description=description, |
|
209 | description=description, | |
210 | owner=owner, |
|
210 | owner=owner, | |
211 | private=private, |
|
211 | private=private, | |
212 | clone_uri=clone_uri, |
|
212 | clone_uri=clone_uri, | |
213 | repo_group=repo_group, |
|
213 | repo_group=repo_group, | |
214 | landing_rev=landing_rev, |
|
214 | landing_rev=landing_rev, | |
215 | fork_of=fork_of, |
|
215 | fork_of=fork_of, | |
216 | copy_fork_permissions=copy_fork_permissions |
|
216 | copy_fork_permissions=copy_fork_permissions | |
217 | ) |
|
217 | ) | |
218 |
|
218 | |||
219 | Session().commit() |
|
219 | Session().commit() | |
220 |
|
220 | |||
221 | base_path = Repository.base_path() |
|
221 | base_path = Repository.base_path() | |
222 | source_repo_path = os.path.join(base_path, fork_of.repo_name) |
|
222 | source_repo_path = os.path.join(base_path, fork_of.repo_name) | |
223 |
|
223 | |||
224 | # now create this repo on Filesystem |
|
224 | # now create this repo on Filesystem | |
225 | RepoModel()._create_filesystem_repo( |
|
225 | RepoModel()._create_filesystem_repo( | |
226 | repo_name=repo_name, |
|
226 | repo_name=repo_name, | |
227 | repo_type=repo_type, |
|
227 | repo_type=repo_type, | |
228 | repo_group=RepoModel()._get_repo_group(repo_group), |
|
228 | repo_group=RepoModel()._get_repo_group(repo_group), | |
229 | clone_uri=source_repo_path, |
|
229 | clone_uri=source_repo_path, | |
230 | ) |
|
230 | ) | |
231 | repo = Repository.get_by_repo_name(repo_name_full) |
|
231 | repo = Repository.get_by_repo_name(repo_name_full) | |
232 | log_create_repository(created_by=owner.username, **repo.get_dict()) |
|
232 | log_create_repository(created_by=owner.username, **repo.get_dict()) | |
233 |
|
233 | |||
234 | # update repo commit caches initially |
|
234 | # update repo commit caches initially | |
235 | config = repo._config |
|
235 | config = repo._config | |
236 | config.set('extensions', 'largefiles', '') |
|
236 | config.set('extensions', 'largefiles', '') | |
237 | repo.update_commit_cache(config=config) |
|
237 | repo.update_commit_cache(config=config) | |
238 |
|
238 | |||
239 | # set new created state |
|
239 | # set new created state | |
240 | repo.set_state(Repository.STATE_CREATED) |
|
240 | repo.set_state(Repository.STATE_CREATED) | |
241 |
|
241 | |||
242 | repo_id = repo.repo_id |
|
242 | repo_id = repo.repo_id | |
243 | repo_data = repo.get_api_data() |
|
243 | repo_data = repo.get_api_data() | |
244 | audit_logger.store( |
|
244 | audit_logger.store( | |
245 | 'repo.fork', action_data={'data': repo_data}, |
|
245 | 'repo.fork', action_data={'data': repo_data}, | |
246 | user=cur_user, |
|
246 | user=cur_user, | |
247 | repo=audit_logger.RepoWrap(repo_name=repo_name, repo_id=repo_id)) |
|
247 | repo=audit_logger.RepoWrap(repo_name=repo_name, repo_id=repo_id)) | |
248 |
|
248 | |||
249 | Session().commit() |
|
249 | Session().commit() | |
250 | except Exception as e: |
|
250 | except Exception as e: | |
251 | log.warning('Exception %s occurred when forking repository, ' |
|
251 | log.warning('Exception %s occurred when forking repository, ' | |
252 | 'doing cleanup...', exc_info=True) |
|
252 | 'doing cleanup...', exc_info=True) | |
253 | if isinstance(e, IntegrityError): |
|
253 | if isinstance(e, IntegrityError): | |
254 | Session().rollback() |
|
254 | Session().rollback() | |
255 |
|
255 | |||
256 | # rollback things manually ! |
|
256 | # rollback things manually ! | |
257 | repo = Repository.get_by_repo_name(repo_name_full) |
|
257 | repo = Repository.get_by_repo_name(repo_name_full) | |
258 | if repo: |
|
258 | if repo: | |
259 | Repository.delete(repo.repo_id) |
|
259 | Repository.delete(repo.repo_id) | |
260 | Session().commit() |
|
260 | Session().commit() | |
261 | RepoModel()._delete_filesystem_repo(repo) |
|
261 | RepoModel()._delete_filesystem_repo(repo) | |
262 | log.info('Cleanup of repo %s finished', repo_name_full) |
|
262 | log.info('Cleanup of repo %s finished', repo_name_full) | |
263 | raise |
|
263 | raise | |
264 |
|
264 | |||
265 | return True |
|
265 | return True | |
266 |
|
266 | |||
267 |
|
267 | |||
268 | @async_task(ignore_result=True) |
|
268 | @async_task(ignore_result=True) | |
269 | def sync_repo(*args, **kwargs): |
|
269 | def sync_repo(*args, **kwargs): | |
270 | from rhodecode.model.scm import ScmModel |
|
270 | from rhodecode.model.scm import ScmModel | |
271 | log = get_logger(sync_repo) |
|
271 | log = get_logger(sync_repo) | |
272 | repo_name = kwargs['repo_name'] |
|
272 | repo_name = kwargs['repo_name'] | |
273 | log.info('Pulling from %s', repo_name) |
|
273 | log.info('Pulling from %s', repo_name) | |
274 | dbrepo = Repository.get_by_repo_name(repo_name) |
|
274 | dbrepo = Repository.get_by_repo_name(repo_name) | |
275 | if dbrepo and dbrepo.clone_uri: |
|
275 | if dbrepo and dbrepo.clone_uri: | |
276 | ScmModel().pull_changes(kwargs['repo_name'], kwargs['username']) |
|
276 | ScmModel().pull_changes(kwargs['repo_name'], kwargs['username']) | |
277 | else: |
|
277 | else: | |
278 | log.debug('Repo `%s` not found or without a clone_url', repo_name) |
|
278 | log.debug('Repo `%s` not found or without a clone_url', repo_name) | |
279 |
|
279 | |||
280 |
|
280 | |||
|
281 | @async_task(ignore_result=True) | |||
|
282 | def check_for_update(): | |||
|
283 | from rhodecode.model.update import UpdateModel | |||
|
284 | update_url = UpdateModel().get_update_url() | |||
|
285 | cur_ver = rhodecode.__version__ | |||
|
286 | ||||
|
287 | try: | |||
|
288 | data = UpdateModel().get_update_data(update_url) | |||
|
289 | latest = data['versions'][0] | |||
|
290 | UpdateModel().store_version(latest['version']) | |||
|
291 | except Exception: | |||
|
292 | pass | |||
|
293 | ||||
|
294 | ||||
281 | @async_task(ignore_result=False) |
|
295 | @async_task(ignore_result=False) | |
282 | def beat_check(*args, **kwargs): |
|
296 | def beat_check(*args, **kwargs): | |
283 | log = get_logger(beat_check) |
|
297 | log = get_logger(beat_check) | |
284 | log.info('Got args: %r and kwargs %r', args, kwargs) |
|
298 | log.info('Got args: %r and kwargs %r', args, kwargs) | |
285 | return time.time() |
|
299 | return time.time() |
@@ -1,27 +1,26 b'' | |||||
1 | ## -*- coding: utf-8 -*- |
|
1 | ## -*- coding: utf-8 -*- | |
2 | ## upgrade block rendered afte on-click check |
|
2 | ## upgrade block rendered afte on-click check | |
3 |
|
3 | |||
4 | <div class="alert ${'alert-warning' if c.should_upgrade else 'alert-success'}"> |
|
4 | <div class="alert ${'alert-warning' if c.should_upgrade else 'alert-success'}"> | |
5 |
<p |
|
5 | <p> | |
6 |
|
||||
7 | %if c.should_upgrade: |
|
6 | %if c.should_upgrade: | |
8 | A <b>new version</b> is available: |
|
7 | A <b>new version</b> is available: | |
9 | %if c.latest_data.get('title'): |
|
8 | %if c.latest_data.get('title'): | |
10 | <b>${h.literal(c.latest_data['title'])}</b> |
|
9 | <b>${h.literal(c.latest_data['title'])}</b> | |
11 | %else: |
|
10 | %else: | |
12 | <b>${c.latest_ver}</b> |
|
11 | <b>${c.latest_ver}</b> | |
13 | %endif |
|
12 | %endif | |
14 | %else: |
|
13 | %else: | |
15 |
|
|
14 | This instance is already running the <b>latest</b> stable version ${c.latest_ver}. | |
16 | %endif |
|
15 | %endif | |
17 | </p> |
|
16 | </p> | |
18 |
|
17 | |||
19 | % if c.should_upgrade and c.important_notices: |
|
18 | % if c.should_upgrade and c.important_notices: | |
20 | <div>Important notes for this release:</div> |
|
19 | <div>Important notes for this release:</div> | |
21 | <ul> |
|
20 | <ul> | |
22 | % for notice in c.important_notices: |
|
21 | % for notice in c.important_notices: | |
23 | <li>- ${notice}</li> |
|
22 | <li>- ${notice}</li> | |
24 | % endfor |
|
23 | % endfor | |
25 | </ul> |
|
24 | </ul> | |
26 | % endif |
|
25 | % endif | |
27 | </div> |
|
26 | </div> |
General Comments 0
You need to be logged in to leave comments.
Login now