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