##// END OF EJS Templates
mercurial: enabled full evolve+topic extensions
marcink -
r3625:4c188069 default
parent child Browse files
Show More
@@ -1,624 +1,645 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2019 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 Database creation, and setup module for RhodeCode Enterprise. Used for creation
22 Database creation, and setup module for RhodeCode Enterprise. Used for creation
23 of database as well as for migration operations
23 of database as well as for migration operations
24 """
24 """
25
25
26 import os
26 import os
27 import sys
27 import sys
28 import time
28 import time
29 import uuid
29 import uuid
30 import logging
30 import logging
31 import getpass
31 import getpass
32 from os.path import dirname as dn, join as jn
32 from os.path import dirname as dn, join as jn
33
33
34 from sqlalchemy.engine import create_engine
34 from sqlalchemy.engine import create_engine
35
35
36 from rhodecode import __dbversion__
36 from rhodecode import __dbversion__
37 from rhodecode.model import init_model
37 from rhodecode.model import init_model
38 from rhodecode.model.user import UserModel
38 from rhodecode.model.user import UserModel
39 from rhodecode.model.db import (
39 from rhodecode.model.db import (
40 User, Permission, RhodeCodeUi, RhodeCodeSetting, UserToPerm,
40 User, Permission, RhodeCodeUi, RhodeCodeSetting, UserToPerm,
41 DbMigrateVersion, RepoGroup, UserRepoGroupToPerm, CacheKey, Repository)
41 DbMigrateVersion, RepoGroup, UserRepoGroupToPerm, CacheKey, Repository)
42 from rhodecode.model.meta import Session, Base
42 from rhodecode.model.meta import Session, Base
43 from rhodecode.model.permission import PermissionModel
43 from rhodecode.model.permission import PermissionModel
44 from rhodecode.model.repo import RepoModel
44 from rhodecode.model.repo import RepoModel
45 from rhodecode.model.repo_group import RepoGroupModel
45 from rhodecode.model.repo_group import RepoGroupModel
46 from rhodecode.model.settings import SettingsModel
46 from rhodecode.model.settings import SettingsModel
47
47
48
48
49 log = logging.getLogger(__name__)
49 log = logging.getLogger(__name__)
50
50
51
51
52 def notify(msg):
52 def notify(msg):
53 """
53 """
54 Notification for migrations messages
54 Notification for migrations messages
55 """
55 """
56 ml = len(msg) + (4 * 2)
56 ml = len(msg) + (4 * 2)
57 print(('\n%s\n*** %s ***\n%s' % ('*' * ml, msg, '*' * ml)).upper())
57 print(('\n%s\n*** %s ***\n%s' % ('*' * ml, msg, '*' * ml)).upper())
58
58
59
59
60 class DbManage(object):
60 class DbManage(object):
61
61
62 def __init__(self, log_sql, dbconf, root, tests=False,
62 def __init__(self, log_sql, dbconf, root, tests=False,
63 SESSION=None, cli_args=None):
63 SESSION=None, cli_args=None):
64 self.dbname = dbconf.split('/')[-1]
64 self.dbname = dbconf.split('/')[-1]
65 self.tests = tests
65 self.tests = tests
66 self.root = root
66 self.root = root
67 self.dburi = dbconf
67 self.dburi = dbconf
68 self.log_sql = log_sql
68 self.log_sql = log_sql
69 self.db_exists = False
69 self.db_exists = False
70 self.cli_args = cli_args or {}
70 self.cli_args = cli_args or {}
71 self.init_db(SESSION=SESSION)
71 self.init_db(SESSION=SESSION)
72 self.ask_ok = self.get_ask_ok_func(self.cli_args.get('force_ask'))
72 self.ask_ok = self.get_ask_ok_func(self.cli_args.get('force_ask'))
73
73
74 def get_ask_ok_func(self, param):
74 def get_ask_ok_func(self, param):
75 if param not in [None]:
75 if param not in [None]:
76 # return a function lambda that has a default set to param
76 # return a function lambda that has a default set to param
77 return lambda *args, **kwargs: param
77 return lambda *args, **kwargs: param
78 else:
78 else:
79 from rhodecode.lib.utils import ask_ok
79 from rhodecode.lib.utils import ask_ok
80 return ask_ok
80 return ask_ok
81
81
82 def init_db(self, SESSION=None):
82 def init_db(self, SESSION=None):
83 if SESSION:
83 if SESSION:
84 self.sa = SESSION
84 self.sa = SESSION
85 else:
85 else:
86 # init new sessions
86 # init new sessions
87 engine = create_engine(self.dburi, echo=self.log_sql)
87 engine = create_engine(self.dburi, echo=self.log_sql)
88 init_model(engine)
88 init_model(engine)
89 self.sa = Session()
89 self.sa = Session()
90
90
91 def create_tables(self, override=False):
91 def create_tables(self, override=False):
92 """
92 """
93 Create a auth database
93 Create a auth database
94 """
94 """
95
95
96 log.info("Existing database with the same name is going to be destroyed.")
96 log.info("Existing database with the same name is going to be destroyed.")
97 log.info("Setup command will run DROP ALL command on that database.")
97 log.info("Setup command will run DROP ALL command on that database.")
98 if self.tests:
98 if self.tests:
99 destroy = True
99 destroy = True
100 else:
100 else:
101 destroy = self.ask_ok('Are you sure that you want to destroy the old database? [y/n]')
101 destroy = self.ask_ok('Are you sure that you want to destroy the old database? [y/n]')
102 if not destroy:
102 if not destroy:
103 log.info('Nothing done.')
103 log.info('Nothing done.')
104 sys.exit(0)
104 sys.exit(0)
105 if destroy:
105 if destroy:
106 Base.metadata.drop_all()
106 Base.metadata.drop_all()
107
107
108 checkfirst = not override
108 checkfirst = not override
109 Base.metadata.create_all(checkfirst=checkfirst)
109 Base.metadata.create_all(checkfirst=checkfirst)
110 log.info('Created tables for %s', self.dbname)
110 log.info('Created tables for %s', self.dbname)
111
111
112 def set_db_version(self):
112 def set_db_version(self):
113 ver = DbMigrateVersion()
113 ver = DbMigrateVersion()
114 ver.version = __dbversion__
114 ver.version = __dbversion__
115 ver.repository_id = 'rhodecode_db_migrations'
115 ver.repository_id = 'rhodecode_db_migrations'
116 ver.repository_path = 'versions'
116 ver.repository_path = 'versions'
117 self.sa.add(ver)
117 self.sa.add(ver)
118 log.info('db version set to: %s', __dbversion__)
118 log.info('db version set to: %s', __dbversion__)
119
119
120 def run_pre_migration_tasks(self):
120 def run_pre_migration_tasks(self):
121 """
121 """
122 Run various tasks before actually doing migrations
122 Run various tasks before actually doing migrations
123 """
123 """
124 # delete cache keys on each upgrade
124 # delete cache keys on each upgrade
125 total = CacheKey.query().count()
125 total = CacheKey.query().count()
126 log.info("Deleting (%s) cache keys now...", total)
126 log.info("Deleting (%s) cache keys now...", total)
127 CacheKey.delete_all_cache()
127 CacheKey.delete_all_cache()
128
128
129 def upgrade(self, version=None):
129 def upgrade(self, version=None):
130 """
130 """
131 Upgrades given database schema to given revision following
131 Upgrades given database schema to given revision following
132 all needed steps, to perform the upgrade
132 all needed steps, to perform the upgrade
133
133
134 """
134 """
135
135
136 from rhodecode.lib.dbmigrate.migrate.versioning import api
136 from rhodecode.lib.dbmigrate.migrate.versioning import api
137 from rhodecode.lib.dbmigrate.migrate.exceptions import \
137 from rhodecode.lib.dbmigrate.migrate.exceptions import \
138 DatabaseNotControlledError
138 DatabaseNotControlledError
139
139
140 if 'sqlite' in self.dburi:
140 if 'sqlite' in self.dburi:
141 print(
141 print(
142 '********************** WARNING **********************\n'
142 '********************** WARNING **********************\n'
143 'Make sure your version of sqlite is at least 3.7.X. \n'
143 'Make sure your version of sqlite is at least 3.7.X. \n'
144 'Earlier versions are known to fail on some migrations\n'
144 'Earlier versions are known to fail on some migrations\n'
145 '*****************************************************\n')
145 '*****************************************************\n')
146
146
147 upgrade = self.ask_ok(
147 upgrade = self.ask_ok(
148 'You are about to perform a database upgrade. Make '
148 'You are about to perform a database upgrade. Make '
149 'sure you have backed up your database. '
149 'sure you have backed up your database. '
150 'Continue ? [y/n]')
150 'Continue ? [y/n]')
151 if not upgrade:
151 if not upgrade:
152 log.info('No upgrade performed')
152 log.info('No upgrade performed')
153 sys.exit(0)
153 sys.exit(0)
154
154
155 repository_path = jn(dn(dn(dn(os.path.realpath(__file__)))),
155 repository_path = jn(dn(dn(dn(os.path.realpath(__file__)))),
156 'rhodecode/lib/dbmigrate')
156 'rhodecode/lib/dbmigrate')
157 db_uri = self.dburi
157 db_uri = self.dburi
158
158
159 if version:
159 if version:
160 DbMigrateVersion.set_version(version)
160 DbMigrateVersion.set_version(version)
161
161
162 try:
162 try:
163 curr_version = api.db_version(db_uri, repository_path)
163 curr_version = api.db_version(db_uri, repository_path)
164 msg = ('Found current database db_uri under version '
164 msg = ('Found current database db_uri under version '
165 'control with version {}'.format(curr_version))
165 'control with version {}'.format(curr_version))
166
166
167 except (RuntimeError, DatabaseNotControlledError):
167 except (RuntimeError, DatabaseNotControlledError):
168 curr_version = 1
168 curr_version = 1
169 msg = ('Current database is not under version control. Setting '
169 msg = ('Current database is not under version control. Setting '
170 'as version %s' % curr_version)
170 'as version %s' % curr_version)
171 api.version_control(db_uri, repository_path, curr_version)
171 api.version_control(db_uri, repository_path, curr_version)
172
172
173 notify(msg)
173 notify(msg)
174
174
175 self.run_pre_migration_tasks()
175 self.run_pre_migration_tasks()
176
176
177 if curr_version == __dbversion__:
177 if curr_version == __dbversion__:
178 log.info('This database is already at the newest version')
178 log.info('This database is already at the newest version')
179 sys.exit(0)
179 sys.exit(0)
180
180
181 upgrade_steps = range(curr_version + 1, __dbversion__ + 1)
181 upgrade_steps = range(curr_version + 1, __dbversion__ + 1)
182 notify('attempting to upgrade database from '
182 notify('attempting to upgrade database from '
183 'version %s to version %s' % (curr_version, __dbversion__))
183 'version %s to version %s' % (curr_version, __dbversion__))
184
184
185 # CALL THE PROPER ORDER OF STEPS TO PERFORM FULL UPGRADE
185 # CALL THE PROPER ORDER OF STEPS TO PERFORM FULL UPGRADE
186 _step = None
186 _step = None
187 for step in upgrade_steps:
187 for step in upgrade_steps:
188 notify('performing upgrade step %s' % step)
188 notify('performing upgrade step %s' % step)
189 time.sleep(0.5)
189 time.sleep(0.5)
190
190
191 api.upgrade(db_uri, repository_path, step)
191 api.upgrade(db_uri, repository_path, step)
192 self.sa.rollback()
192 self.sa.rollback()
193 notify('schema upgrade for step %s completed' % (step,))
193 notify('schema upgrade for step %s completed' % (step,))
194
194
195 _step = step
195 _step = step
196
196
197 notify('upgrade to version %s successful' % _step)
197 notify('upgrade to version %s successful' % _step)
198
198
199 def fix_repo_paths(self):
199 def fix_repo_paths(self):
200 """
200 """
201 Fixes an old RhodeCode version path into new one without a '*'
201 Fixes an old RhodeCode version path into new one without a '*'
202 """
202 """
203
203
204 paths = self.sa.query(RhodeCodeUi)\
204 paths = self.sa.query(RhodeCodeUi)\
205 .filter(RhodeCodeUi.ui_key == '/')\
205 .filter(RhodeCodeUi.ui_key == '/')\
206 .scalar()
206 .scalar()
207
207
208 paths.ui_value = paths.ui_value.replace('*', '')
208 paths.ui_value = paths.ui_value.replace('*', '')
209
209
210 try:
210 try:
211 self.sa.add(paths)
211 self.sa.add(paths)
212 self.sa.commit()
212 self.sa.commit()
213 except Exception:
213 except Exception:
214 self.sa.rollback()
214 self.sa.rollback()
215 raise
215 raise
216
216
217 def fix_default_user(self):
217 def fix_default_user(self):
218 """
218 """
219 Fixes an old default user with some 'nicer' default values,
219 Fixes an old default user with some 'nicer' default values,
220 used mostly for anonymous access
220 used mostly for anonymous access
221 """
221 """
222 def_user = self.sa.query(User)\
222 def_user = self.sa.query(User)\
223 .filter(User.username == User.DEFAULT_USER)\
223 .filter(User.username == User.DEFAULT_USER)\
224 .one()
224 .one()
225
225
226 def_user.name = 'Anonymous'
226 def_user.name = 'Anonymous'
227 def_user.lastname = 'User'
227 def_user.lastname = 'User'
228 def_user.email = User.DEFAULT_USER_EMAIL
228 def_user.email = User.DEFAULT_USER_EMAIL
229
229
230 try:
230 try:
231 self.sa.add(def_user)
231 self.sa.add(def_user)
232 self.sa.commit()
232 self.sa.commit()
233 except Exception:
233 except Exception:
234 self.sa.rollback()
234 self.sa.rollback()
235 raise
235 raise
236
236
237 def fix_settings(self):
237 def fix_settings(self):
238 """
238 """
239 Fixes rhodecode settings and adds ga_code key for google analytics
239 Fixes rhodecode settings and adds ga_code key for google analytics
240 """
240 """
241
241
242 hgsettings3 = RhodeCodeSetting('ga_code', '')
242 hgsettings3 = RhodeCodeSetting('ga_code', '')
243
243
244 try:
244 try:
245 self.sa.add(hgsettings3)
245 self.sa.add(hgsettings3)
246 self.sa.commit()
246 self.sa.commit()
247 except Exception:
247 except Exception:
248 self.sa.rollback()
248 self.sa.rollback()
249 raise
249 raise
250
250
251 def create_admin_and_prompt(self):
251 def create_admin_and_prompt(self):
252
252
253 # defaults
253 # defaults
254 defaults = self.cli_args
254 defaults = self.cli_args
255 username = defaults.get('username')
255 username = defaults.get('username')
256 password = defaults.get('password')
256 password = defaults.get('password')
257 email = defaults.get('email')
257 email = defaults.get('email')
258
258
259 if username is None:
259 if username is None:
260 username = raw_input('Specify admin username:')
260 username = raw_input('Specify admin username:')
261 if password is None:
261 if password is None:
262 password = self._get_admin_password()
262 password = self._get_admin_password()
263 if not password:
263 if not password:
264 # second try
264 # second try
265 password = self._get_admin_password()
265 password = self._get_admin_password()
266 if not password:
266 if not password:
267 sys.exit()
267 sys.exit()
268 if email is None:
268 if email is None:
269 email = raw_input('Specify admin email:')
269 email = raw_input('Specify admin email:')
270 api_key = self.cli_args.get('api_key')
270 api_key = self.cli_args.get('api_key')
271 self.create_user(username, password, email, True,
271 self.create_user(username, password, email, True,
272 strict_creation_check=False,
272 strict_creation_check=False,
273 api_key=api_key)
273 api_key=api_key)
274
274
275 def _get_admin_password(self):
275 def _get_admin_password(self):
276 password = getpass.getpass('Specify admin password '
276 password = getpass.getpass('Specify admin password '
277 '(min 6 chars):')
277 '(min 6 chars):')
278 confirm = getpass.getpass('Confirm password:')
278 confirm = getpass.getpass('Confirm password:')
279
279
280 if password != confirm:
280 if password != confirm:
281 log.error('passwords mismatch')
281 log.error('passwords mismatch')
282 return False
282 return False
283 if len(password) < 6:
283 if len(password) < 6:
284 log.error('password is too short - use at least 6 characters')
284 log.error('password is too short - use at least 6 characters')
285 return False
285 return False
286
286
287 return password
287 return password
288
288
289 def create_test_admin_and_users(self):
289 def create_test_admin_and_users(self):
290 log.info('creating admin and regular test users')
290 log.info('creating admin and regular test users')
291 from rhodecode.tests import TEST_USER_ADMIN_LOGIN, \
291 from rhodecode.tests import TEST_USER_ADMIN_LOGIN, \
292 TEST_USER_ADMIN_PASS, TEST_USER_ADMIN_EMAIL, \
292 TEST_USER_ADMIN_PASS, TEST_USER_ADMIN_EMAIL, \
293 TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS, \
293 TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS, \
294 TEST_USER_REGULAR_EMAIL, TEST_USER_REGULAR2_LOGIN, \
294 TEST_USER_REGULAR_EMAIL, TEST_USER_REGULAR2_LOGIN, \
295 TEST_USER_REGULAR2_PASS, TEST_USER_REGULAR2_EMAIL
295 TEST_USER_REGULAR2_PASS, TEST_USER_REGULAR2_EMAIL
296
296
297 self.create_user(TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS,
297 self.create_user(TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS,
298 TEST_USER_ADMIN_EMAIL, True, api_key=True)
298 TEST_USER_ADMIN_EMAIL, True, api_key=True)
299
299
300 self.create_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS,
300 self.create_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS,
301 TEST_USER_REGULAR_EMAIL, False, api_key=True)
301 TEST_USER_REGULAR_EMAIL, False, api_key=True)
302
302
303 self.create_user(TEST_USER_REGULAR2_LOGIN, TEST_USER_REGULAR2_PASS,
303 self.create_user(TEST_USER_REGULAR2_LOGIN, TEST_USER_REGULAR2_PASS,
304 TEST_USER_REGULAR2_EMAIL, False, api_key=True)
304 TEST_USER_REGULAR2_EMAIL, False, api_key=True)
305
305
306 def create_ui_settings(self, repo_store_path):
306 def create_ui_settings(self, repo_store_path):
307 """
307 """
308 Creates ui settings, fills out hooks
308 Creates ui settings, fills out hooks
309 and disables dotencode
309 and disables dotencode
310 """
310 """
311 settings_model = SettingsModel(sa=self.sa)
311 settings_model = SettingsModel(sa=self.sa)
312 from rhodecode.lib.vcs.backends.hg import largefiles_store
312 from rhodecode.lib.vcs.backends.hg import largefiles_store
313 from rhodecode.lib.vcs.backends.git import lfs_store
313 from rhodecode.lib.vcs.backends.git import lfs_store
314
314
315 # Build HOOKS
315 # Build HOOKS
316 hooks = [
316 hooks = [
317 (RhodeCodeUi.HOOK_REPO_SIZE, 'python:vcsserver.hooks.repo_size'),
317 (RhodeCodeUi.HOOK_REPO_SIZE, 'python:vcsserver.hooks.repo_size'),
318
318
319 # HG
319 # HG
320 (RhodeCodeUi.HOOK_PRE_PULL, 'python:vcsserver.hooks.pre_pull'),
320 (RhodeCodeUi.HOOK_PRE_PULL, 'python:vcsserver.hooks.pre_pull'),
321 (RhodeCodeUi.HOOK_PULL, 'python:vcsserver.hooks.log_pull_action'),
321 (RhodeCodeUi.HOOK_PULL, 'python:vcsserver.hooks.log_pull_action'),
322 (RhodeCodeUi.HOOK_PRE_PUSH, 'python:vcsserver.hooks.pre_push'),
322 (RhodeCodeUi.HOOK_PRE_PUSH, 'python:vcsserver.hooks.pre_push'),
323 (RhodeCodeUi.HOOK_PRETX_PUSH, 'python:vcsserver.hooks.pre_push'),
323 (RhodeCodeUi.HOOK_PRETX_PUSH, 'python:vcsserver.hooks.pre_push'),
324 (RhodeCodeUi.HOOK_PUSH, 'python:vcsserver.hooks.log_push_action'),
324 (RhodeCodeUi.HOOK_PUSH, 'python:vcsserver.hooks.log_push_action'),
325 (RhodeCodeUi.HOOK_PUSH_KEY, 'python:vcsserver.hooks.key_push'),
325 (RhodeCodeUi.HOOK_PUSH_KEY, 'python:vcsserver.hooks.key_push'),
326
326
327 ]
327 ]
328
328
329 for key, value in hooks:
329 for key, value in hooks:
330 hook_obj = settings_model.get_ui_by_key(key)
330 hook_obj = settings_model.get_ui_by_key(key)
331 hooks2 = hook_obj if hook_obj else RhodeCodeUi()
331 hooks2 = hook_obj if hook_obj else RhodeCodeUi()
332 hooks2.ui_section = 'hooks'
332 hooks2.ui_section = 'hooks'
333 hooks2.ui_key = key
333 hooks2.ui_key = key
334 hooks2.ui_value = value
334 hooks2.ui_value = value
335 self.sa.add(hooks2)
335 self.sa.add(hooks2)
336
336
337 # enable largefiles
337 # enable largefiles
338 largefiles = RhodeCodeUi()
338 largefiles = RhodeCodeUi()
339 largefiles.ui_section = 'extensions'
339 largefiles.ui_section = 'extensions'
340 largefiles.ui_key = 'largefiles'
340 largefiles.ui_key = 'largefiles'
341 largefiles.ui_value = ''
341 largefiles.ui_value = ''
342 self.sa.add(largefiles)
342 self.sa.add(largefiles)
343
343
344 # set default largefiles cache dir, defaults to
344 # set default largefiles cache dir, defaults to
345 # /repo_store_location/.cache/largefiles
345 # /repo_store_location/.cache/largefiles
346 largefiles = RhodeCodeUi()
346 largefiles = RhodeCodeUi()
347 largefiles.ui_section = 'largefiles'
347 largefiles.ui_section = 'largefiles'
348 largefiles.ui_key = 'usercache'
348 largefiles.ui_key = 'usercache'
349 largefiles.ui_value = largefiles_store(repo_store_path)
349 largefiles.ui_value = largefiles_store(repo_store_path)
350
350
351 self.sa.add(largefiles)
351 self.sa.add(largefiles)
352
352
353 # set default lfs cache dir, defaults to
353 # set default lfs cache dir, defaults to
354 # /repo_store_location/.cache/lfs_store
354 # /repo_store_location/.cache/lfs_store
355 lfsstore = RhodeCodeUi()
355 lfsstore = RhodeCodeUi()
356 lfsstore.ui_section = 'vcs_git_lfs'
356 lfsstore.ui_section = 'vcs_git_lfs'
357 lfsstore.ui_key = 'store_location'
357 lfsstore.ui_key = 'store_location'
358 lfsstore.ui_value = lfs_store(repo_store_path)
358 lfsstore.ui_value = lfs_store(repo_store_path)
359
359
360 self.sa.add(lfsstore)
360 self.sa.add(lfsstore)
361
361
362 # enable hgsubversion disabled by default
362 # enable hgsubversion disabled by default
363 hgsubversion = RhodeCodeUi()
363 hgsubversion = RhodeCodeUi()
364 hgsubversion.ui_section = 'extensions'
364 hgsubversion.ui_section = 'extensions'
365 hgsubversion.ui_key = 'hgsubversion'
365 hgsubversion.ui_key = 'hgsubversion'
366 hgsubversion.ui_value = ''
366 hgsubversion.ui_value = ''
367 hgsubversion.ui_active = False
367 hgsubversion.ui_active = False
368 self.sa.add(hgsubversion)
368 self.sa.add(hgsubversion)
369
369
370 # enable hgevolve disabled by default
370 # enable hgevolve disabled by default
371 hgevolve = RhodeCodeUi()
371 hgevolve = RhodeCodeUi()
372 hgevolve.ui_section = 'extensions'
372 hgevolve.ui_section = 'extensions'
373 hgevolve.ui_key = 'evolve'
373 hgevolve.ui_key = 'evolve'
374 hgevolve.ui_value = ''
374 hgevolve.ui_value = ''
375 hgevolve.ui_active = False
375 hgevolve.ui_active = False
376 self.sa.add(hgevolve)
376 self.sa.add(hgevolve)
377
377
378 hgevolve = RhodeCodeUi()
379 hgevolve.ui_section = 'experimental'
380 hgevolve.ui_key = 'evolution'
381 hgevolve.ui_value = ''
382 hgevolve.ui_active = False
383 self.sa.add(hgevolve)
384
385 hgevolve = RhodeCodeUi()
386 hgevolve.ui_section = 'experimental'
387 hgevolve.ui_key = 'evolution.exchange'
388 hgevolve.ui_value = ''
389 hgevolve.ui_active = False
390 self.sa.add(hgevolve)
391
392 hgevolve = RhodeCodeUi()
393 hgevolve.ui_section = 'extensions'
394 hgevolve.ui_key = 'topic'
395 hgevolve.ui_value = ''
396 hgevolve.ui_active = False
397 self.sa.add(hgevolve)
398
378 # enable hggit disabled by default
399 # enable hggit disabled by default
379 hggit = RhodeCodeUi()
400 hggit = RhodeCodeUi()
380 hggit.ui_section = 'extensions'
401 hggit.ui_section = 'extensions'
381 hggit.ui_key = 'hggit'
402 hggit.ui_key = 'hggit'
382 hggit.ui_value = ''
403 hggit.ui_value = ''
383 hggit.ui_active = False
404 hggit.ui_active = False
384 self.sa.add(hggit)
405 self.sa.add(hggit)
385
406
386 # set svn branch defaults
407 # set svn branch defaults
387 branches = ["/branches/*", "/trunk"]
408 branches = ["/branches/*", "/trunk"]
388 tags = ["/tags/*"]
409 tags = ["/tags/*"]
389
410
390 for branch in branches:
411 for branch in branches:
391 settings_model.create_ui_section_value(
412 settings_model.create_ui_section_value(
392 RhodeCodeUi.SVN_BRANCH_ID, branch)
413 RhodeCodeUi.SVN_BRANCH_ID, branch)
393
414
394 for tag in tags:
415 for tag in tags:
395 settings_model.create_ui_section_value(RhodeCodeUi.SVN_TAG_ID, tag)
416 settings_model.create_ui_section_value(RhodeCodeUi.SVN_TAG_ID, tag)
396
417
397 def create_auth_plugin_options(self, skip_existing=False):
418 def create_auth_plugin_options(self, skip_existing=False):
398 """
419 """
399 Create default auth plugin settings, and make it active
420 Create default auth plugin settings, and make it active
400
421
401 :param skip_existing:
422 :param skip_existing:
402 """
423 """
403
424
404 for k, v, t in [('auth_plugins', 'egg:rhodecode-enterprise-ce#rhodecode', 'list'),
425 for k, v, t in [('auth_plugins', 'egg:rhodecode-enterprise-ce#rhodecode', 'list'),
405 ('auth_rhodecode_enabled', 'True', 'bool')]:
426 ('auth_rhodecode_enabled', 'True', 'bool')]:
406 if (skip_existing and
427 if (skip_existing and
407 SettingsModel().get_setting_by_name(k) is not None):
428 SettingsModel().get_setting_by_name(k) is not None):
408 log.debug('Skipping option %s', k)
429 log.debug('Skipping option %s', k)
409 continue
430 continue
410 setting = RhodeCodeSetting(k, v, t)
431 setting = RhodeCodeSetting(k, v, t)
411 self.sa.add(setting)
432 self.sa.add(setting)
412
433
413 def create_default_options(self, skip_existing=False):
434 def create_default_options(self, skip_existing=False):
414 """Creates default settings"""
435 """Creates default settings"""
415
436
416 for k, v, t in [
437 for k, v, t in [
417 ('default_repo_enable_locking', False, 'bool'),
438 ('default_repo_enable_locking', False, 'bool'),
418 ('default_repo_enable_downloads', False, 'bool'),
439 ('default_repo_enable_downloads', False, 'bool'),
419 ('default_repo_enable_statistics', False, 'bool'),
440 ('default_repo_enable_statistics', False, 'bool'),
420 ('default_repo_private', False, 'bool'),
441 ('default_repo_private', False, 'bool'),
421 ('default_repo_type', 'hg', 'unicode')]:
442 ('default_repo_type', 'hg', 'unicode')]:
422
443
423 if (skip_existing and
444 if (skip_existing and
424 SettingsModel().get_setting_by_name(k) is not None):
445 SettingsModel().get_setting_by_name(k) is not None):
425 log.debug('Skipping option %s', k)
446 log.debug('Skipping option %s', k)
426 continue
447 continue
427 setting = RhodeCodeSetting(k, v, t)
448 setting = RhodeCodeSetting(k, v, t)
428 self.sa.add(setting)
449 self.sa.add(setting)
429
450
430 def fixup_groups(self):
451 def fixup_groups(self):
431 def_usr = User.get_default_user()
452 def_usr = User.get_default_user()
432 for g in RepoGroup.query().all():
453 for g in RepoGroup.query().all():
433 g.group_name = g.get_new_name(g.name)
454 g.group_name = g.get_new_name(g.name)
434 self.sa.add(g)
455 self.sa.add(g)
435 # get default perm
456 # get default perm
436 default = UserRepoGroupToPerm.query()\
457 default = UserRepoGroupToPerm.query()\
437 .filter(UserRepoGroupToPerm.group == g)\
458 .filter(UserRepoGroupToPerm.group == g)\
438 .filter(UserRepoGroupToPerm.user == def_usr)\
459 .filter(UserRepoGroupToPerm.user == def_usr)\
439 .scalar()
460 .scalar()
440
461
441 if default is None:
462 if default is None:
442 log.debug('missing default permission for group %s adding', g)
463 log.debug('missing default permission for group %s adding', g)
443 perm_obj = RepoGroupModel()._create_default_perms(g)
464 perm_obj = RepoGroupModel()._create_default_perms(g)
444 self.sa.add(perm_obj)
465 self.sa.add(perm_obj)
445
466
446 def reset_permissions(self, username):
467 def reset_permissions(self, username):
447 """
468 """
448 Resets permissions to default state, useful when old systems had
469 Resets permissions to default state, useful when old systems had
449 bad permissions, we must clean them up
470 bad permissions, we must clean them up
450
471
451 :param username:
472 :param username:
452 """
473 """
453 default_user = User.get_by_username(username)
474 default_user = User.get_by_username(username)
454 if not default_user:
475 if not default_user:
455 return
476 return
456
477
457 u2p = UserToPerm.query()\
478 u2p = UserToPerm.query()\
458 .filter(UserToPerm.user == default_user).all()
479 .filter(UserToPerm.user == default_user).all()
459 fixed = False
480 fixed = False
460 if len(u2p) != len(Permission.DEFAULT_USER_PERMISSIONS):
481 if len(u2p) != len(Permission.DEFAULT_USER_PERMISSIONS):
461 for p in u2p:
482 for p in u2p:
462 Session().delete(p)
483 Session().delete(p)
463 fixed = True
484 fixed = True
464 self.populate_default_permissions()
485 self.populate_default_permissions()
465 return fixed
486 return fixed
466
487
467 def update_repo_info(self):
488 def update_repo_info(self):
468 RepoModel.update_repoinfo()
489 RepoModel.update_repoinfo()
469
490
470 def config_prompt(self, test_repo_path='', retries=3):
491 def config_prompt(self, test_repo_path='', retries=3):
471 defaults = self.cli_args
492 defaults = self.cli_args
472 _path = defaults.get('repos_location')
493 _path = defaults.get('repos_location')
473 if retries == 3:
494 if retries == 3:
474 log.info('Setting up repositories config')
495 log.info('Setting up repositories config')
475
496
476 if _path is not None:
497 if _path is not None:
477 path = _path
498 path = _path
478 elif not self.tests and not test_repo_path:
499 elif not self.tests and not test_repo_path:
479 path = raw_input(
500 path = raw_input(
480 'Enter a valid absolute path to store repositories. '
501 'Enter a valid absolute path to store repositories. '
481 'All repositories in that path will be added automatically:'
502 'All repositories in that path will be added automatically:'
482 )
503 )
483 else:
504 else:
484 path = test_repo_path
505 path = test_repo_path
485 path_ok = True
506 path_ok = True
486
507
487 # check proper dir
508 # check proper dir
488 if not os.path.isdir(path):
509 if not os.path.isdir(path):
489 path_ok = False
510 path_ok = False
490 log.error('Given path %s is not a valid directory', path)
511 log.error('Given path %s is not a valid directory', path)
491
512
492 elif not os.path.isabs(path):
513 elif not os.path.isabs(path):
493 path_ok = False
514 path_ok = False
494 log.error('Given path %s is not an absolute path', path)
515 log.error('Given path %s is not an absolute path', path)
495
516
496 # check if path is at least readable.
517 # check if path is at least readable.
497 if not os.access(path, os.R_OK):
518 if not os.access(path, os.R_OK):
498 path_ok = False
519 path_ok = False
499 log.error('Given path %s is not readable', path)
520 log.error('Given path %s is not readable', path)
500
521
501 # check write access, warn user about non writeable paths
522 # check write access, warn user about non writeable paths
502 elif not os.access(path, os.W_OK) and path_ok:
523 elif not os.access(path, os.W_OK) and path_ok:
503 log.warning('No write permission to given path %s', path)
524 log.warning('No write permission to given path %s', path)
504
525
505 q = ('Given path %s is not writeable, do you want to '
526 q = ('Given path %s is not writeable, do you want to '
506 'continue with read only mode ? [y/n]' % (path,))
527 'continue with read only mode ? [y/n]' % (path,))
507 if not self.ask_ok(q):
528 if not self.ask_ok(q):
508 log.error('Canceled by user')
529 log.error('Canceled by user')
509 sys.exit(-1)
530 sys.exit(-1)
510
531
511 if retries == 0:
532 if retries == 0:
512 sys.exit('max retries reached')
533 sys.exit('max retries reached')
513 if not path_ok:
534 if not path_ok:
514 retries -= 1
535 retries -= 1
515 return self.config_prompt(test_repo_path, retries)
536 return self.config_prompt(test_repo_path, retries)
516
537
517 real_path = os.path.normpath(os.path.realpath(path))
538 real_path = os.path.normpath(os.path.realpath(path))
518
539
519 if real_path != os.path.normpath(path):
540 if real_path != os.path.normpath(path):
520 q = ('Path looks like a symlink, RhodeCode Enterprise will store '
541 q = ('Path looks like a symlink, RhodeCode Enterprise will store '
521 'given path as %s ? [y/n]') % (real_path,)
542 'given path as %s ? [y/n]') % (real_path,)
522 if not self.ask_ok(q):
543 if not self.ask_ok(q):
523 log.error('Canceled by user')
544 log.error('Canceled by user')
524 sys.exit(-1)
545 sys.exit(-1)
525
546
526 return real_path
547 return real_path
527
548
528 def create_settings(self, path):
549 def create_settings(self, path):
529
550
530 self.create_ui_settings(path)
551 self.create_ui_settings(path)
531
552
532 ui_config = [
553 ui_config = [
533 ('web', 'push_ssl', 'False'),
554 ('web', 'push_ssl', 'False'),
534 ('web', 'allow_archive', 'gz zip bz2'),
555 ('web', 'allow_archive', 'gz zip bz2'),
535 ('web', 'allow_push', '*'),
556 ('web', 'allow_push', '*'),
536 ('web', 'baseurl', '/'),
557 ('web', 'baseurl', '/'),
537 ('paths', '/', path),
558 ('paths', '/', path),
538 ('phases', 'publish', 'True')
559 ('phases', 'publish', 'True')
539 ]
560 ]
540 for section, key, value in ui_config:
561 for section, key, value in ui_config:
541 ui_conf = RhodeCodeUi()
562 ui_conf = RhodeCodeUi()
542 setattr(ui_conf, 'ui_section', section)
563 setattr(ui_conf, 'ui_section', section)
543 setattr(ui_conf, 'ui_key', key)
564 setattr(ui_conf, 'ui_key', key)
544 setattr(ui_conf, 'ui_value', value)
565 setattr(ui_conf, 'ui_value', value)
545 self.sa.add(ui_conf)
566 self.sa.add(ui_conf)
546
567
547 # rhodecode app settings
568 # rhodecode app settings
548 settings = [
569 settings = [
549 ('realm', 'RhodeCode', 'unicode'),
570 ('realm', 'RhodeCode', 'unicode'),
550 ('title', '', 'unicode'),
571 ('title', '', 'unicode'),
551 ('pre_code', '', 'unicode'),
572 ('pre_code', '', 'unicode'),
552 ('post_code', '', 'unicode'),
573 ('post_code', '', 'unicode'),
553 ('show_public_icon', True, 'bool'),
574 ('show_public_icon', True, 'bool'),
554 ('show_private_icon', True, 'bool'),
575 ('show_private_icon', True, 'bool'),
555 ('stylify_metatags', False, 'bool'),
576 ('stylify_metatags', False, 'bool'),
556 ('dashboard_items', 100, 'int'),
577 ('dashboard_items', 100, 'int'),
557 ('admin_grid_items', 25, 'int'),
578 ('admin_grid_items', 25, 'int'),
558 ('show_version', True, 'bool'),
579 ('show_version', True, 'bool'),
559 ('use_gravatar', False, 'bool'),
580 ('use_gravatar', False, 'bool'),
560 ('gravatar_url', User.DEFAULT_GRAVATAR_URL, 'unicode'),
581 ('gravatar_url', User.DEFAULT_GRAVATAR_URL, 'unicode'),
561 ('clone_uri_tmpl', Repository.DEFAULT_CLONE_URI, 'unicode'),
582 ('clone_uri_tmpl', Repository.DEFAULT_CLONE_URI, 'unicode'),
562 ('support_url', '', 'unicode'),
583 ('support_url', '', 'unicode'),
563 ('update_url', RhodeCodeSetting.DEFAULT_UPDATE_URL, 'unicode'),
584 ('update_url', RhodeCodeSetting.DEFAULT_UPDATE_URL, 'unicode'),
564 ('show_revision_number', True, 'bool'),
585 ('show_revision_number', True, 'bool'),
565 ('show_sha_length', 12, 'int'),
586 ('show_sha_length', 12, 'int'),
566 ]
587 ]
567
588
568 for key, val, type_ in settings:
589 for key, val, type_ in settings:
569 sett = RhodeCodeSetting(key, val, type_)
590 sett = RhodeCodeSetting(key, val, type_)
570 self.sa.add(sett)
591 self.sa.add(sett)
571
592
572 self.create_auth_plugin_options()
593 self.create_auth_plugin_options()
573 self.create_default_options()
594 self.create_default_options()
574
595
575 log.info('created ui config')
596 log.info('created ui config')
576
597
577 def create_user(self, username, password, email='', admin=False,
598 def create_user(self, username, password, email='', admin=False,
578 strict_creation_check=True, api_key=None):
599 strict_creation_check=True, api_key=None):
579 log.info('creating user `%s`', username)
600 log.info('creating user `%s`', username)
580 user = UserModel().create_or_update(
601 user = UserModel().create_or_update(
581 username, password, email, firstname=u'RhodeCode', lastname=u'Admin',
602 username, password, email, firstname=u'RhodeCode', lastname=u'Admin',
582 active=True, admin=admin, extern_type="rhodecode",
603 active=True, admin=admin, extern_type="rhodecode",
583 strict_creation_check=strict_creation_check)
604 strict_creation_check=strict_creation_check)
584
605
585 if api_key:
606 if api_key:
586 log.info('setting a new default auth token for user `%s`', username)
607 log.info('setting a new default auth token for user `%s`', username)
587 UserModel().add_auth_token(
608 UserModel().add_auth_token(
588 user=user, lifetime_minutes=-1,
609 user=user, lifetime_minutes=-1,
589 role=UserModel.auth_token_role.ROLE_ALL,
610 role=UserModel.auth_token_role.ROLE_ALL,
590 description=u'BUILTIN TOKEN')
611 description=u'BUILTIN TOKEN')
591
612
592 def create_default_user(self):
613 def create_default_user(self):
593 log.info('creating default user')
614 log.info('creating default user')
594 # create default user for handling default permissions.
615 # create default user for handling default permissions.
595 user = UserModel().create_or_update(username=User.DEFAULT_USER,
616 user = UserModel().create_or_update(username=User.DEFAULT_USER,
596 password=str(uuid.uuid1())[:20],
617 password=str(uuid.uuid1())[:20],
597 email=User.DEFAULT_USER_EMAIL,
618 email=User.DEFAULT_USER_EMAIL,
598 firstname=u'Anonymous',
619 firstname=u'Anonymous',
599 lastname=u'User',
620 lastname=u'User',
600 strict_creation_check=False)
621 strict_creation_check=False)
601 # based on configuration options activate/de-activate this user which
622 # based on configuration options activate/de-activate this user which
602 # controlls anonymous access
623 # controlls anonymous access
603 if self.cli_args.get('public_access') is False:
624 if self.cli_args.get('public_access') is False:
604 log.info('Public access disabled')
625 log.info('Public access disabled')
605 user.active = False
626 user.active = False
606 Session().add(user)
627 Session().add(user)
607 Session().commit()
628 Session().commit()
608
629
609 def create_permissions(self):
630 def create_permissions(self):
610 """
631 """
611 Creates all permissions defined in the system
632 Creates all permissions defined in the system
612 """
633 """
613 # module.(access|create|change|delete)_[name]
634 # module.(access|create|change|delete)_[name]
614 # module.(none|read|write|admin)
635 # module.(none|read|write|admin)
615 log.info('creating permissions')
636 log.info('creating permissions')
616 PermissionModel(self.sa).create_permissions()
637 PermissionModel(self.sa).create_permissions()
617
638
618 def populate_default_permissions(self):
639 def populate_default_permissions(self):
619 """
640 """
620 Populate default permissions. It will create only the default
641 Populate default permissions. It will create only the default
621 permissions that are missing, and not alter already defined ones
642 permissions that are missing, and not alter already defined ones
622 """
643 """
623 log.info('creating default user permissions')
644 log.info('creating default user permissions')
624 PermissionModel(self.sa).create_default_user_permissions(user=User.DEFAULT_USER)
645 PermissionModel(self.sa).create_default_user_permissions(user=User.DEFAULT_USER)
@@ -1,781 +1,782 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2019 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 Utilities library for RhodeCode
22 Utilities library for RhodeCode
23 """
23 """
24
24
25 import datetime
25 import datetime
26 import decorator
26 import decorator
27 import json
27 import json
28 import logging
28 import logging
29 import os
29 import os
30 import re
30 import re
31 import sys
31 import sys
32 import shutil
32 import shutil
33 import tempfile
33 import tempfile
34 import traceback
34 import traceback
35 import tarfile
35 import tarfile
36 import warnings
36 import warnings
37 import hashlib
37 import hashlib
38 from os.path import join as jn
38 from os.path import join as jn
39
39
40 import paste
40 import paste
41 import pkg_resources
41 import pkg_resources
42 from webhelpers.text import collapse, remove_formatting, strip_tags
42 from webhelpers.text import collapse, remove_formatting, strip_tags
43 from mako import exceptions
43 from mako import exceptions
44 from pyramid.threadlocal import get_current_registry
44 from pyramid.threadlocal import get_current_registry
45 from rhodecode.lib.request import Request
45 from rhodecode.lib.request import Request
46
46
47 from rhodecode.lib.vcs.backends.base import Config
47 from rhodecode.lib.vcs.backends.base import Config
48 from rhodecode.lib.vcs.exceptions import VCSError
48 from rhodecode.lib.vcs.exceptions import VCSError
49 from rhodecode.lib.vcs.utils.helpers import get_scm, get_scm_backend
49 from rhodecode.lib.vcs.utils.helpers import get_scm, get_scm_backend
50 from rhodecode.lib.utils2 import (
50 from rhodecode.lib.utils2 import (
51 safe_str, safe_unicode, get_current_rhodecode_user, md5, sha1)
51 safe_str, safe_unicode, get_current_rhodecode_user, md5, sha1)
52 from rhodecode.model import meta
52 from rhodecode.model import meta
53 from rhodecode.model.db import (
53 from rhodecode.model.db import (
54 Repository, User, RhodeCodeUi, UserLog, RepoGroup, UserGroup)
54 Repository, User, RhodeCodeUi, UserLog, RepoGroup, UserGroup)
55 from rhodecode.model.meta import Session
55 from rhodecode.model.meta import Session
56
56
57
57
58 log = logging.getLogger(__name__)
58 log = logging.getLogger(__name__)
59
59
60 REMOVED_REPO_PAT = re.compile(r'rm__\d{8}_\d{6}_\d{6}__.*')
60 REMOVED_REPO_PAT = re.compile(r'rm__\d{8}_\d{6}_\d{6}__.*')
61
61
62 # String which contains characters that are not allowed in slug names for
62 # String which contains characters that are not allowed in slug names for
63 # repositories or repository groups. It is properly escaped to use it in
63 # repositories or repository groups. It is properly escaped to use it in
64 # regular expressions.
64 # regular expressions.
65 SLUG_BAD_CHARS = re.escape('`?=[]\;\'"<>,/~!@#$%^&*()+{}|:')
65 SLUG_BAD_CHARS = re.escape('`?=[]\;\'"<>,/~!@#$%^&*()+{}|:')
66
66
67 # Regex that matches forbidden characters in repo/group slugs.
67 # Regex that matches forbidden characters in repo/group slugs.
68 SLUG_BAD_CHAR_RE = re.compile('[{}]'.format(SLUG_BAD_CHARS))
68 SLUG_BAD_CHAR_RE = re.compile('[{}]'.format(SLUG_BAD_CHARS))
69
69
70 # Regex that matches allowed characters in repo/group slugs.
70 # Regex that matches allowed characters in repo/group slugs.
71 SLUG_GOOD_CHAR_RE = re.compile('[^{}]'.format(SLUG_BAD_CHARS))
71 SLUG_GOOD_CHAR_RE = re.compile('[^{}]'.format(SLUG_BAD_CHARS))
72
72
73 # Regex that matches whole repo/group slugs.
73 # Regex that matches whole repo/group slugs.
74 SLUG_RE = re.compile('[^{}]+'.format(SLUG_BAD_CHARS))
74 SLUG_RE = re.compile('[^{}]+'.format(SLUG_BAD_CHARS))
75
75
76 _license_cache = None
76 _license_cache = None
77
77
78
78
79 def repo_name_slug(value):
79 def repo_name_slug(value):
80 """
80 """
81 Return slug of name of repository
81 Return slug of name of repository
82 This function is called on each creation/modification
82 This function is called on each creation/modification
83 of repository to prevent bad names in repo
83 of repository to prevent bad names in repo
84 """
84 """
85 replacement_char = '-'
85 replacement_char = '-'
86
86
87 slug = remove_formatting(value)
87 slug = remove_formatting(value)
88 slug = SLUG_BAD_CHAR_RE.sub('', slug)
88 slug = SLUG_BAD_CHAR_RE.sub('', slug)
89 slug = re.sub('[\s]+', '-', slug)
89 slug = re.sub('[\s]+', '-', slug)
90 slug = collapse(slug, replacement_char)
90 slug = collapse(slug, replacement_char)
91 return slug
91 return slug
92
92
93
93
94 #==============================================================================
94 #==============================================================================
95 # PERM DECORATOR HELPERS FOR EXTRACTING NAMES FOR PERM CHECKS
95 # PERM DECORATOR HELPERS FOR EXTRACTING NAMES FOR PERM CHECKS
96 #==============================================================================
96 #==============================================================================
97 def get_repo_slug(request):
97 def get_repo_slug(request):
98 _repo = ''
98 _repo = ''
99
99
100 if hasattr(request, 'db_repo'):
100 if hasattr(request, 'db_repo'):
101 # if our requests has set db reference use it for name, this
101 # if our requests has set db reference use it for name, this
102 # translates the example.com/_<id> into proper repo names
102 # translates the example.com/_<id> into proper repo names
103 _repo = request.db_repo.repo_name
103 _repo = request.db_repo.repo_name
104 elif getattr(request, 'matchdict', None):
104 elif getattr(request, 'matchdict', None):
105 # pyramid
105 # pyramid
106 _repo = request.matchdict.get('repo_name')
106 _repo = request.matchdict.get('repo_name')
107
107
108 if _repo:
108 if _repo:
109 _repo = _repo.rstrip('/')
109 _repo = _repo.rstrip('/')
110 return _repo
110 return _repo
111
111
112
112
113 def get_repo_group_slug(request):
113 def get_repo_group_slug(request):
114 _group = ''
114 _group = ''
115 if hasattr(request, 'db_repo_group'):
115 if hasattr(request, 'db_repo_group'):
116 # if our requests has set db reference use it for name, this
116 # if our requests has set db reference use it for name, this
117 # translates the example.com/_<id> into proper repo group names
117 # translates the example.com/_<id> into proper repo group names
118 _group = request.db_repo_group.group_name
118 _group = request.db_repo_group.group_name
119 elif getattr(request, 'matchdict', None):
119 elif getattr(request, 'matchdict', None):
120 # pyramid
120 # pyramid
121 _group = request.matchdict.get('repo_group_name')
121 _group = request.matchdict.get('repo_group_name')
122
122
123 if _group:
123 if _group:
124 _group = _group.rstrip('/')
124 _group = _group.rstrip('/')
125 return _group
125 return _group
126
126
127
127
128 def get_user_group_slug(request):
128 def get_user_group_slug(request):
129 _user_group = ''
129 _user_group = ''
130
130
131 if hasattr(request, 'db_user_group'):
131 if hasattr(request, 'db_user_group'):
132 _user_group = request.db_user_group.users_group_name
132 _user_group = request.db_user_group.users_group_name
133 elif getattr(request, 'matchdict', None):
133 elif getattr(request, 'matchdict', None):
134 # pyramid
134 # pyramid
135 _user_group = request.matchdict.get('user_group_id')
135 _user_group = request.matchdict.get('user_group_id')
136 _user_group_name = request.matchdict.get('user_group_name')
136 _user_group_name = request.matchdict.get('user_group_name')
137 try:
137 try:
138 if _user_group:
138 if _user_group:
139 _user_group = UserGroup.get(_user_group)
139 _user_group = UserGroup.get(_user_group)
140 elif _user_group_name:
140 elif _user_group_name:
141 _user_group = UserGroup.get_by_group_name(_user_group_name)
141 _user_group = UserGroup.get_by_group_name(_user_group_name)
142
142
143 if _user_group:
143 if _user_group:
144 _user_group = _user_group.users_group_name
144 _user_group = _user_group.users_group_name
145 except Exception:
145 except Exception:
146 log.exception('Failed to get user group by id and name')
146 log.exception('Failed to get user group by id and name')
147 # catch all failures here
147 # catch all failures here
148 return None
148 return None
149
149
150 return _user_group
150 return _user_group
151
151
152
152
153 def get_filesystem_repos(path, recursive=False, skip_removed_repos=True):
153 def get_filesystem_repos(path, recursive=False, skip_removed_repos=True):
154 """
154 """
155 Scans given path for repos and return (name,(type,path)) tuple
155 Scans given path for repos and return (name,(type,path)) tuple
156
156
157 :param path: path to scan for repositories
157 :param path: path to scan for repositories
158 :param recursive: recursive search and return names with subdirs in front
158 :param recursive: recursive search and return names with subdirs in front
159 """
159 """
160
160
161 # remove ending slash for better results
161 # remove ending slash for better results
162 path = path.rstrip(os.sep)
162 path = path.rstrip(os.sep)
163 log.debug('now scanning in %s location recursive:%s...', path, recursive)
163 log.debug('now scanning in %s location recursive:%s...', path, recursive)
164
164
165 def _get_repos(p):
165 def _get_repos(p):
166 dirpaths = _get_dirpaths(p)
166 dirpaths = _get_dirpaths(p)
167 if not _is_dir_writable(p):
167 if not _is_dir_writable(p):
168 log.warning('repo path without write access: %s', p)
168 log.warning('repo path without write access: %s', p)
169
169
170 for dirpath in dirpaths:
170 for dirpath in dirpaths:
171 if os.path.isfile(os.path.join(p, dirpath)):
171 if os.path.isfile(os.path.join(p, dirpath)):
172 continue
172 continue
173 cur_path = os.path.join(p, dirpath)
173 cur_path = os.path.join(p, dirpath)
174
174
175 # skip removed repos
175 # skip removed repos
176 if skip_removed_repos and REMOVED_REPO_PAT.match(dirpath):
176 if skip_removed_repos and REMOVED_REPO_PAT.match(dirpath):
177 continue
177 continue
178
178
179 #skip .<somethin> dirs
179 #skip .<somethin> dirs
180 if dirpath.startswith('.'):
180 if dirpath.startswith('.'):
181 continue
181 continue
182
182
183 try:
183 try:
184 scm_info = get_scm(cur_path)
184 scm_info = get_scm(cur_path)
185 yield scm_info[1].split(path, 1)[-1].lstrip(os.sep), scm_info
185 yield scm_info[1].split(path, 1)[-1].lstrip(os.sep), scm_info
186 except VCSError:
186 except VCSError:
187 if not recursive:
187 if not recursive:
188 continue
188 continue
189 #check if this dir containts other repos for recursive scan
189 #check if this dir containts other repos for recursive scan
190 rec_path = os.path.join(p, dirpath)
190 rec_path = os.path.join(p, dirpath)
191 if os.path.isdir(rec_path):
191 if os.path.isdir(rec_path):
192 for inner_scm in _get_repos(rec_path):
192 for inner_scm in _get_repos(rec_path):
193 yield inner_scm
193 yield inner_scm
194
194
195 return _get_repos(path)
195 return _get_repos(path)
196
196
197
197
198 def _get_dirpaths(p):
198 def _get_dirpaths(p):
199 try:
199 try:
200 # OS-independable way of checking if we have at least read-only
200 # OS-independable way of checking if we have at least read-only
201 # access or not.
201 # access or not.
202 dirpaths = os.listdir(p)
202 dirpaths = os.listdir(p)
203 except OSError:
203 except OSError:
204 log.warning('ignoring repo path without read access: %s', p)
204 log.warning('ignoring repo path without read access: %s', p)
205 return []
205 return []
206
206
207 # os.listpath has a tweak: If a unicode is passed into it, then it tries to
207 # os.listpath has a tweak: If a unicode is passed into it, then it tries to
208 # decode paths and suddenly returns unicode objects itself. The items it
208 # decode paths and suddenly returns unicode objects itself. The items it
209 # cannot decode are returned as strings and cause issues.
209 # cannot decode are returned as strings and cause issues.
210 #
210 #
211 # Those paths are ignored here until a solid solution for path handling has
211 # Those paths are ignored here until a solid solution for path handling has
212 # been built.
212 # been built.
213 expected_type = type(p)
213 expected_type = type(p)
214
214
215 def _has_correct_type(item):
215 def _has_correct_type(item):
216 if type(item) is not expected_type:
216 if type(item) is not expected_type:
217 log.error(
217 log.error(
218 u"Ignoring path %s since it cannot be decoded into unicode.",
218 u"Ignoring path %s since it cannot be decoded into unicode.",
219 # Using "repr" to make sure that we see the byte value in case
219 # Using "repr" to make sure that we see the byte value in case
220 # of support.
220 # of support.
221 repr(item))
221 repr(item))
222 return False
222 return False
223 return True
223 return True
224
224
225 dirpaths = [item for item in dirpaths if _has_correct_type(item)]
225 dirpaths = [item for item in dirpaths if _has_correct_type(item)]
226
226
227 return dirpaths
227 return dirpaths
228
228
229
229
230 def _is_dir_writable(path):
230 def _is_dir_writable(path):
231 """
231 """
232 Probe if `path` is writable.
232 Probe if `path` is writable.
233
233
234 Due to trouble on Cygwin / Windows, this is actually probing if it is
234 Due to trouble on Cygwin / Windows, this is actually probing if it is
235 possible to create a file inside of `path`, stat does not produce reliable
235 possible to create a file inside of `path`, stat does not produce reliable
236 results in this case.
236 results in this case.
237 """
237 """
238 try:
238 try:
239 with tempfile.TemporaryFile(dir=path):
239 with tempfile.TemporaryFile(dir=path):
240 pass
240 pass
241 except OSError:
241 except OSError:
242 return False
242 return False
243 return True
243 return True
244
244
245
245
246 def is_valid_repo(repo_name, base_path, expect_scm=None, explicit_scm=None, config=None):
246 def is_valid_repo(repo_name, base_path, expect_scm=None, explicit_scm=None, config=None):
247 """
247 """
248 Returns True if given path is a valid repository False otherwise.
248 Returns True if given path is a valid repository False otherwise.
249 If expect_scm param is given also, compare if given scm is the same
249 If expect_scm param is given also, compare if given scm is the same
250 as expected from scm parameter. If explicit_scm is given don't try to
250 as expected from scm parameter. If explicit_scm is given don't try to
251 detect the scm, just use the given one to check if repo is valid
251 detect the scm, just use the given one to check if repo is valid
252
252
253 :param repo_name:
253 :param repo_name:
254 :param base_path:
254 :param base_path:
255 :param expect_scm:
255 :param expect_scm:
256 :param explicit_scm:
256 :param explicit_scm:
257 :param config:
257 :param config:
258
258
259 :return True: if given path is a valid repository
259 :return True: if given path is a valid repository
260 """
260 """
261 full_path = os.path.join(safe_str(base_path), safe_str(repo_name))
261 full_path = os.path.join(safe_str(base_path), safe_str(repo_name))
262 log.debug('Checking if `%s` is a valid path for repository. '
262 log.debug('Checking if `%s` is a valid path for repository. '
263 'Explicit type: %s', repo_name, explicit_scm)
263 'Explicit type: %s', repo_name, explicit_scm)
264
264
265 try:
265 try:
266 if explicit_scm:
266 if explicit_scm:
267 detected_scms = [get_scm_backend(explicit_scm)(
267 detected_scms = [get_scm_backend(explicit_scm)(
268 full_path, config=config).alias]
268 full_path, config=config).alias]
269 else:
269 else:
270 detected_scms = get_scm(full_path)
270 detected_scms = get_scm(full_path)
271
271
272 if expect_scm:
272 if expect_scm:
273 return detected_scms[0] == expect_scm
273 return detected_scms[0] == expect_scm
274 log.debug('path: %s is an vcs object:%s', full_path, detected_scms)
274 log.debug('path: %s is an vcs object:%s', full_path, detected_scms)
275 return True
275 return True
276 except VCSError:
276 except VCSError:
277 log.debug('path: %s is not a valid repo !', full_path)
277 log.debug('path: %s is not a valid repo !', full_path)
278 return False
278 return False
279
279
280
280
281 def is_valid_repo_group(repo_group_name, base_path, skip_path_check=False):
281 def is_valid_repo_group(repo_group_name, base_path, skip_path_check=False):
282 """
282 """
283 Returns True if given path is a repository group, False otherwise
283 Returns True if given path is a repository group, False otherwise
284
284
285 :param repo_name:
285 :param repo_name:
286 :param base_path:
286 :param base_path:
287 """
287 """
288 full_path = os.path.join(safe_str(base_path), safe_str(repo_group_name))
288 full_path = os.path.join(safe_str(base_path), safe_str(repo_group_name))
289 log.debug('Checking if `%s` is a valid path for repository group',
289 log.debug('Checking if `%s` is a valid path for repository group',
290 repo_group_name)
290 repo_group_name)
291
291
292 # check if it's not a repo
292 # check if it's not a repo
293 if is_valid_repo(repo_group_name, base_path):
293 if is_valid_repo(repo_group_name, base_path):
294 log.debug('Repo called %s exist, it is not a valid repo group', repo_group_name)
294 log.debug('Repo called %s exist, it is not a valid repo group', repo_group_name)
295 return False
295 return False
296
296
297 try:
297 try:
298 # we need to check bare git repos at higher level
298 # we need to check bare git repos at higher level
299 # since we might match branches/hooks/info/objects or possible
299 # since we might match branches/hooks/info/objects or possible
300 # other things inside bare git repo
300 # other things inside bare git repo
301 maybe_repo = os.path.dirname(full_path)
301 maybe_repo = os.path.dirname(full_path)
302 if maybe_repo == base_path:
302 if maybe_repo == base_path:
303 # skip root level repo check, we know root location CANNOT BE a repo group
303 # skip root level repo check, we know root location CANNOT BE a repo group
304 return False
304 return False
305
305
306 scm_ = get_scm(maybe_repo)
306 scm_ = get_scm(maybe_repo)
307 log.debug('path: %s is a vcs object:%s, not valid repo group', full_path, scm_)
307 log.debug('path: %s is a vcs object:%s, not valid repo group', full_path, scm_)
308 return False
308 return False
309 except VCSError:
309 except VCSError:
310 pass
310 pass
311
311
312 # check if it's a valid path
312 # check if it's a valid path
313 if skip_path_check or os.path.isdir(full_path):
313 if skip_path_check or os.path.isdir(full_path):
314 log.debug('path: %s is a valid repo group !', full_path)
314 log.debug('path: %s is a valid repo group !', full_path)
315 return True
315 return True
316
316
317 log.debug('path: %s is not a valid repo group !', full_path)
317 log.debug('path: %s is not a valid repo group !', full_path)
318 return False
318 return False
319
319
320
320
321 def ask_ok(prompt, retries=4, complaint='[y]es or [n]o please!'):
321 def ask_ok(prompt, retries=4, complaint='[y]es or [n]o please!'):
322 while True:
322 while True:
323 ok = raw_input(prompt)
323 ok = raw_input(prompt)
324 if ok.lower() in ('y', 'ye', 'yes'):
324 if ok.lower() in ('y', 'ye', 'yes'):
325 return True
325 return True
326 if ok.lower() in ('n', 'no', 'nop', 'nope'):
326 if ok.lower() in ('n', 'no', 'nop', 'nope'):
327 return False
327 return False
328 retries = retries - 1
328 retries = retries - 1
329 if retries < 0:
329 if retries < 0:
330 raise IOError
330 raise IOError
331 print(complaint)
331 print(complaint)
332
332
333 # propagated from mercurial documentation
333 # propagated from mercurial documentation
334 ui_sections = [
334 ui_sections = [
335 'alias', 'auth',
335 'alias', 'auth',
336 'decode/encode', 'defaults',
336 'decode/encode', 'defaults',
337 'diff', 'email',
337 'diff', 'email',
338 'extensions', 'format',
338 'extensions', 'format',
339 'merge-patterns', 'merge-tools',
339 'merge-patterns', 'merge-tools',
340 'hooks', 'http_proxy',
340 'hooks', 'http_proxy',
341 'smtp', 'patch',
341 'smtp', 'patch',
342 'paths', 'profiling',
342 'paths', 'profiling',
343 'server', 'trusted',
343 'server', 'trusted',
344 'ui', 'web', ]
344 'ui', 'web', ]
345
345
346
346
347 def config_data_from_db(clear_session=True, repo=None):
347 def config_data_from_db(clear_session=True, repo=None):
348 """
348 """
349 Read the configuration data from the database and return configuration
349 Read the configuration data from the database and return configuration
350 tuples.
350 tuples.
351 """
351 """
352 from rhodecode.model.settings import VcsSettingsModel
352 from rhodecode.model.settings import VcsSettingsModel
353
353
354 config = []
354 config = []
355
355
356 sa = meta.Session()
356 sa = meta.Session()
357 settings_model = VcsSettingsModel(repo=repo, sa=sa)
357 settings_model = VcsSettingsModel(repo=repo, sa=sa)
358
358
359 ui_settings = settings_model.get_ui_settings()
359 ui_settings = settings_model.get_ui_settings()
360
360
361 ui_data = []
361 ui_data = []
362 for setting in ui_settings:
362 for setting in ui_settings:
363 if setting.active:
363 if setting.active:
364 ui_data.append((setting.section, setting.key, setting.value))
364 ui_data.append((setting.section, setting.key, setting.value))
365 config.append((
365 config.append((
366 safe_str(setting.section), safe_str(setting.key),
366 safe_str(setting.section), safe_str(setting.key),
367 safe_str(setting.value)))
367 safe_str(setting.value)))
368 if setting.key == 'push_ssl':
368 if setting.key == 'push_ssl':
369 # force set push_ssl requirement to False, rhodecode
369 # force set push_ssl requirement to False, rhodecode
370 # handles that
370 # handles that
371 config.append((
371 config.append((
372 safe_str(setting.section), safe_str(setting.key), False))
372 safe_str(setting.section), safe_str(setting.key), False))
373 log.debug(
373 log.debug(
374 'settings ui from db: %s',
374 'settings ui from db@repo[%s]: %s',
375 repo,
375 ','.join(map(lambda s: '[{}] {}={}'.format(*s), ui_data)))
376 ','.join(map(lambda s: '[{}] {}={}'.format(*s), ui_data)))
376 if clear_session:
377 if clear_session:
377 meta.Session.remove()
378 meta.Session.remove()
378
379
379 # TODO: mikhail: probably it makes no sense to re-read hooks information.
380 # TODO: mikhail: probably it makes no sense to re-read hooks information.
380 # It's already there and activated/deactivated
381 # It's already there and activated/deactivated
381 skip_entries = []
382 skip_entries = []
382 enabled_hook_classes = get_enabled_hook_classes(ui_settings)
383 enabled_hook_classes = get_enabled_hook_classes(ui_settings)
383 if 'pull' not in enabled_hook_classes:
384 if 'pull' not in enabled_hook_classes:
384 skip_entries.append(('hooks', RhodeCodeUi.HOOK_PRE_PULL))
385 skip_entries.append(('hooks', RhodeCodeUi.HOOK_PRE_PULL))
385 if 'push' not in enabled_hook_classes:
386 if 'push' not in enabled_hook_classes:
386 skip_entries.append(('hooks', RhodeCodeUi.HOOK_PRE_PUSH))
387 skip_entries.append(('hooks', RhodeCodeUi.HOOK_PRE_PUSH))
387 skip_entries.append(('hooks', RhodeCodeUi.HOOK_PRETX_PUSH))
388 skip_entries.append(('hooks', RhodeCodeUi.HOOK_PRETX_PUSH))
388 skip_entries.append(('hooks', RhodeCodeUi.HOOK_PUSH_KEY))
389 skip_entries.append(('hooks', RhodeCodeUi.HOOK_PUSH_KEY))
389
390
390 config = [entry for entry in config if entry[:2] not in skip_entries]
391 config = [entry for entry in config if entry[:2] not in skip_entries]
391
392
392 return config
393 return config
393
394
394
395
395 def make_db_config(clear_session=True, repo=None):
396 def make_db_config(clear_session=True, repo=None):
396 """
397 """
397 Create a :class:`Config` instance based on the values in the database.
398 Create a :class:`Config` instance based on the values in the database.
398 """
399 """
399 config = Config()
400 config = Config()
400 config_data = config_data_from_db(clear_session=clear_session, repo=repo)
401 config_data = config_data_from_db(clear_session=clear_session, repo=repo)
401 for section, option, value in config_data:
402 for section, option, value in config_data:
402 config.set(section, option, value)
403 config.set(section, option, value)
403 return config
404 return config
404
405
405
406
406 def get_enabled_hook_classes(ui_settings):
407 def get_enabled_hook_classes(ui_settings):
407 """
408 """
408 Return the enabled hook classes.
409 Return the enabled hook classes.
409
410
410 :param ui_settings: List of ui_settings as returned
411 :param ui_settings: List of ui_settings as returned
411 by :meth:`VcsSettingsModel.get_ui_settings`
412 by :meth:`VcsSettingsModel.get_ui_settings`
412
413
413 :return: a list with the enabled hook classes. The order is not guaranteed.
414 :return: a list with the enabled hook classes. The order is not guaranteed.
414 :rtype: list
415 :rtype: list
415 """
416 """
416 enabled_hooks = []
417 enabled_hooks = []
417 active_hook_keys = [
418 active_hook_keys = [
418 key for section, key, value, active in ui_settings
419 key for section, key, value, active in ui_settings
419 if section == 'hooks' and active]
420 if section == 'hooks' and active]
420
421
421 hook_names = {
422 hook_names = {
422 RhodeCodeUi.HOOK_PUSH: 'push',
423 RhodeCodeUi.HOOK_PUSH: 'push',
423 RhodeCodeUi.HOOK_PULL: 'pull',
424 RhodeCodeUi.HOOK_PULL: 'pull',
424 RhodeCodeUi.HOOK_REPO_SIZE: 'repo_size'
425 RhodeCodeUi.HOOK_REPO_SIZE: 'repo_size'
425 }
426 }
426
427
427 for key in active_hook_keys:
428 for key in active_hook_keys:
428 hook = hook_names.get(key)
429 hook = hook_names.get(key)
429 if hook:
430 if hook:
430 enabled_hooks.append(hook)
431 enabled_hooks.append(hook)
431
432
432 return enabled_hooks
433 return enabled_hooks
433
434
434
435
435 def set_rhodecode_config(config):
436 def set_rhodecode_config(config):
436 """
437 """
437 Updates pyramid config with new settings from database
438 Updates pyramid config with new settings from database
438
439
439 :param config:
440 :param config:
440 """
441 """
441 from rhodecode.model.settings import SettingsModel
442 from rhodecode.model.settings import SettingsModel
442 app_settings = SettingsModel().get_all_settings()
443 app_settings = SettingsModel().get_all_settings()
443
444
444 for k, v in app_settings.items():
445 for k, v in app_settings.items():
445 config[k] = v
446 config[k] = v
446
447
447
448
448 def get_rhodecode_realm():
449 def get_rhodecode_realm():
449 """
450 """
450 Return the rhodecode realm from database.
451 Return the rhodecode realm from database.
451 """
452 """
452 from rhodecode.model.settings import SettingsModel
453 from rhodecode.model.settings import SettingsModel
453 realm = SettingsModel().get_setting_by_name('realm')
454 realm = SettingsModel().get_setting_by_name('realm')
454 return safe_str(realm.app_settings_value)
455 return safe_str(realm.app_settings_value)
455
456
456
457
457 def get_rhodecode_base_path():
458 def get_rhodecode_base_path():
458 """
459 """
459 Returns the base path. The base path is the filesystem path which points
460 Returns the base path. The base path is the filesystem path which points
460 to the repository store.
461 to the repository store.
461 """
462 """
462 from rhodecode.model.settings import SettingsModel
463 from rhodecode.model.settings import SettingsModel
463 paths_ui = SettingsModel().get_ui_by_section_and_key('paths', '/')
464 paths_ui = SettingsModel().get_ui_by_section_and_key('paths', '/')
464 return safe_str(paths_ui.ui_value)
465 return safe_str(paths_ui.ui_value)
465
466
466
467
467 def map_groups(path):
468 def map_groups(path):
468 """
469 """
469 Given a full path to a repository, create all nested groups that this
470 Given a full path to a repository, create all nested groups that this
470 repo is inside. This function creates parent-child relationships between
471 repo is inside. This function creates parent-child relationships between
471 groups and creates default perms for all new groups.
472 groups and creates default perms for all new groups.
472
473
473 :param paths: full path to repository
474 :param paths: full path to repository
474 """
475 """
475 from rhodecode.model.repo_group import RepoGroupModel
476 from rhodecode.model.repo_group import RepoGroupModel
476 sa = meta.Session()
477 sa = meta.Session()
477 groups = path.split(Repository.NAME_SEP)
478 groups = path.split(Repository.NAME_SEP)
478 parent = None
479 parent = None
479 group = None
480 group = None
480
481
481 # last element is repo in nested groups structure
482 # last element is repo in nested groups structure
482 groups = groups[:-1]
483 groups = groups[:-1]
483 rgm = RepoGroupModel(sa)
484 rgm = RepoGroupModel(sa)
484 owner = User.get_first_super_admin()
485 owner = User.get_first_super_admin()
485 for lvl, group_name in enumerate(groups):
486 for lvl, group_name in enumerate(groups):
486 group_name = '/'.join(groups[:lvl] + [group_name])
487 group_name = '/'.join(groups[:lvl] + [group_name])
487 group = RepoGroup.get_by_group_name(group_name)
488 group = RepoGroup.get_by_group_name(group_name)
488 desc = '%s group' % group_name
489 desc = '%s group' % group_name
489
490
490 # skip folders that are now removed repos
491 # skip folders that are now removed repos
491 if REMOVED_REPO_PAT.match(group_name):
492 if REMOVED_REPO_PAT.match(group_name):
492 break
493 break
493
494
494 if group is None:
495 if group is None:
495 log.debug('creating group level: %s group_name: %s',
496 log.debug('creating group level: %s group_name: %s',
496 lvl, group_name)
497 lvl, group_name)
497 group = RepoGroup(group_name, parent)
498 group = RepoGroup(group_name, parent)
498 group.group_description = desc
499 group.group_description = desc
499 group.user = owner
500 group.user = owner
500 sa.add(group)
501 sa.add(group)
501 perm_obj = rgm._create_default_perms(group)
502 perm_obj = rgm._create_default_perms(group)
502 sa.add(perm_obj)
503 sa.add(perm_obj)
503 sa.flush()
504 sa.flush()
504
505
505 parent = group
506 parent = group
506 return group
507 return group
507
508
508
509
509 def repo2db_mapper(initial_repo_list, remove_obsolete=False):
510 def repo2db_mapper(initial_repo_list, remove_obsolete=False):
510 """
511 """
511 maps all repos given in initial_repo_list, non existing repositories
512 maps all repos given in initial_repo_list, non existing repositories
512 are created, if remove_obsolete is True it also checks for db entries
513 are created, if remove_obsolete is True it also checks for db entries
513 that are not in initial_repo_list and removes them.
514 that are not in initial_repo_list and removes them.
514
515
515 :param initial_repo_list: list of repositories found by scanning methods
516 :param initial_repo_list: list of repositories found by scanning methods
516 :param remove_obsolete: check for obsolete entries in database
517 :param remove_obsolete: check for obsolete entries in database
517 """
518 """
518 from rhodecode.model.repo import RepoModel
519 from rhodecode.model.repo import RepoModel
519 from rhodecode.model.repo_group import RepoGroupModel
520 from rhodecode.model.repo_group import RepoGroupModel
520 from rhodecode.model.settings import SettingsModel
521 from rhodecode.model.settings import SettingsModel
521
522
522 sa = meta.Session()
523 sa = meta.Session()
523 repo_model = RepoModel()
524 repo_model = RepoModel()
524 user = User.get_first_super_admin()
525 user = User.get_first_super_admin()
525 added = []
526 added = []
526
527
527 # creation defaults
528 # creation defaults
528 defs = SettingsModel().get_default_repo_settings(strip_prefix=True)
529 defs = SettingsModel().get_default_repo_settings(strip_prefix=True)
529 enable_statistics = defs.get('repo_enable_statistics')
530 enable_statistics = defs.get('repo_enable_statistics')
530 enable_locking = defs.get('repo_enable_locking')
531 enable_locking = defs.get('repo_enable_locking')
531 enable_downloads = defs.get('repo_enable_downloads')
532 enable_downloads = defs.get('repo_enable_downloads')
532 private = defs.get('repo_private')
533 private = defs.get('repo_private')
533
534
534 for name, repo in initial_repo_list.items():
535 for name, repo in initial_repo_list.items():
535 group = map_groups(name)
536 group = map_groups(name)
536 unicode_name = safe_unicode(name)
537 unicode_name = safe_unicode(name)
537 db_repo = repo_model.get_by_repo_name(unicode_name)
538 db_repo = repo_model.get_by_repo_name(unicode_name)
538 # found repo that is on filesystem not in RhodeCode database
539 # found repo that is on filesystem not in RhodeCode database
539 if not db_repo:
540 if not db_repo:
540 log.info('repository %s not found, creating now', name)
541 log.info('repository %s not found, creating now', name)
541 added.append(name)
542 added.append(name)
542 desc = (repo.description
543 desc = (repo.description
543 if repo.description != 'unknown'
544 if repo.description != 'unknown'
544 else '%s repository' % name)
545 else '%s repository' % name)
545
546
546 db_repo = repo_model._create_repo(
547 db_repo = repo_model._create_repo(
547 repo_name=name,
548 repo_name=name,
548 repo_type=repo.alias,
549 repo_type=repo.alias,
549 description=desc,
550 description=desc,
550 repo_group=getattr(group, 'group_id', None),
551 repo_group=getattr(group, 'group_id', None),
551 owner=user,
552 owner=user,
552 enable_locking=enable_locking,
553 enable_locking=enable_locking,
553 enable_downloads=enable_downloads,
554 enable_downloads=enable_downloads,
554 enable_statistics=enable_statistics,
555 enable_statistics=enable_statistics,
555 private=private,
556 private=private,
556 state=Repository.STATE_CREATED
557 state=Repository.STATE_CREATED
557 )
558 )
558 sa.commit()
559 sa.commit()
559 # we added that repo just now, and make sure we updated server info
560 # we added that repo just now, and make sure we updated server info
560 if db_repo.repo_type == 'git':
561 if db_repo.repo_type == 'git':
561 git_repo = db_repo.scm_instance()
562 git_repo = db_repo.scm_instance()
562 # update repository server-info
563 # update repository server-info
563 log.debug('Running update server info')
564 log.debug('Running update server info')
564 git_repo._update_server_info()
565 git_repo._update_server_info()
565
566
566 db_repo.update_commit_cache()
567 db_repo.update_commit_cache()
567
568
568 config = db_repo._config
569 config = db_repo._config
569 config.set('extensions', 'largefiles', '')
570 config.set('extensions', 'largefiles', '')
570 repo = db_repo.scm_instance(config=config)
571 repo = db_repo.scm_instance(config=config)
571 repo.install_hooks()
572 repo.install_hooks()
572
573
573 removed = []
574 removed = []
574 if remove_obsolete:
575 if remove_obsolete:
575 # remove from database those repositories that are not in the filesystem
576 # remove from database those repositories that are not in the filesystem
576 for repo in sa.query(Repository).all():
577 for repo in sa.query(Repository).all():
577 if repo.repo_name not in initial_repo_list.keys():
578 if repo.repo_name not in initial_repo_list.keys():
578 log.debug("Removing non-existing repository found in db `%s`",
579 log.debug("Removing non-existing repository found in db `%s`",
579 repo.repo_name)
580 repo.repo_name)
580 try:
581 try:
581 RepoModel(sa).delete(repo, forks='detach', fs_remove=False)
582 RepoModel(sa).delete(repo, forks='detach', fs_remove=False)
582 sa.commit()
583 sa.commit()
583 removed.append(repo.repo_name)
584 removed.append(repo.repo_name)
584 except Exception:
585 except Exception:
585 # don't hold further removals on error
586 # don't hold further removals on error
586 log.error(traceback.format_exc())
587 log.error(traceback.format_exc())
587 sa.rollback()
588 sa.rollback()
588
589
589 def splitter(full_repo_name):
590 def splitter(full_repo_name):
590 _parts = full_repo_name.rsplit(RepoGroup.url_sep(), 1)
591 _parts = full_repo_name.rsplit(RepoGroup.url_sep(), 1)
591 gr_name = None
592 gr_name = None
592 if len(_parts) == 2:
593 if len(_parts) == 2:
593 gr_name = _parts[0]
594 gr_name = _parts[0]
594 return gr_name
595 return gr_name
595
596
596 initial_repo_group_list = [splitter(x) for x in
597 initial_repo_group_list = [splitter(x) for x in
597 initial_repo_list.keys() if splitter(x)]
598 initial_repo_list.keys() if splitter(x)]
598
599
599 # remove from database those repository groups that are not in the
600 # remove from database those repository groups that are not in the
600 # filesystem due to parent child relationships we need to delete them
601 # filesystem due to parent child relationships we need to delete them
601 # in a specific order of most nested first
602 # in a specific order of most nested first
602 all_groups = [x.group_name for x in sa.query(RepoGroup).all()]
603 all_groups = [x.group_name for x in sa.query(RepoGroup).all()]
603 nested_sort = lambda gr: len(gr.split('/'))
604 nested_sort = lambda gr: len(gr.split('/'))
604 for group_name in sorted(all_groups, key=nested_sort, reverse=True):
605 for group_name in sorted(all_groups, key=nested_sort, reverse=True):
605 if group_name not in initial_repo_group_list:
606 if group_name not in initial_repo_group_list:
606 repo_group = RepoGroup.get_by_group_name(group_name)
607 repo_group = RepoGroup.get_by_group_name(group_name)
607 if (repo_group.children.all() or
608 if (repo_group.children.all() or
608 not RepoGroupModel().check_exist_filesystem(
609 not RepoGroupModel().check_exist_filesystem(
609 group_name=group_name, exc_on_failure=False)):
610 group_name=group_name, exc_on_failure=False)):
610 continue
611 continue
611
612
612 log.info(
613 log.info(
613 'Removing non-existing repository group found in db `%s`',
614 'Removing non-existing repository group found in db `%s`',
614 group_name)
615 group_name)
615 try:
616 try:
616 RepoGroupModel(sa).delete(group_name, fs_remove=False)
617 RepoGroupModel(sa).delete(group_name, fs_remove=False)
617 sa.commit()
618 sa.commit()
618 removed.append(group_name)
619 removed.append(group_name)
619 except Exception:
620 except Exception:
620 # don't hold further removals on error
621 # don't hold further removals on error
621 log.exception(
622 log.exception(
622 'Unable to remove repository group `%s`',
623 'Unable to remove repository group `%s`',
623 group_name)
624 group_name)
624 sa.rollback()
625 sa.rollback()
625 raise
626 raise
626
627
627 return added, removed
628 return added, removed
628
629
629
630
630 def load_rcextensions(root_path):
631 def load_rcextensions(root_path):
631 import rhodecode
632 import rhodecode
632 from rhodecode.config import conf
633 from rhodecode.config import conf
633
634
634 path = os.path.join(root_path)
635 path = os.path.join(root_path)
635 sys.path.append(path)
636 sys.path.append(path)
636 try:
637 try:
637 rcextensions = __import__('rcextensions')
638 rcextensions = __import__('rcextensions')
638 except ImportError:
639 except ImportError:
639 log.warn('Unable to load rcextensions from %s', path)
640 log.warn('Unable to load rcextensions from %s', path)
640 rcextensions = None
641 rcextensions = None
641
642
642 if rcextensions:
643 if rcextensions:
643 log.debug('Found rcextensions module loaded %s...', rcextensions)
644 log.debug('Found rcextensions module loaded %s...', rcextensions)
644 rhodecode.EXTENSIONS = rcextensions
645 rhodecode.EXTENSIONS = rcextensions
645
646
646 # Additional mappings that are not present in the pygments lexers
647 # Additional mappings that are not present in the pygments lexers
647 conf.LANGUAGES_EXTENSIONS_MAP.update(
648 conf.LANGUAGES_EXTENSIONS_MAP.update(
648 getattr(rhodecode.EXTENSIONS, 'EXTRA_MAPPINGS', {}))
649 getattr(rhodecode.EXTENSIONS, 'EXTRA_MAPPINGS', {}))
649
650
650
651
651 def get_custom_lexer(extension):
652 def get_custom_lexer(extension):
652 """
653 """
653 returns a custom lexer if it is defined in rcextensions module, or None
654 returns a custom lexer if it is defined in rcextensions module, or None
654 if there's no custom lexer defined
655 if there's no custom lexer defined
655 """
656 """
656 import rhodecode
657 import rhodecode
657 from pygments import lexers
658 from pygments import lexers
658
659
659 # custom override made by RhodeCode
660 # custom override made by RhodeCode
660 if extension in ['mako']:
661 if extension in ['mako']:
661 return lexers.get_lexer_by_name('html+mako')
662 return lexers.get_lexer_by_name('html+mako')
662
663
663 # check if we didn't define this extension as other lexer
664 # check if we didn't define this extension as other lexer
664 extensions = rhodecode.EXTENSIONS and getattr(rhodecode.EXTENSIONS, 'EXTRA_LEXERS', None)
665 extensions = rhodecode.EXTENSIONS and getattr(rhodecode.EXTENSIONS, 'EXTRA_LEXERS', None)
665 if extensions and extension in rhodecode.EXTENSIONS.EXTRA_LEXERS:
666 if extensions and extension in rhodecode.EXTENSIONS.EXTRA_LEXERS:
666 _lexer_name = rhodecode.EXTENSIONS.EXTRA_LEXERS[extension]
667 _lexer_name = rhodecode.EXTENSIONS.EXTRA_LEXERS[extension]
667 return lexers.get_lexer_by_name(_lexer_name)
668 return lexers.get_lexer_by_name(_lexer_name)
668
669
669
670
670 #==============================================================================
671 #==============================================================================
671 # TEST FUNCTIONS AND CREATORS
672 # TEST FUNCTIONS AND CREATORS
672 #==============================================================================
673 #==============================================================================
673 def create_test_index(repo_location, config):
674 def create_test_index(repo_location, config):
674 """
675 """
675 Makes default test index.
676 Makes default test index.
676 """
677 """
677 import rc_testdata
678 import rc_testdata
678
679
679 rc_testdata.extract_search_index(
680 rc_testdata.extract_search_index(
680 'vcs_search_index', os.path.dirname(config['search.location']))
681 'vcs_search_index', os.path.dirname(config['search.location']))
681
682
682
683
683 def create_test_directory(test_path):
684 def create_test_directory(test_path):
684 """
685 """
685 Create test directory if it doesn't exist.
686 Create test directory if it doesn't exist.
686 """
687 """
687 if not os.path.isdir(test_path):
688 if not os.path.isdir(test_path):
688 log.debug('Creating testdir %s', test_path)
689 log.debug('Creating testdir %s', test_path)
689 os.makedirs(test_path)
690 os.makedirs(test_path)
690
691
691
692
692 def create_test_database(test_path, config):
693 def create_test_database(test_path, config):
693 """
694 """
694 Makes a fresh database.
695 Makes a fresh database.
695 """
696 """
696 from rhodecode.lib.db_manage import DbManage
697 from rhodecode.lib.db_manage import DbManage
697
698
698 # PART ONE create db
699 # PART ONE create db
699 dbconf = config['sqlalchemy.db1.url']
700 dbconf = config['sqlalchemy.db1.url']
700 log.debug('making test db %s', dbconf)
701 log.debug('making test db %s', dbconf)
701
702
702 dbmanage = DbManage(log_sql=False, dbconf=dbconf, root=config['here'],
703 dbmanage = DbManage(log_sql=False, dbconf=dbconf, root=config['here'],
703 tests=True, cli_args={'force_ask': True})
704 tests=True, cli_args={'force_ask': True})
704 dbmanage.create_tables(override=True)
705 dbmanage.create_tables(override=True)
705 dbmanage.set_db_version()
706 dbmanage.set_db_version()
706 # for tests dynamically set new root paths based on generated content
707 # for tests dynamically set new root paths based on generated content
707 dbmanage.create_settings(dbmanage.config_prompt(test_path))
708 dbmanage.create_settings(dbmanage.config_prompt(test_path))
708 dbmanage.create_default_user()
709 dbmanage.create_default_user()
709 dbmanage.create_test_admin_and_users()
710 dbmanage.create_test_admin_and_users()
710 dbmanage.create_permissions()
711 dbmanage.create_permissions()
711 dbmanage.populate_default_permissions()
712 dbmanage.populate_default_permissions()
712 Session().commit()
713 Session().commit()
713
714
714
715
715 def create_test_repositories(test_path, config):
716 def create_test_repositories(test_path, config):
716 """
717 """
717 Creates test repositories in the temporary directory. Repositories are
718 Creates test repositories in the temporary directory. Repositories are
718 extracted from archives within the rc_testdata package.
719 extracted from archives within the rc_testdata package.
719 """
720 """
720 import rc_testdata
721 import rc_testdata
721 from rhodecode.tests import HG_REPO, GIT_REPO, SVN_REPO
722 from rhodecode.tests import HG_REPO, GIT_REPO, SVN_REPO
722
723
723 log.debug('making test vcs repositories')
724 log.debug('making test vcs repositories')
724
725
725 idx_path = config['search.location']
726 idx_path = config['search.location']
726 data_path = config['cache_dir']
727 data_path = config['cache_dir']
727
728
728 # clean index and data
729 # clean index and data
729 if idx_path and os.path.exists(idx_path):
730 if idx_path and os.path.exists(idx_path):
730 log.debug('remove %s', idx_path)
731 log.debug('remove %s', idx_path)
731 shutil.rmtree(idx_path)
732 shutil.rmtree(idx_path)
732
733
733 if data_path and os.path.exists(data_path):
734 if data_path and os.path.exists(data_path):
734 log.debug('remove %s', data_path)
735 log.debug('remove %s', data_path)
735 shutil.rmtree(data_path)
736 shutil.rmtree(data_path)
736
737
737 rc_testdata.extract_hg_dump('vcs_test_hg', jn(test_path, HG_REPO))
738 rc_testdata.extract_hg_dump('vcs_test_hg', jn(test_path, HG_REPO))
738 rc_testdata.extract_git_dump('vcs_test_git', jn(test_path, GIT_REPO))
739 rc_testdata.extract_git_dump('vcs_test_git', jn(test_path, GIT_REPO))
739
740
740 # Note: Subversion is in the process of being integrated with the system,
741 # Note: Subversion is in the process of being integrated with the system,
741 # until we have a properly packed version of the test svn repository, this
742 # until we have a properly packed version of the test svn repository, this
742 # tries to copy over the repo from a package "rc_testdata"
743 # tries to copy over the repo from a package "rc_testdata"
743 svn_repo_path = rc_testdata.get_svn_repo_archive()
744 svn_repo_path = rc_testdata.get_svn_repo_archive()
744 with tarfile.open(svn_repo_path) as tar:
745 with tarfile.open(svn_repo_path) as tar:
745 tar.extractall(jn(test_path, SVN_REPO))
746 tar.extractall(jn(test_path, SVN_REPO))
746
747
747
748
748 def password_changed(auth_user, session):
749 def password_changed(auth_user, session):
749 # Never report password change in case of default user or anonymous user.
750 # Never report password change in case of default user or anonymous user.
750 if auth_user.username == User.DEFAULT_USER or auth_user.user_id is None:
751 if auth_user.username == User.DEFAULT_USER or auth_user.user_id is None:
751 return False
752 return False
752
753
753 password_hash = md5(auth_user.password) if auth_user.password else None
754 password_hash = md5(auth_user.password) if auth_user.password else None
754 rhodecode_user = session.get('rhodecode_user', {})
755 rhodecode_user = session.get('rhodecode_user', {})
755 session_password_hash = rhodecode_user.get('password', '')
756 session_password_hash = rhodecode_user.get('password', '')
756 return password_hash != session_password_hash
757 return password_hash != session_password_hash
757
758
758
759
759 def read_opensource_licenses():
760 def read_opensource_licenses():
760 global _license_cache
761 global _license_cache
761
762
762 if not _license_cache:
763 if not _license_cache:
763 licenses = pkg_resources.resource_string(
764 licenses = pkg_resources.resource_string(
764 'rhodecode', 'config/licenses.json')
765 'rhodecode', 'config/licenses.json')
765 _license_cache = json.loads(licenses)
766 _license_cache = json.loads(licenses)
766
767
767 return _license_cache
768 return _license_cache
768
769
769
770
770 def generate_platform_uuid():
771 def generate_platform_uuid():
771 """
772 """
772 Generates platform UUID based on it's name
773 Generates platform UUID based on it's name
773 """
774 """
774 import platform
775 import platform
775
776
776 try:
777 try:
777 uuid_list = [platform.platform()]
778 uuid_list = [platform.platform()]
778 return hashlib.sha256(':'.join(uuid_list)).hexdigest()
779 return hashlib.sha256(':'.join(uuid_list)).hexdigest()
779 except Exception as e:
780 except Exception as e:
780 log.error('Failed to generate host uuid: %s', e)
781 log.error('Failed to generate host uuid: %s', e)
781 return 'UNDEFINED'
782 return 'UNDEFINED'
@@ -1,845 +1,886 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2019 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import os
21 import os
22 import hashlib
22 import hashlib
23 import logging
23 import logging
24 from collections import namedtuple
24 from collections import namedtuple
25 from functools import wraps
25 from functools import wraps
26 import bleach
26 import bleach
27
27
28 from rhodecode.lib import rc_cache
28 from rhodecode.lib import rc_cache
29 from rhodecode.lib.utils2 import (
29 from rhodecode.lib.utils2 import (
30 Optional, AttributeDict, safe_str, remove_prefix, str2bool)
30 Optional, AttributeDict, safe_str, remove_prefix, str2bool)
31 from rhodecode.lib.vcs.backends import base
31 from rhodecode.lib.vcs.backends import base
32 from rhodecode.model import BaseModel
32 from rhodecode.model import BaseModel
33 from rhodecode.model.db import (
33 from rhodecode.model.db import (
34 RepoRhodeCodeUi, RepoRhodeCodeSetting, RhodeCodeUi, RhodeCodeSetting, CacheKey)
34 RepoRhodeCodeUi, RepoRhodeCodeSetting, RhodeCodeUi, RhodeCodeSetting, CacheKey)
35 from rhodecode.model.meta import Session
35 from rhodecode.model.meta import Session
36
36
37
37
38 log = logging.getLogger(__name__)
38 log = logging.getLogger(__name__)
39
39
40
40
41 UiSetting = namedtuple(
41 UiSetting = namedtuple(
42 'UiSetting', ['section', 'key', 'value', 'active'])
42 'UiSetting', ['section', 'key', 'value', 'active'])
43
43
44 SOCIAL_PLUGINS_LIST = ['github', 'bitbucket', 'twitter', 'google']
44 SOCIAL_PLUGINS_LIST = ['github', 'bitbucket', 'twitter', 'google']
45
45
46
46
47 class SettingNotFound(Exception):
47 class SettingNotFound(Exception):
48 def __init__(self, setting_id):
48 def __init__(self, setting_id):
49 msg = 'Setting `{}` is not found'.format(setting_id)
49 msg = 'Setting `{}` is not found'.format(setting_id)
50 super(SettingNotFound, self).__init__(msg)
50 super(SettingNotFound, self).__init__(msg)
51
51
52
52
53 class SettingsModel(BaseModel):
53 class SettingsModel(BaseModel):
54 BUILTIN_HOOKS = (
54 BUILTIN_HOOKS = (
55 RhodeCodeUi.HOOK_REPO_SIZE, RhodeCodeUi.HOOK_PUSH,
55 RhodeCodeUi.HOOK_REPO_SIZE, RhodeCodeUi.HOOK_PUSH,
56 RhodeCodeUi.HOOK_PRE_PUSH, RhodeCodeUi.HOOK_PRETX_PUSH,
56 RhodeCodeUi.HOOK_PRE_PUSH, RhodeCodeUi.HOOK_PRETX_PUSH,
57 RhodeCodeUi.HOOK_PULL, RhodeCodeUi.HOOK_PRE_PULL,
57 RhodeCodeUi.HOOK_PULL, RhodeCodeUi.HOOK_PRE_PULL,
58 RhodeCodeUi.HOOK_PUSH_KEY,)
58 RhodeCodeUi.HOOK_PUSH_KEY,)
59 HOOKS_SECTION = 'hooks'
59 HOOKS_SECTION = 'hooks'
60
60
61 def __init__(self, sa=None, repo=None):
61 def __init__(self, sa=None, repo=None):
62 self.repo = repo
62 self.repo = repo
63 self.UiDbModel = RepoRhodeCodeUi if repo else RhodeCodeUi
63 self.UiDbModel = RepoRhodeCodeUi if repo else RhodeCodeUi
64 self.SettingsDbModel = (
64 self.SettingsDbModel = (
65 RepoRhodeCodeSetting if repo else RhodeCodeSetting)
65 RepoRhodeCodeSetting if repo else RhodeCodeSetting)
66 super(SettingsModel, self).__init__(sa)
66 super(SettingsModel, self).__init__(sa)
67
67
68 def get_ui_by_key(self, key):
68 def get_ui_by_key(self, key):
69 q = self.UiDbModel.query()
69 q = self.UiDbModel.query()
70 q = q.filter(self.UiDbModel.ui_key == key)
70 q = q.filter(self.UiDbModel.ui_key == key)
71 q = self._filter_by_repo(RepoRhodeCodeUi, q)
71 q = self._filter_by_repo(RepoRhodeCodeUi, q)
72 return q.scalar()
72 return q.scalar()
73
73
74 def get_ui_by_section(self, section):
74 def get_ui_by_section(self, section):
75 q = self.UiDbModel.query()
75 q = self.UiDbModel.query()
76 q = q.filter(self.UiDbModel.ui_section == section)
76 q = q.filter(self.UiDbModel.ui_section == section)
77 q = self._filter_by_repo(RepoRhodeCodeUi, q)
77 q = self._filter_by_repo(RepoRhodeCodeUi, q)
78 return q.all()
78 return q.all()
79
79
80 def get_ui_by_section_and_key(self, section, key):
80 def get_ui_by_section_and_key(self, section, key):
81 q = self.UiDbModel.query()
81 q = self.UiDbModel.query()
82 q = q.filter(self.UiDbModel.ui_section == section)
82 q = q.filter(self.UiDbModel.ui_section == section)
83 q = q.filter(self.UiDbModel.ui_key == key)
83 q = q.filter(self.UiDbModel.ui_key == key)
84 q = self._filter_by_repo(RepoRhodeCodeUi, q)
84 q = self._filter_by_repo(RepoRhodeCodeUi, q)
85 return q.scalar()
85 return q.scalar()
86
86
87 def get_ui(self, section=None, key=None):
87 def get_ui(self, section=None, key=None):
88 q = self.UiDbModel.query()
88 q = self.UiDbModel.query()
89 q = self._filter_by_repo(RepoRhodeCodeUi, q)
89 q = self._filter_by_repo(RepoRhodeCodeUi, q)
90
90
91 if section:
91 if section:
92 q = q.filter(self.UiDbModel.ui_section == section)
92 q = q.filter(self.UiDbModel.ui_section == section)
93 if key:
93 if key:
94 q = q.filter(self.UiDbModel.ui_key == key)
94 q = q.filter(self.UiDbModel.ui_key == key)
95
95
96 # TODO: mikhail: add caching
96 # TODO: mikhail: add caching
97 result = [
97 result = [
98 UiSetting(
98 UiSetting(
99 section=safe_str(r.ui_section), key=safe_str(r.ui_key),
99 section=safe_str(r.ui_section), key=safe_str(r.ui_key),
100 value=safe_str(r.ui_value), active=r.ui_active
100 value=safe_str(r.ui_value), active=r.ui_active
101 )
101 )
102 for r in q.all()
102 for r in q.all()
103 ]
103 ]
104 return result
104 return result
105
105
106 def get_builtin_hooks(self):
106 def get_builtin_hooks(self):
107 q = self.UiDbModel.query()
107 q = self.UiDbModel.query()
108 q = q.filter(self.UiDbModel.ui_key.in_(self.BUILTIN_HOOKS))
108 q = q.filter(self.UiDbModel.ui_key.in_(self.BUILTIN_HOOKS))
109 return self._get_hooks(q)
109 return self._get_hooks(q)
110
110
111 def get_custom_hooks(self):
111 def get_custom_hooks(self):
112 q = self.UiDbModel.query()
112 q = self.UiDbModel.query()
113 q = q.filter(~self.UiDbModel.ui_key.in_(self.BUILTIN_HOOKS))
113 q = q.filter(~self.UiDbModel.ui_key.in_(self.BUILTIN_HOOKS))
114 return self._get_hooks(q)
114 return self._get_hooks(q)
115
115
116 def create_ui_section_value(self, section, val, key=None, active=True):
116 def create_ui_section_value(self, section, val, key=None, active=True):
117 new_ui = self.UiDbModel()
117 new_ui = self.UiDbModel()
118 new_ui.ui_section = section
118 new_ui.ui_section = section
119 new_ui.ui_value = val
119 new_ui.ui_value = val
120 new_ui.ui_active = active
120 new_ui.ui_active = active
121
121
122 repository_id = ''
122 if self.repo:
123 if self.repo:
123 repo = self._get_repo(self.repo)
124 repo = self._get_repo(self.repo)
124 repository_id = repo.repo_id
125 repository_id = repo.repo_id
125 new_ui.repository_id = repository_id
126 new_ui.repository_id = repository_id
126
127
127 if not key:
128 if not key:
128 # keys are unique so they need appended info
129 # keys are unique so they need appended info
129 if self.repo:
130 if self.repo:
130 key = hashlib.sha1(
131 key = hashlib.sha1(
131 '{}{}{}'.format(section, val, repository_id)).hexdigest()
132 '{}{}{}'.format(section, val, repository_id)).hexdigest()
132 else:
133 else:
133 key = hashlib.sha1('{}{}'.format(section, val)).hexdigest()
134 key = hashlib.sha1('{}{}'.format(section, val)).hexdigest()
134
135
135 new_ui.ui_key = key
136 new_ui.ui_key = key
136
137
137 Session().add(new_ui)
138 Session().add(new_ui)
138 return new_ui
139 return new_ui
139
140
140 def create_or_update_hook(self, key, value):
141 def create_or_update_hook(self, key, value):
141 ui = (
142 ui = (
142 self.get_ui_by_section_and_key(self.HOOKS_SECTION, key) or
143 self.get_ui_by_section_and_key(self.HOOKS_SECTION, key) or
143 self.UiDbModel())
144 self.UiDbModel())
144 ui.ui_section = self.HOOKS_SECTION
145 ui.ui_section = self.HOOKS_SECTION
145 ui.ui_active = True
146 ui.ui_active = True
146 ui.ui_key = key
147 ui.ui_key = key
147 ui.ui_value = value
148 ui.ui_value = value
148
149
149 if self.repo:
150 if self.repo:
150 repo = self._get_repo(self.repo)
151 repo = self._get_repo(self.repo)
151 repository_id = repo.repo_id
152 repository_id = repo.repo_id
152 ui.repository_id = repository_id
153 ui.repository_id = repository_id
153
154
154 Session().add(ui)
155 Session().add(ui)
155 return ui
156 return ui
156
157
157 def delete_ui(self, id_):
158 def delete_ui(self, id_):
158 ui = self.UiDbModel.get(id_)
159 ui = self.UiDbModel.get(id_)
159 if not ui:
160 if not ui:
160 raise SettingNotFound(id_)
161 raise SettingNotFound(id_)
161 Session().delete(ui)
162 Session().delete(ui)
162
163
163 def get_setting_by_name(self, name):
164 def get_setting_by_name(self, name):
164 q = self._get_settings_query()
165 q = self._get_settings_query()
165 q = q.filter(self.SettingsDbModel.app_settings_name == name)
166 q = q.filter(self.SettingsDbModel.app_settings_name == name)
166 return q.scalar()
167 return q.scalar()
167
168
168 def create_or_update_setting(
169 def create_or_update_setting(
169 self, name, val=Optional(''), type_=Optional('unicode')):
170 self, name, val=Optional(''), type_=Optional('unicode')):
170 """
171 """
171 Creates or updates RhodeCode setting. If updates is triggered it will
172 Creates or updates RhodeCode setting. If updates is triggered it will
172 only update parameters that are explicityl set Optional instance will
173 only update parameters that are explicityl set Optional instance will
173 be skipped
174 be skipped
174
175
175 :param name:
176 :param name:
176 :param val:
177 :param val:
177 :param type_:
178 :param type_:
178 :return:
179 :return:
179 """
180 """
180
181
181 res = self.get_setting_by_name(name)
182 res = self.get_setting_by_name(name)
182 repo = self._get_repo(self.repo) if self.repo else None
183 repo = self._get_repo(self.repo) if self.repo else None
183
184
184 if not res:
185 if not res:
185 val = Optional.extract(val)
186 val = Optional.extract(val)
186 type_ = Optional.extract(type_)
187 type_ = Optional.extract(type_)
187
188
188 args = (
189 args = (
189 (repo.repo_id, name, val, type_)
190 (repo.repo_id, name, val, type_)
190 if repo else (name, val, type_))
191 if repo else (name, val, type_))
191 res = self.SettingsDbModel(*args)
192 res = self.SettingsDbModel(*args)
192
193
193 else:
194 else:
194 if self.repo:
195 if self.repo:
195 res.repository_id = repo.repo_id
196 res.repository_id = repo.repo_id
196
197
197 res.app_settings_name = name
198 res.app_settings_name = name
198 if not isinstance(type_, Optional):
199 if not isinstance(type_, Optional):
199 # update if set
200 # update if set
200 res.app_settings_type = type_
201 res.app_settings_type = type_
201 if not isinstance(val, Optional):
202 if not isinstance(val, Optional):
202 # update if set
203 # update if set
203 res.app_settings_value = val
204 res.app_settings_value = val
204
205
205 Session().add(res)
206 Session().add(res)
206 return res
207 return res
207
208
208 def invalidate_settings_cache(self):
209 def invalidate_settings_cache(self):
209 invalidation_namespace = CacheKey.SETTINGS_INVALIDATION_NAMESPACE
210 invalidation_namespace = CacheKey.SETTINGS_INVALIDATION_NAMESPACE
210 CacheKey.set_invalidate(invalidation_namespace)
211 CacheKey.set_invalidate(invalidation_namespace)
211
212
212 def get_all_settings(self, cache=False):
213 def get_all_settings(self, cache=False):
213 region = rc_cache.get_or_create_region('sql_cache_short')
214 region = rc_cache.get_or_create_region('sql_cache_short')
214 invalidation_namespace = CacheKey.SETTINGS_INVALIDATION_NAMESPACE
215 invalidation_namespace = CacheKey.SETTINGS_INVALIDATION_NAMESPACE
215
216
216 @region.conditional_cache_on_arguments(condition=cache)
217 @region.conditional_cache_on_arguments(condition=cache)
217 def _get_all_settings(name, key):
218 def _get_all_settings(name, key):
218 q = self._get_settings_query()
219 q = self._get_settings_query()
219 if not q:
220 if not q:
220 raise Exception('Could not get application settings !')
221 raise Exception('Could not get application settings !')
221
222
222 settings = {
223 settings = {
223 'rhodecode_' + result.app_settings_name: result.app_settings_value
224 'rhodecode_' + result.app_settings_name: result.app_settings_value
224 for result in q
225 for result in q
225 }
226 }
226 return settings
227 return settings
227
228
228 repo = self._get_repo(self.repo) if self.repo else None
229 repo = self._get_repo(self.repo) if self.repo else None
229 key = "settings_repo.{}".format(repo.repo_id) if repo else "settings_app"
230 key = "settings_repo.{}".format(repo.repo_id) if repo else "settings_app"
230
231
231 inv_context_manager = rc_cache.InvalidationContext(
232 inv_context_manager = rc_cache.InvalidationContext(
232 uid='cache_settings', invalidation_namespace=invalidation_namespace)
233 uid='cache_settings', invalidation_namespace=invalidation_namespace)
233 with inv_context_manager as invalidation_context:
234 with inv_context_manager as invalidation_context:
234 # check for stored invalidation signal, and maybe purge the cache
235 # check for stored invalidation signal, and maybe purge the cache
235 # before computing it again
236 # before computing it again
236 if invalidation_context.should_invalidate():
237 if invalidation_context.should_invalidate():
237 # NOTE:(marcink) we flush the whole sql_cache_short region, because it
238 # NOTE:(marcink) we flush the whole sql_cache_short region, because it
238 # reads different settings etc. It's little too much but those caches
239 # reads different settings etc. It's little too much but those caches
239 # are anyway very short lived and it's a safest way.
240 # are anyway very short lived and it's a safest way.
240 region = rc_cache.get_or_create_region('sql_cache_short')
241 region = rc_cache.get_or_create_region('sql_cache_short')
241 region.invalidate()
242 region.invalidate()
242
243
243 result = _get_all_settings('rhodecode_settings', key)
244 result = _get_all_settings('rhodecode_settings', key)
244 log.debug('Fetching app settings for key: %s took: %.3fs', key,
245 log.debug('Fetching app settings for key: %s took: %.3fs', key,
245 inv_context_manager.compute_time)
246 inv_context_manager.compute_time)
246
247
247 return result
248 return result
248
249
249 def get_auth_settings(self):
250 def get_auth_settings(self):
250 q = self._get_settings_query()
251 q = self._get_settings_query()
251 q = q.filter(
252 q = q.filter(
252 self.SettingsDbModel.app_settings_name.startswith('auth_'))
253 self.SettingsDbModel.app_settings_name.startswith('auth_'))
253 rows = q.all()
254 rows = q.all()
254 auth_settings = {
255 auth_settings = {
255 row.app_settings_name: row.app_settings_value for row in rows}
256 row.app_settings_name: row.app_settings_value for row in rows}
256 return auth_settings
257 return auth_settings
257
258
258 def get_auth_plugins(self):
259 def get_auth_plugins(self):
259 auth_plugins = self.get_setting_by_name("auth_plugins")
260 auth_plugins = self.get_setting_by_name("auth_plugins")
260 return auth_plugins.app_settings_value
261 return auth_plugins.app_settings_value
261
262
262 def get_default_repo_settings(self, strip_prefix=False):
263 def get_default_repo_settings(self, strip_prefix=False):
263 q = self._get_settings_query()
264 q = self._get_settings_query()
264 q = q.filter(
265 q = q.filter(
265 self.SettingsDbModel.app_settings_name.startswith('default_'))
266 self.SettingsDbModel.app_settings_name.startswith('default_'))
266 rows = q.all()
267 rows = q.all()
267
268
268 result = {}
269 result = {}
269 for row in rows:
270 for row in rows:
270 key = row.app_settings_name
271 key = row.app_settings_name
271 if strip_prefix:
272 if strip_prefix:
272 key = remove_prefix(key, prefix='default_')
273 key = remove_prefix(key, prefix='default_')
273 result.update({key: row.app_settings_value})
274 result.update({key: row.app_settings_value})
274 return result
275 return result
275
276
276 def get_repo(self):
277 def get_repo(self):
277 repo = self._get_repo(self.repo)
278 repo = self._get_repo(self.repo)
278 if not repo:
279 if not repo:
279 raise Exception(
280 raise Exception(
280 'Repository `{}` cannot be found inside the database'.format(
281 'Repository `{}` cannot be found inside the database'.format(
281 self.repo))
282 self.repo))
282 return repo
283 return repo
283
284
284 def _filter_by_repo(self, model, query):
285 def _filter_by_repo(self, model, query):
285 if self.repo:
286 if self.repo:
286 repo = self.get_repo()
287 repo = self.get_repo()
287 query = query.filter(model.repository_id == repo.repo_id)
288 query = query.filter(model.repository_id == repo.repo_id)
288 return query
289 return query
289
290
290 def _get_hooks(self, query):
291 def _get_hooks(self, query):
291 query = query.filter(self.UiDbModel.ui_section == self.HOOKS_SECTION)
292 query = query.filter(self.UiDbModel.ui_section == self.HOOKS_SECTION)
292 query = self._filter_by_repo(RepoRhodeCodeUi, query)
293 query = self._filter_by_repo(RepoRhodeCodeUi, query)
293 return query.all()
294 return query.all()
294
295
295 def _get_settings_query(self):
296 def _get_settings_query(self):
296 q = self.SettingsDbModel.query()
297 q = self.SettingsDbModel.query()
297 return self._filter_by_repo(RepoRhodeCodeSetting, q)
298 return self._filter_by_repo(RepoRhodeCodeSetting, q)
298
299
299 def list_enabled_social_plugins(self, settings):
300 def list_enabled_social_plugins(self, settings):
300 enabled = []
301 enabled = []
301 for plug in SOCIAL_PLUGINS_LIST:
302 for plug in SOCIAL_PLUGINS_LIST:
302 if str2bool(settings.get('rhodecode_auth_{}_enabled'.format(plug)
303 if str2bool(settings.get('rhodecode_auth_{}_enabled'.format(plug)
303 )):
304 )):
304 enabled.append(plug)
305 enabled.append(plug)
305 return enabled
306 return enabled
306
307
307
308
308 def assert_repo_settings(func):
309 def assert_repo_settings(func):
309 @wraps(func)
310 @wraps(func)
310 def _wrapper(self, *args, **kwargs):
311 def _wrapper(self, *args, **kwargs):
311 if not self.repo_settings:
312 if not self.repo_settings:
312 raise Exception('Repository is not specified')
313 raise Exception('Repository is not specified')
313 return func(self, *args, **kwargs)
314 return func(self, *args, **kwargs)
314 return _wrapper
315 return _wrapper
315
316
316
317
317 class IssueTrackerSettingsModel(object):
318 class IssueTrackerSettingsModel(object):
318 INHERIT_SETTINGS = 'inherit_issue_tracker_settings'
319 INHERIT_SETTINGS = 'inherit_issue_tracker_settings'
319 SETTINGS_PREFIX = 'issuetracker_'
320 SETTINGS_PREFIX = 'issuetracker_'
320
321
321 def __init__(self, sa=None, repo=None):
322 def __init__(self, sa=None, repo=None):
322 self.global_settings = SettingsModel(sa=sa)
323 self.global_settings = SettingsModel(sa=sa)
323 self.repo_settings = SettingsModel(sa=sa, repo=repo) if repo else None
324 self.repo_settings = SettingsModel(sa=sa, repo=repo) if repo else None
324
325
325 @property
326 @property
326 def inherit_global_settings(self):
327 def inherit_global_settings(self):
327 if not self.repo_settings:
328 if not self.repo_settings:
328 return True
329 return True
329 setting = self.repo_settings.get_setting_by_name(self.INHERIT_SETTINGS)
330 setting = self.repo_settings.get_setting_by_name(self.INHERIT_SETTINGS)
330 return setting.app_settings_value if setting else True
331 return setting.app_settings_value if setting else True
331
332
332 @inherit_global_settings.setter
333 @inherit_global_settings.setter
333 def inherit_global_settings(self, value):
334 def inherit_global_settings(self, value):
334 if self.repo_settings:
335 if self.repo_settings:
335 settings = self.repo_settings.create_or_update_setting(
336 settings = self.repo_settings.create_or_update_setting(
336 self.INHERIT_SETTINGS, value, type_='bool')
337 self.INHERIT_SETTINGS, value, type_='bool')
337 Session().add(settings)
338 Session().add(settings)
338
339
339 def _get_keyname(self, key, uid, prefix=''):
340 def _get_keyname(self, key, uid, prefix=''):
340 return '{0}{1}{2}_{3}'.format(
341 return '{0}{1}{2}_{3}'.format(
341 prefix, self.SETTINGS_PREFIX, key, uid)
342 prefix, self.SETTINGS_PREFIX, key, uid)
342
343
343 def _make_dict_for_settings(self, qs):
344 def _make_dict_for_settings(self, qs):
344 prefix_match = self._get_keyname('pat', '', 'rhodecode_')
345 prefix_match = self._get_keyname('pat', '', 'rhodecode_')
345
346
346 issuetracker_entries = {}
347 issuetracker_entries = {}
347 # create keys
348 # create keys
348 for k, v in qs.items():
349 for k, v in qs.items():
349 if k.startswith(prefix_match):
350 if k.startswith(prefix_match):
350 uid = k[len(prefix_match):]
351 uid = k[len(prefix_match):]
351 issuetracker_entries[uid] = None
352 issuetracker_entries[uid] = None
352
353
353 def url_cleaner(input_str):
354 def url_cleaner(input_str):
354 input_str = input_str.replace('"', '').replace("'", '')
355 input_str = input_str.replace('"', '').replace("'", '')
355 input_str = bleach.clean(input_str, strip=True)
356 input_str = bleach.clean(input_str, strip=True)
356 return input_str
357 return input_str
357
358
358 # populate
359 # populate
359 for uid in issuetracker_entries:
360 for uid in issuetracker_entries:
360 url_data = qs.get(self._get_keyname('url', uid, 'rhodecode_'))
361 url_data = qs.get(self._get_keyname('url', uid, 'rhodecode_'))
361
362
362 issuetracker_entries[uid] = AttributeDict({
363 issuetracker_entries[uid] = AttributeDict({
363 'pat': qs.get(
364 'pat': qs.get(
364 self._get_keyname('pat', uid, 'rhodecode_')),
365 self._get_keyname('pat', uid, 'rhodecode_')),
365 'url': url_cleaner(
366 'url': url_cleaner(
366 qs.get(self._get_keyname('url', uid, 'rhodecode_')) or ''),
367 qs.get(self._get_keyname('url', uid, 'rhodecode_')) or ''),
367 'pref': bleach.clean(
368 'pref': bleach.clean(
368 qs.get(self._get_keyname('pref', uid, 'rhodecode_')) or ''),
369 qs.get(self._get_keyname('pref', uid, 'rhodecode_')) or ''),
369 'desc': qs.get(
370 'desc': qs.get(
370 self._get_keyname('desc', uid, 'rhodecode_')),
371 self._get_keyname('desc', uid, 'rhodecode_')),
371 })
372 })
372
373
373 return issuetracker_entries
374 return issuetracker_entries
374
375
375 def get_global_settings(self, cache=False):
376 def get_global_settings(self, cache=False):
376 """
377 """
377 Returns list of global issue tracker settings
378 Returns list of global issue tracker settings
378 """
379 """
379 defaults = self.global_settings.get_all_settings(cache=cache)
380 defaults = self.global_settings.get_all_settings(cache=cache)
380 settings = self._make_dict_for_settings(defaults)
381 settings = self._make_dict_for_settings(defaults)
381 return settings
382 return settings
382
383
383 def get_repo_settings(self, cache=False):
384 def get_repo_settings(self, cache=False):
384 """
385 """
385 Returns list of issue tracker settings per repository
386 Returns list of issue tracker settings per repository
386 """
387 """
387 if not self.repo_settings:
388 if not self.repo_settings:
388 raise Exception('Repository is not specified')
389 raise Exception('Repository is not specified')
389 all_settings = self.repo_settings.get_all_settings(cache=cache)
390 all_settings = self.repo_settings.get_all_settings(cache=cache)
390 settings = self._make_dict_for_settings(all_settings)
391 settings = self._make_dict_for_settings(all_settings)
391 return settings
392 return settings
392
393
393 def get_settings(self, cache=False):
394 def get_settings(self, cache=False):
394 if self.inherit_global_settings:
395 if self.inherit_global_settings:
395 return self.get_global_settings(cache=cache)
396 return self.get_global_settings(cache=cache)
396 else:
397 else:
397 return self.get_repo_settings(cache=cache)
398 return self.get_repo_settings(cache=cache)
398
399
399 def delete_entries(self, uid):
400 def delete_entries(self, uid):
400 if self.repo_settings:
401 if self.repo_settings:
401 all_patterns = self.get_repo_settings()
402 all_patterns = self.get_repo_settings()
402 settings_model = self.repo_settings
403 settings_model = self.repo_settings
403 else:
404 else:
404 all_patterns = self.get_global_settings()
405 all_patterns = self.get_global_settings()
405 settings_model = self.global_settings
406 settings_model = self.global_settings
406 entries = all_patterns.get(uid, [])
407 entries = all_patterns.get(uid, [])
407
408
408 for del_key in entries:
409 for del_key in entries:
409 setting_name = self._get_keyname(del_key, uid)
410 setting_name = self._get_keyname(del_key, uid)
410 entry = settings_model.get_setting_by_name(setting_name)
411 entry = settings_model.get_setting_by_name(setting_name)
411 if entry:
412 if entry:
412 Session().delete(entry)
413 Session().delete(entry)
413
414
414 Session().commit()
415 Session().commit()
415
416
416 def create_or_update_setting(
417 def create_or_update_setting(
417 self, name, val=Optional(''), type_=Optional('unicode')):
418 self, name, val=Optional(''), type_=Optional('unicode')):
418 if self.repo_settings:
419 if self.repo_settings:
419 setting = self.repo_settings.create_or_update_setting(
420 setting = self.repo_settings.create_or_update_setting(
420 name, val, type_)
421 name, val, type_)
421 else:
422 else:
422 setting = self.global_settings.create_or_update_setting(
423 setting = self.global_settings.create_or_update_setting(
423 name, val, type_)
424 name, val, type_)
424 return setting
425 return setting
425
426
426
427
427 class VcsSettingsModel(object):
428 class VcsSettingsModel(object):
428
429
429 INHERIT_SETTINGS = 'inherit_vcs_settings'
430 INHERIT_SETTINGS = 'inherit_vcs_settings'
430 GENERAL_SETTINGS = (
431 GENERAL_SETTINGS = (
431 'use_outdated_comments',
432 'use_outdated_comments',
432 'pr_merge_enabled',
433 'pr_merge_enabled',
433 'hg_use_rebase_for_merging',
434 'hg_use_rebase_for_merging',
434 'hg_close_branch_before_merging',
435 'hg_close_branch_before_merging',
435 'git_use_rebase_for_merging',
436 'git_use_rebase_for_merging',
436 'git_close_branch_before_merging',
437 'git_close_branch_before_merging',
437 'diff_cache',
438 'diff_cache',
438 )
439 )
439
440
440 HOOKS_SETTINGS = (
441 HOOKS_SETTINGS = (
441 ('hooks', 'changegroup.repo_size'),
442 ('hooks', 'changegroup.repo_size'),
442 ('hooks', 'changegroup.push_logger'),
443 ('hooks', 'changegroup.push_logger'),
443 ('hooks', 'outgoing.pull_logger'),)
444 ('hooks', 'outgoing.pull_logger'),
445 )
444 HG_SETTINGS = (
446 HG_SETTINGS = (
445 ('extensions', 'largefiles'),
447 ('extensions', 'largefiles'),
446 ('phases', 'publish'),
448 ('phases', 'publish'),
447 ('extensions', 'evolve'),)
449 ('extensions', 'evolve'),
450 ('extensions', 'topic'),
451 ('experimental', 'evolution'),
452 )
448 GIT_SETTINGS = (
453 GIT_SETTINGS = (
449 ('vcs_git_lfs', 'enabled'),)
454 ('vcs_git_lfs', 'enabled'),
455 )
450 GLOBAL_HG_SETTINGS = (
456 GLOBAL_HG_SETTINGS = (
451 ('extensions', 'largefiles'),
457 ('extensions', 'largefiles'),
452 ('largefiles', 'usercache'),
458 ('largefiles', 'usercache'),
453 ('phases', 'publish'),
459 ('phases', 'publish'),
454 ('extensions', 'hgsubversion'),
460 ('extensions', 'hgsubversion'),
455 ('extensions', 'evolve'),)
461 ('extensions', 'evolve'),
462 ('extensions', 'topic'),
463 ('experimental', 'evolution'),
464 )
465
456 GLOBAL_GIT_SETTINGS = (
466 GLOBAL_GIT_SETTINGS = (
457 ('vcs_git_lfs', 'enabled'),
467 ('vcs_git_lfs', 'enabled'),
458 ('vcs_git_lfs', 'store_location'))
468 ('vcs_git_lfs', 'store_location')
469 )
459
470
460 GLOBAL_SVN_SETTINGS = (
471 GLOBAL_SVN_SETTINGS = (
461 ('vcs_svn_proxy', 'http_requests_enabled'),
472 ('vcs_svn_proxy', 'http_requests_enabled'),
462 ('vcs_svn_proxy', 'http_server_url'))
473 ('vcs_svn_proxy', 'http_server_url')
474 )
463
475
464 SVN_BRANCH_SECTION = 'vcs_svn_branch'
476 SVN_BRANCH_SECTION = 'vcs_svn_branch'
465 SVN_TAG_SECTION = 'vcs_svn_tag'
477 SVN_TAG_SECTION = 'vcs_svn_tag'
466 SSL_SETTING = ('web', 'push_ssl')
478 SSL_SETTING = ('web', 'push_ssl')
467 PATH_SETTING = ('paths', '/')
479 PATH_SETTING = ('paths', '/')
468
480
469 def __init__(self, sa=None, repo=None):
481 def __init__(self, sa=None, repo=None):
470 self.global_settings = SettingsModel(sa=sa)
482 self.global_settings = SettingsModel(sa=sa)
471 self.repo_settings = SettingsModel(sa=sa, repo=repo) if repo else None
483 self.repo_settings = SettingsModel(sa=sa, repo=repo) if repo else None
472 self._ui_settings = (
484 self._ui_settings = (
473 self.HG_SETTINGS + self.GIT_SETTINGS + self.HOOKS_SETTINGS)
485 self.HG_SETTINGS + self.GIT_SETTINGS + self.HOOKS_SETTINGS)
474 self._svn_sections = (self.SVN_BRANCH_SECTION, self.SVN_TAG_SECTION)
486 self._svn_sections = (self.SVN_BRANCH_SECTION, self.SVN_TAG_SECTION)
475
487
476 @property
488 @property
477 @assert_repo_settings
489 @assert_repo_settings
478 def inherit_global_settings(self):
490 def inherit_global_settings(self):
479 setting = self.repo_settings.get_setting_by_name(self.INHERIT_SETTINGS)
491 setting = self.repo_settings.get_setting_by_name(self.INHERIT_SETTINGS)
480 return setting.app_settings_value if setting else True
492 return setting.app_settings_value if setting else True
481
493
482 @inherit_global_settings.setter
494 @inherit_global_settings.setter
483 @assert_repo_settings
495 @assert_repo_settings
484 def inherit_global_settings(self, value):
496 def inherit_global_settings(self, value):
485 self.repo_settings.create_or_update_setting(
497 self.repo_settings.create_or_update_setting(
486 self.INHERIT_SETTINGS, value, type_='bool')
498 self.INHERIT_SETTINGS, value, type_='bool')
487
499
488 def get_global_svn_branch_patterns(self):
500 def get_global_svn_branch_patterns(self):
489 return self.global_settings.get_ui_by_section(self.SVN_BRANCH_SECTION)
501 return self.global_settings.get_ui_by_section(self.SVN_BRANCH_SECTION)
490
502
491 @assert_repo_settings
503 @assert_repo_settings
492 def get_repo_svn_branch_patterns(self):
504 def get_repo_svn_branch_patterns(self):
493 return self.repo_settings.get_ui_by_section(self.SVN_BRANCH_SECTION)
505 return self.repo_settings.get_ui_by_section(self.SVN_BRANCH_SECTION)
494
506
495 def get_global_svn_tag_patterns(self):
507 def get_global_svn_tag_patterns(self):
496 return self.global_settings.get_ui_by_section(self.SVN_TAG_SECTION)
508 return self.global_settings.get_ui_by_section(self.SVN_TAG_SECTION)
497
509
498 @assert_repo_settings
510 @assert_repo_settings
499 def get_repo_svn_tag_patterns(self):
511 def get_repo_svn_tag_patterns(self):
500 return self.repo_settings.get_ui_by_section(self.SVN_TAG_SECTION)
512 return self.repo_settings.get_ui_by_section(self.SVN_TAG_SECTION)
501
513
502 def get_global_settings(self):
514 def get_global_settings(self):
503 return self._collect_all_settings(global_=True)
515 return self._collect_all_settings(global_=True)
504
516
505 @assert_repo_settings
517 @assert_repo_settings
506 def get_repo_settings(self):
518 def get_repo_settings(self):
507 return self._collect_all_settings(global_=False)
519 return self._collect_all_settings(global_=False)
508
520
509 @assert_repo_settings
521 @assert_repo_settings
510 def create_or_update_repo_settings(
522 def create_or_update_repo_settings(
511 self, data, inherit_global_settings=False):
523 self, data, inherit_global_settings=False):
512 from rhodecode.model.scm import ScmModel
524 from rhodecode.model.scm import ScmModel
513
525
514 self.inherit_global_settings = inherit_global_settings
526 self.inherit_global_settings = inherit_global_settings
515
527
516 repo = self.repo_settings.get_repo()
528 repo = self.repo_settings.get_repo()
517 if not inherit_global_settings:
529 if not inherit_global_settings:
518 if repo.repo_type == 'svn':
530 if repo.repo_type == 'svn':
519 self.create_repo_svn_settings(data)
531 self.create_repo_svn_settings(data)
520 else:
532 else:
521 self.create_or_update_repo_hook_settings(data)
533 self.create_or_update_repo_hook_settings(data)
522 self.create_or_update_repo_pr_settings(data)
534 self.create_or_update_repo_pr_settings(data)
523
535
524 if repo.repo_type == 'hg':
536 if repo.repo_type == 'hg':
525 self.create_or_update_repo_hg_settings(data)
537 self.create_or_update_repo_hg_settings(data)
526
538
527 if repo.repo_type == 'git':
539 if repo.repo_type == 'git':
528 self.create_or_update_repo_git_settings(data)
540 self.create_or_update_repo_git_settings(data)
529
541
530 ScmModel().mark_for_invalidation(repo.repo_name, delete=True)
542 ScmModel().mark_for_invalidation(repo.repo_name, delete=True)
531
543
532 @assert_repo_settings
544 @assert_repo_settings
533 def create_or_update_repo_hook_settings(self, data):
545 def create_or_update_repo_hook_settings(self, data):
534 for section, key in self.HOOKS_SETTINGS:
546 for section, key in self.HOOKS_SETTINGS:
535 data_key = self._get_form_ui_key(section, key)
547 data_key = self._get_form_ui_key(section, key)
536 if data_key not in data:
548 if data_key not in data:
537 raise ValueError(
549 raise ValueError(
538 'The given data does not contain {} key'.format(data_key))
550 'The given data does not contain {} key'.format(data_key))
539
551
540 active = data.get(data_key)
552 active = data.get(data_key)
541 repo_setting = self.repo_settings.get_ui_by_section_and_key(
553 repo_setting = self.repo_settings.get_ui_by_section_and_key(
542 section, key)
554 section, key)
543 if not repo_setting:
555 if not repo_setting:
544 global_setting = self.global_settings.\
556 global_setting = self.global_settings.\
545 get_ui_by_section_and_key(section, key)
557 get_ui_by_section_and_key(section, key)
546 self.repo_settings.create_ui_section_value(
558 self.repo_settings.create_ui_section_value(
547 section, global_setting.ui_value, key=key, active=active)
559 section, global_setting.ui_value, key=key, active=active)
548 else:
560 else:
549 repo_setting.ui_active = active
561 repo_setting.ui_active = active
550 Session().add(repo_setting)
562 Session().add(repo_setting)
551
563
552 def update_global_hook_settings(self, data):
564 def update_global_hook_settings(self, data):
553 for section, key in self.HOOKS_SETTINGS:
565 for section, key in self.HOOKS_SETTINGS:
554 data_key = self._get_form_ui_key(section, key)
566 data_key = self._get_form_ui_key(section, key)
555 if data_key not in data:
567 if data_key not in data:
556 raise ValueError(
568 raise ValueError(
557 'The given data does not contain {} key'.format(data_key))
569 'The given data does not contain {} key'.format(data_key))
558 active = data.get(data_key)
570 active = data.get(data_key)
559 repo_setting = self.global_settings.get_ui_by_section_and_key(
571 repo_setting = self.global_settings.get_ui_by_section_and_key(
560 section, key)
572 section, key)
561 repo_setting.ui_active = active
573 repo_setting.ui_active = active
562 Session().add(repo_setting)
574 Session().add(repo_setting)
563
575
564 @assert_repo_settings
576 @assert_repo_settings
565 def create_or_update_repo_pr_settings(self, data):
577 def create_or_update_repo_pr_settings(self, data):
566 return self._create_or_update_general_settings(
578 return self._create_or_update_general_settings(
567 self.repo_settings, data)
579 self.repo_settings, data)
568
580
569 def create_or_update_global_pr_settings(self, data):
581 def create_or_update_global_pr_settings(self, data):
570 return self._create_or_update_general_settings(
582 return self._create_or_update_general_settings(
571 self.global_settings, data)
583 self.global_settings, data)
572
584
573 @assert_repo_settings
585 @assert_repo_settings
574 def create_repo_svn_settings(self, data):
586 def create_repo_svn_settings(self, data):
575 return self._create_svn_settings(self.repo_settings, data)
587 return self._create_svn_settings(self.repo_settings, data)
576
588
589 def _set_evolution(self, settings, is_enabled):
590 if is_enabled:
591 # if evolve is active set evolution=all
592
593 self._create_or_update_ui(
594 settings, *('experimental', 'evolution'), value='all',
595 active=True)
596 self._create_or_update_ui(
597 settings, *('experimental', 'evolution.exchange'), value='yes',
598 active=True)
599 # if evolve is active set topics server support
600 self._create_or_update_ui(
601 settings, *('extensions', 'topic'), value='',
602 active=True)
603
604 else:
605 self._create_or_update_ui(
606 settings, *('experimental', 'evolution'), value='',
607 active=False)
608 self._create_or_update_ui(
609 settings, *('experimental', 'evolution.exchange'), value='no',
610 active=False)
611 self._create_or_update_ui(
612 settings, *('extensions', 'topic'), value='',
613 active=False)
614
577 @assert_repo_settings
615 @assert_repo_settings
578 def create_or_update_repo_hg_settings(self, data):
616 def create_or_update_repo_hg_settings(self, data):
579 largefiles, phases, evolve = \
617 largefiles, phases, evolve = \
580 self.HG_SETTINGS
618 self.HG_SETTINGS[:3]
581 largefiles_key, phases_key, evolve_key = \
619 largefiles_key, phases_key, evolve_key = \
582 self._get_settings_keys(self.HG_SETTINGS, data)
620 self._get_settings_keys(self.HG_SETTINGS[:3], data)
583
621
584 self._create_or_update_ui(
622 self._create_or_update_ui(
585 self.repo_settings, *largefiles, value='',
623 self.repo_settings, *largefiles, value='',
586 active=data[largefiles_key])
624 active=data[largefiles_key])
587 self._create_or_update_ui(
625 self._create_or_update_ui(
588 self.repo_settings, *evolve, value='',
626 self.repo_settings, *evolve, value='',
589 active=data[evolve_key])
627 active=data[evolve_key])
628 self._set_evolution(self.repo_settings, is_enabled=data[evolve_key])
629
590 self._create_or_update_ui(
630 self._create_or_update_ui(
591 self.repo_settings, *phases, value=safe_str(data[phases_key]))
631 self.repo_settings, *phases, value=safe_str(data[phases_key]))
592
632
593 def create_or_update_global_hg_settings(self, data):
633 def create_or_update_global_hg_settings(self, data):
594 largefiles, largefiles_store, phases, hgsubversion, evolve \
634 largefiles, largefiles_store, phases, hgsubversion, evolve \
595 = self.GLOBAL_HG_SETTINGS
635 = self.GLOBAL_HG_SETTINGS[:5]
596 largefiles_key, largefiles_store_key, phases_key, subversion_key, evolve_key \
636 largefiles_key, largefiles_store_key, phases_key, subversion_key, evolve_key \
597 = self._get_settings_keys(self.GLOBAL_HG_SETTINGS, data)
637 = self._get_settings_keys(self.GLOBAL_HG_SETTINGS[:5], data)
598
638
599 self._create_or_update_ui(
639 self._create_or_update_ui(
600 self.global_settings, *largefiles, value='',
640 self.global_settings, *largefiles, value='',
601 active=data[largefiles_key])
641 active=data[largefiles_key])
602 self._create_or_update_ui(
642 self._create_or_update_ui(
603 self.global_settings, *largefiles_store,
643 self.global_settings, *largefiles_store, value=data[largefiles_store_key])
604 value=data[largefiles_store_key])
605 self._create_or_update_ui(
644 self._create_or_update_ui(
606 self.global_settings, *phases, value=safe_str(data[phases_key]))
645 self.global_settings, *phases, value=safe_str(data[phases_key]))
607 self._create_or_update_ui(
646 self._create_or_update_ui(
608 self.global_settings, *hgsubversion, active=data[subversion_key])
647 self.global_settings, *hgsubversion, active=data[subversion_key])
609 self._create_or_update_ui(
648 self._create_or_update_ui(
610 self.global_settings, *evolve, value='',
649 self.global_settings, *evolve, value='',
611 active=data[evolve_key])
650 active=data[evolve_key])
651 self._set_evolution(self.global_settings, is_enabled=data[evolve_key])
612
652
613 def create_or_update_repo_git_settings(self, data):
653 def create_or_update_repo_git_settings(self, data):
614 # NOTE(marcink): # comma make unpack work properly
654 # NOTE(marcink): # comma makes unpack work properly
615 lfs_enabled, \
655 lfs_enabled, \
616 = self.GIT_SETTINGS
656 = self.GIT_SETTINGS
617
657
618 lfs_enabled_key, \
658 lfs_enabled_key, \
619 = self._get_settings_keys(self.GIT_SETTINGS, data)
659 = self._get_settings_keys(self.GIT_SETTINGS, data)
620
660
621 self._create_or_update_ui(
661 self._create_or_update_ui(
622 self.repo_settings, *lfs_enabled, value=data[lfs_enabled_key],
662 self.repo_settings, *lfs_enabled, value=data[lfs_enabled_key],
623 active=data[lfs_enabled_key])
663 active=data[lfs_enabled_key])
624
664
625 def create_or_update_global_git_settings(self, data):
665 def create_or_update_global_git_settings(self, data):
626 lfs_enabled, lfs_store_location \
666 lfs_enabled, lfs_store_location \
627 = self.GLOBAL_GIT_SETTINGS
667 = self.GLOBAL_GIT_SETTINGS
628 lfs_enabled_key, lfs_store_location_key \
668 lfs_enabled_key, lfs_store_location_key \
629 = self._get_settings_keys(self.GLOBAL_GIT_SETTINGS, data)
669 = self._get_settings_keys(self.GLOBAL_GIT_SETTINGS, data)
630
670
631 self._create_or_update_ui(
671 self._create_or_update_ui(
632 self.global_settings, *lfs_enabled, value=data[lfs_enabled_key],
672 self.global_settings, *lfs_enabled, value=data[lfs_enabled_key],
633 active=data[lfs_enabled_key])
673 active=data[lfs_enabled_key])
634 self._create_or_update_ui(
674 self._create_or_update_ui(
635 self.global_settings, *lfs_store_location,
675 self.global_settings, *lfs_store_location,
636 value=data[lfs_store_location_key])
676 value=data[lfs_store_location_key])
637
677
638 def create_or_update_global_svn_settings(self, data):
678 def create_or_update_global_svn_settings(self, data):
639 # branch/tags patterns
679 # branch/tags patterns
640 self._create_svn_settings(self.global_settings, data)
680 self._create_svn_settings(self.global_settings, data)
641
681
642 http_requests_enabled, http_server_url = self.GLOBAL_SVN_SETTINGS
682 http_requests_enabled, http_server_url = self.GLOBAL_SVN_SETTINGS
643 http_requests_enabled_key, http_server_url_key = self._get_settings_keys(
683 http_requests_enabled_key, http_server_url_key = self._get_settings_keys(
644 self.GLOBAL_SVN_SETTINGS, data)
684 self.GLOBAL_SVN_SETTINGS, data)
645
685
646 self._create_or_update_ui(
686 self._create_or_update_ui(
647 self.global_settings, *http_requests_enabled,
687 self.global_settings, *http_requests_enabled,
648 value=safe_str(data[http_requests_enabled_key]))
688 value=safe_str(data[http_requests_enabled_key]))
649 self._create_or_update_ui(
689 self._create_or_update_ui(
650 self.global_settings, *http_server_url,
690 self.global_settings, *http_server_url,
651 value=data[http_server_url_key])
691 value=data[http_server_url_key])
652
692
653 def update_global_ssl_setting(self, value):
693 def update_global_ssl_setting(self, value):
654 self._create_or_update_ui(
694 self._create_or_update_ui(
655 self.global_settings, *self.SSL_SETTING, value=value)
695 self.global_settings, *self.SSL_SETTING, value=value)
656
696
657 def update_global_path_setting(self, value):
697 def update_global_path_setting(self, value):
658 self._create_or_update_ui(
698 self._create_or_update_ui(
659 self.global_settings, *self.PATH_SETTING, value=value)
699 self.global_settings, *self.PATH_SETTING, value=value)
660
700
661 @assert_repo_settings
701 @assert_repo_settings
662 def delete_repo_svn_pattern(self, id_):
702 def delete_repo_svn_pattern(self, id_):
663 ui = self.repo_settings.UiDbModel.get(id_)
703 ui = self.repo_settings.UiDbModel.get(id_)
664 if ui and ui.repository.repo_name == self.repo_settings.repo:
704 if ui and ui.repository.repo_name == self.repo_settings.repo:
665 # only delete if it's the same repo as initialized settings
705 # only delete if it's the same repo as initialized settings
666 self.repo_settings.delete_ui(id_)
706 self.repo_settings.delete_ui(id_)
667 else:
707 else:
668 # raise error as if we wouldn't find this option
708 # raise error as if we wouldn't find this option
669 self.repo_settings.delete_ui(-1)
709 self.repo_settings.delete_ui(-1)
670
710
671 def delete_global_svn_pattern(self, id_):
711 def delete_global_svn_pattern(self, id_):
672 self.global_settings.delete_ui(id_)
712 self.global_settings.delete_ui(id_)
673
713
674 @assert_repo_settings
714 @assert_repo_settings
675 def get_repo_ui_settings(self, section=None, key=None):
715 def get_repo_ui_settings(self, section=None, key=None):
676 global_uis = self.global_settings.get_ui(section, key)
716 global_uis = self.global_settings.get_ui(section, key)
677 repo_uis = self.repo_settings.get_ui(section, key)
717 repo_uis = self.repo_settings.get_ui(section, key)
718
678 filtered_repo_uis = self._filter_ui_settings(repo_uis)
719 filtered_repo_uis = self._filter_ui_settings(repo_uis)
679 filtered_repo_uis_keys = [
720 filtered_repo_uis_keys = [
680 (s.section, s.key) for s in filtered_repo_uis]
721 (s.section, s.key) for s in filtered_repo_uis]
681
722
682 def _is_global_ui_filtered(ui):
723 def _is_global_ui_filtered(ui):
683 return (
724 return (
684 (ui.section, ui.key) in filtered_repo_uis_keys
725 (ui.section, ui.key) in filtered_repo_uis_keys
685 or ui.section in self._svn_sections)
726 or ui.section in self._svn_sections)
686
727
687 filtered_global_uis = [
728 filtered_global_uis = [
688 ui for ui in global_uis if not _is_global_ui_filtered(ui)]
729 ui for ui in global_uis if not _is_global_ui_filtered(ui)]
689
730
690 return filtered_global_uis + filtered_repo_uis
731 return filtered_global_uis + filtered_repo_uis
691
732
692 def get_global_ui_settings(self, section=None, key=None):
733 def get_global_ui_settings(self, section=None, key=None):
693 return self.global_settings.get_ui(section, key)
734 return self.global_settings.get_ui(section, key)
694
735
695 def get_ui_settings_as_config_obj(self, section=None, key=None):
736 def get_ui_settings_as_config_obj(self, section=None, key=None):
696 config = base.Config()
737 config = base.Config()
697
738
698 ui_settings = self.get_ui_settings(section=section, key=key)
739 ui_settings = self.get_ui_settings(section=section, key=key)
699
740
700 for entry in ui_settings:
741 for entry in ui_settings:
701 config.set(entry.section, entry.key, entry.value)
742 config.set(entry.section, entry.key, entry.value)
702
743
703 return config
744 return config
704
745
705 def get_ui_settings(self, section=None, key=None):
746 def get_ui_settings(self, section=None, key=None):
706 if not self.repo_settings or self.inherit_global_settings:
747 if not self.repo_settings or self.inherit_global_settings:
707 return self.get_global_ui_settings(section, key)
748 return self.get_global_ui_settings(section, key)
708 else:
749 else:
709 return self.get_repo_ui_settings(section, key)
750 return self.get_repo_ui_settings(section, key)
710
751
711 def get_svn_patterns(self, section=None):
752 def get_svn_patterns(self, section=None):
712 if not self.repo_settings:
753 if not self.repo_settings:
713 return self.get_global_ui_settings(section)
754 return self.get_global_ui_settings(section)
714 else:
755 else:
715 return self.get_repo_ui_settings(section)
756 return self.get_repo_ui_settings(section)
716
757
717 @assert_repo_settings
758 @assert_repo_settings
718 def get_repo_general_settings(self):
759 def get_repo_general_settings(self):
719 global_settings = self.global_settings.get_all_settings()
760 global_settings = self.global_settings.get_all_settings()
720 repo_settings = self.repo_settings.get_all_settings()
761 repo_settings = self.repo_settings.get_all_settings()
721 filtered_repo_settings = self._filter_general_settings(repo_settings)
762 filtered_repo_settings = self._filter_general_settings(repo_settings)
722 global_settings.update(filtered_repo_settings)
763 global_settings.update(filtered_repo_settings)
723 return global_settings
764 return global_settings
724
765
725 def get_global_general_settings(self):
766 def get_global_general_settings(self):
726 return self.global_settings.get_all_settings()
767 return self.global_settings.get_all_settings()
727
768
728 def get_general_settings(self):
769 def get_general_settings(self):
729 if not self.repo_settings or self.inherit_global_settings:
770 if not self.repo_settings or self.inherit_global_settings:
730 return self.get_global_general_settings()
771 return self.get_global_general_settings()
731 else:
772 else:
732 return self.get_repo_general_settings()
773 return self.get_repo_general_settings()
733
774
734 def get_repos_location(self):
775 def get_repos_location(self):
735 return self.global_settings.get_ui_by_key('/').ui_value
776 return self.global_settings.get_ui_by_key('/').ui_value
736
777
737 def _filter_ui_settings(self, settings):
778 def _filter_ui_settings(self, settings):
738 filtered_settings = [
779 filtered_settings = [
739 s for s in settings if self._should_keep_setting(s)]
780 s for s in settings if self._should_keep_setting(s)]
740 return filtered_settings
781 return filtered_settings
741
782
742 def _should_keep_setting(self, setting):
783 def _should_keep_setting(self, setting):
743 keep = (
784 keep = (
744 (setting.section, setting.key) in self._ui_settings or
785 (setting.section, setting.key) in self._ui_settings or
745 setting.section in self._svn_sections)
786 setting.section in self._svn_sections)
746 return keep
787 return keep
747
788
748 def _filter_general_settings(self, settings):
789 def _filter_general_settings(self, settings):
749 keys = ['rhodecode_{}'.format(key) for key in self.GENERAL_SETTINGS]
790 keys = ['rhodecode_{}'.format(key) for key in self.GENERAL_SETTINGS]
750 return {
791 return {
751 k: settings[k]
792 k: settings[k]
752 for k in settings if k in keys}
793 for k in settings if k in keys}
753
794
754 def _collect_all_settings(self, global_=False):
795 def _collect_all_settings(self, global_=False):
755 settings = self.global_settings if global_ else self.repo_settings
796 settings = self.global_settings if global_ else self.repo_settings
756 result = {}
797 result = {}
757
798
758 for section, key in self._ui_settings:
799 for section, key in self._ui_settings:
759 ui = settings.get_ui_by_section_and_key(section, key)
800 ui = settings.get_ui_by_section_and_key(section, key)
760 result_key = self._get_form_ui_key(section, key)
801 result_key = self._get_form_ui_key(section, key)
761
802
762 if ui:
803 if ui:
763 if section in ('hooks', 'extensions'):
804 if section in ('hooks', 'extensions'):
764 result[result_key] = ui.ui_active
805 result[result_key] = ui.ui_active
765 elif result_key in ['vcs_git_lfs_enabled']:
806 elif result_key in ['vcs_git_lfs_enabled']:
766 result[result_key] = ui.ui_active
807 result[result_key] = ui.ui_active
767 else:
808 else:
768 result[result_key] = ui.ui_value
809 result[result_key] = ui.ui_value
769
810
770 for name in self.GENERAL_SETTINGS:
811 for name in self.GENERAL_SETTINGS:
771 setting = settings.get_setting_by_name(name)
812 setting = settings.get_setting_by_name(name)
772 if setting:
813 if setting:
773 result_key = 'rhodecode_{}'.format(name)
814 result_key = 'rhodecode_{}'.format(name)
774 result[result_key] = setting.app_settings_value
815 result[result_key] = setting.app_settings_value
775
816
776 return result
817 return result
777
818
778 def _get_form_ui_key(self, section, key):
819 def _get_form_ui_key(self, section, key):
779 return '{section}_{key}'.format(
820 return '{section}_{key}'.format(
780 section=section, key=key.replace('.', '_'))
821 section=section, key=key.replace('.', '_'))
781
822
782 def _create_or_update_ui(
823 def _create_or_update_ui(
783 self, settings, section, key, value=None, active=None):
824 self, settings, section, key, value=None, active=None):
784 ui = settings.get_ui_by_section_and_key(section, key)
825 ui = settings.get_ui_by_section_and_key(section, key)
785 if not ui:
826 if not ui:
786 active = True if active is None else active
827 active = True if active is None else active
787 settings.create_ui_section_value(
828 settings.create_ui_section_value(
788 section, value, key=key, active=active)
829 section, value, key=key, active=active)
789 else:
830 else:
790 if active is not None:
831 if active is not None:
791 ui.ui_active = active
832 ui.ui_active = active
792 if value is not None:
833 if value is not None:
793 ui.ui_value = value
834 ui.ui_value = value
794 Session().add(ui)
835 Session().add(ui)
795
836
796 def _create_svn_settings(self, settings, data):
837 def _create_svn_settings(self, settings, data):
797 svn_settings = {
838 svn_settings = {
798 'new_svn_branch': self.SVN_BRANCH_SECTION,
839 'new_svn_branch': self.SVN_BRANCH_SECTION,
799 'new_svn_tag': self.SVN_TAG_SECTION
840 'new_svn_tag': self.SVN_TAG_SECTION
800 }
841 }
801 for key in svn_settings:
842 for key in svn_settings:
802 if data.get(key):
843 if data.get(key):
803 settings.create_ui_section_value(svn_settings[key], data[key])
844 settings.create_ui_section_value(svn_settings[key], data[key])
804
845
805 def _create_or_update_general_settings(self, settings, data):
846 def _create_or_update_general_settings(self, settings, data):
806 for name in self.GENERAL_SETTINGS:
847 for name in self.GENERAL_SETTINGS:
807 data_key = 'rhodecode_{}'.format(name)
848 data_key = 'rhodecode_{}'.format(name)
808 if data_key not in data:
849 if data_key not in data:
809 raise ValueError(
850 raise ValueError(
810 'The given data does not contain {} key'.format(data_key))
851 'The given data does not contain {} key'.format(data_key))
811 setting = settings.create_or_update_setting(
852 setting = settings.create_or_update_setting(
812 name, data[data_key], 'bool')
853 name, data[data_key], 'bool')
813 Session().add(setting)
854 Session().add(setting)
814
855
815 def _get_settings_keys(self, settings, data):
856 def _get_settings_keys(self, settings, data):
816 data_keys = [self._get_form_ui_key(*s) for s in settings]
857 data_keys = [self._get_form_ui_key(*s) for s in settings]
817 for data_key in data_keys:
858 for data_key in data_keys:
818 if data_key not in data:
859 if data_key not in data:
819 raise ValueError(
860 raise ValueError(
820 'The given data does not contain {} key'.format(data_key))
861 'The given data does not contain {} key'.format(data_key))
821 return data_keys
862 return data_keys
822
863
823 def create_largeobjects_dirs_if_needed(self, repo_store_path):
864 def create_largeobjects_dirs_if_needed(self, repo_store_path):
824 """
865 """
825 This is subscribed to the `pyramid.events.ApplicationCreated` event. It
866 This is subscribed to the `pyramid.events.ApplicationCreated` event. It
826 does a repository scan if enabled in the settings.
867 does a repository scan if enabled in the settings.
827 """
868 """
828
869
829 from rhodecode.lib.vcs.backends.hg import largefiles_store
870 from rhodecode.lib.vcs.backends.hg import largefiles_store
830 from rhodecode.lib.vcs.backends.git import lfs_store
871 from rhodecode.lib.vcs.backends.git import lfs_store
831
872
832 paths = [
873 paths = [
833 largefiles_store(repo_store_path),
874 largefiles_store(repo_store_path),
834 lfs_store(repo_store_path)]
875 lfs_store(repo_store_path)]
835
876
836 for path in paths:
877 for path in paths:
837 if os.path.isdir(path):
878 if os.path.isdir(path):
838 continue
879 continue
839 if os.path.isfile(path):
880 if os.path.isfile(path):
840 continue
881 continue
841 # not a file nor dir, we try to create it
882 # not a file nor dir, we try to create it
842 try:
883 try:
843 os.makedirs(path)
884 os.makedirs(path)
844 except Exception:
885 except Exception:
845 log.warning('Failed to create largefiles dir:%s', path)
886 log.warning('Failed to create largefiles dir:%s', path)
@@ -1,384 +1,384 b''
1 ## snippet for displaying vcs settings
1 ## snippet for displaying vcs settings
2 ## usage:
2 ## usage:
3 ## <%namespace name="vcss" file="/base/vcssettings.mako"/>
3 ## <%namespace name="vcss" file="/base/vcssettings.mako"/>
4 ## ${vcss.vcs_settings_fields()}
4 ## ${vcss.vcs_settings_fields()}
5
5
6 <%def name="vcs_settings_fields(suffix='', svn_branch_patterns=None, svn_tag_patterns=None, repo_type=None, display_globals=False, allow_repo_location_change=False, **kwargs)">
6 <%def name="vcs_settings_fields(suffix='', svn_branch_patterns=None, svn_tag_patterns=None, repo_type=None, display_globals=False, allow_repo_location_change=False, **kwargs)">
7 % if display_globals:
7 % if display_globals:
8 <div class="panel panel-default">
8 <div class="panel panel-default">
9 <div class="panel-heading" id="general">
9 <div class="panel-heading" id="general">
10 <h3 class="panel-title">${_('General')}<a class="permalink" href="#general"> ΒΆ</a></h3>
10 <h3 class="panel-title">${_('General')}<a class="permalink" href="#general"> ΒΆ</a></h3>
11 </div>
11 </div>
12 <div class="panel-body">
12 <div class="panel-body">
13 <div class="field">
13 <div class="field">
14 <div class="checkbox">
14 <div class="checkbox">
15 ${h.checkbox('web_push_ssl' + suffix, 'True')}
15 ${h.checkbox('web_push_ssl' + suffix, 'True')}
16 <label for="web_push_ssl${suffix}">${_('Require SSL for vcs operations')}</label>
16 <label for="web_push_ssl${suffix}">${_('Require SSL for vcs operations')}</label>
17 </div>
17 </div>
18 <div class="label">
18 <div class="label">
19 <span class="help-block">${_('Activate to set RhodeCode to require SSL for pushing or pulling. If SSL certificate is missing it will return a HTTP Error 406: Not Acceptable.')}</span>
19 <span class="help-block">${_('Activate to set RhodeCode to require SSL for pushing or pulling. If SSL certificate is missing it will return a HTTP Error 406: Not Acceptable.')}</span>
20 </div>
20 </div>
21 </div>
21 </div>
22 </div>
22 </div>
23 </div>
23 </div>
24 % endif
24 % endif
25
25
26 % if display_globals:
26 % if display_globals:
27 <div class="panel panel-default">
27 <div class="panel panel-default">
28 <div class="panel-heading" id="vcs-storage-options">
28 <div class="panel-heading" id="vcs-storage-options">
29 <h3 class="panel-title">${_('Main Storage Location')}<a class="permalink" href="#vcs-storage-options"> ΒΆ</a></h3>
29 <h3 class="panel-title">${_('Main Storage Location')}<a class="permalink" href="#vcs-storage-options"> ΒΆ</a></h3>
30 </div>
30 </div>
31 <div class="panel-body">
31 <div class="panel-body">
32 <div class="field">
32 <div class="field">
33 <div class="inputx locked_input">
33 <div class="inputx locked_input">
34 %if allow_repo_location_change:
34 %if allow_repo_location_change:
35 ${h.text('paths_root_path',size=59,readonly="readonly", class_="disabled")}
35 ${h.text('paths_root_path',size=59,readonly="readonly", class_="disabled")}
36 <span id="path_unlock" class="tooltip"
36 <span id="path_unlock" class="tooltip"
37 title="${h.tooltip(_('Click to unlock. You must restart RhodeCode in order to make this setting take effect.'))}">
37 title="${h.tooltip(_('Click to unlock. You must restart RhodeCode in order to make this setting take effect.'))}">
38 <div class="btn btn-default lock_input_button"><i id="path_unlock_icon" class="icon-lock"></i></div>
38 <div class="btn btn-default lock_input_button"><i id="path_unlock_icon" class="icon-lock"></i></div>
39 </span>
39 </span>
40 %else:
40 %else:
41 ${_('Repository location change is disabled. You can enable this by changing the `allow_repo_location_change` inside .ini file.')}
41 ${_('Repository location change is disabled. You can enable this by changing the `allow_repo_location_change` inside .ini file.')}
42 ## form still requires this but we cannot internally change it anyway
42 ## form still requires this but we cannot internally change it anyway
43 ${h.hidden('paths_root_path',size=30,readonly="readonly", class_="disabled")}
43 ${h.hidden('paths_root_path',size=30,readonly="readonly", class_="disabled")}
44 %endif
44 %endif
45 </div>
45 </div>
46 </div>
46 </div>
47 <div class="label">
47 <div class="label">
48 <span class="help-block">${_('Filesystem location where repositories should be stored. After changing this value a restart and rescan of the repository folder are required.')}</span>
48 <span class="help-block">${_('Filesystem location where repositories should be stored. After changing this value a restart and rescan of the repository folder are required.')}</span>
49 </div>
49 </div>
50 </div>
50 </div>
51 </div>
51 </div>
52 % endif
52 % endif
53
53
54 % if display_globals or repo_type in ['git', 'hg']:
54 % if display_globals or repo_type in ['git', 'hg']:
55 <div class="panel panel-default">
55 <div class="panel panel-default">
56 <div class="panel-heading" id="vcs-hooks-options">
56 <div class="panel-heading" id="vcs-hooks-options">
57 <h3 class="panel-title">${_('Internal Hooks')}<a class="permalink" href="#vcs-hooks-options"> ΒΆ</a></h3>
57 <h3 class="panel-title">${_('Internal Hooks')}<a class="permalink" href="#vcs-hooks-options"> ΒΆ</a></h3>
58 </div>
58 </div>
59 <div class="panel-body">
59 <div class="panel-body">
60 <div class="field">
60 <div class="field">
61 <div class="checkbox">
61 <div class="checkbox">
62 ${h.checkbox('hooks_changegroup_repo_size' + suffix, 'True', **kwargs)}
62 ${h.checkbox('hooks_changegroup_repo_size' + suffix, 'True', **kwargs)}
63 <label for="hooks_changegroup_repo_size${suffix}">${_('Show repository size after push')}</label>
63 <label for="hooks_changegroup_repo_size${suffix}">${_('Show repository size after push')}</label>
64 </div>
64 </div>
65
65
66 <div class="label">
66 <div class="label">
67 <span class="help-block">${_('Trigger a hook that calculates repository size after each push.')}</span>
67 <span class="help-block">${_('Trigger a hook that calculates repository size after each push.')}</span>
68 </div>
68 </div>
69 <div class="checkbox">
69 <div class="checkbox">
70 ${h.checkbox('hooks_changegroup_push_logger' + suffix, 'True', **kwargs)}
70 ${h.checkbox('hooks_changegroup_push_logger' + suffix, 'True', **kwargs)}
71 <label for="hooks_changegroup_push_logger${suffix}">${_('Execute pre/post push hooks')}</label>
71 <label for="hooks_changegroup_push_logger${suffix}">${_('Execute pre/post push hooks')}</label>
72 </div>
72 </div>
73 <div class="label">
73 <div class="label">
74 <span class="help-block">${_('Execute Built in pre/post push hooks. This also executes rcextensions hooks.')}</span>
74 <span class="help-block">${_('Execute Built in pre/post push hooks. This also executes rcextensions hooks.')}</span>
75 </div>
75 </div>
76 <div class="checkbox">
76 <div class="checkbox">
77 ${h.checkbox('hooks_outgoing_pull_logger' + suffix, 'True', **kwargs)}
77 ${h.checkbox('hooks_outgoing_pull_logger' + suffix, 'True', **kwargs)}
78 <label for="hooks_outgoing_pull_logger${suffix}">${_('Execute pre/post pull hooks')}</label>
78 <label for="hooks_outgoing_pull_logger${suffix}">${_('Execute pre/post pull hooks')}</label>
79 </div>
79 </div>
80 <div class="label">
80 <div class="label">
81 <span class="help-block">${_('Execute Built in pre/post pull hooks. This also executes rcextensions hooks.')}</span>
81 <span class="help-block">${_('Execute Built in pre/post pull hooks. This also executes rcextensions hooks.')}</span>
82 </div>
82 </div>
83 </div>
83 </div>
84 </div>
84 </div>
85 </div>
85 </div>
86 % endif
86 % endif
87
87
88 % if display_globals or repo_type in ['hg']:
88 % if display_globals or repo_type in ['hg']:
89 <div class="panel panel-default">
89 <div class="panel panel-default">
90 <div class="panel-heading" id="vcs-hg-options">
90 <div class="panel-heading" id="vcs-hg-options">
91 <h3 class="panel-title">${_('Mercurial Settings')}<a class="permalink" href="#vcs-hg-options"> ΒΆ</a></h3>
91 <h3 class="panel-title">${_('Mercurial Settings')}<a class="permalink" href="#vcs-hg-options"> ΒΆ</a></h3>
92 </div>
92 </div>
93 <div class="panel-body">
93 <div class="panel-body">
94 <div class="checkbox">
94 <div class="checkbox">
95 ${h.checkbox('extensions_largefiles' + suffix, 'True', **kwargs)}
95 ${h.checkbox('extensions_largefiles' + suffix, 'True', **kwargs)}
96 <label for="extensions_largefiles${suffix}">${_('Enable largefiles extension')}</label>
96 <label for="extensions_largefiles${suffix}">${_('Enable largefiles extension')}</label>
97 </div>
97 </div>
98 <div class="label">
98 <div class="label">
99 % if display_globals:
99 % if display_globals:
100 <span class="help-block">${_('Enable Largefiles extensions for all repositories.')}</span>
100 <span class="help-block">${_('Enable Largefiles extensions for all repositories.')}</span>
101 % else:
101 % else:
102 <span class="help-block">${_('Enable Largefiles extensions for this repository.')}</span>
102 <span class="help-block">${_('Enable Largefiles extensions for this repository.')}</span>
103 % endif
103 % endif
104 </div>
104 </div>
105
105
106 % if display_globals:
106 % if display_globals:
107 <div class="field">
107 <div class="field">
108 <div class="input">
108 <div class="input">
109 ${h.text('largefiles_usercache' + suffix, size=59)}
109 ${h.text('largefiles_usercache' + suffix, size=59)}
110 </div>
110 </div>
111 </div>
111 </div>
112 <div class="label">
112 <div class="label">
113 <span class="help-block">${_('Filesystem location where Mercurial largefile objects should be stored.')}</span>
113 <span class="help-block">${_('Filesystem location where Mercurial largefile objects should be stored.')}</span>
114 </div>
114 </div>
115 % endif
115 % endif
116
116
117 <div class="checkbox">
117 <div class="checkbox">
118 ${h.checkbox('phases_publish' + suffix, 'True', **kwargs)}
118 ${h.checkbox('phases_publish' + suffix, 'True', **kwargs)}
119 <label for="phases_publish${suffix}">${_('Set repositories as publishing') if display_globals else _('Set repository as publishing')}</label>
119 <label for="phases_publish${suffix}">${_('Set repositories as publishing') if display_globals else _('Set repository as publishing')}</label>
120 </div>
120 </div>
121 <div class="label">
121 <div class="label">
122 <span class="help-block">${_('When this is enabled all commits in the repository are seen as public commits by clients.')}</span>
122 <span class="help-block">${_('When this is enabled all commits in the repository are seen as public commits by clients.')}</span>
123 </div>
123 </div>
124 % if display_globals:
124 % if display_globals:
125 <div class="checkbox">
125 <div class="checkbox">
126 ${h.checkbox('extensions_hgsubversion' + suffix,'True')}
126 ${h.checkbox('extensions_hgsubversion' + suffix,'True')}
127 <label for="extensions_hgsubversion${suffix}">${_('Enable hgsubversion extension')}</label>
127 <label for="extensions_hgsubversion${suffix}">${_('Enable hgsubversion extension')}</label>
128 </div>
128 </div>
129 <div class="label">
129 <div class="label">
130 <span class="help-block">${_('Requires hgsubversion library to be installed. Allows cloning remote SVN repositories and migrates them to Mercurial type.')}</span>
130 <span class="help-block">${_('Requires hgsubversion library to be installed. Allows cloning remote SVN repositories and migrates them to Mercurial type.')}</span>
131 </div>
131 </div>
132 % endif
132 % endif
133
133
134 <div class="checkbox">
134 <div class="checkbox">
135 ${h.checkbox('extensions_evolve' + suffix, 'True', **kwargs)}
135 ${h.checkbox('extensions_evolve' + suffix, 'True', **kwargs)}
136 <label for="extensions_evolve${suffix}">${_('Enable evolve extension')}</label>
136 <label for="extensions_evolve${suffix}">${_('Enable Evolve and Topic extension')}</label>
137 </div>
137 </div>
138 <div class="label">
138 <div class="label">
139 % if display_globals:
139 % if display_globals:
140 <span class="help-block">${_('Enable evolve extension for all repositories.')}</span>
140 <span class="help-block">${_('Enable Evolve and Topic extensions for all repositories.')}</span>
141 % else:
141 % else:
142 <span class="help-block">${_('Enable evolve extension for this repository.')}</span>
142 <span class="help-block">${_('Enable Evolve and Topic extensions for this repository.')}</span>
143 % endif
143 % endif
144 </div>
144 </div>
145
145
146 </div>
146 </div>
147 </div>
147 </div>
148 % endif
148 % endif
149
149
150 % if display_globals or repo_type in ['git']:
150 % if display_globals or repo_type in ['git']:
151 <div class="panel panel-default">
151 <div class="panel panel-default">
152 <div class="panel-heading" id="vcs-git-options">
152 <div class="panel-heading" id="vcs-git-options">
153 <h3 class="panel-title">${_('Git Settings')}<a class="permalink" href="#vcs-git-options"> ΒΆ</a></h3>
153 <h3 class="panel-title">${_('Git Settings')}<a class="permalink" href="#vcs-git-options"> ΒΆ</a></h3>
154 </div>
154 </div>
155 <div class="panel-body">
155 <div class="panel-body">
156 <div class="checkbox">
156 <div class="checkbox">
157 ${h.checkbox('vcs_git_lfs_enabled' + suffix, 'True', **kwargs)}
157 ${h.checkbox('vcs_git_lfs_enabled' + suffix, 'True', **kwargs)}
158 <label for="vcs_git_lfs_enabled${suffix}">${_('Enable lfs extension')}</label>
158 <label for="vcs_git_lfs_enabled${suffix}">${_('Enable lfs extension')}</label>
159 </div>
159 </div>
160 <div class="label">
160 <div class="label">
161 % if display_globals:
161 % if display_globals:
162 <span class="help-block">${_('Enable lfs extensions for all repositories.')}</span>
162 <span class="help-block">${_('Enable lfs extensions for all repositories.')}</span>
163 % else:
163 % else:
164 <span class="help-block">${_('Enable lfs extensions for this repository.')}</span>
164 <span class="help-block">${_('Enable lfs extensions for this repository.')}</span>
165 % endif
165 % endif
166 </div>
166 </div>
167
167
168 % if display_globals:
168 % if display_globals:
169 <div class="field">
169 <div class="field">
170 <div class="input">
170 <div class="input">
171 ${h.text('vcs_git_lfs_store_location' + suffix, size=59)}
171 ${h.text('vcs_git_lfs_store_location' + suffix, size=59)}
172 </div>
172 </div>
173 </div>
173 </div>
174 <div class="label">
174 <div class="label">
175 <span class="help-block">${_('Filesystem location where Git lfs objects should be stored.')}</span>
175 <span class="help-block">${_('Filesystem location where Git lfs objects should be stored.')}</span>
176 </div>
176 </div>
177 % endif
177 % endif
178 </div>
178 </div>
179 </div>
179 </div>
180 % endif
180 % endif
181
181
182
182
183 % if display_globals:
183 % if display_globals:
184 <div class="panel panel-default">
184 <div class="panel panel-default">
185 <div class="panel-heading" id="vcs-global-svn-options">
185 <div class="panel-heading" id="vcs-global-svn-options">
186 <h3 class="panel-title">${_('Global Subversion Settings')}<a class="permalink" href="#vcs-global-svn-options"> ΒΆ</a></h3>
186 <h3 class="panel-title">${_('Global Subversion Settings')}<a class="permalink" href="#vcs-global-svn-options"> ΒΆ</a></h3>
187 </div>
187 </div>
188 <div class="panel-body">
188 <div class="panel-body">
189 <div class="field">
189 <div class="field">
190 <div class="checkbox">
190 <div class="checkbox">
191 ${h.checkbox('vcs_svn_proxy_http_requests_enabled' + suffix, 'True', **kwargs)}
191 ${h.checkbox('vcs_svn_proxy_http_requests_enabled' + suffix, 'True', **kwargs)}
192 <label for="vcs_svn_proxy_http_requests_enabled${suffix}">${_('Proxy subversion HTTP requests')}</label>
192 <label for="vcs_svn_proxy_http_requests_enabled${suffix}">${_('Proxy subversion HTTP requests')}</label>
193 </div>
193 </div>
194 <div class="label">
194 <div class="label">
195 <span class="help-block">
195 <span class="help-block">
196 ${_('Subversion HTTP Support. Enables communication with SVN over HTTP protocol.')}
196 ${_('Subversion HTTP Support. Enables communication with SVN over HTTP protocol.')}
197 <a href="${h.route_url('enterprise_svn_setup')}" target="_blank">${_('SVN Protocol setup Documentation')}</a>.
197 <a href="${h.route_url('enterprise_svn_setup')}" target="_blank">${_('SVN Protocol setup Documentation')}</a>.
198 </span>
198 </span>
199 </div>
199 </div>
200 </div>
200 </div>
201 <div class="field">
201 <div class="field">
202 <div class="label">
202 <div class="label">
203 <label for="vcs_svn_proxy_http_server_url">${_('Subversion HTTP Server URL')}</label><br/>
203 <label for="vcs_svn_proxy_http_server_url">${_('Subversion HTTP Server URL')}</label><br/>
204 </div>
204 </div>
205 <div class="input">
205 <div class="input">
206 ${h.text('vcs_svn_proxy_http_server_url',size=59)}
206 ${h.text('vcs_svn_proxy_http_server_url',size=59)}
207 % if c.svn_proxy_generate_config:
207 % if c.svn_proxy_generate_config:
208 <span class="buttons">
208 <span class="buttons">
209 <button class="btn btn-primary" id="vcs_svn_generate_cfg">${_('Generate Apache Config')}</button>
209 <button class="btn btn-primary" id="vcs_svn_generate_cfg">${_('Generate Apache Config')}</button>
210 </span>
210 </span>
211 % endif
211 % endif
212 </div>
212 </div>
213 </div>
213 </div>
214 </div>
214 </div>
215 </div>
215 </div>
216 % endif
216 % endif
217
217
218 % if display_globals or repo_type in ['svn']:
218 % if display_globals or repo_type in ['svn']:
219 <div class="panel panel-default">
219 <div class="panel panel-default">
220 <div class="panel-heading" id="vcs-svn-options">
220 <div class="panel-heading" id="vcs-svn-options">
221 <h3 class="panel-title">${_('Subversion Settings')}<a class="permalink" href="#vcs-svn-options"> ΒΆ</a></h3>
221 <h3 class="panel-title">${_('Subversion Settings')}<a class="permalink" href="#vcs-svn-options"> ΒΆ</a></h3>
222 </div>
222 </div>
223 <div class="panel-body">
223 <div class="panel-body">
224 <div class="field">
224 <div class="field">
225 <div class="content" >
225 <div class="content" >
226 <label>${_('Repository patterns')}</label><br/>
226 <label>${_('Repository patterns')}</label><br/>
227 </div>
227 </div>
228 </div>
228 </div>
229 <div class="label">
229 <div class="label">
230 <span class="help-block">${_('Patterns for identifying SVN branches and tags. For recursive search, use "*". Eg.: "/branches/*"')}</span>
230 <span class="help-block">${_('Patterns for identifying SVN branches and tags. For recursive search, use "*". Eg.: "/branches/*"')}</span>
231 </div>
231 </div>
232
232
233 <div class="field branch_patterns">
233 <div class="field branch_patterns">
234 <div class="input" >
234 <div class="input" >
235 <label>${_('Branches')}:</label><br/>
235 <label>${_('Branches')}:</label><br/>
236 </div>
236 </div>
237 % if svn_branch_patterns:
237 % if svn_branch_patterns:
238 % for branch in svn_branch_patterns:
238 % for branch in svn_branch_patterns:
239 <div class="input adjacent" id="${'id%s' % branch.ui_id}">
239 <div class="input adjacent" id="${'id%s' % branch.ui_id}">
240 ${h.hidden('branch_ui_key' + suffix, branch.ui_key)}
240 ${h.hidden('branch_ui_key' + suffix, branch.ui_key)}
241 ${h.text('branch_value_%d' % branch.ui_id + suffix, branch.ui_value, size=59, readonly="readonly", class_='disabled')}
241 ${h.text('branch_value_%d' % branch.ui_id + suffix, branch.ui_value, size=59, readonly="readonly", class_='disabled')}
242 % if kwargs.get('disabled') != 'disabled':
242 % if kwargs.get('disabled') != 'disabled':
243 <span class="btn btn-x" onclick="ajaxDeletePattern(${branch.ui_id},'${'id%s' % branch.ui_id}')">
243 <span class="btn btn-x" onclick="ajaxDeletePattern(${branch.ui_id},'${'id%s' % branch.ui_id}')">
244 ${_('Delete')}
244 ${_('Delete')}
245 </span>
245 </span>
246 % endif
246 % endif
247 </div>
247 </div>
248 % endfor
248 % endfor
249 %endif
249 %endif
250 </div>
250 </div>
251 % if kwargs.get('disabled') != 'disabled':
251 % if kwargs.get('disabled') != 'disabled':
252 <div class="field branch_patterns">
252 <div class="field branch_patterns">
253 <div class="input" >
253 <div class="input" >
254 ${h.text('new_svn_branch',size=59,placeholder='New branch pattern')}
254 ${h.text('new_svn_branch',size=59,placeholder='New branch pattern')}
255 </div>
255 </div>
256 </div>
256 </div>
257 % endif
257 % endif
258 <div class="field tag_patterns">
258 <div class="field tag_patterns">
259 <div class="input" >
259 <div class="input" >
260 <label>${_('Tags')}:</label><br/>
260 <label>${_('Tags')}:</label><br/>
261 </div>
261 </div>
262 % if svn_tag_patterns:
262 % if svn_tag_patterns:
263 % for tag in svn_tag_patterns:
263 % for tag in svn_tag_patterns:
264 <div class="input" id="${'id%s' % tag.ui_id + suffix}">
264 <div class="input" id="${'id%s' % tag.ui_id + suffix}">
265 ${h.hidden('tag_ui_key' + suffix, tag.ui_key)}
265 ${h.hidden('tag_ui_key' + suffix, tag.ui_key)}
266 ${h.text('tag_ui_value_new_%d' % tag.ui_id + suffix, tag.ui_value, size=59, readonly="readonly", class_='disabled tag_input')}
266 ${h.text('tag_ui_value_new_%d' % tag.ui_id + suffix, tag.ui_value, size=59, readonly="readonly", class_='disabled tag_input')}
267 % if kwargs.get('disabled') != 'disabled':
267 % if kwargs.get('disabled') != 'disabled':
268 <span class="btn btn-x" onclick="ajaxDeletePattern(${tag.ui_id},'${'id%s' % tag.ui_id}')">
268 <span class="btn btn-x" onclick="ajaxDeletePattern(${tag.ui_id},'${'id%s' % tag.ui_id}')">
269 ${_('Delete')}
269 ${_('Delete')}
270 </span>
270 </span>
271 %endif
271 %endif
272 </div>
272 </div>
273 % endfor
273 % endfor
274 % endif
274 % endif
275 </div>
275 </div>
276 % if kwargs.get('disabled') != 'disabled':
276 % if kwargs.get('disabled') != 'disabled':
277 <div class="field tag_patterns">
277 <div class="field tag_patterns">
278 <div class="input" >
278 <div class="input" >
279 ${h.text('new_svn_tag' + suffix, size=59, placeholder='New tag pattern')}
279 ${h.text('new_svn_tag' + suffix, size=59, placeholder='New tag pattern')}
280 </div>
280 </div>
281 </div>
281 </div>
282 %endif
282 %endif
283 </div>
283 </div>
284 </div>
284 </div>
285 % else:
285 % else:
286 ${h.hidden('new_svn_branch' + suffix, '')}
286 ${h.hidden('new_svn_branch' + suffix, '')}
287 ${h.hidden('new_svn_tag' + suffix, '')}
287 ${h.hidden('new_svn_tag' + suffix, '')}
288 % endif
288 % endif
289
289
290
290
291 % if display_globals or repo_type in ['hg', 'git']:
291 % if display_globals or repo_type in ['hg', 'git']:
292 <div class="panel panel-default">
292 <div class="panel panel-default">
293 <div class="panel-heading" id="vcs-pull-requests-options">
293 <div class="panel-heading" id="vcs-pull-requests-options">
294 <h3 class="panel-title">${_('Pull Request Settings')}<a class="permalink" href="#vcs-pull-requests-options"> ΒΆ</a></h3>
294 <h3 class="panel-title">${_('Pull Request Settings')}<a class="permalink" href="#vcs-pull-requests-options"> ΒΆ</a></h3>
295 </div>
295 </div>
296 <div class="panel-body">
296 <div class="panel-body">
297 <div class="checkbox">
297 <div class="checkbox">
298 ${h.checkbox('rhodecode_pr_merge_enabled' + suffix, 'True', **kwargs)}
298 ${h.checkbox('rhodecode_pr_merge_enabled' + suffix, 'True', **kwargs)}
299 <label for="rhodecode_pr_merge_enabled${suffix}">${_('Enable server-side merge for pull requests')}</label>
299 <label for="rhodecode_pr_merge_enabled${suffix}">${_('Enable server-side merge for pull requests')}</label>
300 </div>
300 </div>
301 <div class="label">
301 <div class="label">
302 <span class="help-block">${_('Note: when this feature is enabled, it only runs hooks defined in the rcextension package. Custom hooks added on the Admin -> Settings -> Hooks page will not be run when pull requests are automatically merged from the web interface.')}</span>
302 <span class="help-block">${_('Note: when this feature is enabled, it only runs hooks defined in the rcextension package. Custom hooks added on the Admin -> Settings -> Hooks page will not be run when pull requests are automatically merged from the web interface.')}</span>
303 </div>
303 </div>
304 <div class="checkbox">
304 <div class="checkbox">
305 ${h.checkbox('rhodecode_use_outdated_comments' + suffix, 'True', **kwargs)}
305 ${h.checkbox('rhodecode_use_outdated_comments' + suffix, 'True', **kwargs)}
306 <label for="rhodecode_use_outdated_comments${suffix}">${_('Invalidate and relocate inline comments during update')}</label>
306 <label for="rhodecode_use_outdated_comments${suffix}">${_('Invalidate and relocate inline comments during update')}</label>
307 </div>
307 </div>
308 <div class="label">
308 <div class="label">
309 <span class="help-block">${_('During the update of a pull request, the position of inline comments will be updated and outdated inline comments will be hidden.')}</span>
309 <span class="help-block">${_('During the update of a pull request, the position of inline comments will be updated and outdated inline comments will be hidden.')}</span>
310 </div>
310 </div>
311 </div>
311 </div>
312 </div>
312 </div>
313 % endif
313 % endif
314
314
315 % if display_globals or repo_type in ['hg', 'git', 'svn']:
315 % if display_globals or repo_type in ['hg', 'git', 'svn']:
316 <div class="panel panel-default">
316 <div class="panel panel-default">
317 <div class="panel-heading" id="vcs-pull-requests-options">
317 <div class="panel-heading" id="vcs-pull-requests-options">
318 <h3 class="panel-title">${_('Diff cache')}<a class="permalink" href="#vcs-pull-requests-options"> ΒΆ</a></h3>
318 <h3 class="panel-title">${_('Diff cache')}<a class="permalink" href="#vcs-pull-requests-options"> ΒΆ</a></h3>
319 </div>
319 </div>
320 <div class="panel-body">
320 <div class="panel-body">
321 <div class="checkbox">
321 <div class="checkbox">
322 ${h.checkbox('rhodecode_diff_cache' + suffix, 'True', **kwargs)}
322 ${h.checkbox('rhodecode_diff_cache' + suffix, 'True', **kwargs)}
323 <label for="rhodecode_diff_cache${suffix}">${_('Enable caching diffs for pull requests cache and commits')}</label>
323 <label for="rhodecode_diff_cache${suffix}">${_('Enable caching diffs for pull requests cache and commits')}</label>
324 </div>
324 </div>
325 </div>
325 </div>
326 </div>
326 </div>
327 % endif
327 % endif
328
328
329 % if display_globals or repo_type in ['hg',]:
329 % if display_globals or repo_type in ['hg',]:
330 <div class="panel panel-default">
330 <div class="panel panel-default">
331 <div class="panel-heading" id="vcs-pull-requests-options">
331 <div class="panel-heading" id="vcs-pull-requests-options">
332 <h3 class="panel-title">${_('Mercurial Pull Request Settings')}<a class="permalink" href="#vcs-hg-pull-requests-options"> ΒΆ</a></h3>
332 <h3 class="panel-title">${_('Mercurial Pull Request Settings')}<a class="permalink" href="#vcs-hg-pull-requests-options"> ΒΆ</a></h3>
333 </div>
333 </div>
334 <div class="panel-body">
334 <div class="panel-body">
335 ## Specific HG settings
335 ## Specific HG settings
336 <div class="checkbox">
336 <div class="checkbox">
337 ${h.checkbox('rhodecode_hg_use_rebase_for_merging' + suffix, 'True', **kwargs)}
337 ${h.checkbox('rhodecode_hg_use_rebase_for_merging' + suffix, 'True', **kwargs)}
338 <label for="rhodecode_hg_use_rebase_for_merging${suffix}">${_('Use rebase as merge strategy')}</label>
338 <label for="rhodecode_hg_use_rebase_for_merging${suffix}">${_('Use rebase as merge strategy')}</label>
339 </div>
339 </div>
340 <div class="label">
340 <div class="label">
341 <span class="help-block">${_('Use rebase instead of creating a merge commit when merging via web interface.')}</span>
341 <span class="help-block">${_('Use rebase instead of creating a merge commit when merging via web interface.')}</span>
342 </div>
342 </div>
343
343
344 <div class="checkbox">
344 <div class="checkbox">
345 ${h.checkbox('rhodecode_hg_close_branch_before_merging' + suffix, 'True', **kwargs)}
345 ${h.checkbox('rhodecode_hg_close_branch_before_merging' + suffix, 'True', **kwargs)}
346 <label for="rhodecode_hg_close_branch_before_merging{suffix}">${_('Close branch before merging it')}</label>
346 <label for="rhodecode_hg_close_branch_before_merging{suffix}">${_('Close branch before merging it')}</label>
347 </div>
347 </div>
348 <div class="label">
348 <div class="label">
349 <span class="help-block">${_('Close branch before merging it into destination branch. No effect when rebase strategy is use.')}</span>
349 <span class="help-block">${_('Close branch before merging it into destination branch. No effect when rebase strategy is use.')}</span>
350 </div>
350 </div>
351
351
352
352
353 </div>
353 </div>
354 </div>
354 </div>
355 % endif
355 % endif
356
356
357 ## DISABLED FOR GIT FOR NOW as the rebase/close is not supported yet
357 ## DISABLED FOR GIT FOR NOW as the rebase/close is not supported yet
358 ## % if display_globals or repo_type in ['git']:
358 ## % if display_globals or repo_type in ['git']:
359 ## <div class="panel panel-default">
359 ## <div class="panel panel-default">
360 ## <div class="panel-heading" id="vcs-pull-requests-options">
360 ## <div class="panel-heading" id="vcs-pull-requests-options">
361 ## <h3 class="panel-title">${_('Git Pull Request Settings')}<a class="permalink" href="#vcs-git-pull-requests-options"> ΒΆ</a></h3>
361 ## <h3 class="panel-title">${_('Git Pull Request Settings')}<a class="permalink" href="#vcs-git-pull-requests-options"> ΒΆ</a></h3>
362 ## </div>
362 ## </div>
363 ## <div class="panel-body">
363 ## <div class="panel-body">
364 ## <div class="checkbox">
364 ## <div class="checkbox">
365 ## ${h.checkbox('rhodecode_git_use_rebase_for_merging' + suffix, 'True', **kwargs)}
365 ## ${h.checkbox('rhodecode_git_use_rebase_for_merging' + suffix, 'True', **kwargs)}
366 ## <label for="rhodecode_git_use_rebase_for_merging${suffix}">${_('Use rebase as merge strategy')}</label>
366 ## <label for="rhodecode_git_use_rebase_for_merging${suffix}">${_('Use rebase as merge strategy')}</label>
367 ## </div>
367 ## </div>
368 ## <div class="label">
368 ## <div class="label">
369 ## <span class="help-block">${_('Use rebase instead of creating a merge commit when merging via web interface.')}</span>
369 ## <span class="help-block">${_('Use rebase instead of creating a merge commit when merging via web interface.')}</span>
370 ## </div>
370 ## </div>
371 ##
371 ##
372 ## <div class="checkbox">
372 ## <div class="checkbox">
373 ## ${h.checkbox('rhodecode_git_close_branch_before_merging' + suffix, 'True', **kwargs)}
373 ## ${h.checkbox('rhodecode_git_close_branch_before_merging' + suffix, 'True', **kwargs)}
374 ## <label for="rhodecode_git_close_branch_before_merging{suffix}">${_('Delete branch after merging it')}</label>
374 ## <label for="rhodecode_git_close_branch_before_merging{suffix}">${_('Delete branch after merging it')}</label>
375 ## </div>
375 ## </div>
376 ## <div class="label">
376 ## <div class="label">
377 ## <span class="help-block">${_('Delete branch after merging it into destination branch. No effect when rebase strategy is use.')}</span>
377 ## <span class="help-block">${_('Delete branch after merging it into destination branch. No effect when rebase strategy is use.')}</span>
378 ## </div>
378 ## </div>
379 ## </div>
379 ## </div>
380 ## </div>
380 ## </div>
381 ## % endif
381 ## % endif
382
382
383
383
384 </%def>
384 </%def>
@@ -1,1082 +1,1080 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2019 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 from rhodecode.lib.utils2 import str2bool
24 from rhodecode.lib.utils2 import str2bool
25 from rhodecode.model.meta import Session
25 from rhodecode.model.meta import Session
26 from rhodecode.model.settings import VcsSettingsModel, UiSetting
26 from rhodecode.model.settings import VcsSettingsModel, UiSetting
27
27
28
28
29 HOOKS_FORM_DATA = {
29 HOOKS_FORM_DATA = {
30 'hooks_changegroup_repo_size': True,
30 'hooks_changegroup_repo_size': True,
31 'hooks_changegroup_push_logger': True,
31 'hooks_changegroup_push_logger': True,
32 'hooks_outgoing_pull_logger': True
32 'hooks_outgoing_pull_logger': True
33 }
33 }
34
34
35 SVN_FORM_DATA = {
35 SVN_FORM_DATA = {
36 'new_svn_branch': 'test-branch',
36 'new_svn_branch': 'test-branch',
37 'new_svn_tag': 'test-tag'
37 'new_svn_tag': 'test-tag'
38 }
38 }
39
39
40 GENERAL_FORM_DATA = {
40 GENERAL_FORM_DATA = {
41 'rhodecode_pr_merge_enabled': True,
41 'rhodecode_pr_merge_enabled': True,
42 'rhodecode_use_outdated_comments': True,
42 'rhodecode_use_outdated_comments': True,
43 'rhodecode_hg_use_rebase_for_merging': True,
43 'rhodecode_hg_use_rebase_for_merging': True,
44 'rhodecode_hg_close_branch_before_merging': True,
44 'rhodecode_hg_close_branch_before_merging': True,
45 'rhodecode_git_use_rebase_for_merging': True,
45 'rhodecode_git_use_rebase_for_merging': True,
46 'rhodecode_git_close_branch_before_merging': True,
46 'rhodecode_git_close_branch_before_merging': True,
47 'rhodecode_diff_cache': True,
47 'rhodecode_diff_cache': True,
48 }
48 }
49
49
50
50
51 class TestInheritGlobalSettingsProperty(object):
51 class TestInheritGlobalSettingsProperty(object):
52 def test_get_raises_exception_when_repository_not_specified(self):
52 def test_get_raises_exception_when_repository_not_specified(self):
53 model = VcsSettingsModel()
53 model = VcsSettingsModel()
54 with pytest.raises(Exception) as exc_info:
54 with pytest.raises(Exception) as exc_info:
55 model.inherit_global_settings
55 model.inherit_global_settings
56 assert str(exc_info.value) == 'Repository is not specified'
56 assert str(exc_info.value) == 'Repository is not specified'
57
57
58 def test_true_is_returned_when_value_is_not_found(self, repo_stub):
58 def test_true_is_returned_when_value_is_not_found(self, repo_stub):
59 model = VcsSettingsModel(repo=repo_stub.repo_name)
59 model = VcsSettingsModel(repo=repo_stub.repo_name)
60 assert model.inherit_global_settings is True
60 assert model.inherit_global_settings is True
61
61
62 def test_value_is_returned(self, repo_stub, settings_util):
62 def test_value_is_returned(self, repo_stub, settings_util):
63 model = VcsSettingsModel(repo=repo_stub.repo_name)
63 model = VcsSettingsModel(repo=repo_stub.repo_name)
64 settings_util.create_repo_rhodecode_setting(
64 settings_util.create_repo_rhodecode_setting(
65 repo_stub, VcsSettingsModel.INHERIT_SETTINGS, False, 'bool')
65 repo_stub, VcsSettingsModel.INHERIT_SETTINGS, False, 'bool')
66 assert model.inherit_global_settings is False
66 assert model.inherit_global_settings is False
67
67
68 def test_value_is_set(self, repo_stub):
68 def test_value_is_set(self, repo_stub):
69 model = VcsSettingsModel(repo=repo_stub.repo_name)
69 model = VcsSettingsModel(repo=repo_stub.repo_name)
70 model.inherit_global_settings = False
70 model.inherit_global_settings = False
71 setting = model.repo_settings.get_setting_by_name(
71 setting = model.repo_settings.get_setting_by_name(
72 VcsSettingsModel.INHERIT_SETTINGS)
72 VcsSettingsModel.INHERIT_SETTINGS)
73 try:
73 try:
74 assert setting.app_settings_type == 'bool'
74 assert setting.app_settings_type == 'bool'
75 assert setting.app_settings_value is False
75 assert setting.app_settings_value is False
76 finally:
76 finally:
77 Session().delete(setting)
77 Session().delete(setting)
78 Session().commit()
78 Session().commit()
79
79
80 def test_set_raises_exception_when_repository_not_specified(self):
80 def test_set_raises_exception_when_repository_not_specified(self):
81 model = VcsSettingsModel()
81 model = VcsSettingsModel()
82 with pytest.raises(Exception) as exc_info:
82 with pytest.raises(Exception) as exc_info:
83 model.inherit_global_settings = False
83 model.inherit_global_settings = False
84 assert str(exc_info.value) == 'Repository is not specified'
84 assert str(exc_info.value) == 'Repository is not specified'
85
85
86
86
87 class TestVcsSettingsModel(object):
87 class TestVcsSettingsModel(object):
88 def test_global_svn_branch_patterns(self):
88 def test_global_svn_branch_patterns(self):
89 model = VcsSettingsModel()
89 model = VcsSettingsModel()
90 expected_result = {'test': 'test'}
90 expected_result = {'test': 'test'}
91 with mock.patch.object(model, 'global_settings') as settings_mock:
91 with mock.patch.object(model, 'global_settings') as settings_mock:
92 get_settings = settings_mock.get_ui_by_section
92 get_settings = settings_mock.get_ui_by_section
93 get_settings.return_value = expected_result
93 get_settings.return_value = expected_result
94 settings_mock.return_value = expected_result
94 settings_mock.return_value = expected_result
95 result = model.get_global_svn_branch_patterns()
95 result = model.get_global_svn_branch_patterns()
96
96
97 get_settings.assert_called_once_with(model.SVN_BRANCH_SECTION)
97 get_settings.assert_called_once_with(model.SVN_BRANCH_SECTION)
98 assert expected_result == result
98 assert expected_result == result
99
99
100 def test_repo_svn_branch_patterns(self):
100 def test_repo_svn_branch_patterns(self):
101 model = VcsSettingsModel()
101 model = VcsSettingsModel()
102 expected_result = {'test': 'test'}
102 expected_result = {'test': 'test'}
103 with mock.patch.object(model, 'repo_settings') as settings_mock:
103 with mock.patch.object(model, 'repo_settings') as settings_mock:
104 get_settings = settings_mock.get_ui_by_section
104 get_settings = settings_mock.get_ui_by_section
105 get_settings.return_value = expected_result
105 get_settings.return_value = expected_result
106 settings_mock.return_value = expected_result
106 settings_mock.return_value = expected_result
107 result = model.get_repo_svn_branch_patterns()
107 result = model.get_repo_svn_branch_patterns()
108
108
109 get_settings.assert_called_once_with(model.SVN_BRANCH_SECTION)
109 get_settings.assert_called_once_with(model.SVN_BRANCH_SECTION)
110 assert expected_result == result
110 assert expected_result == result
111
111
112 def test_repo_svn_branch_patterns_raises_exception_when_repo_is_not_set(
112 def test_repo_svn_branch_patterns_raises_exception_when_repo_is_not_set(
113 self):
113 self):
114 model = VcsSettingsModel()
114 model = VcsSettingsModel()
115 with pytest.raises(Exception) as exc_info:
115 with pytest.raises(Exception) as exc_info:
116 model.get_repo_svn_branch_patterns()
116 model.get_repo_svn_branch_patterns()
117 assert str(exc_info.value) == 'Repository is not specified'
117 assert str(exc_info.value) == 'Repository is not specified'
118
118
119 def test_global_svn_tag_patterns(self):
119 def test_global_svn_tag_patterns(self):
120 model = VcsSettingsModel()
120 model = VcsSettingsModel()
121 expected_result = {'test': 'test'}
121 expected_result = {'test': 'test'}
122 with mock.patch.object(model, 'global_settings') as settings_mock:
122 with mock.patch.object(model, 'global_settings') as settings_mock:
123 get_settings = settings_mock.get_ui_by_section
123 get_settings = settings_mock.get_ui_by_section
124 get_settings.return_value = expected_result
124 get_settings.return_value = expected_result
125 settings_mock.return_value = expected_result
125 settings_mock.return_value = expected_result
126 result = model.get_global_svn_tag_patterns()
126 result = model.get_global_svn_tag_patterns()
127
127
128 get_settings.assert_called_once_with(model.SVN_TAG_SECTION)
128 get_settings.assert_called_once_with(model.SVN_TAG_SECTION)
129 assert expected_result == result
129 assert expected_result == result
130
130
131 def test_repo_svn_tag_patterns(self):
131 def test_repo_svn_tag_patterns(self):
132 model = VcsSettingsModel()
132 model = VcsSettingsModel()
133 expected_result = {'test': 'test'}
133 expected_result = {'test': 'test'}
134 with mock.patch.object(model, 'repo_settings') as settings_mock:
134 with mock.patch.object(model, 'repo_settings') as settings_mock:
135 get_settings = settings_mock.get_ui_by_section
135 get_settings = settings_mock.get_ui_by_section
136 get_settings.return_value = expected_result
136 get_settings.return_value = expected_result
137 settings_mock.return_value = expected_result
137 settings_mock.return_value = expected_result
138 result = model.get_repo_svn_tag_patterns()
138 result = model.get_repo_svn_tag_patterns()
139
139
140 get_settings.assert_called_once_with(model.SVN_TAG_SECTION)
140 get_settings.assert_called_once_with(model.SVN_TAG_SECTION)
141 assert expected_result == result
141 assert expected_result == result
142
142
143 def test_repo_svn_tag_patterns_raises_exception_when_repo_is_not_set(self):
143 def test_repo_svn_tag_patterns_raises_exception_when_repo_is_not_set(self):
144 model = VcsSettingsModel()
144 model = VcsSettingsModel()
145 with pytest.raises(Exception) as exc_info:
145 with pytest.raises(Exception) as exc_info:
146 model.get_repo_svn_tag_patterns()
146 model.get_repo_svn_tag_patterns()
147 assert str(exc_info.value) == 'Repository is not specified'
147 assert str(exc_info.value) == 'Repository is not specified'
148
148
149 def test_get_global_settings(self):
149 def test_get_global_settings(self):
150 expected_result = {'test': 'test'}
150 expected_result = {'test': 'test'}
151 model = VcsSettingsModel()
151 model = VcsSettingsModel()
152 with mock.patch.object(model, '_collect_all_settings') as collect_mock:
152 with mock.patch.object(model, '_collect_all_settings') as collect_mock:
153 collect_mock.return_value = expected_result
153 collect_mock.return_value = expected_result
154 result = model.get_global_settings()
154 result = model.get_global_settings()
155
155
156 collect_mock.assert_called_once_with(global_=True)
156 collect_mock.assert_called_once_with(global_=True)
157 assert result == expected_result
157 assert result == expected_result
158
158
159 def test_get_repo_settings(self, repo_stub):
159 def test_get_repo_settings(self, repo_stub):
160 model = VcsSettingsModel(repo=repo_stub.repo_name)
160 model = VcsSettingsModel(repo=repo_stub.repo_name)
161 expected_result = {'test': 'test'}
161 expected_result = {'test': 'test'}
162 with mock.patch.object(model, '_collect_all_settings') as collect_mock:
162 with mock.patch.object(model, '_collect_all_settings') as collect_mock:
163 collect_mock.return_value = expected_result
163 collect_mock.return_value = expected_result
164 result = model.get_repo_settings()
164 result = model.get_repo_settings()
165
165
166 collect_mock.assert_called_once_with(global_=False)
166 collect_mock.assert_called_once_with(global_=False)
167 assert result == expected_result
167 assert result == expected_result
168
168
169 @pytest.mark.parametrize('settings, global_', [
169 @pytest.mark.parametrize('settings, global_', [
170 ('global_settings', True),
170 ('global_settings', True),
171 ('repo_settings', False)
171 ('repo_settings', False)
172 ])
172 ])
173 def test_collect_all_settings(self, settings, global_):
173 def test_collect_all_settings(self, settings, global_):
174 model = VcsSettingsModel()
174 model = VcsSettingsModel()
175 result_mock = self._mock_result()
175 result_mock = self._mock_result()
176
176
177 settings_patch = mock.patch.object(model, settings)
177 settings_patch = mock.patch.object(model, settings)
178 with settings_patch as settings_mock:
178 with settings_patch as settings_mock:
179 settings_mock.get_ui_by_section_and_key.return_value = result_mock
179 settings_mock.get_ui_by_section_and_key.return_value = result_mock
180 settings_mock.get_setting_by_name.return_value = result_mock
180 settings_mock.get_setting_by_name.return_value = result_mock
181 result = model._collect_all_settings(global_=global_)
181 result = model._collect_all_settings(global_=global_)
182
182
183 ui_settings = model.HG_SETTINGS + model.GIT_SETTINGS + model.HOOKS_SETTINGS
183 ui_settings = model.HG_SETTINGS + model.GIT_SETTINGS + model.HOOKS_SETTINGS
184 self._assert_get_settings_calls(
184 self._assert_get_settings_calls(
185 settings_mock, ui_settings, model.GENERAL_SETTINGS)
185 settings_mock, ui_settings, model.GENERAL_SETTINGS)
186 self._assert_collect_all_settings_result(
186 self._assert_collect_all_settings_result(
187 ui_settings, model.GENERAL_SETTINGS, result)
187 ui_settings, model.GENERAL_SETTINGS, result)
188
188
189 @pytest.mark.parametrize('settings, global_', [
189 @pytest.mark.parametrize('settings, global_', [
190 ('global_settings', True),
190 ('global_settings', True),
191 ('repo_settings', False)
191 ('repo_settings', False)
192 ])
192 ])
193 def test_collect_all_settings_without_empty_value(self, settings, global_):
193 def test_collect_all_settings_without_empty_value(self, settings, global_):
194 model = VcsSettingsModel()
194 model = VcsSettingsModel()
195
195
196 settings_patch = mock.patch.object(model, settings)
196 settings_patch = mock.patch.object(model, settings)
197 with settings_patch as settings_mock:
197 with settings_patch as settings_mock:
198 settings_mock.get_ui_by_section_and_key.return_value = None
198 settings_mock.get_ui_by_section_and_key.return_value = None
199 settings_mock.get_setting_by_name.return_value = None
199 settings_mock.get_setting_by_name.return_value = None
200 result = model._collect_all_settings(global_=global_)
200 result = model._collect_all_settings(global_=global_)
201
201
202 assert result == {}
202 assert result == {}
203
203
204 def _mock_result(self):
204 def _mock_result(self):
205 result_mock = mock.Mock()
205 result_mock = mock.Mock()
206 result_mock.ui_value = 'ui_value'
206 result_mock.ui_value = 'ui_value'
207 result_mock.ui_active = True
207 result_mock.ui_active = True
208 result_mock.app_settings_value = 'setting_value'
208 result_mock.app_settings_value = 'setting_value'
209 return result_mock
209 return result_mock
210
210
211 def _assert_get_settings_calls(
211 def _assert_get_settings_calls(
212 self, settings_mock, ui_settings, general_settings):
212 self, settings_mock, ui_settings, general_settings):
213 assert (
213 assert (
214 settings_mock.get_ui_by_section_and_key.call_count ==
214 settings_mock.get_ui_by_section_and_key.call_count ==
215 len(ui_settings))
215 len(ui_settings))
216 assert (
216 assert (
217 settings_mock.get_setting_by_name.call_count ==
217 settings_mock.get_setting_by_name.call_count ==
218 len(general_settings))
218 len(general_settings))
219
219
220 for section, key in ui_settings:
220 for section, key in ui_settings:
221 expected_call = mock.call(section, key)
221 expected_call = mock.call(section, key)
222 assert (
222 assert (
223 expected_call in
223 expected_call in
224 settings_mock.get_ui_by_section_and_key.call_args_list)
224 settings_mock.get_ui_by_section_and_key.call_args_list)
225
225
226 for name in general_settings:
226 for name in general_settings:
227 expected_call = mock.call(name)
227 expected_call = mock.call(name)
228 assert (
228 assert (
229 expected_call in
229 expected_call in
230 settings_mock.get_setting_by_name.call_args_list)
230 settings_mock.get_setting_by_name.call_args_list)
231
231
232 def _assert_collect_all_settings_result(
232 def _assert_collect_all_settings_result(
233 self, ui_settings, general_settings, result):
233 self, ui_settings, general_settings, result):
234 expected_result = {}
234 expected_result = {}
235 for section, key in ui_settings:
235 for section, key in ui_settings:
236 key = '{}_{}'.format(section, key.replace('.', '_'))
236 key = '{}_{}'.format(section, key.replace('.', '_'))
237
237
238 if section in ('extensions', 'hooks'):
238 if section in ('extensions', 'hooks'):
239 value = True
239 value = True
240 elif key in ['vcs_git_lfs_enabled']:
240 elif key in ['vcs_git_lfs_enabled']:
241 value = True
241 value = True
242 else:
242 else:
243 value = 'ui_value'
243 value = 'ui_value'
244 expected_result[key] = value
244 expected_result[key] = value
245
245
246 for name in general_settings:
246 for name in general_settings:
247 key = 'rhodecode_' + name
247 key = 'rhodecode_' + name
248 expected_result[key] = 'setting_value'
248 expected_result[key] = 'setting_value'
249
249
250 assert expected_result == result
250 assert expected_result == result
251
251
252
252
253 class TestCreateOrUpdateRepoHookSettings(object):
253 class TestCreateOrUpdateRepoHookSettings(object):
254 def test_create_when_no_repo_object_found(self, repo_stub):
254 def test_create_when_no_repo_object_found(self, repo_stub):
255 model = VcsSettingsModel(repo=repo_stub.repo_name)
255 model = VcsSettingsModel(repo=repo_stub.repo_name)
256
256
257 self._create_settings(model, HOOKS_FORM_DATA)
257 self._create_settings(model, HOOKS_FORM_DATA)
258
258
259 cleanup = []
259 cleanup = []
260 try:
260 try:
261 for section, key in model.HOOKS_SETTINGS:
261 for section, key in model.HOOKS_SETTINGS:
262 ui = model.repo_settings.get_ui_by_section_and_key(
262 ui = model.repo_settings.get_ui_by_section_and_key(
263 section, key)
263 section, key)
264 assert ui.ui_active is True
264 assert ui.ui_active is True
265 cleanup.append(ui)
265 cleanup.append(ui)
266 finally:
266 finally:
267 for ui in cleanup:
267 for ui in cleanup:
268 Session().delete(ui)
268 Session().delete(ui)
269 Session().commit()
269 Session().commit()
270
270
271 def test_create_raises_exception_when_data_incomplete(self, repo_stub):
271 def test_create_raises_exception_when_data_incomplete(self, repo_stub):
272 model = VcsSettingsModel(repo=repo_stub.repo_name)
272 model = VcsSettingsModel(repo=repo_stub.repo_name)
273
273
274 deleted_key = 'hooks_changegroup_repo_size'
274 deleted_key = 'hooks_changegroup_repo_size'
275 data = HOOKS_FORM_DATA.copy()
275 data = HOOKS_FORM_DATA.copy()
276 data.pop(deleted_key)
276 data.pop(deleted_key)
277
277
278 with pytest.raises(ValueError) as exc_info:
278 with pytest.raises(ValueError) as exc_info:
279 model.create_or_update_repo_hook_settings(data)
279 model.create_or_update_repo_hook_settings(data)
280 msg = 'The given data does not contain {} key'.format(deleted_key)
280 msg = 'The given data does not contain {} key'.format(deleted_key)
281 assert str(exc_info.value) == msg
281 assert str(exc_info.value) == msg
282
282
283 def test_update_when_repo_object_found(self, repo_stub, settings_util):
283 def test_update_when_repo_object_found(self, repo_stub, settings_util):
284 model = VcsSettingsModel(repo=repo_stub.repo_name)
284 model = VcsSettingsModel(repo=repo_stub.repo_name)
285 for section, key in model.HOOKS_SETTINGS:
285 for section, key in model.HOOKS_SETTINGS:
286 settings_util.create_repo_rhodecode_ui(
286 settings_util.create_repo_rhodecode_ui(
287 repo_stub, section, None, key=key, active=False)
287 repo_stub, section, None, key=key, active=False)
288 model.create_or_update_repo_hook_settings(HOOKS_FORM_DATA)
288 model.create_or_update_repo_hook_settings(HOOKS_FORM_DATA)
289 for section, key in model.HOOKS_SETTINGS:
289 for section, key in model.HOOKS_SETTINGS:
290 ui = model.repo_settings.get_ui_by_section_and_key(section, key)
290 ui = model.repo_settings.get_ui_by_section_and_key(section, key)
291 assert ui.ui_active is True
291 assert ui.ui_active is True
292
292
293 def _create_settings(self, model, data):
293 def _create_settings(self, model, data):
294 global_patch = mock.patch.object(model, 'global_settings')
294 global_patch = mock.patch.object(model, 'global_settings')
295 global_setting = mock.Mock()
295 global_setting = mock.Mock()
296 global_setting.ui_value = 'Test value'
296 global_setting.ui_value = 'Test value'
297 with global_patch as global_mock:
297 with global_patch as global_mock:
298 global_mock.get_ui_by_section_and_key.return_value = global_setting
298 global_mock.get_ui_by_section_and_key.return_value = global_setting
299 model.create_or_update_repo_hook_settings(HOOKS_FORM_DATA)
299 model.create_or_update_repo_hook_settings(HOOKS_FORM_DATA)
300
300
301
301
302 class TestUpdateGlobalHookSettings(object):
302 class TestUpdateGlobalHookSettings(object):
303 def test_update_raises_exception_when_data_incomplete(self):
303 def test_update_raises_exception_when_data_incomplete(self):
304 model = VcsSettingsModel()
304 model = VcsSettingsModel()
305
305
306 deleted_key = 'hooks_changegroup_repo_size'
306 deleted_key = 'hooks_changegroup_repo_size'
307 data = HOOKS_FORM_DATA.copy()
307 data = HOOKS_FORM_DATA.copy()
308 data.pop(deleted_key)
308 data.pop(deleted_key)
309
309
310 with pytest.raises(ValueError) as exc_info:
310 with pytest.raises(ValueError) as exc_info:
311 model.update_global_hook_settings(data)
311 model.update_global_hook_settings(data)
312 msg = 'The given data does not contain {} key'.format(deleted_key)
312 msg = 'The given data does not contain {} key'.format(deleted_key)
313 assert str(exc_info.value) == msg
313 assert str(exc_info.value) == msg
314
314
315 def test_update_global_hook_settings(self, settings_util):
315 def test_update_global_hook_settings(self, settings_util):
316 model = VcsSettingsModel()
316 model = VcsSettingsModel()
317 setting_mock = mock.MagicMock()
317 setting_mock = mock.MagicMock()
318 setting_mock.ui_active = False
318 setting_mock.ui_active = False
319 get_settings_patcher = mock.patch.object(
319 get_settings_patcher = mock.patch.object(
320 model.global_settings, 'get_ui_by_section_and_key',
320 model.global_settings, 'get_ui_by_section_and_key',
321 return_value=setting_mock)
321 return_value=setting_mock)
322 session_patcher = mock.patch('rhodecode.model.settings.Session')
322 session_patcher = mock.patch('rhodecode.model.settings.Session')
323 with get_settings_patcher as get_settings_mock, session_patcher:
323 with get_settings_patcher as get_settings_mock, session_patcher:
324 model.update_global_hook_settings(HOOKS_FORM_DATA)
324 model.update_global_hook_settings(HOOKS_FORM_DATA)
325 assert setting_mock.ui_active is True
325 assert setting_mock.ui_active is True
326 assert get_settings_mock.call_count == 3
326 assert get_settings_mock.call_count == 3
327
327
328
328
329 class TestCreateOrUpdateRepoGeneralSettings(object):
329 class TestCreateOrUpdateRepoGeneralSettings(object):
330 def test_calls_create_or_update_general_settings(self, repo_stub):
330 def test_calls_create_or_update_general_settings(self, repo_stub):
331 model = VcsSettingsModel(repo=repo_stub.repo_name)
331 model = VcsSettingsModel(repo=repo_stub.repo_name)
332 create_patch = mock.patch.object(
332 create_patch = mock.patch.object(
333 model, '_create_or_update_general_settings')
333 model, '_create_or_update_general_settings')
334 with create_patch as create_mock:
334 with create_patch as create_mock:
335 model.create_or_update_repo_pr_settings(GENERAL_FORM_DATA)
335 model.create_or_update_repo_pr_settings(GENERAL_FORM_DATA)
336 create_mock.assert_called_once_with(
336 create_mock.assert_called_once_with(
337 model.repo_settings, GENERAL_FORM_DATA)
337 model.repo_settings, GENERAL_FORM_DATA)
338
338
339 def test_raises_exception_when_repository_is_not_specified(self):
339 def test_raises_exception_when_repository_is_not_specified(self):
340 model = VcsSettingsModel()
340 model = VcsSettingsModel()
341 with pytest.raises(Exception) as exc_info:
341 with pytest.raises(Exception) as exc_info:
342 model.create_or_update_repo_pr_settings(GENERAL_FORM_DATA)
342 model.create_or_update_repo_pr_settings(GENERAL_FORM_DATA)
343 assert str(exc_info.value) == 'Repository is not specified'
343 assert str(exc_info.value) == 'Repository is not specified'
344
344
345
345
346 class TestCreateOrUpdatGlobalGeneralSettings(object):
346 class TestCreateOrUpdatGlobalGeneralSettings(object):
347 def test_calls_create_or_update_general_settings(self):
347 def test_calls_create_or_update_general_settings(self):
348 model = VcsSettingsModel()
348 model = VcsSettingsModel()
349 create_patch = mock.patch.object(
349 create_patch = mock.patch.object(
350 model, '_create_or_update_general_settings')
350 model, '_create_or_update_general_settings')
351 with create_patch as create_mock:
351 with create_patch as create_mock:
352 model.create_or_update_global_pr_settings(GENERAL_FORM_DATA)
352 model.create_or_update_global_pr_settings(GENERAL_FORM_DATA)
353 create_mock.assert_called_once_with(
353 create_mock.assert_called_once_with(
354 model.global_settings, GENERAL_FORM_DATA)
354 model.global_settings, GENERAL_FORM_DATA)
355
355
356
356
357 class TestCreateOrUpdateGeneralSettings(object):
357 class TestCreateOrUpdateGeneralSettings(object):
358 def test_create_when_no_repo_settings_found(self, repo_stub):
358 def test_create_when_no_repo_settings_found(self, repo_stub):
359 model = VcsSettingsModel(repo=repo_stub.repo_name)
359 model = VcsSettingsModel(repo=repo_stub.repo_name)
360 model._create_or_update_general_settings(
360 model._create_or_update_general_settings(
361 model.repo_settings, GENERAL_FORM_DATA)
361 model.repo_settings, GENERAL_FORM_DATA)
362
362
363 cleanup = []
363 cleanup = []
364 try:
364 try:
365 for name in model.GENERAL_SETTINGS:
365 for name in model.GENERAL_SETTINGS:
366 setting = model.repo_settings.get_setting_by_name(name)
366 setting = model.repo_settings.get_setting_by_name(name)
367 assert setting.app_settings_value is True
367 assert setting.app_settings_value is True
368 cleanup.append(setting)
368 cleanup.append(setting)
369 finally:
369 finally:
370 for setting in cleanup:
370 for setting in cleanup:
371 Session().delete(setting)
371 Session().delete(setting)
372 Session().commit()
372 Session().commit()
373
373
374 def test_create_raises_exception_when_data_incomplete(self, repo_stub):
374 def test_create_raises_exception_when_data_incomplete(self, repo_stub):
375 model = VcsSettingsModel(repo=repo_stub.repo_name)
375 model = VcsSettingsModel(repo=repo_stub.repo_name)
376
376
377 deleted_key = 'rhodecode_pr_merge_enabled'
377 deleted_key = 'rhodecode_pr_merge_enabled'
378 data = GENERAL_FORM_DATA.copy()
378 data = GENERAL_FORM_DATA.copy()
379 data.pop(deleted_key)
379 data.pop(deleted_key)
380
380
381 with pytest.raises(ValueError) as exc_info:
381 with pytest.raises(ValueError) as exc_info:
382 model._create_or_update_general_settings(model.repo_settings, data)
382 model._create_or_update_general_settings(model.repo_settings, data)
383
383
384 msg = 'The given data does not contain {} key'.format(deleted_key)
384 msg = 'The given data does not contain {} key'.format(deleted_key)
385 assert str(exc_info.value) == msg
385 assert str(exc_info.value) == msg
386
386
387 def test_update_when_repo_setting_found(self, repo_stub, settings_util):
387 def test_update_when_repo_setting_found(self, repo_stub, settings_util):
388 model = VcsSettingsModel(repo=repo_stub.repo_name)
388 model = VcsSettingsModel(repo=repo_stub.repo_name)
389 for name in model.GENERAL_SETTINGS:
389 for name in model.GENERAL_SETTINGS:
390 settings_util.create_repo_rhodecode_setting(
390 settings_util.create_repo_rhodecode_setting(
391 repo_stub, name, False, 'bool')
391 repo_stub, name, False, 'bool')
392
392
393 model._create_or_update_general_settings(
393 model._create_or_update_general_settings(
394 model.repo_settings, GENERAL_FORM_DATA)
394 model.repo_settings, GENERAL_FORM_DATA)
395
395
396 for name in model.GENERAL_SETTINGS:
396 for name in model.GENERAL_SETTINGS:
397 setting = model.repo_settings.get_setting_by_name(name)
397 setting = model.repo_settings.get_setting_by_name(name)
398 assert setting.app_settings_value is True
398 assert setting.app_settings_value is True
399
399
400
400
401 class TestCreateRepoSvnSettings(object):
401 class TestCreateRepoSvnSettings(object):
402 def test_calls_create_svn_settings(self, repo_stub):
402 def test_calls_create_svn_settings(self, repo_stub):
403 model = VcsSettingsModel(repo=repo_stub.repo_name)
403 model = VcsSettingsModel(repo=repo_stub.repo_name)
404 with mock.patch.object(model, '_create_svn_settings') as create_mock:
404 with mock.patch.object(model, '_create_svn_settings') as create_mock:
405 model.create_repo_svn_settings(SVN_FORM_DATA)
405 model.create_repo_svn_settings(SVN_FORM_DATA)
406 create_mock.assert_called_once_with(model.repo_settings, SVN_FORM_DATA)
406 create_mock.assert_called_once_with(model.repo_settings, SVN_FORM_DATA)
407
407
408 def test_raises_exception_when_repository_is_not_specified(self):
408 def test_raises_exception_when_repository_is_not_specified(self):
409 model = VcsSettingsModel()
409 model = VcsSettingsModel()
410 with pytest.raises(Exception) as exc_info:
410 with pytest.raises(Exception) as exc_info:
411 model.create_repo_svn_settings(SVN_FORM_DATA)
411 model.create_repo_svn_settings(SVN_FORM_DATA)
412 assert str(exc_info.value) == 'Repository is not specified'
412 assert str(exc_info.value) == 'Repository is not specified'
413
413
414
414
415 class TestCreateSvnSettings(object):
415 class TestCreateSvnSettings(object):
416 def test_create(self, repo_stub):
416 def test_create(self, repo_stub):
417 model = VcsSettingsModel(repo=repo_stub.repo_name)
417 model = VcsSettingsModel(repo=repo_stub.repo_name)
418 model._create_svn_settings(model.repo_settings, SVN_FORM_DATA)
418 model._create_svn_settings(model.repo_settings, SVN_FORM_DATA)
419 Session().commit()
419 Session().commit()
420
420
421 branch_ui = model.repo_settings.get_ui_by_section(
421 branch_ui = model.repo_settings.get_ui_by_section(
422 model.SVN_BRANCH_SECTION)
422 model.SVN_BRANCH_SECTION)
423 tag_ui = model.repo_settings.get_ui_by_section(
423 tag_ui = model.repo_settings.get_ui_by_section(
424 model.SVN_TAG_SECTION)
424 model.SVN_TAG_SECTION)
425
425
426 try:
426 try:
427 assert len(branch_ui) == 1
427 assert len(branch_ui) == 1
428 assert len(tag_ui) == 1
428 assert len(tag_ui) == 1
429 finally:
429 finally:
430 Session().delete(branch_ui[0])
430 Session().delete(branch_ui[0])
431 Session().delete(tag_ui[0])
431 Session().delete(tag_ui[0])
432 Session().commit()
432 Session().commit()
433
433
434 def test_create_tag(self, repo_stub):
434 def test_create_tag(self, repo_stub):
435 model = VcsSettingsModel(repo=repo_stub.repo_name)
435 model = VcsSettingsModel(repo=repo_stub.repo_name)
436 data = SVN_FORM_DATA.copy()
436 data = SVN_FORM_DATA.copy()
437 data.pop('new_svn_branch')
437 data.pop('new_svn_branch')
438 model._create_svn_settings(model.repo_settings, data)
438 model._create_svn_settings(model.repo_settings, data)
439 Session().commit()
439 Session().commit()
440
440
441 branch_ui = model.repo_settings.get_ui_by_section(
441 branch_ui = model.repo_settings.get_ui_by_section(
442 model.SVN_BRANCH_SECTION)
442 model.SVN_BRANCH_SECTION)
443 tag_ui = model.repo_settings.get_ui_by_section(
443 tag_ui = model.repo_settings.get_ui_by_section(
444 model.SVN_TAG_SECTION)
444 model.SVN_TAG_SECTION)
445
445
446 try:
446 try:
447 assert len(branch_ui) == 0
447 assert len(branch_ui) == 0
448 assert len(tag_ui) == 1
448 assert len(tag_ui) == 1
449 finally:
449 finally:
450 Session().delete(tag_ui[0])
450 Session().delete(tag_ui[0])
451 Session().commit()
451 Session().commit()
452
452
453 def test_create_nothing_when_no_svn_settings_specified(self, repo_stub):
453 def test_create_nothing_when_no_svn_settings_specified(self, repo_stub):
454 model = VcsSettingsModel(repo=repo_stub.repo_name)
454 model = VcsSettingsModel(repo=repo_stub.repo_name)
455 model._create_svn_settings(model.repo_settings, {})
455 model._create_svn_settings(model.repo_settings, {})
456 Session().commit()
456 Session().commit()
457
457
458 branch_ui = model.repo_settings.get_ui_by_section(
458 branch_ui = model.repo_settings.get_ui_by_section(
459 model.SVN_BRANCH_SECTION)
459 model.SVN_BRANCH_SECTION)
460 tag_ui = model.repo_settings.get_ui_by_section(
460 tag_ui = model.repo_settings.get_ui_by_section(
461 model.SVN_TAG_SECTION)
461 model.SVN_TAG_SECTION)
462
462
463 assert len(branch_ui) == 0
463 assert len(branch_ui) == 0
464 assert len(tag_ui) == 0
464 assert len(tag_ui) == 0
465
465
466 def test_create_nothing_when_empty_settings_specified(self, repo_stub):
466 def test_create_nothing_when_empty_settings_specified(self, repo_stub):
467 model = VcsSettingsModel(repo=repo_stub.repo_name)
467 model = VcsSettingsModel(repo=repo_stub.repo_name)
468 data = {
468 data = {
469 'new_svn_branch': '',
469 'new_svn_branch': '',
470 'new_svn_tag': ''
470 'new_svn_tag': ''
471 }
471 }
472 model._create_svn_settings(model.repo_settings, data)
472 model._create_svn_settings(model.repo_settings, data)
473 Session().commit()
473 Session().commit()
474
474
475 branch_ui = model.repo_settings.get_ui_by_section(
475 branch_ui = model.repo_settings.get_ui_by_section(
476 model.SVN_BRANCH_SECTION)
476 model.SVN_BRANCH_SECTION)
477 tag_ui = model.repo_settings.get_ui_by_section(
477 tag_ui = model.repo_settings.get_ui_by_section(
478 model.SVN_TAG_SECTION)
478 model.SVN_TAG_SECTION)
479
479
480 assert len(branch_ui) == 0
480 assert len(branch_ui) == 0
481 assert len(tag_ui) == 0
481 assert len(tag_ui) == 0
482
482
483
483
484 class TestCreateOrUpdateUi(object):
484 class TestCreateOrUpdateUi(object):
485 def test_create(self, repo_stub):
485 def test_create(self, repo_stub):
486 model = VcsSettingsModel(repo=repo_stub.repo_name)
486 model = VcsSettingsModel(repo=repo_stub.repo_name)
487 model._create_or_update_ui(
487 model._create_or_update_ui(
488 model.repo_settings, 'test-section', 'test-key', active=False,
488 model.repo_settings, 'test-section', 'test-key', active=False,
489 value='False')
489 value='False')
490 Session().commit()
490 Session().commit()
491
491
492 created_ui = model.repo_settings.get_ui_by_section_and_key(
492 created_ui = model.repo_settings.get_ui_by_section_and_key(
493 'test-section', 'test-key')
493 'test-section', 'test-key')
494
494
495 try:
495 try:
496 assert created_ui.ui_active is False
496 assert created_ui.ui_active is False
497 assert str2bool(created_ui.ui_value) is False
497 assert str2bool(created_ui.ui_value) is False
498 finally:
498 finally:
499 Session().delete(created_ui)
499 Session().delete(created_ui)
500 Session().commit()
500 Session().commit()
501
501
502 def test_update(self, repo_stub, settings_util):
502 def test_update(self, repo_stub, settings_util):
503 model = VcsSettingsModel(repo=repo_stub.repo_name)
503 model = VcsSettingsModel(repo=repo_stub.repo_name)
504
504 # care about only 3 first settings
505 largefiles, phases, evolve = model.HG_SETTINGS
505 largefiles, phases, evolve = model.HG_SETTINGS[:3]
506
506
507 section = 'test-section'
507 section = 'test-section'
508 key = 'test-key'
508 key = 'test-key'
509 settings_util.create_repo_rhodecode_ui(
509 settings_util.create_repo_rhodecode_ui(
510 repo_stub, section, 'True', key=key, active=True)
510 repo_stub, section, 'True', key=key, active=True)
511
511
512 model._create_or_update_ui(
512 model._create_or_update_ui(
513 model.repo_settings, section, key, active=False, value='False')
513 model.repo_settings, section, key, active=False, value='False')
514 Session().commit()
514 Session().commit()
515
515
516 created_ui = model.repo_settings.get_ui_by_section_and_key(
516 created_ui = model.repo_settings.get_ui_by_section_and_key(
517 section, key)
517 section, key)
518 assert created_ui.ui_active is False
518 assert created_ui.ui_active is False
519 assert str2bool(created_ui.ui_value) is False
519 assert str2bool(created_ui.ui_value) is False
520
520
521
521
522 class TestCreateOrUpdateRepoHgSettings(object):
522 class TestCreateOrUpdateRepoHgSettings(object):
523 FORM_DATA = {
523 FORM_DATA = {
524 'extensions_largefiles': False,
524 'extensions_largefiles': False,
525 'extensions_evolve': False,
525 'extensions_evolve': False,
526 'phases_publish': False
526 'phases_publish': False
527 }
527 }
528
528
529 def test_creates_repo_hg_settings_when_data_is_correct(self, repo_stub):
529 def test_creates_repo_hg_settings_when_data_is_correct(self, repo_stub):
530 model = VcsSettingsModel(repo=repo_stub.repo_name)
530 model = VcsSettingsModel(repo=repo_stub.repo_name)
531 with mock.patch.object(model, '_create_or_update_ui') as create_mock:
531 with mock.patch.object(model, '_create_or_update_ui') as create_mock:
532 model.create_or_update_repo_hg_settings(self.FORM_DATA)
532 model.create_or_update_repo_hg_settings(self.FORM_DATA)
533 expected_calls = [
533 expected_calls = [
534 mock.call(model.repo_settings, 'extensions', 'largefiles',
534 mock.call(model.repo_settings, 'extensions', 'largefiles', active=False, value=''),
535 active=False, value=''),
535 mock.call(model.repo_settings, 'extensions', 'evolve', active=False, value=''),
536 mock.call(model.repo_settings, 'extensions', 'evolve',
536 mock.call(model.repo_settings, 'experimental', 'evolution', active=False, value=''),
537 active=False, value=''),
537 mock.call(model.repo_settings, 'experimental', 'evolution.exchange', active=False, value='no'),
538 mock.call(model.repo_settings, 'extensions', 'topic', active=False, value=''),
538 mock.call(model.repo_settings, 'phases', 'publish', value='False'),
539 mock.call(model.repo_settings, 'phases', 'publish', value='False'),
539 ]
540 ]
540 assert expected_calls == create_mock.call_args_list
541 assert expected_calls == create_mock.call_args_list
541
542
542 @pytest.mark.parametrize('field_to_remove', FORM_DATA.keys())
543 @pytest.mark.parametrize('field_to_remove', FORM_DATA.keys())
543 def test_key_is_not_found(self, repo_stub, field_to_remove):
544 def test_key_is_not_found(self, repo_stub, field_to_remove):
544 model = VcsSettingsModel(repo=repo_stub.repo_name)
545 model = VcsSettingsModel(repo=repo_stub.repo_name)
545 data = self.FORM_DATA.copy()
546 data = self.FORM_DATA.copy()
546 data.pop(field_to_remove)
547 data.pop(field_to_remove)
547 with pytest.raises(ValueError) as exc_info:
548 with pytest.raises(ValueError) as exc_info:
548 model.create_or_update_repo_hg_settings(data)
549 model.create_or_update_repo_hg_settings(data)
549 expected_message = 'The given data does not contain {} key'.format(
550 expected_message = 'The given data does not contain {} key'.format(
550 field_to_remove)
551 field_to_remove)
551 assert str(exc_info.value) == expected_message
552 assert str(exc_info.value) == expected_message
552
553
553 def test_create_raises_exception_when_repository_not_specified(self):
554 def test_create_raises_exception_when_repository_not_specified(self):
554 model = VcsSettingsModel()
555 model = VcsSettingsModel()
555 with pytest.raises(Exception) as exc_info:
556 with pytest.raises(Exception) as exc_info:
556 model.create_or_update_repo_hg_settings(self.FORM_DATA)
557 model.create_or_update_repo_hg_settings(self.FORM_DATA)
557 assert str(exc_info.value) == 'Repository is not specified'
558 assert str(exc_info.value) == 'Repository is not specified'
558
559
559
560
560 class TestUpdateGlobalSslSetting(object):
561 class TestUpdateGlobalSslSetting(object):
561 def test_updates_global_hg_settings(self):
562 def test_updates_global_hg_settings(self):
562 model = VcsSettingsModel()
563 model = VcsSettingsModel()
563 with mock.patch.object(model, '_create_or_update_ui') as create_mock:
564 with mock.patch.object(model, '_create_or_update_ui') as create_mock:
564 model.update_global_ssl_setting('False')
565 model.update_global_ssl_setting('False')
565 create_mock.assert_called_once_with(
566 create_mock.assert_called_once_with(
566 model.global_settings, 'web', 'push_ssl', value='False')
567 model.global_settings, 'web', 'push_ssl', value='False')
567
568
568
569
569 class TestUpdateGlobalPathSetting(object):
570 class TestUpdateGlobalPathSetting(object):
570 def test_updates_global_path_settings(self):
571 def test_updates_global_path_settings(self):
571 model = VcsSettingsModel()
572 model = VcsSettingsModel()
572 with mock.patch.object(model, '_create_or_update_ui') as create_mock:
573 with mock.patch.object(model, '_create_or_update_ui') as create_mock:
573 model.update_global_path_setting('False')
574 model.update_global_path_setting('False')
574 create_mock.assert_called_once_with(
575 create_mock.assert_called_once_with(
575 model.global_settings, 'paths', '/', value='False')
576 model.global_settings, 'paths', '/', value='False')
576
577
577
578
578 class TestCreateOrUpdateGlobalHgSettings(object):
579 class TestCreateOrUpdateGlobalHgSettings(object):
579 FORM_DATA = {
580 FORM_DATA = {
580 'extensions_largefiles': False,
581 'extensions_largefiles': False,
581 'largefiles_usercache': '/example/largefiles-store',
582 'largefiles_usercache': '/example/largefiles-store',
582 'phases_publish': False,
583 'phases_publish': False,
583 'extensions_hgsubversion': False,
584 'extensions_hgsubversion': False,
584 'extensions_evolve': False
585 'extensions_evolve': False
585 }
586 }
586
587
587 def test_creates_repo_hg_settings_when_data_is_correct(self):
588 def test_creates_repo_hg_settings_when_data_is_correct(self):
588 model = VcsSettingsModel()
589 model = VcsSettingsModel()
589 with mock.patch.object(model, '_create_or_update_ui') as create_mock:
590 with mock.patch.object(model, '_create_or_update_ui') as create_mock:
590 model.create_or_update_global_hg_settings(self.FORM_DATA)
591 model.create_or_update_global_hg_settings(self.FORM_DATA)
591 expected_calls = [
592 expected_calls = [
592 mock.call(model.global_settings, 'extensions', 'largefiles',
593 mock.call(model.global_settings, 'extensions', 'largefiles', active=False, value=''),
593 active=False, value=''),
594 mock.call(model.global_settings, 'largefiles', 'usercache', value='/example/largefiles-store'),
594 mock.call(model.global_settings, 'largefiles', 'usercache',
595 mock.call(model.global_settings, 'phases', 'publish', value='False'),
595 value='/example/largefiles-store'),
596 mock.call(model.global_settings, 'extensions', 'hgsubversion', active=False),
596 mock.call(model.global_settings, 'phases', 'publish',
597 mock.call(model.global_settings, 'extensions', 'evolve', active=False, value=''),
597 value='False'),
598 mock.call(model.global_settings, 'experimental', 'evolution', active=False, value=''),
598 mock.call(model.global_settings, 'extensions', 'hgsubversion',
599 mock.call(model.global_settings, 'experimental', 'evolution.exchange', active=False, value='no'),
599 active=False),
600 mock.call(model.global_settings, 'extensions', 'topic', active=False, value=''),
600 mock.call(model.global_settings, 'extensions', 'evolve',
601 active=False, value='')
602 ]
601 ]
602
603 assert expected_calls == create_mock.call_args_list
603 assert expected_calls == create_mock.call_args_list
604
604
605 @pytest.mark.parametrize('field_to_remove', FORM_DATA.keys())
605 @pytest.mark.parametrize('field_to_remove', FORM_DATA.keys())
606 def test_key_is_not_found(self, repo_stub, field_to_remove):
606 def test_key_is_not_found(self, repo_stub, field_to_remove):
607 model = VcsSettingsModel(repo=repo_stub.repo_name)
607 model = VcsSettingsModel(repo=repo_stub.repo_name)
608 data = self.FORM_DATA.copy()
608 data = self.FORM_DATA.copy()
609 data.pop(field_to_remove)
609 data.pop(field_to_remove)
610 with pytest.raises(Exception) as exc_info:
610 with pytest.raises(Exception) as exc_info:
611 model.create_or_update_global_hg_settings(data)
611 model.create_or_update_global_hg_settings(data)
612 expected_message = 'The given data does not contain {} key'.format(
612 expected_message = 'The given data does not contain {} key'.format(
613 field_to_remove)
613 field_to_remove)
614 assert str(exc_info.value) == expected_message
614 assert str(exc_info.value) == expected_message
615
615
616
616
617 class TestCreateOrUpdateGlobalGitSettings(object):
617 class TestCreateOrUpdateGlobalGitSettings(object):
618 FORM_DATA = {
618 FORM_DATA = {
619 'vcs_git_lfs_enabled': False,
619 'vcs_git_lfs_enabled': False,
620 'vcs_git_lfs_store_location': '/example/lfs-store',
620 'vcs_git_lfs_store_location': '/example/lfs-store',
621 }
621 }
622
622
623 def test_creates_repo_hg_settings_when_data_is_correct(self):
623 def test_creates_repo_hg_settings_when_data_is_correct(self):
624 model = VcsSettingsModel()
624 model = VcsSettingsModel()
625 with mock.patch.object(model, '_create_or_update_ui') as create_mock:
625 with mock.patch.object(model, '_create_or_update_ui') as create_mock:
626 model.create_or_update_global_git_settings(self.FORM_DATA)
626 model.create_or_update_global_git_settings(self.FORM_DATA)
627 expected_calls = [
627 expected_calls = [
628 mock.call(model.global_settings, 'vcs_git_lfs', 'enabled',
628 mock.call(model.global_settings, 'vcs_git_lfs', 'enabled', active=False, value=False),
629 active=False, value=False),
629 mock.call(model.global_settings, 'vcs_git_lfs', 'store_location', value='/example/lfs-store'),
630 mock.call(model.global_settings, 'vcs_git_lfs', 'store_location',
631 value='/example/lfs-store'),
632 ]
630 ]
633 assert expected_calls == create_mock.call_args_list
631 assert expected_calls == create_mock.call_args_list
634
632
635
633
636 class TestDeleteRepoSvnPattern(object):
634 class TestDeleteRepoSvnPattern(object):
637 def test_success_when_repo_is_set(self, backend_svn, settings_util):
635 def test_success_when_repo_is_set(self, backend_svn, settings_util):
638 repo = backend_svn.create_repo()
636 repo = backend_svn.create_repo()
639 repo_name = repo.repo_name
637 repo_name = repo.repo_name
640
638
641 model = VcsSettingsModel(repo=repo_name)
639 model = VcsSettingsModel(repo=repo_name)
642 entry = settings_util.create_repo_rhodecode_ui(
640 entry = settings_util.create_repo_rhodecode_ui(
643 repo, VcsSettingsModel.SVN_BRANCH_SECTION, 'svn-branch')
641 repo, VcsSettingsModel.SVN_BRANCH_SECTION, 'svn-branch')
644 Session().commit()
642 Session().commit()
645
643
646 model.delete_repo_svn_pattern(entry.ui_id)
644 model.delete_repo_svn_pattern(entry.ui_id)
647
645
648 def test_fail_when_delete_id_from_other_repo(self, backend_svn):
646 def test_fail_when_delete_id_from_other_repo(self, backend_svn):
649 repo_name = backend_svn.repo_name
647 repo_name = backend_svn.repo_name
650 model = VcsSettingsModel(repo=repo_name)
648 model = VcsSettingsModel(repo=repo_name)
651 delete_ui_patch = mock.patch.object(model.repo_settings, 'delete_ui')
649 delete_ui_patch = mock.patch.object(model.repo_settings, 'delete_ui')
652 with delete_ui_patch as delete_ui_mock:
650 with delete_ui_patch as delete_ui_mock:
653 model.delete_repo_svn_pattern(123)
651 model.delete_repo_svn_pattern(123)
654 delete_ui_mock.assert_called_once_with(-1)
652 delete_ui_mock.assert_called_once_with(-1)
655
653
656 def test_raises_exception_when_repository_is_not_specified(self):
654 def test_raises_exception_when_repository_is_not_specified(self):
657 model = VcsSettingsModel()
655 model = VcsSettingsModel()
658 with pytest.raises(Exception) as exc_info:
656 with pytest.raises(Exception) as exc_info:
659 model.delete_repo_svn_pattern(123)
657 model.delete_repo_svn_pattern(123)
660 assert str(exc_info.value) == 'Repository is not specified'
658 assert str(exc_info.value) == 'Repository is not specified'
661
659
662
660
663 class TestDeleteGlobalSvnPattern(object):
661 class TestDeleteGlobalSvnPattern(object):
664 def test_delete_global_svn_pattern_calls_delete_ui(self):
662 def test_delete_global_svn_pattern_calls_delete_ui(self):
665 model = VcsSettingsModel()
663 model = VcsSettingsModel()
666 delete_ui_patch = mock.patch.object(model.global_settings, 'delete_ui')
664 delete_ui_patch = mock.patch.object(model.global_settings, 'delete_ui')
667 with delete_ui_patch as delete_ui_mock:
665 with delete_ui_patch as delete_ui_mock:
668 model.delete_global_svn_pattern(123)
666 model.delete_global_svn_pattern(123)
669 delete_ui_mock.assert_called_once_with(123)
667 delete_ui_mock.assert_called_once_with(123)
670
668
671
669
672 class TestFilterUiSettings(object):
670 class TestFilterUiSettings(object):
673 def test_settings_are_filtered(self):
671 def test_settings_are_filtered(self):
674 model = VcsSettingsModel()
672 model = VcsSettingsModel()
675 repo_settings = [
673 repo_settings = [
676 UiSetting('extensions', 'largefiles', '', True),
674 UiSetting('extensions', 'largefiles', '', True),
677 UiSetting('phases', 'publish', 'True', True),
675 UiSetting('phases', 'publish', 'True', True),
678 UiSetting('hooks', 'changegroup.repo_size', 'hook', True),
676 UiSetting('hooks', 'changegroup.repo_size', 'hook', True),
679 UiSetting('hooks', 'changegroup.push_logger', 'hook', True),
677 UiSetting('hooks', 'changegroup.push_logger', 'hook', True),
680 UiSetting('hooks', 'outgoing.pull_logger', 'hook', True),
678 UiSetting('hooks', 'outgoing.pull_logger', 'hook', True),
681 UiSetting(
679 UiSetting(
682 'vcs_svn_branch', '84223c972204fa545ca1b22dac7bef5b68d7442d',
680 'vcs_svn_branch', '84223c972204fa545ca1b22dac7bef5b68d7442d',
683 'test_branch', True),
681 'test_branch', True),
684 UiSetting(
682 UiSetting(
685 'vcs_svn_tag', '84229c972204fa545ca1b22dac7bef5b68d7442d',
683 'vcs_svn_tag', '84229c972204fa545ca1b22dac7bef5b68d7442d',
686 'test_tag', True),
684 'test_tag', True),
687 ]
685 ]
688 non_repo_settings = [
686 non_repo_settings = [
689 UiSetting('largefiles', 'usercache', '/example/largefiles-store', True),
687 UiSetting('largefiles', 'usercache', '/example/largefiles-store', True),
690 UiSetting('test', 'outgoing.pull_logger', 'hook', True),
688 UiSetting('test', 'outgoing.pull_logger', 'hook', True),
691 UiSetting('hooks', 'test2', 'hook', True),
689 UiSetting('hooks', 'test2', 'hook', True),
692 UiSetting(
690 UiSetting(
693 'vcs_svn_repo', '84229c972204fa545ca1b22dac7bef5b68d7442d',
691 'vcs_svn_repo', '84229c972204fa545ca1b22dac7bef5b68d7442d',
694 'test_tag', True),
692 'test_tag', True),
695 ]
693 ]
696 settings = repo_settings + non_repo_settings
694 settings = repo_settings + non_repo_settings
697 filtered_settings = model._filter_ui_settings(settings)
695 filtered_settings = model._filter_ui_settings(settings)
698 assert sorted(filtered_settings) == sorted(repo_settings)
696 assert sorted(filtered_settings) == sorted(repo_settings)
699
697
700
698
701 class TestFilterGeneralSettings(object):
699 class TestFilterGeneralSettings(object):
702 def test_settings_are_filtered(self):
700 def test_settings_are_filtered(self):
703 model = VcsSettingsModel()
701 model = VcsSettingsModel()
704 settings = {
702 settings = {
705 'rhodecode_abcde': 'value1',
703 'rhodecode_abcde': 'value1',
706 'rhodecode_vwxyz': 'value2',
704 'rhodecode_vwxyz': 'value2',
707 }
705 }
708 general_settings = {
706 general_settings = {
709 'rhodecode_{}'.format(key): 'value'
707 'rhodecode_{}'.format(key): 'value'
710 for key in VcsSettingsModel.GENERAL_SETTINGS
708 for key in VcsSettingsModel.GENERAL_SETTINGS
711 }
709 }
712 settings.update(general_settings)
710 settings.update(general_settings)
713
711
714 filtered_settings = model._filter_general_settings(general_settings)
712 filtered_settings = model._filter_general_settings(general_settings)
715 assert sorted(filtered_settings) == sorted(general_settings)
713 assert sorted(filtered_settings) == sorted(general_settings)
716
714
717
715
718 class TestGetRepoUiSettings(object):
716 class TestGetRepoUiSettings(object):
719 def test_global_uis_are_returned_when_no_repo_uis_found(
717 def test_global_uis_are_returned_when_no_repo_uis_found(
720 self, repo_stub):
718 self, repo_stub):
721 model = VcsSettingsModel(repo=repo_stub.repo_name)
719 model = VcsSettingsModel(repo=repo_stub.repo_name)
722 result = model.get_repo_ui_settings()
720 result = model.get_repo_ui_settings()
723 svn_sections = (
721 svn_sections = (
724 VcsSettingsModel.SVN_TAG_SECTION,
722 VcsSettingsModel.SVN_TAG_SECTION,
725 VcsSettingsModel.SVN_BRANCH_SECTION)
723 VcsSettingsModel.SVN_BRANCH_SECTION)
726 expected_result = [
724 expected_result = [
727 s for s in model.global_settings.get_ui()
725 s for s in model.global_settings.get_ui()
728 if s.section not in svn_sections]
726 if s.section not in svn_sections]
729 assert sorted(result) == sorted(expected_result)
727 assert sorted(result) == sorted(expected_result)
730
728
731 def test_repo_uis_are_overriding_global_uis(
729 def test_repo_uis_are_overriding_global_uis(
732 self, repo_stub, settings_util):
730 self, repo_stub, settings_util):
733 for section, key in VcsSettingsModel.HOOKS_SETTINGS:
731 for section, key in VcsSettingsModel.HOOKS_SETTINGS:
734 settings_util.create_repo_rhodecode_ui(
732 settings_util.create_repo_rhodecode_ui(
735 repo_stub, section, 'repo', key=key, active=False)
733 repo_stub, section, 'repo', key=key, active=False)
736 model = VcsSettingsModel(repo=repo_stub.repo_name)
734 model = VcsSettingsModel(repo=repo_stub.repo_name)
737 result = model.get_repo_ui_settings()
735 result = model.get_repo_ui_settings()
738 for setting in result:
736 for setting in result:
739 locator = (setting.section, setting.key)
737 locator = (setting.section, setting.key)
740 if locator in VcsSettingsModel.HOOKS_SETTINGS:
738 if locator in VcsSettingsModel.HOOKS_SETTINGS:
741 assert setting.value == 'repo'
739 assert setting.value == 'repo'
742
740
743 assert setting.active is False
741 assert setting.active is False
744
742
745 def test_global_svn_patterns_are_not_in_list(
743 def test_global_svn_patterns_are_not_in_list(
746 self, repo_stub, settings_util):
744 self, repo_stub, settings_util):
747 svn_sections = (
745 svn_sections = (
748 VcsSettingsModel.SVN_TAG_SECTION,
746 VcsSettingsModel.SVN_TAG_SECTION,
749 VcsSettingsModel.SVN_BRANCH_SECTION)
747 VcsSettingsModel.SVN_BRANCH_SECTION)
750 for section in svn_sections:
748 for section in svn_sections:
751 settings_util.create_rhodecode_ui(
749 settings_util.create_rhodecode_ui(
752 section, 'repo', key='deadbeef' + section, active=False)
750 section, 'repo', key='deadbeef' + section, active=False)
753 model = VcsSettingsModel(repo=repo_stub.repo_name)
751 model = VcsSettingsModel(repo=repo_stub.repo_name)
754 result = model.get_repo_ui_settings()
752 result = model.get_repo_ui_settings()
755 for setting in result:
753 for setting in result:
756 assert setting.section not in svn_sections
754 assert setting.section not in svn_sections
757
755
758 def test_repo_uis_filtered_by_section_are_returned(
756 def test_repo_uis_filtered_by_section_are_returned(
759 self, repo_stub, settings_util):
757 self, repo_stub, settings_util):
760 for section, key in VcsSettingsModel.HOOKS_SETTINGS:
758 for section, key in VcsSettingsModel.HOOKS_SETTINGS:
761 settings_util.create_repo_rhodecode_ui(
759 settings_util.create_repo_rhodecode_ui(
762 repo_stub, section, 'repo', key=key, active=False)
760 repo_stub, section, 'repo', key=key, active=False)
763 model = VcsSettingsModel(repo=repo_stub.repo_name)
761 model = VcsSettingsModel(repo=repo_stub.repo_name)
764 section, key = VcsSettingsModel.HOOKS_SETTINGS[0]
762 section, key = VcsSettingsModel.HOOKS_SETTINGS[0]
765 result = model.get_repo_ui_settings(section=section)
763 result = model.get_repo_ui_settings(section=section)
766 for setting in result:
764 for setting in result:
767 assert setting.section == section
765 assert setting.section == section
768
766
769 def test_repo_uis_filtered_by_key_are_returned(
767 def test_repo_uis_filtered_by_key_are_returned(
770 self, repo_stub, settings_util):
768 self, repo_stub, settings_util):
771 for section, key in VcsSettingsModel.HOOKS_SETTINGS:
769 for section, key in VcsSettingsModel.HOOKS_SETTINGS:
772 settings_util.create_repo_rhodecode_ui(
770 settings_util.create_repo_rhodecode_ui(
773 repo_stub, section, 'repo', key=key, active=False)
771 repo_stub, section, 'repo', key=key, active=False)
774 model = VcsSettingsModel(repo=repo_stub.repo_name)
772 model = VcsSettingsModel(repo=repo_stub.repo_name)
775 section, key = VcsSettingsModel.HOOKS_SETTINGS[0]
773 section, key = VcsSettingsModel.HOOKS_SETTINGS[0]
776 result = model.get_repo_ui_settings(key=key)
774 result = model.get_repo_ui_settings(key=key)
777 for setting in result:
775 for setting in result:
778 assert setting.key == key
776 assert setting.key == key
779
777
780 def test_raises_exception_when_repository_is_not_specified(self):
778 def test_raises_exception_when_repository_is_not_specified(self):
781 model = VcsSettingsModel()
779 model = VcsSettingsModel()
782 with pytest.raises(Exception) as exc_info:
780 with pytest.raises(Exception) as exc_info:
783 model.get_repo_ui_settings()
781 model.get_repo_ui_settings()
784 assert str(exc_info.value) == 'Repository is not specified'
782 assert str(exc_info.value) == 'Repository is not specified'
785
783
786
784
787 class TestGetRepoGeneralSettings(object):
785 class TestGetRepoGeneralSettings(object):
788 def test_global_settings_are_returned_when_no_repo_settings_found(
786 def test_global_settings_are_returned_when_no_repo_settings_found(
789 self, repo_stub):
787 self, repo_stub):
790 model = VcsSettingsModel(repo=repo_stub.repo_name)
788 model = VcsSettingsModel(repo=repo_stub.repo_name)
791 result = model.get_repo_general_settings()
789 result = model.get_repo_general_settings()
792 expected_result = model.global_settings.get_all_settings()
790 expected_result = model.global_settings.get_all_settings()
793 assert sorted(result) == sorted(expected_result)
791 assert sorted(result) == sorted(expected_result)
794
792
795 def test_repo_uis_are_overriding_global_uis(
793 def test_repo_uis_are_overriding_global_uis(
796 self, repo_stub, settings_util):
794 self, repo_stub, settings_util):
797 for key in VcsSettingsModel.GENERAL_SETTINGS:
795 for key in VcsSettingsModel.GENERAL_SETTINGS:
798 settings_util.create_repo_rhodecode_setting(
796 settings_util.create_repo_rhodecode_setting(
799 repo_stub, key, 'abcde', type_='unicode')
797 repo_stub, key, 'abcde', type_='unicode')
800 model = VcsSettingsModel(repo=repo_stub.repo_name)
798 model = VcsSettingsModel(repo=repo_stub.repo_name)
801 result = model.get_repo_ui_settings()
799 result = model.get_repo_ui_settings()
802 for key in result:
800 for key in result:
803 if key in VcsSettingsModel.GENERAL_SETTINGS:
801 if key in VcsSettingsModel.GENERAL_SETTINGS:
804 assert result[key] == 'abcde'
802 assert result[key] == 'abcde'
805
803
806 def test_raises_exception_when_repository_is_not_specified(self):
804 def test_raises_exception_when_repository_is_not_specified(self):
807 model = VcsSettingsModel()
805 model = VcsSettingsModel()
808 with pytest.raises(Exception) as exc_info:
806 with pytest.raises(Exception) as exc_info:
809 model.get_repo_general_settings()
807 model.get_repo_general_settings()
810 assert str(exc_info.value) == 'Repository is not specified'
808 assert str(exc_info.value) == 'Repository is not specified'
811
809
812
810
813 class TestGetGlobalGeneralSettings(object):
811 class TestGetGlobalGeneralSettings(object):
814 def test_global_settings_are_returned(self, repo_stub):
812 def test_global_settings_are_returned(self, repo_stub):
815 model = VcsSettingsModel()
813 model = VcsSettingsModel()
816 result = model.get_global_general_settings()
814 result = model.get_global_general_settings()
817 expected_result = model.global_settings.get_all_settings()
815 expected_result = model.global_settings.get_all_settings()
818 assert sorted(result) == sorted(expected_result)
816 assert sorted(result) == sorted(expected_result)
819
817
820 def test_repo_uis_are_not_overriding_global_uis(
818 def test_repo_uis_are_not_overriding_global_uis(
821 self, repo_stub, settings_util):
819 self, repo_stub, settings_util):
822 for key in VcsSettingsModel.GENERAL_SETTINGS:
820 for key in VcsSettingsModel.GENERAL_SETTINGS:
823 settings_util.create_repo_rhodecode_setting(
821 settings_util.create_repo_rhodecode_setting(
824 repo_stub, key, 'abcde', type_='unicode')
822 repo_stub, key, 'abcde', type_='unicode')
825 model = VcsSettingsModel(repo=repo_stub.repo_name)
823 model = VcsSettingsModel(repo=repo_stub.repo_name)
826 result = model.get_global_general_settings()
824 result = model.get_global_general_settings()
827 expected_result = model.global_settings.get_all_settings()
825 expected_result = model.global_settings.get_all_settings()
828 assert sorted(result) == sorted(expected_result)
826 assert sorted(result) == sorted(expected_result)
829
827
830
828
831 class TestGetGlobalUiSettings(object):
829 class TestGetGlobalUiSettings(object):
832 def test_global_uis_are_returned(self, repo_stub):
830 def test_global_uis_are_returned(self, repo_stub):
833 model = VcsSettingsModel()
831 model = VcsSettingsModel()
834 result = model.get_global_ui_settings()
832 result = model.get_global_ui_settings()
835 expected_result = model.global_settings.get_ui()
833 expected_result = model.global_settings.get_ui()
836 assert sorted(result) == sorted(expected_result)
834 assert sorted(result) == sorted(expected_result)
837
835
838 def test_repo_uis_are_not_overriding_global_uis(
836 def test_repo_uis_are_not_overriding_global_uis(
839 self, repo_stub, settings_util):
837 self, repo_stub, settings_util):
840 for section, key in VcsSettingsModel.HOOKS_SETTINGS:
838 for section, key in VcsSettingsModel.HOOKS_SETTINGS:
841 settings_util.create_repo_rhodecode_ui(
839 settings_util.create_repo_rhodecode_ui(
842 repo_stub, section, 'repo', key=key, active=False)
840 repo_stub, section, 'repo', key=key, active=False)
843 model = VcsSettingsModel(repo=repo_stub.repo_name)
841 model = VcsSettingsModel(repo=repo_stub.repo_name)
844 result = model.get_global_ui_settings()
842 result = model.get_global_ui_settings()
845 expected_result = model.global_settings.get_ui()
843 expected_result = model.global_settings.get_ui()
846 assert sorted(result) == sorted(expected_result)
844 assert sorted(result) == sorted(expected_result)
847
845
848 def test_ui_settings_filtered_by_section(
846 def test_ui_settings_filtered_by_section(
849 self, repo_stub, settings_util):
847 self, repo_stub, settings_util):
850 model = VcsSettingsModel(repo=repo_stub.repo_name)
848 model = VcsSettingsModel(repo=repo_stub.repo_name)
851 section, key = VcsSettingsModel.HOOKS_SETTINGS[0]
849 section, key = VcsSettingsModel.HOOKS_SETTINGS[0]
852 result = model.get_global_ui_settings(section=section)
850 result = model.get_global_ui_settings(section=section)
853 expected_result = model.global_settings.get_ui(section=section)
851 expected_result = model.global_settings.get_ui(section=section)
854 assert sorted(result) == sorted(expected_result)
852 assert sorted(result) == sorted(expected_result)
855
853
856 def test_ui_settings_filtered_by_key(
854 def test_ui_settings_filtered_by_key(
857 self, repo_stub, settings_util):
855 self, repo_stub, settings_util):
858 model = VcsSettingsModel(repo=repo_stub.repo_name)
856 model = VcsSettingsModel(repo=repo_stub.repo_name)
859 section, key = VcsSettingsModel.HOOKS_SETTINGS[0]
857 section, key = VcsSettingsModel.HOOKS_SETTINGS[0]
860 result = model.get_global_ui_settings(key=key)
858 result = model.get_global_ui_settings(key=key)
861 expected_result = model.global_settings.get_ui(key=key)
859 expected_result = model.global_settings.get_ui(key=key)
862 assert sorted(result) == sorted(expected_result)
860 assert sorted(result) == sorted(expected_result)
863
861
864
862
865 class TestGetGeneralSettings(object):
863 class TestGetGeneralSettings(object):
866 def test_global_settings_are_returned_when_inherited_is_true(
864 def test_global_settings_are_returned_when_inherited_is_true(
867 self, repo_stub, settings_util):
865 self, repo_stub, settings_util):
868 model = VcsSettingsModel(repo=repo_stub.repo_name)
866 model = VcsSettingsModel(repo=repo_stub.repo_name)
869 model.inherit_global_settings = True
867 model.inherit_global_settings = True
870 for key in VcsSettingsModel.GENERAL_SETTINGS:
868 for key in VcsSettingsModel.GENERAL_SETTINGS:
871 settings_util.create_repo_rhodecode_setting(
869 settings_util.create_repo_rhodecode_setting(
872 repo_stub, key, 'abcde', type_='unicode')
870 repo_stub, key, 'abcde', type_='unicode')
873 result = model.get_general_settings()
871 result = model.get_general_settings()
874 expected_result = model.get_global_general_settings()
872 expected_result = model.get_global_general_settings()
875 assert sorted(result) == sorted(expected_result)
873 assert sorted(result) == sorted(expected_result)
876
874
877 def test_repo_settings_are_returned_when_inherited_is_false(
875 def test_repo_settings_are_returned_when_inherited_is_false(
878 self, repo_stub, settings_util):
876 self, repo_stub, settings_util):
879 model = VcsSettingsModel(repo=repo_stub.repo_name)
877 model = VcsSettingsModel(repo=repo_stub.repo_name)
880 model.inherit_global_settings = False
878 model.inherit_global_settings = False
881 for key in VcsSettingsModel.GENERAL_SETTINGS:
879 for key in VcsSettingsModel.GENERAL_SETTINGS:
882 settings_util.create_repo_rhodecode_setting(
880 settings_util.create_repo_rhodecode_setting(
883 repo_stub, key, 'abcde', type_='unicode')
881 repo_stub, key, 'abcde', type_='unicode')
884 result = model.get_general_settings()
882 result = model.get_general_settings()
885 expected_result = model.get_repo_general_settings()
883 expected_result = model.get_repo_general_settings()
886 assert sorted(result) == sorted(expected_result)
884 assert sorted(result) == sorted(expected_result)
887
885
888 def test_global_settings_are_returned_when_no_repository_specified(self):
886 def test_global_settings_are_returned_when_no_repository_specified(self):
889 model = VcsSettingsModel()
887 model = VcsSettingsModel()
890 result = model.get_general_settings()
888 result = model.get_general_settings()
891 expected_result = model.get_global_general_settings()
889 expected_result = model.get_global_general_settings()
892 assert sorted(result) == sorted(expected_result)
890 assert sorted(result) == sorted(expected_result)
893
891
894
892
895 class TestGetUiSettings(object):
893 class TestGetUiSettings(object):
896 def test_global_settings_are_returned_when_inherited_is_true(
894 def test_global_settings_are_returned_when_inherited_is_true(
897 self, repo_stub, settings_util):
895 self, repo_stub, settings_util):
898 model = VcsSettingsModel(repo=repo_stub.repo_name)
896 model = VcsSettingsModel(repo=repo_stub.repo_name)
899 model.inherit_global_settings = True
897 model.inherit_global_settings = True
900 for section, key in VcsSettingsModel.HOOKS_SETTINGS:
898 for section, key in VcsSettingsModel.HOOKS_SETTINGS:
901 settings_util.create_repo_rhodecode_ui(
899 settings_util.create_repo_rhodecode_ui(
902 repo_stub, section, 'repo', key=key, active=True)
900 repo_stub, section, 'repo', key=key, active=True)
903 result = model.get_ui_settings()
901 result = model.get_ui_settings()
904 expected_result = model.get_global_ui_settings()
902 expected_result = model.get_global_ui_settings()
905 assert sorted(result) == sorted(expected_result)
903 assert sorted(result) == sorted(expected_result)
906
904
907 def test_repo_settings_are_returned_when_inherited_is_false(
905 def test_repo_settings_are_returned_when_inherited_is_false(
908 self, repo_stub, settings_util):
906 self, repo_stub, settings_util):
909 model = VcsSettingsModel(repo=repo_stub.repo_name)
907 model = VcsSettingsModel(repo=repo_stub.repo_name)
910 model.inherit_global_settings = False
908 model.inherit_global_settings = False
911 for section, key in VcsSettingsModel.HOOKS_SETTINGS:
909 for section, key in VcsSettingsModel.HOOKS_SETTINGS:
912 settings_util.create_repo_rhodecode_ui(
910 settings_util.create_repo_rhodecode_ui(
913 repo_stub, section, 'repo', key=key, active=True)
911 repo_stub, section, 'repo', key=key, active=True)
914 result = model.get_ui_settings()
912 result = model.get_ui_settings()
915 expected_result = model.get_repo_ui_settings()
913 expected_result = model.get_repo_ui_settings()
916 assert sorted(result) == sorted(expected_result)
914 assert sorted(result) == sorted(expected_result)
917
915
918 def test_repo_settings_filtered_by_section_and_key(self, repo_stub):
916 def test_repo_settings_filtered_by_section_and_key(self, repo_stub):
919 model = VcsSettingsModel(repo=repo_stub.repo_name)
917 model = VcsSettingsModel(repo=repo_stub.repo_name)
920 model.inherit_global_settings = False
918 model.inherit_global_settings = False
921 args = ('section', 'key')
919 args = ('section', 'key')
922 with mock.patch.object(model, 'get_repo_ui_settings') as settings_mock:
920 with mock.patch.object(model, 'get_repo_ui_settings') as settings_mock:
923 model.get_ui_settings(*args)
921 model.get_ui_settings(*args)
924 settings_mock.assert_called_once_with(*args)
922 settings_mock.assert_called_once_with(*args)
925
923
926 def test_global_settings_filtered_by_section_and_key(self):
924 def test_global_settings_filtered_by_section_and_key(self):
927 model = VcsSettingsModel()
925 model = VcsSettingsModel()
928 args = ('section', 'key')
926 args = ('section', 'key')
929 with mock.patch.object(model, 'get_global_ui_settings') as (
927 with mock.patch.object(model, 'get_global_ui_settings') as (
930 settings_mock):
928 settings_mock):
931 model.get_ui_settings(*args)
929 model.get_ui_settings(*args)
932 settings_mock.assert_called_once_with(*args)
930 settings_mock.assert_called_once_with(*args)
933
931
934 def test_global_settings_are_returned_when_no_repository_specified(self):
932 def test_global_settings_are_returned_when_no_repository_specified(self):
935 model = VcsSettingsModel()
933 model = VcsSettingsModel()
936 result = model.get_ui_settings()
934 result = model.get_ui_settings()
937 expected_result = model.get_global_ui_settings()
935 expected_result = model.get_global_ui_settings()
938 assert sorted(result) == sorted(expected_result)
936 assert sorted(result) == sorted(expected_result)
939
937
940
938
941 class TestGetSvnPatterns(object):
939 class TestGetSvnPatterns(object):
942 def test_repo_settings_filtered_by_section_and_key(self, repo_stub):
940 def test_repo_settings_filtered_by_section_and_key(self, repo_stub):
943 model = VcsSettingsModel(repo=repo_stub.repo_name)
941 model = VcsSettingsModel(repo=repo_stub.repo_name)
944 args = ('section', )
942 args = ('section', )
945 with mock.patch.object(model, 'get_repo_ui_settings') as settings_mock:
943 with mock.patch.object(model, 'get_repo_ui_settings') as settings_mock:
946 model.get_svn_patterns(*args)
944 model.get_svn_patterns(*args)
947 settings_mock.assert_called_once_with(*args)
945 settings_mock.assert_called_once_with(*args)
948
946
949 def test_global_settings_filtered_by_section_and_key(self):
947 def test_global_settings_filtered_by_section_and_key(self):
950 model = VcsSettingsModel()
948 model = VcsSettingsModel()
951 args = ('section', )
949 args = ('section', )
952 with mock.patch.object(model, 'get_global_ui_settings') as (
950 with mock.patch.object(model, 'get_global_ui_settings') as (
953 settings_mock):
951 settings_mock):
954 model.get_svn_patterns(*args)
952 model.get_svn_patterns(*args)
955 settings_mock.assert_called_once_with(*args)
953 settings_mock.assert_called_once_with(*args)
956
954
957
955
958 class TestGetReposLocation(object):
956 class TestGetReposLocation(object):
959 def test_returns_repos_location(self, repo_stub):
957 def test_returns_repos_location(self, repo_stub):
960 model = VcsSettingsModel()
958 model = VcsSettingsModel()
961
959
962 result_mock = mock.Mock()
960 result_mock = mock.Mock()
963 result_mock.ui_value = '/tmp'
961 result_mock.ui_value = '/tmp'
964
962
965 with mock.patch.object(model, 'global_settings') as settings_mock:
963 with mock.patch.object(model, 'global_settings') as settings_mock:
966 settings_mock.get_ui_by_key.return_value = result_mock
964 settings_mock.get_ui_by_key.return_value = result_mock
967 result = model.get_repos_location()
965 result = model.get_repos_location()
968
966
969 settings_mock.get_ui_by_key.assert_called_once_with('/')
967 settings_mock.get_ui_by_key.assert_called_once_with('/')
970 assert result == '/tmp'
968 assert result == '/tmp'
971
969
972
970
973 class TestCreateOrUpdateRepoSettings(object):
971 class TestCreateOrUpdateRepoSettings(object):
974 FORM_DATA = {
972 FORM_DATA = {
975 'inherit_global_settings': False,
973 'inherit_global_settings': False,
976 'hooks_changegroup_repo_size': False,
974 'hooks_changegroup_repo_size': False,
977 'hooks_changegroup_push_logger': False,
975 'hooks_changegroup_push_logger': False,
978 'hooks_outgoing_pull_logger': False,
976 'hooks_outgoing_pull_logger': False,
979 'extensions_largefiles': False,
977 'extensions_largefiles': False,
980 'extensions_evolve': False,
978 'extensions_evolve': False,
981 'largefiles_usercache': '/example/largefiles-store',
979 'largefiles_usercache': '/example/largefiles-store',
982 'vcs_git_lfs_enabled': False,
980 'vcs_git_lfs_enabled': False,
983 'vcs_git_lfs_store_location': '/',
981 'vcs_git_lfs_store_location': '/',
984 'phases_publish': 'False',
982 'phases_publish': 'False',
985 'rhodecode_pr_merge_enabled': False,
983 'rhodecode_pr_merge_enabled': False,
986 'rhodecode_use_outdated_comments': False,
984 'rhodecode_use_outdated_comments': False,
987 'new_svn_branch': '',
985 'new_svn_branch': '',
988 'new_svn_tag': ''
986 'new_svn_tag': ''
989 }
987 }
990
988
991 def test_get_raises_exception_when_repository_not_specified(self):
989 def test_get_raises_exception_when_repository_not_specified(self):
992 model = VcsSettingsModel()
990 model = VcsSettingsModel()
993 with pytest.raises(Exception) as exc_info:
991 with pytest.raises(Exception) as exc_info:
994 model.create_or_update_repo_settings(data=self.FORM_DATA)
992 model.create_or_update_repo_settings(data=self.FORM_DATA)
995 assert str(exc_info.value) == 'Repository is not specified'
993 assert str(exc_info.value) == 'Repository is not specified'
996
994
997 def test_only_svn_settings_are_updated_when_type_is_svn(self, backend_svn):
995 def test_only_svn_settings_are_updated_when_type_is_svn(self, backend_svn):
998 repo = backend_svn.create_repo()
996 repo = backend_svn.create_repo()
999 model = VcsSettingsModel(repo=repo)
997 model = VcsSettingsModel(repo=repo)
1000 with self._patch_model(model) as mocks:
998 with self._patch_model(model) as mocks:
1001 model.create_or_update_repo_settings(
999 model.create_or_update_repo_settings(
1002 data=self.FORM_DATA, inherit_global_settings=False)
1000 data=self.FORM_DATA, inherit_global_settings=False)
1003 mocks['create_repo_svn_settings'].assert_called_once_with(
1001 mocks['create_repo_svn_settings'].assert_called_once_with(
1004 self.FORM_DATA)
1002 self.FORM_DATA)
1005 non_called_methods = (
1003 non_called_methods = (
1006 'create_or_update_repo_hook_settings',
1004 'create_or_update_repo_hook_settings',
1007 'create_or_update_repo_pr_settings',
1005 'create_or_update_repo_pr_settings',
1008 'create_or_update_repo_hg_settings')
1006 'create_or_update_repo_hg_settings')
1009 for method in non_called_methods:
1007 for method in non_called_methods:
1010 assert mocks[method].call_count == 0
1008 assert mocks[method].call_count == 0
1011
1009
1012 def test_non_svn_settings_are_updated_when_type_is_hg(self, backend_hg):
1010 def test_non_svn_settings_are_updated_when_type_is_hg(self, backend_hg):
1013 repo = backend_hg.create_repo()
1011 repo = backend_hg.create_repo()
1014 model = VcsSettingsModel(repo=repo)
1012 model = VcsSettingsModel(repo=repo)
1015 with self._patch_model(model) as mocks:
1013 with self._patch_model(model) as mocks:
1016 model.create_or_update_repo_settings(
1014 model.create_or_update_repo_settings(
1017 data=self.FORM_DATA, inherit_global_settings=False)
1015 data=self.FORM_DATA, inherit_global_settings=False)
1018
1016
1019 assert mocks['create_repo_svn_settings'].call_count == 0
1017 assert mocks['create_repo_svn_settings'].call_count == 0
1020 called_methods = (
1018 called_methods = (
1021 'create_or_update_repo_hook_settings',
1019 'create_or_update_repo_hook_settings',
1022 'create_or_update_repo_pr_settings',
1020 'create_or_update_repo_pr_settings',
1023 'create_or_update_repo_hg_settings')
1021 'create_or_update_repo_hg_settings')
1024 for method in called_methods:
1022 for method in called_methods:
1025 mocks[method].assert_called_once_with(self.FORM_DATA)
1023 mocks[method].assert_called_once_with(self.FORM_DATA)
1026
1024
1027 def test_non_svn_and_hg_settings_are_updated_when_type_is_git(
1025 def test_non_svn_and_hg_settings_are_updated_when_type_is_git(
1028 self, backend_git):
1026 self, backend_git):
1029 repo = backend_git.create_repo()
1027 repo = backend_git.create_repo()
1030 model = VcsSettingsModel(repo=repo)
1028 model = VcsSettingsModel(repo=repo)
1031 with self._patch_model(model) as mocks:
1029 with self._patch_model(model) as mocks:
1032 model.create_or_update_repo_settings(
1030 model.create_or_update_repo_settings(
1033 data=self.FORM_DATA, inherit_global_settings=False)
1031 data=self.FORM_DATA, inherit_global_settings=False)
1034
1032
1035 assert mocks['create_repo_svn_settings'].call_count == 0
1033 assert mocks['create_repo_svn_settings'].call_count == 0
1036 called_methods = (
1034 called_methods = (
1037 'create_or_update_repo_hook_settings',
1035 'create_or_update_repo_hook_settings',
1038 'create_or_update_repo_pr_settings')
1036 'create_or_update_repo_pr_settings')
1039 non_called_methods = (
1037 non_called_methods = (
1040 'create_repo_svn_settings',
1038 'create_repo_svn_settings',
1041 'create_or_update_repo_hg_settings'
1039 'create_or_update_repo_hg_settings'
1042 )
1040 )
1043 for method in called_methods:
1041 for method in called_methods:
1044 mocks[method].assert_called_once_with(self.FORM_DATA)
1042 mocks[method].assert_called_once_with(self.FORM_DATA)
1045 for method in non_called_methods:
1043 for method in non_called_methods:
1046 assert mocks[method].call_count == 0
1044 assert mocks[method].call_count == 0
1047
1045
1048 def test_no_methods_are_called_when_settings_are_inherited(
1046 def test_no_methods_are_called_when_settings_are_inherited(
1049 self, backend):
1047 self, backend):
1050 repo = backend.create_repo()
1048 repo = backend.create_repo()
1051 model = VcsSettingsModel(repo=repo)
1049 model = VcsSettingsModel(repo=repo)
1052 with self._patch_model(model) as mocks:
1050 with self._patch_model(model) as mocks:
1053 model.create_or_update_repo_settings(
1051 model.create_or_update_repo_settings(
1054 data=self.FORM_DATA, inherit_global_settings=True)
1052 data=self.FORM_DATA, inherit_global_settings=True)
1055 for method_name in mocks:
1053 for method_name in mocks:
1056 assert mocks[method_name].call_count == 0
1054 assert mocks[method_name].call_count == 0
1057
1055
1058 def test_cache_is_marked_for_invalidation(self, repo_stub):
1056 def test_cache_is_marked_for_invalidation(self, repo_stub):
1059 model = VcsSettingsModel(repo=repo_stub)
1057 model = VcsSettingsModel(repo=repo_stub)
1060 invalidation_patcher = mock.patch(
1058 invalidation_patcher = mock.patch(
1061 'rhodecode.model.scm.ScmModel.mark_for_invalidation')
1059 'rhodecode.model.scm.ScmModel.mark_for_invalidation')
1062 with invalidation_patcher as invalidation_mock:
1060 with invalidation_patcher as invalidation_mock:
1063 model.create_or_update_repo_settings(
1061 model.create_or_update_repo_settings(
1064 data=self.FORM_DATA, inherit_global_settings=True)
1062 data=self.FORM_DATA, inherit_global_settings=True)
1065 invalidation_mock.assert_called_once_with(
1063 invalidation_mock.assert_called_once_with(
1066 repo_stub.repo_name, delete=True)
1064 repo_stub.repo_name, delete=True)
1067
1065
1068 def test_inherit_flag_is_saved(self, repo_stub):
1066 def test_inherit_flag_is_saved(self, repo_stub):
1069 model = VcsSettingsModel(repo=repo_stub)
1067 model = VcsSettingsModel(repo=repo_stub)
1070 model.inherit_global_settings = True
1068 model.inherit_global_settings = True
1071 with self._patch_model(model):
1069 with self._patch_model(model):
1072 model.create_or_update_repo_settings(
1070 model.create_or_update_repo_settings(
1073 data=self.FORM_DATA, inherit_global_settings=False)
1071 data=self.FORM_DATA, inherit_global_settings=False)
1074 assert model.inherit_global_settings is False
1072 assert model.inherit_global_settings is False
1075
1073
1076 def _patch_model(self, model):
1074 def _patch_model(self, model):
1077 return mock.patch.multiple(
1075 return mock.patch.multiple(
1078 model,
1076 model,
1079 create_repo_svn_settings=mock.DEFAULT,
1077 create_repo_svn_settings=mock.DEFAULT,
1080 create_or_update_repo_hook_settings=mock.DEFAULT,
1078 create_or_update_repo_hook_settings=mock.DEFAULT,
1081 create_or_update_repo_pr_settings=mock.DEFAULT,
1079 create_or_update_repo_pr_settings=mock.DEFAULT,
1082 create_or_update_repo_hg_settings=mock.DEFAULT)
1080 create_or_update_repo_hg_settings=mock.DEFAULT)
General Comments 0
You need to be logged in to leave comments. Login now