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