##// END OF EJS Templates
lib: Fix missing space char in bad slug characters string.
Martin Bornhold -
r906:1074a41b default
parent child Browse files
Show More
@@ -1,1003 +1,1003 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2016 RhodeCode GmbH
3 # Copyright (C) 2010-2016 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 """
21 """
22 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 shutil
31 import shutil
32 import tempfile
32 import tempfile
33 import traceback
33 import traceback
34 import tarfile
34 import tarfile
35 import warnings
35 import warnings
36 from os.path import join as jn
36 from os.path import join as jn
37
37
38 import paste
38 import paste
39 import pkg_resources
39 import pkg_resources
40 from paste.script.command import Command, BadCommand
40 from paste.script.command import Command, BadCommand
41 from webhelpers.text import collapse, remove_formatting, strip_tags
41 from webhelpers.text import collapse, remove_formatting, strip_tags
42 from mako import exceptions
42 from mako import exceptions
43 from pyramid.threadlocal import get_current_registry
43 from pyramid.threadlocal import get_current_registry
44
44
45 from rhodecode.lib.fakemod import create_module
45 from rhodecode.lib.fakemod import create_module
46 from rhodecode.lib.vcs.backends.base import Config
46 from rhodecode.lib.vcs.backends.base import Config
47 from rhodecode.lib.vcs.exceptions import VCSError
47 from rhodecode.lib.vcs.exceptions import VCSError
48 from rhodecode.lib.vcs.utils.helpers import get_scm, get_scm_backend
48 from rhodecode.lib.vcs.utils.helpers import get_scm, get_scm_backend
49 from rhodecode.lib.utils2 import (
49 from rhodecode.lib.utils2 import (
50 safe_str, safe_unicode, get_current_rhodecode_user, md5)
50 safe_str, safe_unicode, get_current_rhodecode_user, md5)
51 from rhodecode.model import meta
51 from rhodecode.model import meta
52 from rhodecode.model.db import (
52 from rhodecode.model.db import (
53 Repository, User, RhodeCodeUi, UserLog, RepoGroup, UserGroup)
53 Repository, User, RhodeCodeUi, UserLog, RepoGroup, UserGroup)
54 from rhodecode.model.meta import Session
54 from rhodecode.model.meta import Session
55
55
56
56
57 log = logging.getLogger(__name__)
57 log = logging.getLogger(__name__)
58
58
59 REMOVED_REPO_PAT = re.compile(r'rm__\d{8}_\d{6}_\d{6}__.*')
59 REMOVED_REPO_PAT = re.compile(r'rm__\d{8}_\d{6}_\d{6}__.*')
60
60
61 # String which contains characters that are not allowed in slug names for
61 # String which contains characters that are not allowed in slug names for
62 # repositories or repository groups. It is properly escaped to use it in
62 # repositories or repository groups. It is properly escaped to use it in
63 # regular expressions.
63 # regular expressions.
64 SLUG_BAD_CHARS = re.escape('`?=[]\;\'"<>,/~!@#$%^&*()+{}|:')
64 SLUG_BAD_CHARS = re.escape('`?=[]\;\'"<>,/~!@#$%^&*()+{}|: ')
65
65
66 # Regex that matches forbidden characters in repo/group slugs.
66 # Regex that matches forbidden characters in repo/group slugs.
67 SLUG_BAD_CHAR_RE = re.compile('[{}]'.format(SLUG_BAD_CHARS))
67 SLUG_BAD_CHAR_RE = re.compile('[{}]'.format(SLUG_BAD_CHARS))
68
68
69 # Regex that matches allowed characters in repo/group slugs.
69 # Regex that matches allowed characters in repo/group slugs.
70 SLUG_GOOD_CHAR_RE = re.compile('[^{}]'.format(SLUG_BAD_CHARS))
70 SLUG_GOOD_CHAR_RE = re.compile('[^{}]'.format(SLUG_BAD_CHARS))
71
71
72 # Regex that matches whole repo/group slugs.
72 # Regex that matches whole repo/group slugs.
73 SLUG_RE = re.compile('[^{}]+'.format(SLUG_BAD_CHARS))
73 SLUG_RE = re.compile('[^{}]+'.format(SLUG_BAD_CHARS))
74
74
75 _license_cache = None
75 _license_cache = None
76
76
77
77
78 def repo_name_slug(value):
78 def repo_name_slug(value):
79 """
79 """
80 Return slug of name of repository
80 Return slug of name of repository
81 This function is called on each creation/modification
81 This function is called on each creation/modification
82 of repository to prevent bad names in repo
82 of repository to prevent bad names in repo
83 """
83 """
84 replacement_char = '-'
84 replacement_char = '-'
85
85
86 slug = remove_formatting(value)
86 slug = remove_formatting(value)
87 slug = SLUG_BAD_CHAR_RE.sub(replacement_char, slug)
87 slug = SLUG_BAD_CHAR_RE.sub(replacement_char, slug)
88 slug = collapse(slug, replacement_char)
88 slug = collapse(slug, replacement_char)
89 return slug
89 return slug
90
90
91
91
92 #==============================================================================
92 #==============================================================================
93 # PERM DECORATOR HELPERS FOR EXTRACTING NAMES FOR PERM CHECKS
93 # PERM DECORATOR HELPERS FOR EXTRACTING NAMES FOR PERM CHECKS
94 #==============================================================================
94 #==============================================================================
95 def get_repo_slug(request):
95 def get_repo_slug(request):
96 _repo = request.environ['pylons.routes_dict'].get('repo_name')
96 _repo = request.environ['pylons.routes_dict'].get('repo_name')
97 if _repo:
97 if _repo:
98 _repo = _repo.rstrip('/')
98 _repo = _repo.rstrip('/')
99 return _repo
99 return _repo
100
100
101
101
102 def get_repo_group_slug(request):
102 def get_repo_group_slug(request):
103 _group = request.environ['pylons.routes_dict'].get('group_name')
103 _group = request.environ['pylons.routes_dict'].get('group_name')
104 if _group:
104 if _group:
105 _group = _group.rstrip('/')
105 _group = _group.rstrip('/')
106 return _group
106 return _group
107
107
108
108
109 def get_user_group_slug(request):
109 def get_user_group_slug(request):
110 _group = request.environ['pylons.routes_dict'].get('user_group_id')
110 _group = request.environ['pylons.routes_dict'].get('user_group_id')
111 try:
111 try:
112 _group = UserGroup.get(_group)
112 _group = UserGroup.get(_group)
113 if _group:
113 if _group:
114 _group = _group.users_group_name
114 _group = _group.users_group_name
115 except Exception:
115 except Exception:
116 log.debug(traceback.format_exc())
116 log.debug(traceback.format_exc())
117 #catch all failures here
117 #catch all failures here
118 pass
118 pass
119
119
120 return _group
120 return _group
121
121
122
122
123 def action_logger(user, action, repo, ipaddr='', sa=None, commit=False):
123 def action_logger(user, action, repo, ipaddr='', sa=None, commit=False):
124 """
124 """
125 Action logger for various actions made by users
125 Action logger for various actions made by users
126
126
127 :param user: user that made this action, can be a unique username string or
127 :param user: user that made this action, can be a unique username string or
128 object containing user_id attribute
128 object containing user_id attribute
129 :param action: action to log, should be on of predefined unique actions for
129 :param action: action to log, should be on of predefined unique actions for
130 easy translations
130 easy translations
131 :param repo: string name of repository or object containing repo_id,
131 :param repo: string name of repository or object containing repo_id,
132 that action was made on
132 that action was made on
133 :param ipaddr: optional ip address from what the action was made
133 :param ipaddr: optional ip address from what the action was made
134 :param sa: optional sqlalchemy session
134 :param sa: optional sqlalchemy session
135
135
136 """
136 """
137
137
138 if not sa:
138 if not sa:
139 sa = meta.Session()
139 sa = meta.Session()
140 # if we don't get explicit IP address try to get one from registered user
140 # if we don't get explicit IP address try to get one from registered user
141 # in tmpl context var
141 # in tmpl context var
142 if not ipaddr:
142 if not ipaddr:
143 ipaddr = getattr(get_current_rhodecode_user(), 'ip_addr', '')
143 ipaddr = getattr(get_current_rhodecode_user(), 'ip_addr', '')
144
144
145 try:
145 try:
146 if getattr(user, 'user_id', None):
146 if getattr(user, 'user_id', None):
147 user_obj = User.get(user.user_id)
147 user_obj = User.get(user.user_id)
148 elif isinstance(user, basestring):
148 elif isinstance(user, basestring):
149 user_obj = User.get_by_username(user)
149 user_obj = User.get_by_username(user)
150 else:
150 else:
151 raise Exception('You have to provide a user object or a username')
151 raise Exception('You have to provide a user object or a username')
152
152
153 if getattr(repo, 'repo_id', None):
153 if getattr(repo, 'repo_id', None):
154 repo_obj = Repository.get(repo.repo_id)
154 repo_obj = Repository.get(repo.repo_id)
155 repo_name = repo_obj.repo_name
155 repo_name = repo_obj.repo_name
156 elif isinstance(repo, basestring):
156 elif isinstance(repo, basestring):
157 repo_name = repo.lstrip('/')
157 repo_name = repo.lstrip('/')
158 repo_obj = Repository.get_by_repo_name(repo_name)
158 repo_obj = Repository.get_by_repo_name(repo_name)
159 else:
159 else:
160 repo_obj = None
160 repo_obj = None
161 repo_name = ''
161 repo_name = ''
162
162
163 user_log = UserLog()
163 user_log = UserLog()
164 user_log.user_id = user_obj.user_id
164 user_log.user_id = user_obj.user_id
165 user_log.username = user_obj.username
165 user_log.username = user_obj.username
166 action = safe_unicode(action)
166 action = safe_unicode(action)
167 user_log.action = action[:1200000]
167 user_log.action = action[:1200000]
168
168
169 user_log.repository = repo_obj
169 user_log.repository = repo_obj
170 user_log.repository_name = repo_name
170 user_log.repository_name = repo_name
171
171
172 user_log.action_date = datetime.datetime.now()
172 user_log.action_date = datetime.datetime.now()
173 user_log.user_ip = ipaddr
173 user_log.user_ip = ipaddr
174 sa.add(user_log)
174 sa.add(user_log)
175
175
176 log.info('Logging action:`%s` on repo:`%s` by user:%s ip:%s',
176 log.info('Logging action:`%s` on repo:`%s` by user:%s ip:%s',
177 action, safe_unicode(repo), user_obj, ipaddr)
177 action, safe_unicode(repo), user_obj, ipaddr)
178 if commit:
178 if commit:
179 sa.commit()
179 sa.commit()
180 except Exception:
180 except Exception:
181 log.error(traceback.format_exc())
181 log.error(traceback.format_exc())
182 raise
182 raise
183
183
184
184
185 def get_filesystem_repos(path, recursive=False, skip_removed_repos=True):
185 def get_filesystem_repos(path, recursive=False, skip_removed_repos=True):
186 """
186 """
187 Scans given path for repos and return (name,(type,path)) tuple
187 Scans given path for repos and return (name,(type,path)) tuple
188
188
189 :param path: path to scan for repositories
189 :param path: path to scan for repositories
190 :param recursive: recursive search and return names with subdirs in front
190 :param recursive: recursive search and return names with subdirs in front
191 """
191 """
192
192
193 # remove ending slash for better results
193 # remove ending slash for better results
194 path = path.rstrip(os.sep)
194 path = path.rstrip(os.sep)
195 log.debug('now scanning in %s location recursive:%s...', path, recursive)
195 log.debug('now scanning in %s location recursive:%s...', path, recursive)
196
196
197 def _get_repos(p):
197 def _get_repos(p):
198 dirpaths = _get_dirpaths(p)
198 dirpaths = _get_dirpaths(p)
199 if not _is_dir_writable(p):
199 if not _is_dir_writable(p):
200 log.warning('repo path without write access: %s', p)
200 log.warning('repo path without write access: %s', p)
201
201
202 for dirpath in dirpaths:
202 for dirpath in dirpaths:
203 if os.path.isfile(os.path.join(p, dirpath)):
203 if os.path.isfile(os.path.join(p, dirpath)):
204 continue
204 continue
205 cur_path = os.path.join(p, dirpath)
205 cur_path = os.path.join(p, dirpath)
206
206
207 # skip removed repos
207 # skip removed repos
208 if skip_removed_repos and REMOVED_REPO_PAT.match(dirpath):
208 if skip_removed_repos and REMOVED_REPO_PAT.match(dirpath):
209 continue
209 continue
210
210
211 #skip .<somethin> dirs
211 #skip .<somethin> dirs
212 if dirpath.startswith('.'):
212 if dirpath.startswith('.'):
213 continue
213 continue
214
214
215 try:
215 try:
216 scm_info = get_scm(cur_path)
216 scm_info = get_scm(cur_path)
217 yield scm_info[1].split(path, 1)[-1].lstrip(os.sep), scm_info
217 yield scm_info[1].split(path, 1)[-1].lstrip(os.sep), scm_info
218 except VCSError:
218 except VCSError:
219 if not recursive:
219 if not recursive:
220 continue
220 continue
221 #check if this dir containts other repos for recursive scan
221 #check if this dir containts other repos for recursive scan
222 rec_path = os.path.join(p, dirpath)
222 rec_path = os.path.join(p, dirpath)
223 if os.path.isdir(rec_path):
223 if os.path.isdir(rec_path):
224 for inner_scm in _get_repos(rec_path):
224 for inner_scm in _get_repos(rec_path):
225 yield inner_scm
225 yield inner_scm
226
226
227 return _get_repos(path)
227 return _get_repos(path)
228
228
229
229
230 def _get_dirpaths(p):
230 def _get_dirpaths(p):
231 try:
231 try:
232 # OS-independable way of checking if we have at least read-only
232 # OS-independable way of checking if we have at least read-only
233 # access or not.
233 # access or not.
234 dirpaths = os.listdir(p)
234 dirpaths = os.listdir(p)
235 except OSError:
235 except OSError:
236 log.warning('ignoring repo path without read access: %s', p)
236 log.warning('ignoring repo path without read access: %s', p)
237 return []
237 return []
238
238
239 # os.listpath has a tweak: If a unicode is passed into it, then it tries to
239 # os.listpath has a tweak: If a unicode is passed into it, then it tries to
240 # decode paths and suddenly returns unicode objects itself. The items it
240 # decode paths and suddenly returns unicode objects itself. The items it
241 # cannot decode are returned as strings and cause issues.
241 # cannot decode are returned as strings and cause issues.
242 #
242 #
243 # Those paths are ignored here until a solid solution for path handling has
243 # Those paths are ignored here until a solid solution for path handling has
244 # been built.
244 # been built.
245 expected_type = type(p)
245 expected_type = type(p)
246
246
247 def _has_correct_type(item):
247 def _has_correct_type(item):
248 if type(item) is not expected_type:
248 if type(item) is not expected_type:
249 log.error(
249 log.error(
250 u"Ignoring path %s since it cannot be decoded into unicode.",
250 u"Ignoring path %s since it cannot be decoded into unicode.",
251 # Using "repr" to make sure that we see the byte value in case
251 # Using "repr" to make sure that we see the byte value in case
252 # of support.
252 # of support.
253 repr(item))
253 repr(item))
254 return False
254 return False
255 return True
255 return True
256
256
257 dirpaths = [item for item in dirpaths if _has_correct_type(item)]
257 dirpaths = [item for item in dirpaths if _has_correct_type(item)]
258
258
259 return dirpaths
259 return dirpaths
260
260
261
261
262 def _is_dir_writable(path):
262 def _is_dir_writable(path):
263 """
263 """
264 Probe if `path` is writable.
264 Probe if `path` is writable.
265
265
266 Due to trouble on Cygwin / Windows, this is actually probing if it is
266 Due to trouble on Cygwin / Windows, this is actually probing if it is
267 possible to create a file inside of `path`, stat does not produce reliable
267 possible to create a file inside of `path`, stat does not produce reliable
268 results in this case.
268 results in this case.
269 """
269 """
270 try:
270 try:
271 with tempfile.TemporaryFile(dir=path):
271 with tempfile.TemporaryFile(dir=path):
272 pass
272 pass
273 except OSError:
273 except OSError:
274 return False
274 return False
275 return True
275 return True
276
276
277
277
278 def is_valid_repo(repo_name, base_path, expect_scm=None, explicit_scm=None):
278 def is_valid_repo(repo_name, base_path, expect_scm=None, explicit_scm=None):
279 """
279 """
280 Returns True if given path is a valid repository False otherwise.
280 Returns True if given path is a valid repository False otherwise.
281 If expect_scm param is given also, compare if given scm is the same
281 If expect_scm param is given also, compare if given scm is the same
282 as expected from scm parameter. If explicit_scm is given don't try to
282 as expected from scm parameter. If explicit_scm is given don't try to
283 detect the scm, just use the given one to check if repo is valid
283 detect the scm, just use the given one to check if repo is valid
284
284
285 :param repo_name:
285 :param repo_name:
286 :param base_path:
286 :param base_path:
287 :param expect_scm:
287 :param expect_scm:
288 :param explicit_scm:
288 :param explicit_scm:
289
289
290 :return True: if given path is a valid repository
290 :return True: if given path is a valid repository
291 """
291 """
292 full_path = os.path.join(safe_str(base_path), safe_str(repo_name))
292 full_path = os.path.join(safe_str(base_path), safe_str(repo_name))
293 log.debug('Checking if `%s` is a valid path for repository', repo_name)
293 log.debug('Checking if `%s` is a valid path for repository', repo_name)
294
294
295 try:
295 try:
296 if explicit_scm:
296 if explicit_scm:
297 detected_scms = [get_scm_backend(explicit_scm)]
297 detected_scms = [get_scm_backend(explicit_scm)]
298 else:
298 else:
299 detected_scms = get_scm(full_path)
299 detected_scms = get_scm(full_path)
300
300
301 if expect_scm:
301 if expect_scm:
302 return detected_scms[0] == expect_scm
302 return detected_scms[0] == expect_scm
303 log.debug('path: %s is an vcs object:%s', full_path, detected_scms)
303 log.debug('path: %s is an vcs object:%s', full_path, detected_scms)
304 return True
304 return True
305 except VCSError:
305 except VCSError:
306 log.debug('path: %s is not a valid repo !', full_path)
306 log.debug('path: %s is not a valid repo !', full_path)
307 return False
307 return False
308
308
309
309
310 def is_valid_repo_group(repo_group_name, base_path, skip_path_check=False):
310 def is_valid_repo_group(repo_group_name, base_path, skip_path_check=False):
311 """
311 """
312 Returns True if given path is a repository group, False otherwise
312 Returns True if given path is a repository group, False otherwise
313
313
314 :param repo_name:
314 :param repo_name:
315 :param base_path:
315 :param base_path:
316 """
316 """
317 full_path = os.path.join(safe_str(base_path), safe_str(repo_group_name))
317 full_path = os.path.join(safe_str(base_path), safe_str(repo_group_name))
318 log.debug('Checking if `%s` is a valid path for repository group',
318 log.debug('Checking if `%s` is a valid path for repository group',
319 repo_group_name)
319 repo_group_name)
320
320
321 # check if it's not a repo
321 # check if it's not a repo
322 if is_valid_repo(repo_group_name, base_path):
322 if is_valid_repo(repo_group_name, base_path):
323 log.debug('Repo called %s exist, it is not a valid '
323 log.debug('Repo called %s exist, it is not a valid '
324 'repo group' % repo_group_name)
324 'repo group' % repo_group_name)
325 return False
325 return False
326
326
327 try:
327 try:
328 # we need to check bare git repos at higher level
328 # we need to check bare git repos at higher level
329 # since we might match branches/hooks/info/objects or possible
329 # since we might match branches/hooks/info/objects or possible
330 # other things inside bare git repo
330 # other things inside bare git repo
331 scm_ = get_scm(os.path.dirname(full_path))
331 scm_ = get_scm(os.path.dirname(full_path))
332 log.debug('path: %s is a vcs object:%s, not valid '
332 log.debug('path: %s is a vcs object:%s, not valid '
333 'repo group' % (full_path, scm_))
333 'repo group' % (full_path, scm_))
334 return False
334 return False
335 except VCSError:
335 except VCSError:
336 pass
336 pass
337
337
338 # check if it's a valid path
338 # check if it's a valid path
339 if skip_path_check or os.path.isdir(full_path):
339 if skip_path_check or os.path.isdir(full_path):
340 log.debug('path: %s is a valid repo group !', full_path)
340 log.debug('path: %s is a valid repo group !', full_path)
341 return True
341 return True
342
342
343 log.debug('path: %s is not a valid repo group !', full_path)
343 log.debug('path: %s is not a valid repo group !', full_path)
344 return False
344 return False
345
345
346
346
347 def ask_ok(prompt, retries=4, complaint='[y]es or [n]o please!'):
347 def ask_ok(prompt, retries=4, complaint='[y]es or [n]o please!'):
348 while True:
348 while True:
349 ok = raw_input(prompt)
349 ok = raw_input(prompt)
350 if ok.lower() in ('y', 'ye', 'yes'):
350 if ok.lower() in ('y', 'ye', 'yes'):
351 return True
351 return True
352 if ok.lower() in ('n', 'no', 'nop', 'nope'):
352 if ok.lower() in ('n', 'no', 'nop', 'nope'):
353 return False
353 return False
354 retries = retries - 1
354 retries = retries - 1
355 if retries < 0:
355 if retries < 0:
356 raise IOError
356 raise IOError
357 print(complaint)
357 print(complaint)
358
358
359 # propagated from mercurial documentation
359 # propagated from mercurial documentation
360 ui_sections = [
360 ui_sections = [
361 'alias', 'auth',
361 'alias', 'auth',
362 'decode/encode', 'defaults',
362 'decode/encode', 'defaults',
363 'diff', 'email',
363 'diff', 'email',
364 'extensions', 'format',
364 'extensions', 'format',
365 'merge-patterns', 'merge-tools',
365 'merge-patterns', 'merge-tools',
366 'hooks', 'http_proxy',
366 'hooks', 'http_proxy',
367 'smtp', 'patch',
367 'smtp', 'patch',
368 'paths', 'profiling',
368 'paths', 'profiling',
369 'server', 'trusted',
369 'server', 'trusted',
370 'ui', 'web', ]
370 'ui', 'web', ]
371
371
372
372
373 def config_data_from_db(clear_session=True, repo=None):
373 def config_data_from_db(clear_session=True, repo=None):
374 """
374 """
375 Read the configuration data from the database and return configuration
375 Read the configuration data from the database and return configuration
376 tuples.
376 tuples.
377 """
377 """
378 from rhodecode.model.settings import VcsSettingsModel
378 from rhodecode.model.settings import VcsSettingsModel
379
379
380 config = []
380 config = []
381
381
382 sa = meta.Session()
382 sa = meta.Session()
383 settings_model = VcsSettingsModel(repo=repo, sa=sa)
383 settings_model = VcsSettingsModel(repo=repo, sa=sa)
384
384
385 ui_settings = settings_model.get_ui_settings()
385 ui_settings = settings_model.get_ui_settings()
386
386
387 for setting in ui_settings:
387 for setting in ui_settings:
388 if setting.active:
388 if setting.active:
389 log.debug(
389 log.debug(
390 'settings ui from db: [%s] %s=%s',
390 'settings ui from db: [%s] %s=%s',
391 setting.section, setting.key, setting.value)
391 setting.section, setting.key, setting.value)
392 config.append((
392 config.append((
393 safe_str(setting.section), safe_str(setting.key),
393 safe_str(setting.section), safe_str(setting.key),
394 safe_str(setting.value)))
394 safe_str(setting.value)))
395 if setting.key == 'push_ssl':
395 if setting.key == 'push_ssl':
396 # force set push_ssl requirement to False, rhodecode
396 # force set push_ssl requirement to False, rhodecode
397 # handles that
397 # handles that
398 config.append((
398 config.append((
399 safe_str(setting.section), safe_str(setting.key), False))
399 safe_str(setting.section), safe_str(setting.key), False))
400 if clear_session:
400 if clear_session:
401 meta.Session.remove()
401 meta.Session.remove()
402
402
403 # TODO: mikhail: probably it makes no sense to re-read hooks information.
403 # TODO: mikhail: probably it makes no sense to re-read hooks information.
404 # It's already there and activated/deactivated
404 # It's already there and activated/deactivated
405 skip_entries = []
405 skip_entries = []
406 enabled_hook_classes = get_enabled_hook_classes(ui_settings)
406 enabled_hook_classes = get_enabled_hook_classes(ui_settings)
407 if 'pull' not in enabled_hook_classes:
407 if 'pull' not in enabled_hook_classes:
408 skip_entries.append(('hooks', RhodeCodeUi.HOOK_PRE_PULL))
408 skip_entries.append(('hooks', RhodeCodeUi.HOOK_PRE_PULL))
409 if 'push' not in enabled_hook_classes:
409 if 'push' not in enabled_hook_classes:
410 skip_entries.append(('hooks', RhodeCodeUi.HOOK_PRE_PUSH))
410 skip_entries.append(('hooks', RhodeCodeUi.HOOK_PRE_PUSH))
411
411
412 config = [entry for entry in config if entry[:2] not in skip_entries]
412 config = [entry for entry in config if entry[:2] not in skip_entries]
413
413
414 return config
414 return config
415
415
416
416
417 def make_db_config(clear_session=True, repo=None):
417 def make_db_config(clear_session=True, repo=None):
418 """
418 """
419 Create a :class:`Config` instance based on the values in the database.
419 Create a :class:`Config` instance based on the values in the database.
420 """
420 """
421 config = Config()
421 config = Config()
422 config_data = config_data_from_db(clear_session=clear_session, repo=repo)
422 config_data = config_data_from_db(clear_session=clear_session, repo=repo)
423 for section, option, value in config_data:
423 for section, option, value in config_data:
424 config.set(section, option, value)
424 config.set(section, option, value)
425 return config
425 return config
426
426
427
427
428 def get_enabled_hook_classes(ui_settings):
428 def get_enabled_hook_classes(ui_settings):
429 """
429 """
430 Return the enabled hook classes.
430 Return the enabled hook classes.
431
431
432 :param ui_settings: List of ui_settings as returned
432 :param ui_settings: List of ui_settings as returned
433 by :meth:`VcsSettingsModel.get_ui_settings`
433 by :meth:`VcsSettingsModel.get_ui_settings`
434
434
435 :return: a list with the enabled hook classes. The order is not guaranteed.
435 :return: a list with the enabled hook classes. The order is not guaranteed.
436 :rtype: list
436 :rtype: list
437 """
437 """
438 enabled_hooks = []
438 enabled_hooks = []
439 active_hook_keys = [
439 active_hook_keys = [
440 key for section, key, value, active in ui_settings
440 key for section, key, value, active in ui_settings
441 if section == 'hooks' and active]
441 if section == 'hooks' and active]
442
442
443 hook_names = {
443 hook_names = {
444 RhodeCodeUi.HOOK_PUSH: 'push',
444 RhodeCodeUi.HOOK_PUSH: 'push',
445 RhodeCodeUi.HOOK_PULL: 'pull',
445 RhodeCodeUi.HOOK_PULL: 'pull',
446 RhodeCodeUi.HOOK_REPO_SIZE: 'repo_size'
446 RhodeCodeUi.HOOK_REPO_SIZE: 'repo_size'
447 }
447 }
448
448
449 for key in active_hook_keys:
449 for key in active_hook_keys:
450 hook = hook_names.get(key)
450 hook = hook_names.get(key)
451 if hook:
451 if hook:
452 enabled_hooks.append(hook)
452 enabled_hooks.append(hook)
453
453
454 return enabled_hooks
454 return enabled_hooks
455
455
456
456
457 def set_rhodecode_config(config):
457 def set_rhodecode_config(config):
458 """
458 """
459 Updates pylons config with new settings from database
459 Updates pylons config with new settings from database
460
460
461 :param config:
461 :param config:
462 """
462 """
463 from rhodecode.model.settings import SettingsModel
463 from rhodecode.model.settings import SettingsModel
464 app_settings = SettingsModel().get_all_settings()
464 app_settings = SettingsModel().get_all_settings()
465
465
466 for k, v in app_settings.items():
466 for k, v in app_settings.items():
467 config[k] = v
467 config[k] = v
468
468
469
469
470 def get_rhodecode_realm():
470 def get_rhodecode_realm():
471 """
471 """
472 Return the rhodecode realm from database.
472 Return the rhodecode realm from database.
473 """
473 """
474 from rhodecode.model.settings import SettingsModel
474 from rhodecode.model.settings import SettingsModel
475 realm = SettingsModel().get_setting_by_name('realm')
475 realm = SettingsModel().get_setting_by_name('realm')
476 return safe_str(realm.app_settings_value)
476 return safe_str(realm.app_settings_value)
477
477
478
478
479 def get_rhodecode_base_path():
479 def get_rhodecode_base_path():
480 """
480 """
481 Returns the base path. The base path is the filesystem path which points
481 Returns the base path. The base path is the filesystem path which points
482 to the repository store.
482 to the repository store.
483 """
483 """
484 from rhodecode.model.settings import SettingsModel
484 from rhodecode.model.settings import SettingsModel
485 paths_ui = SettingsModel().get_ui_by_section_and_key('paths', '/')
485 paths_ui = SettingsModel().get_ui_by_section_and_key('paths', '/')
486 return safe_str(paths_ui.ui_value)
486 return safe_str(paths_ui.ui_value)
487
487
488
488
489 def map_groups(path):
489 def map_groups(path):
490 """
490 """
491 Given a full path to a repository, create all nested groups that this
491 Given a full path to a repository, create all nested groups that this
492 repo is inside. This function creates parent-child relationships between
492 repo is inside. This function creates parent-child relationships between
493 groups and creates default perms for all new groups.
493 groups and creates default perms for all new groups.
494
494
495 :param paths: full path to repository
495 :param paths: full path to repository
496 """
496 """
497 from rhodecode.model.repo_group import RepoGroupModel
497 from rhodecode.model.repo_group import RepoGroupModel
498 sa = meta.Session()
498 sa = meta.Session()
499 groups = path.split(Repository.NAME_SEP)
499 groups = path.split(Repository.NAME_SEP)
500 parent = None
500 parent = None
501 group = None
501 group = None
502
502
503 # last element is repo in nested groups structure
503 # last element is repo in nested groups structure
504 groups = groups[:-1]
504 groups = groups[:-1]
505 rgm = RepoGroupModel(sa)
505 rgm = RepoGroupModel(sa)
506 owner = User.get_first_super_admin()
506 owner = User.get_first_super_admin()
507 for lvl, group_name in enumerate(groups):
507 for lvl, group_name in enumerate(groups):
508 group_name = '/'.join(groups[:lvl] + [group_name])
508 group_name = '/'.join(groups[:lvl] + [group_name])
509 group = RepoGroup.get_by_group_name(group_name)
509 group = RepoGroup.get_by_group_name(group_name)
510 desc = '%s group' % group_name
510 desc = '%s group' % group_name
511
511
512 # skip folders that are now removed repos
512 # skip folders that are now removed repos
513 if REMOVED_REPO_PAT.match(group_name):
513 if REMOVED_REPO_PAT.match(group_name):
514 break
514 break
515
515
516 if group is None:
516 if group is None:
517 log.debug('creating group level: %s group_name: %s',
517 log.debug('creating group level: %s group_name: %s',
518 lvl, group_name)
518 lvl, group_name)
519 group = RepoGroup(group_name, parent)
519 group = RepoGroup(group_name, parent)
520 group.group_description = desc
520 group.group_description = desc
521 group.user = owner
521 group.user = owner
522 sa.add(group)
522 sa.add(group)
523 perm_obj = rgm._create_default_perms(group)
523 perm_obj = rgm._create_default_perms(group)
524 sa.add(perm_obj)
524 sa.add(perm_obj)
525 sa.flush()
525 sa.flush()
526
526
527 parent = group
527 parent = group
528 return group
528 return group
529
529
530
530
531 def repo2db_mapper(initial_repo_list, remove_obsolete=False):
531 def repo2db_mapper(initial_repo_list, remove_obsolete=False):
532 """
532 """
533 maps all repos given in initial_repo_list, non existing repositories
533 maps all repos given in initial_repo_list, non existing repositories
534 are created, if remove_obsolete is True it also checks for db entries
534 are created, if remove_obsolete is True it also checks for db entries
535 that are not in initial_repo_list and removes them.
535 that are not in initial_repo_list and removes them.
536
536
537 :param initial_repo_list: list of repositories found by scanning methods
537 :param initial_repo_list: list of repositories found by scanning methods
538 :param remove_obsolete: check for obsolete entries in database
538 :param remove_obsolete: check for obsolete entries in database
539 """
539 """
540 from rhodecode.model.repo import RepoModel
540 from rhodecode.model.repo import RepoModel
541 from rhodecode.model.scm import ScmModel
541 from rhodecode.model.scm import ScmModel
542 from rhodecode.model.repo_group import RepoGroupModel
542 from rhodecode.model.repo_group import RepoGroupModel
543 from rhodecode.model.settings import SettingsModel
543 from rhodecode.model.settings import SettingsModel
544
544
545 sa = meta.Session()
545 sa = meta.Session()
546 repo_model = RepoModel()
546 repo_model = RepoModel()
547 user = User.get_first_super_admin()
547 user = User.get_first_super_admin()
548 added = []
548 added = []
549
549
550 # creation defaults
550 # creation defaults
551 defs = SettingsModel().get_default_repo_settings(strip_prefix=True)
551 defs = SettingsModel().get_default_repo_settings(strip_prefix=True)
552 enable_statistics = defs.get('repo_enable_statistics')
552 enable_statistics = defs.get('repo_enable_statistics')
553 enable_locking = defs.get('repo_enable_locking')
553 enable_locking = defs.get('repo_enable_locking')
554 enable_downloads = defs.get('repo_enable_downloads')
554 enable_downloads = defs.get('repo_enable_downloads')
555 private = defs.get('repo_private')
555 private = defs.get('repo_private')
556
556
557 for name, repo in initial_repo_list.items():
557 for name, repo in initial_repo_list.items():
558 group = map_groups(name)
558 group = map_groups(name)
559 unicode_name = safe_unicode(name)
559 unicode_name = safe_unicode(name)
560 db_repo = repo_model.get_by_repo_name(unicode_name)
560 db_repo = repo_model.get_by_repo_name(unicode_name)
561 # found repo that is on filesystem not in RhodeCode database
561 # found repo that is on filesystem not in RhodeCode database
562 if not db_repo:
562 if not db_repo:
563 log.info('repository %s not found, creating now', name)
563 log.info('repository %s not found, creating now', name)
564 added.append(name)
564 added.append(name)
565 desc = (repo.description
565 desc = (repo.description
566 if repo.description != 'unknown'
566 if repo.description != 'unknown'
567 else '%s repository' % name)
567 else '%s repository' % name)
568
568
569 db_repo = repo_model._create_repo(
569 db_repo = repo_model._create_repo(
570 repo_name=name,
570 repo_name=name,
571 repo_type=repo.alias,
571 repo_type=repo.alias,
572 description=desc,
572 description=desc,
573 repo_group=getattr(group, 'group_id', None),
573 repo_group=getattr(group, 'group_id', None),
574 owner=user,
574 owner=user,
575 enable_locking=enable_locking,
575 enable_locking=enable_locking,
576 enable_downloads=enable_downloads,
576 enable_downloads=enable_downloads,
577 enable_statistics=enable_statistics,
577 enable_statistics=enable_statistics,
578 private=private,
578 private=private,
579 state=Repository.STATE_CREATED
579 state=Repository.STATE_CREATED
580 )
580 )
581 sa.commit()
581 sa.commit()
582 # we added that repo just now, and make sure we updated server info
582 # we added that repo just now, and make sure we updated server info
583 if db_repo.repo_type == 'git':
583 if db_repo.repo_type == 'git':
584 git_repo = db_repo.scm_instance()
584 git_repo = db_repo.scm_instance()
585 # update repository server-info
585 # update repository server-info
586 log.debug('Running update server info')
586 log.debug('Running update server info')
587 git_repo._update_server_info()
587 git_repo._update_server_info()
588
588
589 db_repo.update_commit_cache()
589 db_repo.update_commit_cache()
590
590
591 config = db_repo._config
591 config = db_repo._config
592 config.set('extensions', 'largefiles', '')
592 config.set('extensions', 'largefiles', '')
593 ScmModel().install_hooks(
593 ScmModel().install_hooks(
594 db_repo.scm_instance(config=config),
594 db_repo.scm_instance(config=config),
595 repo_type=db_repo.repo_type)
595 repo_type=db_repo.repo_type)
596
596
597 removed = []
597 removed = []
598 if remove_obsolete:
598 if remove_obsolete:
599 # remove from database those repositories that are not in the filesystem
599 # remove from database those repositories that are not in the filesystem
600 for repo in sa.query(Repository).all():
600 for repo in sa.query(Repository).all():
601 if repo.repo_name not in initial_repo_list.keys():
601 if repo.repo_name not in initial_repo_list.keys():
602 log.debug("Removing non-existing repository found in db `%s`",
602 log.debug("Removing non-existing repository found in db `%s`",
603 repo.repo_name)
603 repo.repo_name)
604 try:
604 try:
605 RepoModel(sa).delete(repo, forks='detach', fs_remove=False)
605 RepoModel(sa).delete(repo, forks='detach', fs_remove=False)
606 sa.commit()
606 sa.commit()
607 removed.append(repo.repo_name)
607 removed.append(repo.repo_name)
608 except Exception:
608 except Exception:
609 # don't hold further removals on error
609 # don't hold further removals on error
610 log.error(traceback.format_exc())
610 log.error(traceback.format_exc())
611 sa.rollback()
611 sa.rollback()
612
612
613 def splitter(full_repo_name):
613 def splitter(full_repo_name):
614 _parts = full_repo_name.rsplit(RepoGroup.url_sep(), 1)
614 _parts = full_repo_name.rsplit(RepoGroup.url_sep(), 1)
615 gr_name = None
615 gr_name = None
616 if len(_parts) == 2:
616 if len(_parts) == 2:
617 gr_name = _parts[0]
617 gr_name = _parts[0]
618 return gr_name
618 return gr_name
619
619
620 initial_repo_group_list = [splitter(x) for x in
620 initial_repo_group_list = [splitter(x) for x in
621 initial_repo_list.keys() if splitter(x)]
621 initial_repo_list.keys() if splitter(x)]
622
622
623 # remove from database those repository groups that are not in the
623 # remove from database those repository groups that are not in the
624 # filesystem due to parent child relationships we need to delete them
624 # filesystem due to parent child relationships we need to delete them
625 # in a specific order of most nested first
625 # in a specific order of most nested first
626 all_groups = [x.group_name for x in sa.query(RepoGroup).all()]
626 all_groups = [x.group_name for x in sa.query(RepoGroup).all()]
627 nested_sort = lambda gr: len(gr.split('/'))
627 nested_sort = lambda gr: len(gr.split('/'))
628 for group_name in sorted(all_groups, key=nested_sort, reverse=True):
628 for group_name in sorted(all_groups, key=nested_sort, reverse=True):
629 if group_name not in initial_repo_group_list:
629 if group_name not in initial_repo_group_list:
630 repo_group = RepoGroup.get_by_group_name(group_name)
630 repo_group = RepoGroup.get_by_group_name(group_name)
631 if (repo_group.children.all() or
631 if (repo_group.children.all() or
632 not RepoGroupModel().check_exist_filesystem(
632 not RepoGroupModel().check_exist_filesystem(
633 group_name=group_name, exc_on_failure=False)):
633 group_name=group_name, exc_on_failure=False)):
634 continue
634 continue
635
635
636 log.info(
636 log.info(
637 'Removing non-existing repository group found in db `%s`',
637 'Removing non-existing repository group found in db `%s`',
638 group_name)
638 group_name)
639 try:
639 try:
640 RepoGroupModel(sa).delete(group_name, fs_remove=False)
640 RepoGroupModel(sa).delete(group_name, fs_remove=False)
641 sa.commit()
641 sa.commit()
642 removed.append(group_name)
642 removed.append(group_name)
643 except Exception:
643 except Exception:
644 # don't hold further removals on error
644 # don't hold further removals on error
645 log.exception(
645 log.exception(
646 'Unable to remove repository group `%s`',
646 'Unable to remove repository group `%s`',
647 group_name)
647 group_name)
648 sa.rollback()
648 sa.rollback()
649 raise
649 raise
650
650
651 return added, removed
651 return added, removed
652
652
653
653
654 def get_default_cache_settings(settings):
654 def get_default_cache_settings(settings):
655 cache_settings = {}
655 cache_settings = {}
656 for key in settings.keys():
656 for key in settings.keys():
657 for prefix in ['beaker.cache.', 'cache.']:
657 for prefix in ['beaker.cache.', 'cache.']:
658 if key.startswith(prefix):
658 if key.startswith(prefix):
659 name = key.split(prefix)[1].strip()
659 name = key.split(prefix)[1].strip()
660 cache_settings[name] = settings[key].strip()
660 cache_settings[name] = settings[key].strip()
661 return cache_settings
661 return cache_settings
662
662
663
663
664 # set cache regions for beaker so celery can utilise it
664 # set cache regions for beaker so celery can utilise it
665 def add_cache(settings):
665 def add_cache(settings):
666 from rhodecode.lib import caches
666 from rhodecode.lib import caches
667 cache_settings = {'regions': None}
667 cache_settings = {'regions': None}
668 # main cache settings used as default ...
668 # main cache settings used as default ...
669 cache_settings.update(get_default_cache_settings(settings))
669 cache_settings.update(get_default_cache_settings(settings))
670
670
671 if cache_settings['regions']:
671 if cache_settings['regions']:
672 for region in cache_settings['regions'].split(','):
672 for region in cache_settings['regions'].split(','):
673 region = region.strip()
673 region = region.strip()
674 region_settings = {}
674 region_settings = {}
675 for key, value in cache_settings.items():
675 for key, value in cache_settings.items():
676 if key.startswith(region):
676 if key.startswith(region):
677 region_settings[key.split('.')[1]] = value
677 region_settings[key.split('.')[1]] = value
678
678
679 caches.configure_cache_region(
679 caches.configure_cache_region(
680 region, region_settings, cache_settings)
680 region, region_settings, cache_settings)
681
681
682
682
683 def load_rcextensions(root_path):
683 def load_rcextensions(root_path):
684 import rhodecode
684 import rhodecode
685 from rhodecode.config import conf
685 from rhodecode.config import conf
686
686
687 path = os.path.join(root_path, 'rcextensions', '__init__.py')
687 path = os.path.join(root_path, 'rcextensions', '__init__.py')
688 if os.path.isfile(path):
688 if os.path.isfile(path):
689 rcext = create_module('rc', path)
689 rcext = create_module('rc', path)
690 EXT = rhodecode.EXTENSIONS = rcext
690 EXT = rhodecode.EXTENSIONS = rcext
691 log.debug('Found rcextensions now loading %s...', rcext)
691 log.debug('Found rcextensions now loading %s...', rcext)
692
692
693 # Additional mappings that are not present in the pygments lexers
693 # Additional mappings that are not present in the pygments lexers
694 conf.LANGUAGES_EXTENSIONS_MAP.update(getattr(EXT, 'EXTRA_MAPPINGS', {}))
694 conf.LANGUAGES_EXTENSIONS_MAP.update(getattr(EXT, 'EXTRA_MAPPINGS', {}))
695
695
696 # auto check if the module is not missing any data, set to default if is
696 # auto check if the module is not missing any data, set to default if is
697 # this will help autoupdate new feature of rcext module
697 # this will help autoupdate new feature of rcext module
698 #from rhodecode.config import rcextensions
698 #from rhodecode.config import rcextensions
699 #for k in dir(rcextensions):
699 #for k in dir(rcextensions):
700 # if not k.startswith('_') and not hasattr(EXT, k):
700 # if not k.startswith('_') and not hasattr(EXT, k):
701 # setattr(EXT, k, getattr(rcextensions, k))
701 # setattr(EXT, k, getattr(rcextensions, k))
702
702
703
703
704 def get_custom_lexer(extension):
704 def get_custom_lexer(extension):
705 """
705 """
706 returns a custom lexer if it is defined in rcextensions module, or None
706 returns a custom lexer if it is defined in rcextensions module, or None
707 if there's no custom lexer defined
707 if there's no custom lexer defined
708 """
708 """
709 import rhodecode
709 import rhodecode
710 from pygments import lexers
710 from pygments import lexers
711 # check if we didn't define this extension as other lexer
711 # check if we didn't define this extension as other lexer
712 extensions = rhodecode.EXTENSIONS and getattr(rhodecode.EXTENSIONS, 'EXTRA_LEXERS', None)
712 extensions = rhodecode.EXTENSIONS and getattr(rhodecode.EXTENSIONS, 'EXTRA_LEXERS', None)
713 if extensions and extension in rhodecode.EXTENSIONS.EXTRA_LEXERS:
713 if extensions and extension in rhodecode.EXTENSIONS.EXTRA_LEXERS:
714 _lexer_name = rhodecode.EXTENSIONS.EXTRA_LEXERS[extension]
714 _lexer_name = rhodecode.EXTENSIONS.EXTRA_LEXERS[extension]
715 return lexers.get_lexer_by_name(_lexer_name)
715 return lexers.get_lexer_by_name(_lexer_name)
716
716
717
717
718 #==============================================================================
718 #==============================================================================
719 # TEST FUNCTIONS AND CREATORS
719 # TEST FUNCTIONS AND CREATORS
720 #==============================================================================
720 #==============================================================================
721 def create_test_index(repo_location, config):
721 def create_test_index(repo_location, config):
722 """
722 """
723 Makes default test index.
723 Makes default test index.
724 """
724 """
725 import rc_testdata
725 import rc_testdata
726
726
727 rc_testdata.extract_search_index(
727 rc_testdata.extract_search_index(
728 'vcs_search_index', os.path.dirname(config['search.location']))
728 'vcs_search_index', os.path.dirname(config['search.location']))
729
729
730
730
731 def create_test_directory(test_path):
731 def create_test_directory(test_path):
732 """
732 """
733 Create test directory if it doesn't exist.
733 Create test directory if it doesn't exist.
734 """
734 """
735 if not os.path.isdir(test_path):
735 if not os.path.isdir(test_path):
736 log.debug('Creating testdir %s', test_path)
736 log.debug('Creating testdir %s', test_path)
737 os.makedirs(test_path)
737 os.makedirs(test_path)
738
738
739
739
740 def create_test_database(test_path, config):
740 def create_test_database(test_path, config):
741 """
741 """
742 Makes a fresh database.
742 Makes a fresh database.
743 """
743 """
744 from rhodecode.lib.db_manage import DbManage
744 from rhodecode.lib.db_manage import DbManage
745
745
746 # PART ONE create db
746 # PART ONE create db
747 dbconf = config['sqlalchemy.db1.url']
747 dbconf = config['sqlalchemy.db1.url']
748 log.debug('making test db %s', dbconf)
748 log.debug('making test db %s', dbconf)
749
749
750 dbmanage = DbManage(log_sql=False, dbconf=dbconf, root=config['here'],
750 dbmanage = DbManage(log_sql=False, dbconf=dbconf, root=config['here'],
751 tests=True, cli_args={'force_ask': True})
751 tests=True, cli_args={'force_ask': True})
752 dbmanage.create_tables(override=True)
752 dbmanage.create_tables(override=True)
753 dbmanage.set_db_version()
753 dbmanage.set_db_version()
754 # for tests dynamically set new root paths based on generated content
754 # for tests dynamically set new root paths based on generated content
755 dbmanage.create_settings(dbmanage.config_prompt(test_path))
755 dbmanage.create_settings(dbmanage.config_prompt(test_path))
756 dbmanage.create_default_user()
756 dbmanage.create_default_user()
757 dbmanage.create_test_admin_and_users()
757 dbmanage.create_test_admin_and_users()
758 dbmanage.create_permissions()
758 dbmanage.create_permissions()
759 dbmanage.populate_default_permissions()
759 dbmanage.populate_default_permissions()
760 Session().commit()
760 Session().commit()
761
761
762
762
763 def create_test_repositories(test_path, config):
763 def create_test_repositories(test_path, config):
764 """
764 """
765 Creates test repositories in the temporary directory. Repositories are
765 Creates test repositories in the temporary directory. Repositories are
766 extracted from archives within the rc_testdata package.
766 extracted from archives within the rc_testdata package.
767 """
767 """
768 import rc_testdata
768 import rc_testdata
769 from rhodecode.tests import HG_REPO, GIT_REPO, SVN_REPO
769 from rhodecode.tests import HG_REPO, GIT_REPO, SVN_REPO
770
770
771 log.debug('making test vcs repositories')
771 log.debug('making test vcs repositories')
772
772
773 idx_path = config['search.location']
773 idx_path = config['search.location']
774 data_path = config['cache_dir']
774 data_path = config['cache_dir']
775
775
776 # clean index and data
776 # clean index and data
777 if idx_path and os.path.exists(idx_path):
777 if idx_path and os.path.exists(idx_path):
778 log.debug('remove %s', idx_path)
778 log.debug('remove %s', idx_path)
779 shutil.rmtree(idx_path)
779 shutil.rmtree(idx_path)
780
780
781 if data_path and os.path.exists(data_path):
781 if data_path and os.path.exists(data_path):
782 log.debug('remove %s', data_path)
782 log.debug('remove %s', data_path)
783 shutil.rmtree(data_path)
783 shutil.rmtree(data_path)
784
784
785 rc_testdata.extract_hg_dump('vcs_test_hg', jn(test_path, HG_REPO))
785 rc_testdata.extract_hg_dump('vcs_test_hg', jn(test_path, HG_REPO))
786 rc_testdata.extract_git_dump('vcs_test_git', jn(test_path, GIT_REPO))
786 rc_testdata.extract_git_dump('vcs_test_git', jn(test_path, GIT_REPO))
787
787
788 # Note: Subversion is in the process of being integrated with the system,
788 # Note: Subversion is in the process of being integrated with the system,
789 # until we have a properly packed version of the test svn repository, this
789 # until we have a properly packed version of the test svn repository, this
790 # tries to copy over the repo from a package "rc_testdata"
790 # tries to copy over the repo from a package "rc_testdata"
791 svn_repo_path = rc_testdata.get_svn_repo_archive()
791 svn_repo_path = rc_testdata.get_svn_repo_archive()
792 with tarfile.open(svn_repo_path) as tar:
792 with tarfile.open(svn_repo_path) as tar:
793 tar.extractall(jn(test_path, SVN_REPO))
793 tar.extractall(jn(test_path, SVN_REPO))
794
794
795
795
796 #==============================================================================
796 #==============================================================================
797 # PASTER COMMANDS
797 # PASTER COMMANDS
798 #==============================================================================
798 #==============================================================================
799 class BasePasterCommand(Command):
799 class BasePasterCommand(Command):
800 """
800 """
801 Abstract Base Class for paster commands.
801 Abstract Base Class for paster commands.
802
802
803 The celery commands are somewhat aggressive about loading
803 The celery commands are somewhat aggressive about loading
804 celery.conf, and since our module sets the `CELERY_LOADER`
804 celery.conf, and since our module sets the `CELERY_LOADER`
805 environment variable to our loader, we have to bootstrap a bit and
805 environment variable to our loader, we have to bootstrap a bit and
806 make sure we've had a chance to load the pylons config off of the
806 make sure we've had a chance to load the pylons config off of the
807 command line, otherwise everything fails.
807 command line, otherwise everything fails.
808 """
808 """
809 min_args = 1
809 min_args = 1
810 min_args_error = "Please provide a paster config file as an argument."
810 min_args_error = "Please provide a paster config file as an argument."
811 takes_config_file = 1
811 takes_config_file = 1
812 requires_config_file = True
812 requires_config_file = True
813
813
814 def notify_msg(self, msg, log=False):
814 def notify_msg(self, msg, log=False):
815 """Make a notification to user, additionally if logger is passed
815 """Make a notification to user, additionally if logger is passed
816 it logs this action using given logger
816 it logs this action using given logger
817
817
818 :param msg: message that will be printed to user
818 :param msg: message that will be printed to user
819 :param log: logging instance, to use to additionally log this message
819 :param log: logging instance, to use to additionally log this message
820
820
821 """
821 """
822 if log and isinstance(log, logging):
822 if log and isinstance(log, logging):
823 log(msg)
823 log(msg)
824
824
825 def run(self, args):
825 def run(self, args):
826 """
826 """
827 Overrides Command.run
827 Overrides Command.run
828
828
829 Checks for a config file argument and loads it.
829 Checks for a config file argument and loads it.
830 """
830 """
831 if len(args) < self.min_args:
831 if len(args) < self.min_args:
832 raise BadCommand(
832 raise BadCommand(
833 self.min_args_error % {'min_args': self.min_args,
833 self.min_args_error % {'min_args': self.min_args,
834 'actual_args': len(args)})
834 'actual_args': len(args)})
835
835
836 # Decrement because we're going to lob off the first argument.
836 # Decrement because we're going to lob off the first argument.
837 # @@ This is hacky
837 # @@ This is hacky
838 self.min_args -= 1
838 self.min_args -= 1
839 self.bootstrap_config(args[0])
839 self.bootstrap_config(args[0])
840 self.update_parser()
840 self.update_parser()
841 return super(BasePasterCommand, self).run(args[1:])
841 return super(BasePasterCommand, self).run(args[1:])
842
842
843 def update_parser(self):
843 def update_parser(self):
844 """
844 """
845 Abstract method. Allows for the class' parser to be updated
845 Abstract method. Allows for the class' parser to be updated
846 before the superclass' `run` method is called. Necessary to
846 before the superclass' `run` method is called. Necessary to
847 allow options/arguments to be passed through to the underlying
847 allow options/arguments to be passed through to the underlying
848 celery command.
848 celery command.
849 """
849 """
850 raise NotImplementedError("Abstract Method.")
850 raise NotImplementedError("Abstract Method.")
851
851
852 def bootstrap_config(self, conf):
852 def bootstrap_config(self, conf):
853 """
853 """
854 Loads the pylons configuration.
854 Loads the pylons configuration.
855 """
855 """
856 from pylons import config as pylonsconfig
856 from pylons import config as pylonsconfig
857
857
858 self.path_to_ini_file = os.path.realpath(conf)
858 self.path_to_ini_file = os.path.realpath(conf)
859 conf = paste.deploy.appconfig('config:' + self.path_to_ini_file)
859 conf = paste.deploy.appconfig('config:' + self.path_to_ini_file)
860 pylonsconfig.init_app(conf.global_conf, conf.local_conf)
860 pylonsconfig.init_app(conf.global_conf, conf.local_conf)
861
861
862 def _init_session(self):
862 def _init_session(self):
863 """
863 """
864 Inits SqlAlchemy Session
864 Inits SqlAlchemy Session
865 """
865 """
866 logging.config.fileConfig(self.path_to_ini_file)
866 logging.config.fileConfig(self.path_to_ini_file)
867 from pylons import config
867 from pylons import config
868 from rhodecode.config.utils import initialize_database
868 from rhodecode.config.utils import initialize_database
869
869
870 # get to remove repos !!
870 # get to remove repos !!
871 add_cache(config)
871 add_cache(config)
872 initialize_database(config)
872 initialize_database(config)
873
873
874
874
875 @decorator.decorator
875 @decorator.decorator
876 def jsonify(func, *args, **kwargs):
876 def jsonify(func, *args, **kwargs):
877 """Action decorator that formats output for JSON
877 """Action decorator that formats output for JSON
878
878
879 Given a function that will return content, this decorator will turn
879 Given a function that will return content, this decorator will turn
880 the result into JSON, with a content-type of 'application/json' and
880 the result into JSON, with a content-type of 'application/json' and
881 output it.
881 output it.
882
882
883 """
883 """
884 from pylons.decorators.util import get_pylons
884 from pylons.decorators.util import get_pylons
885 from rhodecode.lib.ext_json import json
885 from rhodecode.lib.ext_json import json
886 pylons = get_pylons(args)
886 pylons = get_pylons(args)
887 pylons.response.headers['Content-Type'] = 'application/json; charset=utf-8'
887 pylons.response.headers['Content-Type'] = 'application/json; charset=utf-8'
888 data = func(*args, **kwargs)
888 data = func(*args, **kwargs)
889 if isinstance(data, (list, tuple)):
889 if isinstance(data, (list, tuple)):
890 msg = "JSON responses with Array envelopes are susceptible to " \
890 msg = "JSON responses with Array envelopes are susceptible to " \
891 "cross-site data leak attacks, see " \
891 "cross-site data leak attacks, see " \
892 "http://wiki.pylonshq.com/display/pylonsfaq/Warnings"
892 "http://wiki.pylonshq.com/display/pylonsfaq/Warnings"
893 warnings.warn(msg, Warning, 2)
893 warnings.warn(msg, Warning, 2)
894 log.warning(msg)
894 log.warning(msg)
895 log.debug("Returning JSON wrapped action output")
895 log.debug("Returning JSON wrapped action output")
896 return json.dumps(data, encoding='utf-8')
896 return json.dumps(data, encoding='utf-8')
897
897
898
898
899 class PartialRenderer(object):
899 class PartialRenderer(object):
900 """
900 """
901 Partial renderer used to render chunks of html used in datagrids
901 Partial renderer used to render chunks of html used in datagrids
902 use like::
902 use like::
903
903
904 _render = PartialRenderer('data_table/_dt_elements.html')
904 _render = PartialRenderer('data_table/_dt_elements.html')
905 _render('quick_menu', args, kwargs)
905 _render('quick_menu', args, kwargs)
906 PartialRenderer.h,
906 PartialRenderer.h,
907 c,
907 c,
908 _,
908 _,
909 ungettext
909 ungettext
910 are the template stuff initialized inside and can be re-used later
910 are the template stuff initialized inside and can be re-used later
911
911
912 :param tmpl_name: template path relate to /templates/ dir
912 :param tmpl_name: template path relate to /templates/ dir
913 """
913 """
914
914
915 def __init__(self, tmpl_name):
915 def __init__(self, tmpl_name):
916 import rhodecode
916 import rhodecode
917 from pylons import request, tmpl_context as c
917 from pylons import request, tmpl_context as c
918 from pylons.i18n.translation import _, ungettext
918 from pylons.i18n.translation import _, ungettext
919 from rhodecode.lib import helpers as h
919 from rhodecode.lib import helpers as h
920
920
921 self.tmpl_name = tmpl_name
921 self.tmpl_name = tmpl_name
922 self.rhodecode = rhodecode
922 self.rhodecode = rhodecode
923 self.c = c
923 self.c = c
924 self._ = _
924 self._ = _
925 self.ungettext = ungettext
925 self.ungettext = ungettext
926 self.h = h
926 self.h = h
927 self.request = request
927 self.request = request
928
928
929 def _mako_lookup(self):
929 def _mako_lookup(self):
930 _tmpl_lookup = self.rhodecode.CONFIG['pylons.app_globals'].mako_lookup
930 _tmpl_lookup = self.rhodecode.CONFIG['pylons.app_globals'].mako_lookup
931 return _tmpl_lookup.get_template(self.tmpl_name)
931 return _tmpl_lookup.get_template(self.tmpl_name)
932
932
933 def _update_kwargs_for_render(self, kwargs):
933 def _update_kwargs_for_render(self, kwargs):
934 """
934 """
935 Inject params required for Mako rendering
935 Inject params required for Mako rendering
936 """
936 """
937 _kwargs = {
937 _kwargs = {
938 '_': self._,
938 '_': self._,
939 'h': self.h,
939 'h': self.h,
940 'c': self.c,
940 'c': self.c,
941 'request': self.request,
941 'request': self.request,
942 'ungettext': self.ungettext,
942 'ungettext': self.ungettext,
943 }
943 }
944 _kwargs.update(kwargs)
944 _kwargs.update(kwargs)
945 return _kwargs
945 return _kwargs
946
946
947 def _render_with_exc(self, render_func, args, kwargs):
947 def _render_with_exc(self, render_func, args, kwargs):
948 try:
948 try:
949 return render_func.render(*args, **kwargs)
949 return render_func.render(*args, **kwargs)
950 except:
950 except:
951 log.error(exceptions.text_error_template().render())
951 log.error(exceptions.text_error_template().render())
952 raise
952 raise
953
953
954 def _get_template(self, template_obj, def_name):
954 def _get_template(self, template_obj, def_name):
955 if def_name:
955 if def_name:
956 tmpl = template_obj.get_def(def_name)
956 tmpl = template_obj.get_def(def_name)
957 else:
957 else:
958 tmpl = template_obj
958 tmpl = template_obj
959 return tmpl
959 return tmpl
960
960
961 def render(self, def_name, *args, **kwargs):
961 def render(self, def_name, *args, **kwargs):
962 lookup_obj = self._mako_lookup()
962 lookup_obj = self._mako_lookup()
963 tmpl = self._get_template(lookup_obj, def_name=def_name)
963 tmpl = self._get_template(lookup_obj, def_name=def_name)
964 kwargs = self._update_kwargs_for_render(kwargs)
964 kwargs = self._update_kwargs_for_render(kwargs)
965 return self._render_with_exc(tmpl, args, kwargs)
965 return self._render_with_exc(tmpl, args, kwargs)
966
966
967 def __call__(self, tmpl, *args, **kwargs):
967 def __call__(self, tmpl, *args, **kwargs):
968 return self.render(tmpl, *args, **kwargs)
968 return self.render(tmpl, *args, **kwargs)
969
969
970
970
971 def password_changed(auth_user, session):
971 def password_changed(auth_user, session):
972 # Never report password change in case of default user or anonymous user.
972 # Never report password change in case of default user or anonymous user.
973 if auth_user.username == User.DEFAULT_USER or auth_user.user_id is None:
973 if auth_user.username == User.DEFAULT_USER or auth_user.user_id is None:
974 return False
974 return False
975
975
976 password_hash = md5(auth_user.password) if auth_user.password else None
976 password_hash = md5(auth_user.password) if auth_user.password else None
977 rhodecode_user = session.get('rhodecode_user', {})
977 rhodecode_user = session.get('rhodecode_user', {})
978 session_password_hash = rhodecode_user.get('password', '')
978 session_password_hash = rhodecode_user.get('password', '')
979 return password_hash != session_password_hash
979 return password_hash != session_password_hash
980
980
981
981
982 def read_opensource_licenses():
982 def read_opensource_licenses():
983 global _license_cache
983 global _license_cache
984
984
985 if not _license_cache:
985 if not _license_cache:
986 licenses = pkg_resources.resource_string(
986 licenses = pkg_resources.resource_string(
987 'rhodecode', 'config/licenses.json')
987 'rhodecode', 'config/licenses.json')
988 _license_cache = json.loads(licenses)
988 _license_cache = json.loads(licenses)
989
989
990 return _license_cache
990 return _license_cache
991
991
992
992
993 def get_registry(request):
993 def get_registry(request):
994 """
994 """
995 Utility to get the pyramid registry from a request. During migration to
995 Utility to get the pyramid registry from a request. During migration to
996 pyramid we sometimes want to use the pyramid registry from pylons context.
996 pyramid we sometimes want to use the pyramid registry from pylons context.
997 Therefore this utility returns `request.registry` for pyramid requests and
997 Therefore this utility returns `request.registry` for pyramid requests and
998 uses `get_current_registry()` for pylons requests.
998 uses `get_current_registry()` for pylons requests.
999 """
999 """
1000 try:
1000 try:
1001 return request.registry
1001 return request.registry
1002 except AttributeError:
1002 except AttributeError:
1003 return get_current_registry()
1003 return get_current_registry()
General Comments 0
You need to be logged in to leave comments. Login now