##// END OF EJS Templates
Made config file free configuration based on database and capable of beeing manage via application settings + some code cleanups
marcink -
r341:1ef52a70 default
parent child Browse files
Show More
@@ -1,70 +1,73 b''
1 """Pylons environment configuration"""
1 """Pylons environment configuration"""
2 from mako.lookup import TemplateLookup
2 from mako.lookup import TemplateLookup
3 from pylons.configuration import PylonsConfig
3 from pylons.configuration import PylonsConfig
4 from pylons.error import handle_mako_error
4 from pylons.error import handle_mako_error
5 from pylons_app.config.routing import make_map
5 from pylons_app.config.routing import make_map
6 from pylons_app.lib.auth import set_available_permissions, set_base_path
6 from pylons_app.lib.auth import set_available_permissions, set_base_path
7 from pylons_app.lib.utils import repo2db_mapper
7 from pylons_app.lib.utils import repo2db_mapper, make_ui, set_hg_app_config
8 from pylons_app.model import init_model
8 from pylons_app.model import init_model
9 from pylons_app.model.hg_model import _get_repos_cached_initial
9 from pylons_app.model.hg_model import _get_repos_cached_initial
10 from sqlalchemy import engine_from_config
10 from sqlalchemy import engine_from_config
11 import logging
11 import logging
12 import os
12 import os
13 import pylons_app.lib.app_globals as app_globals
13 import pylons_app.lib.app_globals as app_globals
14 import pylons_app.lib.helpers
14 import pylons_app.lib.helpers
15
15
16 log = logging.getLogger(__name__)
16 log = logging.getLogger(__name__)
17
17
18 def load_environment(global_conf, app_conf):
18 def load_environment(global_conf, app_conf):
19 """Configure the Pylons environment via the ``pylons.config``
19 """Configure the Pylons environment via the ``pylons.config``
20 object
20 object
21 """
21 """
22 config = PylonsConfig()
22 config = PylonsConfig()
23
23
24 # Pylons paths
24 # Pylons paths
25 root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
25 root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
26 paths = dict(root=root,
26 paths = dict(root=root,
27 controllers=os.path.join(root, 'controllers'),
27 controllers=os.path.join(root, 'controllers'),
28 static_files=os.path.join(root, 'public'),
28 static_files=os.path.join(root, 'public'),
29 templates=[os.path.join(root, 'templates')])
29 templates=[os.path.join(root, 'templates')])
30
30
31 # Initialize config with the basic options
31 # Initialize config with the basic options
32 config.init_app(global_conf, app_conf, package='pylons_app', paths=paths)
32 config.init_app(global_conf, app_conf, package='pylons_app', paths=paths)
33
33
34 config['routes.map'] = make_map(config)
34 config['routes.map'] = make_map(config)
35 config['pylons.app_globals'] = app_globals.Globals(config)
35 config['pylons.app_globals'] = app_globals.Globals(config)
36 config['pylons.h'] = pylons_app.lib.helpers
36 config['pylons.h'] = pylons_app.lib.helpers
37
37
38 # Setup cache object as early as possible
38 # Setup cache object as early as possible
39 import pylons
39 import pylons
40 pylons.cache._push_object(config['pylons.app_globals'].cache)
40 pylons.cache._push_object(config['pylons.app_globals'].cache)
41
41
42 # Create the Mako TemplateLookup, with the default auto-escaping
42 # Create the Mako TemplateLookup, with the default auto-escaping
43 config['pylons.app_globals'].mako_lookup = TemplateLookup(
43 config['pylons.app_globals'].mako_lookup = TemplateLookup(
44 directories=paths['templates'],
44 directories=paths['templates'],
45 error_handler=handle_mako_error,
45 error_handler=handle_mako_error,
46 module_directory=os.path.join(app_conf['cache_dir'], 'templates'),
46 module_directory=os.path.join(app_conf['cache_dir'], 'templates'),
47 input_encoding='utf-8', default_filters=['escape'],
47 input_encoding='utf-8', default_filters=['escape'],
48 imports=['from webhelpers.html import escape'])
48 imports=['from webhelpers.html import escape'])
49
49
50 #sets the c attribute access when don't existing attribute are accessed
50 #sets the c attribute access when don't existing attribute are accessed
51 config['pylons.strict_tmpl_context'] = True
51 config['pylons.strict_tmpl_context'] = True
52
52
53 #MULTIPLE DB configs
53 #MULTIPLE DB configs
54 # Setup the SQLAlchemy database engine
54 # Setup the SQLAlchemy database engine
55 if config['debug']:
55 if config['debug']:
56 #use query time debugging.
56 #use query time debugging.
57 from pylons_app.lib.timerproxy import TimerProxy
57 from pylons_app.lib.timerproxy import TimerProxy
58 sa_engine_db1 = engine_from_config(config, 'sqlalchemy.db1.',
58 sa_engine_db1 = engine_from_config(config, 'sqlalchemy.db1.',
59 proxy=TimerProxy())
59 proxy=TimerProxy())
60 else:
60 else:
61 sa_engine_db1 = engine_from_config(config, 'sqlalchemy.db1.')
61 sa_engine_db1 = engine_from_config(config, 'sqlalchemy.db1.')
62
62
63 init_model(sa_engine_db1)
63 init_model(sa_engine_db1)
64 config['pylons.app_globals'].baseui = make_ui('db')
65
64 repo2db_mapper(_get_repos_cached_initial(config['pylons.app_globals']))
66 repo2db_mapper(_get_repos_cached_initial(config['pylons.app_globals']))
65 set_available_permissions(config)
67 set_available_permissions(config)
66 set_base_path(config)
68 set_base_path(config)
69 set_hg_app_config(config)
67 # CONFIGURATION OPTIONS HERE (note: all config options will override
70 # CONFIGURATION OPTIONS HERE (note: all config options will override
68 # any Pylons config options)
71 # any Pylons config options)
69
72
70 return config
73 return config
@@ -1,73 +1,72 b''
1 """Pylons middleware initialization"""
1 """Pylons middleware initialization"""
2 from beaker.middleware import SessionMiddleware
2 from beaker.middleware import SessionMiddleware
3 from paste.cascade import Cascade
3 from paste.cascade import Cascade
4 from paste.registry import RegistryManager
4 from paste.registry import RegistryManager
5 from paste.urlparser import StaticURLParser
5 from paste.urlparser import StaticURLParser
6 from paste.deploy.converters import asbool
6 from paste.deploy.converters import asbool
7 from pylons.middleware import ErrorHandler, StatusCodeRedirect
7 from pylons.middleware import ErrorHandler, StatusCodeRedirect
8 from pylons.wsgiapp import PylonsApp
8 from pylons.wsgiapp import PylonsApp
9 from routes.middleware import RoutesMiddleware
9 from routes.middleware import RoutesMiddleware
10 from pylons_app.lib.middleware.simplehg import SimpleHg
10 from pylons_app.lib.middleware.simplehg import SimpleHg
11 from pylons_app.lib.middleware.https_fixup import HttpsFixup
11 from pylons_app.lib.middleware.https_fixup import HttpsFixup
12 from pylons_app.config.environment import load_environment
12 from pylons_app.config.environment import load_environment
13
13
14 def make_app(global_conf, full_stack=True, static_files=True, **app_conf):
14 def make_app(global_conf, full_stack=True, static_files=True, **app_conf):
15 """Create a Pylons WSGI application and return it
15 """Create a Pylons WSGI application and return it
16
16
17 ``global_conf``
17 ``global_conf``
18 The inherited configuration for this application. Normally from
18 The inherited configuration for this application. Normally from
19 the [DEFAULT] section of the Paste ini file.
19 the [DEFAULT] section of the Paste ini file.
20
20
21 ``full_stack``
21 ``full_stack``
22 Whether or not this application provides a full WSGI stack (by
22 Whether or not this application provides a full WSGI stack (by
23 default, meaning it handles its own exceptions and errors).
23 default, meaning it handles its own exceptions and errors).
24 Disable full_stack when this application is "managed" by
24 Disable full_stack when this application is "managed" by
25 another WSGI middleware.
25 another WSGI middleware.
26
26
27 ``app_conf``
27 ``app_conf``
28 The application's local configuration. Normally specified in
28 The application's local configuration. Normally specified in
29 the [app:<name>] section of the Paste ini file (where <name>
29 the [app:<name>] section of the Paste ini file (where <name>
30 defaults to main).
30 defaults to main).
31
31
32 """
32 """
33 # Configure the Pylons environment
33 # Configure the Pylons environment
34 config = load_environment(global_conf, app_conf)
34 config = load_environment(global_conf, app_conf)
35
35
36 # The Pylons WSGI app
36 # The Pylons WSGI app
37 app = PylonsApp(config=config)
37 app = PylonsApp(config=config)
38
38
39 # Routing/Session/Cache Middleware
39 # Routing/Session/Cache Middleware
40 app = RoutesMiddleware(app, config['routes.map'])
40 app = RoutesMiddleware(app, config['routes.map'])
41 app = SessionMiddleware(app, config)
41 app = SessionMiddleware(app, config)
42
42
43 # CUSTOM MIDDLEWARE HERE (filtered by error handling middlewares)
43 # CUSTOM MIDDLEWARE HERE (filtered by error handling middlewares)
44 #set the https based on HTTP_X_URL_SCHEME
45
44
46 app = SimpleHg(app, config)
45 app = SimpleHg(app, config)
47
46
48 if asbool(full_stack):
47 if asbool(full_stack):
49 # Handle Python exceptions
48 # Handle Python exceptions
50 app = ErrorHandler(app, global_conf, **config['pylons.errorware'])
49 app = ErrorHandler(app, global_conf, **config['pylons.errorware'])
51
50
52 # Display error documents for 401, 403, 404 status codes (and
51 # Display error documents for 401, 403, 404 status codes (and
53 # 500 when debug is disabled)
52 # 500 when debug is disabled)
54 if asbool(config['debug']):
53 if asbool(config['debug']):
55 app = StatusCodeRedirect(app)
54 app = StatusCodeRedirect(app)
56 else:
55 else:
57 app = StatusCodeRedirect(app, [400, 401, 403, 404, 500])
56 app = StatusCodeRedirect(app, [400, 401, 403, 404, 500])
58
57
59 #enable https redirets based on HTTP_X_URL_SCHEME set by proxy
58 #enable https redirets based on HTTP_X_URL_SCHEME set by proxy
60 app = HttpsFixup(app)
59 app = HttpsFixup(app)
61
60
62 # Establish the Registry for this application
61 # Establish the Registry for this application
63 app = RegistryManager(app)
62 app = RegistryManager(app)
64
63
65 if asbool(static_files):
64 if asbool(static_files):
66 # Serve static files
65 # Serve static files
67 static_app = StaticURLParser(config['pylons.paths']['static_files'])
66 static_app = StaticURLParser(config['pylons.paths']['static_files'])
68 app = Cascade([static_app, app])
67 app = Cascade([static_app, app])
69
68
70 app.config = config
69 app.config = config
71
70
72 return app
71 return app
73
72
@@ -1,97 +1,95 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # settings controller for pylons
3 # settings controller for pylons
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5
5
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
9 # of the License or (at your opinion) any later version of the license.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
19 # MA 02110-1301, USA.
20 """
20 """
21 Created on June 30, 2010
21 Created on June 30, 2010
22 settings controller for pylons
22 settings controller for pylons
23 @author: marcink
23 @author: marcink
24 """
24 """
25 from formencode import htmlfill
25 from formencode import htmlfill
26 from pylons import tmpl_context as c, request, url
26 from pylons import tmpl_context as c, request, url
27 from pylons.controllers.util import redirect
27 from pylons.controllers.util import redirect
28 from pylons.i18n.translation import _
28 from pylons.i18n.translation import _
29 from pylons_app.lib.auth import LoginRequired, HasRepoPermissionAllDecorator
29 from pylons_app.lib.auth import LoginRequired, HasRepoPermissionAllDecorator
30 from pylons_app.lib.base import BaseController, render
30 from pylons_app.lib.base import BaseController, render
31 from pylons_app.lib.utils import invalidate_cache
31 from pylons_app.lib.utils import invalidate_cache
32 from pylons_app.model.forms import RepoSettingsForm
32 from pylons_app.model.forms import RepoSettingsForm
33 from pylons_app.model.repo_model import RepoModel
33 from pylons_app.model.repo_model import RepoModel
34 import formencode
34 import formencode
35 import logging
35 import logging
36 import pylons_app.lib.helpers as h
36 import pylons_app.lib.helpers as h
37 log = logging.getLogger(__name__)
37 log = logging.getLogger(__name__)
38
38
39 class SettingsController(BaseController):
39 class SettingsController(BaseController):
40
40
41 @LoginRequired()
41 @LoginRequired()
42 @HasRepoPermissionAllDecorator('repository.admin')
42 @HasRepoPermissionAllDecorator('repository.admin')
43 def __before__(self):
43 def __before__(self):
44 super(SettingsController, self).__before__()
44 super(SettingsController, self).__before__()
45
45
46 def index(self, repo_name):
46 def index(self, repo_name):
47 repo_model = RepoModel()
47 repo_model = RepoModel()
48 c.repo_info = repo = repo_model.get(repo_name)
48 c.repo_info = repo = repo_model.get(repo_name)
49 if not repo:
49 if not repo:
50 h.flash(_('%s repository is not mapped to db perhaps'
50 h.flash(_('%s repository is not mapped to db perhaps'
51 ' it was created or renamed from the filesystem'
51 ' it was created or renamed from the filesystem'
52 ' please run the application again'
52 ' please run the application again'
53 ' in order to rescan repositories') % repo_name,
53 ' in order to rescan repositories') % repo_name,
54 category='error')
54 category='error')
55
55
56 return redirect(url('repos'))
56 return redirect(url('repos'))
57 defaults = c.repo_info.__dict__
57 defaults = c.repo_info.__dict__
58 defaults.update({'user':c.repo_info.user.username})
58 defaults.update({'user':c.repo_info.user.username})
59 c.users_array = repo_model.get_users_js()
59 c.users_array = repo_model.get_users_js()
60
60
61 for p in c.repo_info.repo2perm:
61 for p in c.repo_info.repo2perm:
62 defaults.update({'perm_%s' % p.user.username:
62 defaults.update({'perm_%s' % p.user.username:
63 p.permission.permission_name})
63 p.permission.permission_name})
64
64
65 return htmlfill.render(
65 return htmlfill.render(
66 render('settings/repo_settings.html'),
66 render('settings/repo_settings.html'),
67 defaults=defaults,
67 defaults=defaults,
68 encoding="UTF-8",
68 encoding="UTF-8",
69 force_defaults=False
69 force_defaults=False
70 )
70 )
71
71
72 def update(self, repo_name):
72 def update(self, repo_name):
73 print request.POST
74 print 'x' * 110
75 repo_model = RepoModel()
73 repo_model = RepoModel()
76 _form = RepoSettingsForm(edit=True)()
74 _form = RepoSettingsForm(edit=True)()
77 try:
75 try:
78 form_result = _form.to_python(dict(request.POST))
76 form_result = _form.to_python(dict(request.POST))
79 repo_model.update(repo_name, form_result)
77 repo_model.update(repo_name, form_result)
80 invalidate_cache('cached_repo_list')
78 invalidate_cache('cached_repo_list')
81 h.flash(_('Repository %s updated succesfully' % repo_name),
79 h.flash(_('Repository %s updated succesfully' % repo_name),
82 category='success')
80 category='success')
83
81
84 except formencode.Invalid as errors:
82 except formencode.Invalid as errors:
85 c.repo_info = repo_model.get(repo_name)
83 c.repo_info = repo_model.get(repo_name)
86 c.users_array = repo_model.get_users_js()
84 c.users_array = repo_model.get_users_js()
87 errors.value.update({'user':c.repo_info.user.username})
85 errors.value.update({'user':c.repo_info.user.username})
88 c.form_errors = errors.error_dict
86 c.form_errors = errors.error_dict
89 return htmlfill.render(
87 return htmlfill.render(
90 render('admin/repos/repo_edit.html'),
88 render('admin/repos/repo_edit.html'),
91 defaults=errors.value,
89 defaults=errors.value,
92 encoding="UTF-8")
90 encoding="UTF-8")
93 except Exception:
91 except Exception:
94 h.flash(_('error occured during update of repository %s') \
92 h.flash(_('error occured during update of repository %s') \
95 % form_result['repo_name'], category='error')
93 % form_result['repo_name'], category='error')
96
94
97 return redirect(url('repo_settings_home', repo_name=repo_name))
95 return redirect(url('repo_settings_home', repo_name=repo_name))
@@ -1,25 +1,33 b''
1 """The application's Globals object"""
1 """The application's Globals object"""
2
2
3 from beaker.cache import CacheManager
3 from beaker.cache import CacheManager
4 from beaker.util import parse_cache_config_options
4 from beaker.util import parse_cache_config_options
5 from pylons_app.lib.utils import make_ui
5 from vcs.utils.lazy import LazyProperty
6
6
7 class Globals(object):
7 class Globals(object):
8
9 """Globals acts as a container for objects available throughout the
8 """Globals acts as a container for objects available throughout the
10 life of the application
9 life of the application
11
10
12 """
11 """
13
12
14 def __init__(self, config):
13 def __init__(self, config):
15 """One instance of Globals is created during application
14 """One instance of Globals is created during application
16 initialization and is available during requests via the
15 initialization and is available during requests via the
17 'app_globals' variable
16 'app_globals' variable
18
17
19 """
18 """
20 self.cache = CacheManager(**parse_cache_config_options(config))
19 self.cache = CacheManager(**parse_cache_config_options(config))
21 self.baseui = make_ui(config['hg_app_repo_conf'])
22 self.paths = self.baseui.configitems('paths')
23 self.base_path = self.paths[0][1].replace('*', '')
24 self.changeset_annotation_colors = {}
20 self.changeset_annotation_colors = {}
25 self.available_permissions = None # propagated after init_model
21 self.available_permissions = None # propagated after init_model
22 self.app_title = None # propagated after init_model
23 self.baseui = None # propagated after init_model
24
25 @LazyProperty
26 def paths(self):
27 if self.baseui:
28 return self.baseui.configitems('paths')
29
30 @LazyProperty
31 def base_path(self):
32 if self.baseui:
33 return self.paths[0][1].replace('*', '')
@@ -1,134 +1,188 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # database managment for hg app
3 # database managment for hg app
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5
5
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
9 # of the License or (at your opinion) any later version of the license.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
19 # MA 02110-1301, USA.
20
20
21 """
21 """
22 Created on April 10, 2010
22 Created on April 10, 2010
23 database managment and creation for hg app
23 database managment and creation for hg app
24 @author: marcink
24 @author: marcink
25 """
25 """
26
26
27 from os.path import dirname as dn, join as jn
27 from os.path import dirname as dn, join as jn
28 import os
28 import os
29 import sys
29 import sys
30 import uuid
30 import uuid
31 ROOT = dn(dn(dn(os.path.realpath(__file__))))
31 ROOT = dn(dn(dn(os.path.realpath(__file__))))
32 sys.path.append(ROOT)
32 sys.path.append(ROOT)
33
33
34 from pylons_app.lib.auth import get_crypt_password
34 from pylons_app.lib.auth import get_crypt_password
35 from pylons_app.model import init_model
35 from pylons_app.model import init_model
36 from pylons_app.model.db import User, Permission
36 from pylons_app.model.db import User, Permission, HgAppUi
37 from pylons_app.model.meta import Session, Base
37 from pylons_app.model.meta import Session, Base
38 from sqlalchemy.engine import create_engine
38 from sqlalchemy.engine import create_engine
39 import logging
39 import logging
40
40
41 log = logging.getLogger('db manage')
41 log = logging.getLogger('db manage')
42 log.setLevel(logging.DEBUG)
42 log.setLevel(logging.DEBUG)
43 console_handler = logging.StreamHandler()
43 console_handler = logging.StreamHandler()
44 console_handler.setFormatter(logging.Formatter("%(asctime)s.%(msecs)03d"
44 console_handler.setFormatter(logging.Formatter("%(asctime)s.%(msecs)03d"
45 " %(levelname)-5.5s [%(name)s] %(message)s"))
45 " %(levelname)-5.5s [%(name)s] %(message)s"))
46 log.addHandler(console_handler)
46 log.addHandler(console_handler)
47
47
48 class DbManage(object):
48 class DbManage(object):
49 def __init__(self, log_sql):
49 def __init__(self, log_sql):
50 self.dbname = 'hg_app.db'
50 self.dbname = 'hg_app.db'
51 dburi = 'sqlite:////%s' % jn(ROOT, self.dbname)
51 dburi = 'sqlite:////%s' % jn(ROOT, self.dbname)
52 engine = create_engine(dburi, echo=log_sql)
52 engine = create_engine(dburi, echo=log_sql)
53 init_model(engine)
53 init_model(engine)
54 self.sa = Session()
54 self.sa = Session()
55 self.db_exists = False
55 self.db_exists = False
56
56
57 def check_for_db(self, override):
57 def check_for_db(self, override):
58 log.info('checking for exisiting db')
58 log.info('checking for exisiting db')
59 if os.path.isfile(jn(ROOT, self.dbname)):
59 if os.path.isfile(jn(ROOT, self.dbname)):
60 self.db_exists = True
60 self.db_exists = True
61 log.info('database exisist')
61 log.info('database exisist')
62 if not override:
62 if not override:
63 raise Exception('database already exists')
63 raise Exception('database already exists')
64
64
65 def create_tables(self, override=False):
65 def create_tables(self, override=False):
66 """
66 """
67 Create a auth database
67 Create a auth database
68 """
68 """
69 self.check_for_db(override)
69 self.check_for_db(override)
70 if override:
70 if override:
71 log.info("database exisist and it's going to be destroyed")
71 log.info("database exisist and it's going to be destroyed")
72 if self.db_exists:
72 if self.db_exists:
73 os.remove(jn(ROOT, self.dbname))
73 os.remove(jn(ROOT, self.dbname))
74 Base.metadata.create_all(checkfirst=override)
74 Base.metadata.create_all(checkfirst=override)
75 log.info('Created tables for %s', self.dbname)
75 log.info('Created tables for %s', self.dbname)
76
76
77 def admin_prompt(self):
77 def admin_prompt(self):
78 import getpass
78 import getpass
79 username = raw_input('Specify admin username:')
79 username = raw_input('Specify admin username:')
80 password = getpass.getpass('Specify admin password:')
80 password = getpass.getpass('Specify admin password:')
81 self.create_user(username, password, True)
81 self.create_user(username, password, True)
82
83 def config_prompt(self):
84 log.info('Seting up repositories.config')
82
85
86
87 path = raw_input('Specify valid full path to your repositories'
88 ' you can change this later application settings:')
89
90 if not os.path.isdir(path):
91 log.error('You entered wrong path')
92 sys.exit()
93
94 hooks = HgAppUi()
95 hooks.ui_section = 'hooks'
96 hooks.ui_key = 'changegroup'
97 hooks.ui_value = 'hg update >&2'
98
99 web1 = HgAppUi()
100 web1.ui_section = 'web'
101 web1.ui_key = 'push_ssl'
102 web1.ui_value = 'false'
103
104 web2 = HgAppUi()
105 web2.ui_section = 'web'
106 web2.ui_key = 'allow_archive'
107 web2.ui_value = 'gz zip bz2'
108
109 web3 = HgAppUi()
110 web3.ui_section = 'web'
111 web3.ui_key = 'allow_push'
112 web3.ui_value = '*'
113
114 web4 = HgAppUi()
115 web4.ui_section = 'web'
116 web4.ui_key = 'baseurl'
117 web4.ui_value = '/'
118
119 paths = HgAppUi()
120 paths.ui_section = 'paths'
121 paths.ui_key = '/'
122 paths.ui_value = os.path.join(path, '*')
123
124
125 try:
126 self.sa.add(hooks)
127 self.sa.add(web1)
128 self.sa.add(web2)
129 self.sa.add(web3)
130 self.sa.add(web4)
131 self.sa.add(paths)
132 self.sa.commit()
133 except:
134 self.sa.rollback()
135 raise
136 log.info('created ui config')
137
83 def create_user(self, username, password, admin=False):
138 def create_user(self, username, password, admin=False):
84
139
85 log.info('creating default user')
140 log.info('creating default user')
86 #create default user for handling default permissions.
141 #create default user for handling default permissions.
87 def_user = User()
142 def_user = User()
88 def_user.username = 'default'
143 def_user.username = 'default'
89 def_user.password = get_crypt_password(str(uuid.uuid1())[:8])
144 def_user.password = get_crypt_password(str(uuid.uuid1())[:8])
90 def_user.name = 'default'
145 def_user.name = 'default'
91 def_user.lastname = 'default'
146 def_user.lastname = 'default'
92 def_user.email = 'default@default.com'
147 def_user.email = 'default@default.com'
93 def_user.admin = False
148 def_user.admin = False
94 def_user.active = False
149 def_user.active = False
95
150
96 self.sa.add(def_user)
97
98 log.info('creating administrator user %s', username)
151 log.info('creating administrator user %s', username)
99 new_user = User()
152 new_user = User()
100 new_user.username = username
153 new_user.username = username
101 new_user.password = get_crypt_password(password)
154 new_user.password = get_crypt_password(password)
102 new_user.name = 'Hg'
155 new_user.name = 'Hg'
103 new_user.lastname = 'Admin'
156 new_user.lastname = 'Admin'
104 new_user.email = 'admin@localhost'
157 new_user.email = 'admin@localhost'
105 new_user.admin = admin
158 new_user.admin = admin
106 new_user.active = True
159 new_user.active = True
107
160
108 try:
161 try:
162 self.sa.add(def_user)
109 self.sa.add(new_user)
163 self.sa.add(new_user)
110 self.sa.commit()
164 self.sa.commit()
111 except:
165 except:
112 self.sa.rollback()
166 self.sa.rollback()
113 raise
167 raise
114
168
115 def create_permissions(self):
169 def create_permissions(self):
116 #module.(access|create|change|delete)_[name]
170 #module.(access|create|change|delete)_[name]
117 #module.(read|write|owner)
171 #module.(read|write|owner)
118 perms = [('repository.none', 'Repository no access'),
172 perms = [('repository.none', 'Repository no access'),
119 ('repository.read', 'Repository read access'),
173 ('repository.read', 'Repository read access'),
120 ('repository.write', 'Repository write access'),
174 ('repository.write', 'Repository write access'),
121 ('repository.admin', 'Repository admin access'),
175 ('repository.admin', 'Repository admin access'),
122 ('hg.admin', 'Hg Administrator'),
176 ('hg.admin', 'Hg Administrator'),
123 ]
177 ]
124
178
125 for p in perms:
179 for p in perms:
126 new_perm = Permission()
180 new_perm = Permission()
127 new_perm.permission_name = p[0]
181 new_perm.permission_name = p[0]
128 new_perm.permission_longname = p[1]
182 new_perm.permission_longname = p[1]
129 try:
183 try:
130 self.sa.add(new_perm)
184 self.sa.add(new_perm)
131 self.sa.commit()
185 self.sa.commit()
132 except:
186 except:
133 self.sa.rollback()
187 self.sa.rollback()
134 raise
188 raise
@@ -1,225 +1,226 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # middleware to handle mercurial api calls
3 # middleware to handle mercurial api calls
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5
5
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
9 # of the License or (at your opinion) any later version of the license.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
19 # MA 02110-1301, USA.
20
20
21 """
21 """
22 Created on 2010-04-28
22 Created on 2010-04-28
23
23
24 @author: marcink
24 @author: marcink
25 SimpleHG middleware for handling mercurial protocol request (push/clone etc.)
25 SimpleHG middleware for handling mercurial protocol request (push/clone etc.)
26 It's implemented with basic auth function
26 It's implemented with basic auth function
27 """
27 """
28 from datetime import datetime
28 from datetime import datetime
29 from itertools import chain
29 from itertools import chain
30 from mercurial.hgweb import hgweb
30 from mercurial.hgweb import hgweb
31 from mercurial.hgweb.request import wsgiapplication
31 from mercurial.hgweb.request import wsgiapplication
32 from mercurial.error import RepoError
32 from mercurial.error import RepoError
33 from paste.auth.basic import AuthBasicAuthenticator
33 from paste.auth.basic import AuthBasicAuthenticator
34 from paste.httpheaders import REMOTE_USER, AUTH_TYPE
34 from paste.httpheaders import REMOTE_USER, AUTH_TYPE
35 from pylons_app.lib.auth import authfunc, HasPermissionAnyMiddleware
35 from pylons_app.lib.auth import authfunc, HasPermissionAnyMiddleware
36 from pylons_app.lib.utils import is_mercurial, make_ui, invalidate_cache, \
36 from pylons_app.lib.utils import is_mercurial, make_ui, invalidate_cache, \
37 check_repo_fast
37 check_repo_fast
38 from pylons_app.model import meta
38 from pylons_app.model import meta
39 from pylons_app.model.db import UserLog, User
39 from pylons_app.model.db import UserLog, User
40 import pylons_app.lib.helpers as h
40 import pylons_app.lib.helpers as h
41 from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError
41 from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError
42 import logging
42 import logging
43 import os
43 import os
44 import traceback
44 import traceback
45 log = logging.getLogger(__name__)
45 log = logging.getLogger(__name__)
46
46
47 class SimpleHg(object):
47 class SimpleHg(object):
48
48
49 def __init__(self, application, config):
49 def __init__(self, application, config):
50 self.application = application
50 self.application = application
51 self.config = config
51 self.config = config
52 #authenticate this mercurial request using
52 #authenticate this mercurial request using
53 realm = '%s %s' % (self.config['hg_app_name'], 'mercurial repository')
53 realm = self.config['hg_app_auth_realm']
54 self.authenticate = AuthBasicAuthenticator(realm, authfunc)
54 self.authenticate = AuthBasicAuthenticator(realm, authfunc)
55
55
56 def __call__(self, environ, start_response):
56 def __call__(self, environ, start_response):
57 if not is_mercurial(environ):
57 if not is_mercurial(environ):
58 return self.application(environ, start_response)
58 return self.application(environ, start_response)
59 else:
59 else:
60 #===================================================================
60 #===================================================================
61 # AUTHENTICATE THIS MERCURIAL REQUEST
61 # AUTHENTICATE THIS MERCURIAL REQUEST
62 #===================================================================
62 #===================================================================
63 username = REMOTE_USER(environ)
63 username = REMOTE_USER(environ)
64 if not username:
64 if not username:
65 result = self.authenticate(environ)
65 result = self.authenticate(environ)
66 if isinstance(result, str):
66 if isinstance(result, str):
67 AUTH_TYPE.update(environ, 'basic')
67 AUTH_TYPE.update(environ, 'basic')
68 REMOTE_USER.update(environ, result)
68 REMOTE_USER.update(environ, result)
69 else:
69 else:
70 return result.wsgi_application(environ, start_response)
70 return result.wsgi_application(environ, start_response)
71
71
72 try:
72 try:
73 repo_name = '/'.join(environ['PATH_INFO'].split('/')[1:])
73 repo_name = '/'.join(environ['PATH_INFO'].split('/')[1:])
74 except:
74 except:
75 log.error(traceback.format_exc())
75 log.error(traceback.format_exc())
76 return HTTPInternalServerError()(environ, start_response)
76 return HTTPInternalServerError()(environ, start_response)
77
77
78 #===================================================================
78 #===================================================================
79 # CHECK PERMISSIONS FOR THIS REQUEST
79 # CHECK PERMISSIONS FOR THIS REQUEST
80 #===================================================================
80 #===================================================================
81 action = self.__get_action(environ)
81 action = self.__get_action(environ)
82 if action:
82 if action:
83 username = self.__get_environ_user(environ)
83 username = self.__get_environ_user(environ)
84 try:
84 try:
85 sa = meta.Session
85 sa = meta.Session
86 user = sa.query(User)\
86 user = sa.query(User)\
87 .filter(User.username == username).one()
87 .filter(User.username == username).one()
88 except:
88 except:
89 log.error(traceback.format_exc())
89 log.error(traceback.format_exc())
90 return HTTPInternalServerError()(environ, start_response)
90 return HTTPInternalServerError()(environ, start_response)
91 #check permissions for this repository
91 #check permissions for this repository
92 if action == 'pull':
92 if action == 'pull':
93 if not HasPermissionAnyMiddleware('repository.read',
93 if not HasPermissionAnyMiddleware('repository.read',
94 'repository.write',
94 'repository.write',
95 'repository.admin')\
95 'repository.admin')\
96 (user, repo_name):
96 (user, repo_name):
97 return HTTPForbidden()(environ, start_response)
97 return HTTPForbidden()(environ, start_response)
98 if action == 'push':
98 if action == 'push':
99 if not HasPermissionAnyMiddleware('repository.write',
99 if not HasPermissionAnyMiddleware('repository.write',
100 'repository.admin')\
100 'repository.admin')\
101 (user, repo_name):
101 (user, repo_name):
102 return HTTPForbidden()(environ, start_response)
102 return HTTPForbidden()(environ, start_response)
103
103
104 #log action
104 #log action
105 proxy_key = 'HTTP_X_REAL_IP'
105 proxy_key = 'HTTP_X_REAL_IP'
106 def_key = 'REMOTE_ADDR'
106 def_key = 'REMOTE_ADDR'
107 ipaddr = environ.get(proxy_key, environ.get(def_key, '0.0.0.0'))
107 ipaddr = environ.get(proxy_key, environ.get(def_key, '0.0.0.0'))
108 self.__log_user_action(user, action, repo_name, ipaddr)
108 self.__log_user_action(user, action, repo_name, ipaddr)
109
109
110 #===================================================================
110 #===================================================================
111 # MERCURIAL REQUEST HANDLING
111 # MERCURIAL REQUEST HANDLING
112 #===================================================================
112 #===================================================================
113 environ['PATH_INFO'] = '/'#since we wrap into hgweb, reset the path
113 environ['PATH_INFO'] = '/'#since we wrap into hgweb, reset the path
114 self.baseui = make_ui(self.config['hg_app_repo_conf'])
114 self.baseui = make_ui('db')
115 self.basepath = self.config['base_path']
115 self.basepath = self.config['base_path']
116 self.repo_path = os.path.join(self.basepath, repo_name)
116 self.repo_path = os.path.join(self.basepath, repo_name)
117
117
118 #quick check if that dir exists...
118 #quick check if that dir exists...
119 if check_repo_fast(repo_name, self.basepath):
119 if check_repo_fast(repo_name, self.basepath):
120 return HTTPNotFound()(environ, start_response)
120 return HTTPNotFound()(environ, start_response)
121
122 try:
121 try:
123 app = wsgiapplication(self.__make_app)
122 app = wsgiapplication(self.__make_app)
124 except RepoError as e:
123 except RepoError as e:
125 if str(e).find('not found') != -1:
124 if str(e).find('not found') != -1:
126 return HTTPNotFound()(environ, start_response)
125 return HTTPNotFound()(environ, start_response)
127 except Exception:
126 except Exception:
128 log.error(traceback.format_exc())
127 log.error(traceback.format_exc())
129 return HTTPInternalServerError()(environ, start_response)
128 return HTTPInternalServerError()(environ, start_response)
130
129
131 #invalidate cache on push
130 #invalidate cache on push
132 if action == 'push':
131 if action == 'push':
133 self.__invalidate_cache(repo_name)
132 self.__invalidate_cache(repo_name)
134 messages = []
133 messages = []
135 messages.append('thank you for using hg-app')
134 messages.append('thank you for using hg-app')
136
135
137 return self.msg_wrapper(app, environ, start_response, messages)
136 return self.msg_wrapper(app, environ, start_response, messages)
138 else:
137 else:
139 return app(environ, start_response)
138 return app(environ, start_response)
140
139
141
140
142 def msg_wrapper(self, app, environ, start_response, messages=[]):
141 def msg_wrapper(self, app, environ, start_response, messages=[]):
143 """
142 """
144 Wrapper for custom messages that come out of mercurial respond messages
143 Wrapper for custom messages that come out of mercurial respond messages
145 is a list of messages that the user will see at the end of response
144 is a list of messages that the user will see at the end of response
146 from merurial protocol actions that involves remote answers
145 from merurial protocol actions that involves remote answers
147 @param app:
146 @param app:
148 @param environ:
147 @param environ:
149 @param start_response:
148 @param start_response:
150 """
149 """
151 def custom_messages(msg_list):
150 def custom_messages(msg_list):
152 for msg in msg_list:
151 for msg in msg_list:
153 yield msg + '\n'
152 yield msg + '\n'
154 org_response = app(environ, start_response)
153 org_response = app(environ, start_response)
155 return chain(org_response, custom_messages(messages))
154 return chain(org_response, custom_messages(messages))
156
155
157 def __make_app(self):
156 def __make_app(self):
158 hgserve = hgweb(self.repo_path)
157 hgserve = hgweb(str(self.repo_path), baseui=self.baseui)
159 return self.__load_web_settings(hgserve)
158 return self.__load_web_settings(hgserve)
160
159
161 def __get_environ_user(self, environ):
160 def __get_environ_user(self, environ):
162 return environ.get('REMOTE_USER')
161 return environ.get('REMOTE_USER')
163
162
164 def __get_size(self, repo_path, content_size):
163 def __get_size(self, repo_path, content_size):
165 size = int(content_size)
164 size = int(content_size)
166 for path, dirs, files in os.walk(repo_path):
165 for path, dirs, files in os.walk(repo_path):
167 if path.find('.hg') == -1:
166 if path.find('.hg') == -1:
168 for f in files:
167 for f in files:
169 size += os.path.getsize(os.path.join(path, f))
168 size += os.path.getsize(os.path.join(path, f))
170 return size
169 return size
171 return h.format_byte_size(size)
170 return h.format_byte_size(size)
172
171
173 def __get_action(self, environ):
172 def __get_action(self, environ):
174 """
173 """
175 Maps mercurial request commands into a pull or push command.
174 Maps mercurial request commands into a pull or push command.
176 @param environ:
175 @param environ:
177 """
176 """
178 mapping = {'changegroup': 'pull',
177 mapping = {'changegroup': 'pull',
179 'changegroupsubset': 'pull',
178 'changegroupsubset': 'pull',
180 'stream_out': 'pull',
179 'stream_out': 'pull',
181 'listkeys': 'pull',
180 'listkeys': 'pull',
182 'unbundle': 'push',
181 'unbundle': 'push',
183 'pushkey': 'push', }
182 'pushkey': 'push', }
184
183
185 for qry in environ['QUERY_STRING'].split('&'):
184 for qry in environ['QUERY_STRING'].split('&'):
186 if qry.startswith('cmd'):
185 if qry.startswith('cmd'):
187 cmd = qry.split('=')[-1]
186 cmd = qry.split('=')[-1]
188 if mapping.has_key(cmd):
187 if mapping.has_key(cmd):
189 return mapping[cmd]
188 return mapping[cmd]
190
189
191 def __log_user_action(self, user, action, repo, ipaddr):
190 def __log_user_action(self, user, action, repo, ipaddr):
192 sa = meta.Session
191 sa = meta.Session
193 try:
192 try:
194 user_log = UserLog()
193 user_log = UserLog()
195 user_log.user_id = user.user_id
194 user_log.user_id = user.user_id
196 user_log.action = action
195 user_log.action = action
197 user_log.repository = repo.replace('/', '')
196 user_log.repository = repo.replace('/', '')
198 user_log.action_date = datetime.now()
197 user_log.action_date = datetime.now()
199 user_log.user_ip = ipaddr
198 user_log.user_ip = ipaddr
200 sa.add(user_log)
199 sa.add(user_log)
201 sa.commit()
200 sa.commit()
202 log.info('Adding user %s, action %s on %s',
201 log.info('Adding user %s, action %s on %s',
203 user.username, action, repo)
202 user.username, action, repo)
204 except Exception as e:
203 except Exception as e:
205 sa.rollback()
204 sa.rollback()
206 log.error('could not log user action:%s', str(e))
205 log.error('could not log user action:%s', str(e))
207
206
208 def __invalidate_cache(self, repo_name):
207 def __invalidate_cache(self, repo_name):
209 """we know that some change was made to repositories and we should
208 """we know that some change was made to repositories and we should
210 invalidate the cache to see the changes right away but only for
209 invalidate the cache to see the changes right away but only for
211 push requests"""
210 push requests"""
212 invalidate_cache('cached_repo_list')
211 invalidate_cache('cached_repo_list')
213 invalidate_cache('full_changelog', repo_name)
212 invalidate_cache('full_changelog', repo_name)
214
213
215
214
216 def __load_web_settings(self, hgserve):
215 def __load_web_settings(self, hgserve):
217 repoui = make_ui(os.path.join(self.repo_path, '.hg', 'hgrc'), False)
218 #set the global ui for hgserve
216 #set the global ui for hgserve
219 hgserve.repo.ui = self.baseui
217 hgserve.repo.ui = self.baseui
220
218
219 hgrc = os.path.join(self.repo_path, '.hg', 'hgrc')
220 repoui = make_ui('file', hgrc, False)
221
221 if repoui:
222 if repoui:
222 #set the repository based config
223 #set the repository based config
223 hgserve.repo.ui = repoui
224 hgserve.repo.ui = repoui
224
225
225 return hgserve
226 return hgserve
@@ -1,184 +1,189 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # Utilities for hg app
3 # Utilities for hg app
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 # This program is free software; you can redistribute it and/or
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; version 2
7 # as published by the Free Software Foundation; version 2
8 # of the License or (at your opinion) any later version of the license.
8 # of the License or (at your opinion) any later version of the license.
9 #
9 #
10 # This program is distributed in the hope that it will be useful,
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
13 # GNU General Public License for more details.
14 #
14 #
15 # You should have received a copy of the GNU General Public License
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 # MA 02110-1301, USA.
18 # MA 02110-1301, USA.
19
19
20 """
20 """
21 Created on April 18, 2010
21 Created on April 18, 2010
22 Utilities for hg app
22 Utilities for hg app
23 @author: marcink
23 @author: marcink
24 """
24 """
25
25
26 import os
26 import os
27 import logging
27 import logging
28 from mercurial import ui, config, hg
28 from mercurial import ui, config, hg
29 from mercurial.error import RepoError
29 from mercurial.error import RepoError
30 from pylons_app.model.db import Repository, User
30 from pylons_app.model.db import Repository, User, HgAppUi
31 log = logging.getLogger(__name__)
31 log = logging.getLogger(__name__)
32
32
33
33
34 def get_repo_slug(request):
34 def get_repo_slug(request):
35 return request.environ['pylons.routes_dict'].get('repo_name')
35 return request.environ['pylons.routes_dict'].get('repo_name')
36
36
37 def is_mercurial(environ):
37 def is_mercurial(environ):
38 """
38 """
39 Returns True if request's target is mercurial server - header
39 Returns True if request's target is mercurial server - header
40 ``HTTP_ACCEPT`` of such request would start with ``application/mercurial``.
40 ``HTTP_ACCEPT`` of such request would start with ``application/mercurial``.
41 """
41 """
42 http_accept = environ.get('HTTP_ACCEPT')
42 http_accept = environ.get('HTTP_ACCEPT')
43 if http_accept and http_accept.startswith('application/mercurial'):
43 if http_accept and http_accept.startswith('application/mercurial'):
44 return True
44 return True
45 return False
45 return False
46
46
47 def check_repo_dir(paths):
47 def check_repo_dir(paths):
48 repos_path = paths[0][1].split('/')
48 repos_path = paths[0][1].split('/')
49 if repos_path[-1] in ['*', '**']:
49 if repos_path[-1] in ['*', '**']:
50 repos_path = repos_path[:-1]
50 repos_path = repos_path[:-1]
51 if repos_path[0] != '/':
51 if repos_path[0] != '/':
52 repos_path[0] = '/'
52 repos_path[0] = '/'
53 if not os.path.isdir(os.path.join(*repos_path)):
53 if not os.path.isdir(os.path.join(*repos_path)):
54 raise Exception('Not a valid repository in %s' % paths[0][1])
54 raise Exception('Not a valid repository in %s' % paths[0][1])
55
55
56 def check_repo_fast(repo_name, base_path):
56 def check_repo_fast(repo_name, base_path):
57 if os.path.isdir(os.path.join(base_path, repo_name)):return False
57 if os.path.isdir(os.path.join(base_path, repo_name)):return False
58 return True
58 return True
59
59
60 def check_repo(repo_name, base_path, verify=True):
60 def check_repo(repo_name, base_path, verify=True):
61
61
62 repo_path = os.path.join(base_path, repo_name)
62 repo_path = os.path.join(base_path, repo_name)
63
63
64 try:
64 try:
65 if not check_repo_fast(repo_name, base_path):
65 if not check_repo_fast(repo_name, base_path):
66 return False
66 return False
67 r = hg.repository(ui.ui(), repo_path)
67 r = hg.repository(ui.ui(), repo_path)
68 if verify:
68 if verify:
69 hg.verify(r)
69 hg.verify(r)
70 #here we hnow that repo exists it was verified
70 #here we hnow that repo exists it was verified
71 log.info('%s repo is already created', repo_name)
71 log.info('%s repo is already created', repo_name)
72 return False
72 return False
73 except RepoError:
73 except RepoError:
74 #it means that there is no valid repo there...
74 #it means that there is no valid repo there...
75 log.info('%s repo is free for creation', repo_name)
75 log.info('%s repo is free for creation', repo_name)
76 return True
76 return True
77
77
78 def make_ui(path=None, checkpaths=True):
78 def make_ui(read_from='file', path=None, checkpaths=True):
79 """
79 """
80 A funcion that will read python rc files and make an ui from read options
80 A function that will read python rc files or database
81 and make an mercurial ui object from read options
81
82
82 @param path: path to mercurial config file
83 @param path: path to mercurial config file
84 @param checkpaths: check the path
85 @param read_from: read from 'file' or 'db'
83 """
86 """
84 if not path:
85 log.error('repos config path is empty !')
86
87 if not os.path.isfile(path):
88 log.warning('Unable to read config file %s' % path)
89 return False
90 #propagated from mercurial documentation
87 #propagated from mercurial documentation
91 sections = [
88 sections = ['alias', 'auth',
92 'alias',
89 'decode/encode', 'defaults',
93 'auth',
90 'diff', 'email',
94 'decode/encode',
91 'extensions', 'format',
95 'defaults',
92 'merge-patterns', 'merge-tools',
96 'diff',
93 'hooks', 'http_proxy',
97 'email',
94 'smtp', 'patch',
98 'extensions',
95 'paths', 'profiling',
99 'format',
96 'server', 'trusted',
100 'merge-patterns',
97 'ui', 'web', ]
101 'merge-tools',
98 baseui = ui.ui()
102 'hooks',
103 'http_proxy',
104 'smtp',
105 'patch',
106 'paths',
107 'profiling',
108 'server',
109 'trusted',
110 'ui',
111 'web',
112 ]
113
99
114 baseui = ui.ui()
100
115 cfg = config.config()
101 if read_from == 'file':
116 cfg.read(path)
102 if not os.path.isfile(path):
117 if checkpaths:check_repo_dir(cfg.items('paths'))
103 log.warning('Unable to read config file %s' % path)
118
104 return False
119 for section in sections:
105
120 for k, v in cfg.items(section):
106 cfg = config.config()
121 baseui.setconfig(section, k, v)
107 cfg.read(path)
108 for section in sections:
109 for k, v in cfg.items(section):
110 baseui.setconfig(section, k, v)
111 if checkpaths:check_repo_dir(cfg.items('paths'))
112
113
114 elif read_from == 'db':
115 from pylons_app.model.meta import Session
116 sa = Session()
117
118 hg_ui = sa.query(HgAppUi).all()
119 for ui_ in hg_ui:
120 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
121
122
122
123 return baseui
123 return baseui
124
124
125
126 def set_hg_app_config(config):
127 config['hg_app_auth_realm'] = 'realm'
128 config['hg_app_name'] = 'app name'
129
125 def invalidate_cache(name, *args):
130 def invalidate_cache(name, *args):
126 """Invalidates given name cache"""
131 """Invalidates given name cache"""
127
132
128 from beaker.cache import region_invalidate
133 from beaker.cache import region_invalidate
129 log.info('INVALIDATING CACHE FOR %s', name)
134 log.info('INVALIDATING CACHE FOR %s', name)
130
135
131 """propagate our arguments to make sure invalidation works. First
136 """propagate our arguments to make sure invalidation works. First
132 argument has to be the name of cached func name give to cache decorator
137 argument has to be the name of cached func name give to cache decorator
133 without that the invalidation would not work"""
138 without that the invalidation would not work"""
134 tmp = [name]
139 tmp = [name]
135 tmp.extend(args)
140 tmp.extend(args)
136 args = tuple(tmp)
141 args = tuple(tmp)
137
142
138 if name == 'cached_repo_list':
143 if name == 'cached_repo_list':
139 from pylons_app.model.hg_model import _get_repos_cached
144 from pylons_app.model.hg_model import _get_repos_cached
140 region_invalidate(_get_repos_cached, None, *args)
145 region_invalidate(_get_repos_cached, None, *args)
141
146
142 if name == 'full_changelog':
147 if name == 'full_changelog':
143 from pylons_app.model.hg_model import _full_changelog_cached
148 from pylons_app.model.hg_model import _full_changelog_cached
144 region_invalidate(_full_changelog_cached, None, *args)
149 region_invalidate(_full_changelog_cached, None, *args)
145
150
146 from vcs.backends.base import BaseChangeset
151 from vcs.backends.base import BaseChangeset
147 from vcs.utils.lazy import LazyProperty
152 from vcs.utils.lazy import LazyProperty
148 class EmptyChangeset(BaseChangeset):
153 class EmptyChangeset(BaseChangeset):
149
154
150 revision = -1
155 revision = -1
151 message = ''
156 message = ''
152
157
153 @LazyProperty
158 @LazyProperty
154 def raw_id(self):
159 def raw_id(self):
155 """
160 """
156 Returns raw string identifing this changeset, useful for web
161 Returns raw string identifing this changeset, useful for web
157 representation.
162 representation.
158 """
163 """
159 return '0' * 12
164 return '0' * 12
160
165
161
166
162 def repo2db_mapper(initial_repo_list):
167 def repo2db_mapper(initial_repo_list):
163 """
168 """
164 maps all found repositories into db
169 maps all found repositories into db
165 """
170 """
166 from pylons_app.model.meta import Session
171 from pylons_app.model.meta import Session
167 from pylons_app.model.repo_model import RepoModel
172 from pylons_app.model.repo_model import RepoModel
168
173
169 sa = Session()
174 sa = Session()
170 user = sa.query(User).filter(User.admin == True).first()
175 user = sa.query(User).filter(User.admin == True).first()
171
176
172 rm = RepoModel()
177 rm = RepoModel()
173
178
174 for name, repo in initial_repo_list.items():
179 for name, repo in initial_repo_list.items():
175 if not sa.query(Repository).get(name):
180 if not sa.query(Repository).get(name):
176 log.info('repository %s not found creating default', name)
181 log.info('repository %s not found creating default', name)
177
182
178 form_data = {
183 form_data = {
179 'repo_name':name,
184 'repo_name':name,
180 'description':repo.description if repo.description != 'unknown' else \
185 'description':repo.description if repo.description != 'unknown' else \
181 'auto description for %s' % name,
186 'auto description for %s' % name,
182 'private':False
187 'private':False
183 }
188 }
184 rm.create(form_data, user, just_db=True)
189 rm.create(form_data, user, just_db=True)
@@ -1,71 +1,86 b''
1 from pylons_app.model.meta import Base
1 from pylons_app.model.meta import Base
2 from sqlalchemy.orm import relation, backref
2 from sqlalchemy.orm import relation, backref
3 from sqlalchemy import *
3 from sqlalchemy import *
4 from vcs.utils.lazy import LazyProperty
4 from vcs.utils.lazy import LazyProperty
5
5
6 class HgAppSettings(Base):
7 __tablename__ = 'hg_app_settings'
8 __table_args__ = {'useexisting':True}
9 app_settings_id = Column("app_settings_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
10 app_title = Column("app_title", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
11 app_auth_realm = Column("auth_realm", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
12
13 class HgAppUi(Base):
14 __tablename__ = 'hg_app_ui'
15 __table_args__ = {'useexisting':True}
16 ui_id = Column("ui_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
17 ui_section = Column("ui_section", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
18 ui_key = Column("ui_key", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
19 ui_value = Column("ui_value", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
20
6 class User(Base):
21 class User(Base):
7 __tablename__ = 'users'
22 __tablename__ = 'users'
8 __table_args__ = {'useexisting':True}
23 __table_args__ = {'useexisting':True}
9 user_id = Column("user_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
24 user_id = Column("user_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
10 username = Column("username", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
25 username = Column("username", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
11 password = Column("password", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
26 password = Column("password", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
12 active = Column("active", BOOLEAN(), nullable=True, unique=None, default=None)
27 active = Column("active", BOOLEAN(), nullable=True, unique=None, default=None)
13 admin = Column("admin", BOOLEAN(), nullable=True, unique=None, default=False)
28 admin = Column("admin", BOOLEAN(), nullable=True, unique=None, default=False)
14 name = Column("name", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
29 name = Column("name", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
15 lastname = Column("lastname", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
30 lastname = Column("lastname", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
16 email = Column("email", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
31 email = Column("email", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
17 last_login = Column("last_login", DATETIME(timezone=False), nullable=True, unique=None, default=None)
32 last_login = Column("last_login", DATETIME(timezone=False), nullable=True, unique=None, default=None)
18
33
19 user_log = relation('UserLog')
34 user_log = relation('UserLog')
20
35
21 @LazyProperty
36 @LazyProperty
22 def full_contact(self):
37 def full_contact(self):
23 return '%s %s <%s>' % (self.name, self.lastname, self.email)
38 return '%s %s <%s>' % (self.name, self.lastname, self.email)
24
39
25 def __repr__(self):
40 def __repr__(self):
26 return "<User('%s:%s')>" % (self.user_id, self.username)
41 return "<User('%s:%s')>" % (self.user_id, self.username)
27
42
28 class UserLog(Base):
43 class UserLog(Base):
29 __tablename__ = 'user_logs'
44 __tablename__ = 'user_logs'
30 __table_args__ = {'useexisting':True}
45 __table_args__ = {'useexisting':True}
31 user_log_id = Column("user_log_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
46 user_log_id = Column("user_log_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
32 user_id = Column("user_id", INTEGER(), ForeignKey(u'users.user_id'), nullable=False, unique=None, default=None)
47 user_id = Column("user_id", INTEGER(), ForeignKey(u'users.user_id'), nullable=False, unique=None, default=None)
33 user_ip = Column("user_ip", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
48 user_ip = Column("user_ip", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
34 repository = Column("repository", TEXT(length=None, convert_unicode=False, assert_unicode=None), ForeignKey(u'repositories.repo_name'), nullable=False, unique=None, default=None)
49 repository = Column("repository", TEXT(length=None, convert_unicode=False, assert_unicode=None), ForeignKey(u'repositories.repo_name'), nullable=False, unique=None, default=None)
35 action = Column("action", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
50 action = Column("action", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
36 action_date = Column("action_date", DATETIME(timezone=False), nullable=True, unique=None, default=None)
51 action_date = Column("action_date", DATETIME(timezone=False), nullable=True, unique=None, default=None)
37
52
38 user = relation('User')
53 user = relation('User')
39
54
40 class Repository(Base):
55 class Repository(Base):
41 __tablename__ = 'repositories'
56 __tablename__ = 'repositories'
42 __table_args__ = {'useexisting':True}
57 __table_args__ = {'useexisting':True}
43 repo_name = Column("repo_name", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None, primary_key=True)
58 repo_name = Column("repo_name", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None, primary_key=True)
44 user_id = Column("user_id", INTEGER(), ForeignKey(u'users.user_id'), nullable=False, unique=False, default=None)
59 user_id = Column("user_id", INTEGER(), ForeignKey(u'users.user_id'), nullable=False, unique=False, default=None)
45 private = Column("private", BOOLEAN(), nullable=True, unique=None, default=None)
60 private = Column("private", BOOLEAN(), nullable=True, unique=None, default=None)
46 description = Column("description", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
61 description = Column("description", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
47
62
48 user = relation('User')
63 user = relation('User')
49 repo2perm = relation('Repo2Perm', cascade='all')
64 repo2perm = relation('Repo2Perm', cascade='all')
50
65
51 class Permission(Base):
66 class Permission(Base):
52 __tablename__ = 'permissions'
67 __tablename__ = 'permissions'
53 __table_args__ = {'useexisting':True}
68 __table_args__ = {'useexisting':True}
54 permission_id = Column("permission_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
69 permission_id = Column("permission_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
55 permission_name = Column("permission_name", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
70 permission_name = Column("permission_name", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
56 permission_longname = Column("permission_longname", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
71 permission_longname = Column("permission_longname", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
57
72
58 def __repr__(self):
73 def __repr__(self):
59 return "<Permission('%s:%s')>" % (self.permission_id, self.permission_name)
74 return "<Permission('%s:%s')>" % (self.permission_id, self.permission_name)
60
75
61 class Repo2Perm(Base):
76 class Repo2Perm(Base):
62 __tablename__ = 'repo_to_perm'
77 __tablename__ = 'repo_to_perm'
63 __table_args__ = (UniqueConstraint('user_id', 'repository'), {'useexisting':True})
78 __table_args__ = (UniqueConstraint('user_id', 'repository'), {'useexisting':True})
64 repo2perm_id = Column("repo2perm_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
79 repo2perm_id = Column("repo2perm_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
65 user_id = Column("user_id", INTEGER(), ForeignKey(u'users.user_id'), nullable=False, unique=None, default=None)
80 user_id = Column("user_id", INTEGER(), ForeignKey(u'users.user_id'), nullable=False, unique=None, default=None)
66 permission_id = Column("permission_id", INTEGER(), ForeignKey(u'permissions.permission_id'), nullable=False, unique=None, default=None)
81 permission_id = Column("permission_id", INTEGER(), ForeignKey(u'permissions.permission_id'), nullable=False, unique=None, default=None)
67 repository = Column("repository", TEXT(length=None, convert_unicode=False, assert_unicode=None), ForeignKey(u'repositories.repo_name'), nullable=False, unique=None, default=None)
82 repository = Column("repository", TEXT(length=None, convert_unicode=False, assert_unicode=None), ForeignKey(u'repositories.repo_name'), nullable=False, unique=None, default=None)
68
83
69 user = relation('User')
84 user = relation('User')
70 permission = relation('Permission')
85 permission = relation('Permission')
71
86
@@ -1,49 +1,23 b''
1 """Setup the pylons_app application"""
1 """Setup the pylons_app application"""
2
2
3 from os.path import dirname as dn, join as jn
3 from os.path import dirname as dn, join as jn
4 from pylons_app.config.environment import load_environment
4 from pylons_app.config.environment import load_environment
5 from pylons_app.lib.db_manage import DbManage
5 from pylons_app.lib.db_manage import DbManage
6 import logging
6 import logging
7 import os
7 import os
8 import sys
8 import sys
9
9
10 log = logging.getLogger(__name__)
10 log = logging.getLogger(__name__)
11
11
12 ROOT = dn(dn(os.path.realpath(__file__)))
12 ROOT = dn(dn(os.path.realpath(__file__)))
13 sys.path.append(ROOT)
13 sys.path.append(ROOT)
14
14
15
16 def setup_repository():
17 log.info('Seting up repositories.config')
18 fname = 'repositories.config'
19
20 try:
21 tmpl = open(jn(ROOT, 'pylons_app', 'config', 'repositories.config_tmpl')).read()
22 except IOError:
23 raise
24
25 path = raw_input('Specify valid full path to your repositories'
26 ' you can change this later in repositories.config file:')
27
28 if not os.path.isdir(path):
29 log.error('You entered wrong path')
30 sys.exit()
31
32
33 path = jn(path, '*')
34 dest_path = jn(ROOT, fname)
35 f = open(dest_path, 'wb')
36 f.write(tmpl % {'repo_location':path})
37 f.close()
38 log.info('created repositories.config in %s', dest_path)
39
40
41 def setup_app(command, conf, vars):
15 def setup_app(command, conf, vars):
42 """Place any commands to setup pylons_app here"""
16 """Place any commands to setup pylons_app here"""
43 setup_repository()
44 dbmanage = DbManage(log_sql=True)
17 dbmanage = DbManage(log_sql=True)
45 dbmanage.create_tables(override=True)
18 dbmanage.create_tables(override=True)
19 dbmanage.config_prompt()
46 dbmanage.admin_prompt()
20 dbmanage.admin_prompt()
47 dbmanage.create_permissions()
21 dbmanage.create_permissions()
48 load_environment(conf.global_conf, conf.local_conf)
22 load_environment(conf.global_conf, conf.local_conf)
49
23
1 NO CONTENT: file was removed
NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now