Show More
@@ -0,0 +1,31 b'' | |||||
|
1 | # -*- coding: utf-8 -*- | |||
|
2 | # This program is free software: you can redistribute it and/or modify | |||
|
3 | # it under the terms of the GNU General Public License as published by | |||
|
4 | # the Free Software Foundation, either version 3 of the License, or | |||
|
5 | # (at your option) any later version. | |||
|
6 | # | |||
|
7 | # This program is distributed in the hope that it will be useful, | |||
|
8 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
|
9 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
|
10 | # GNU General Public License for more details. | |||
|
11 | # | |||
|
12 | # You should have received a copy of the GNU General Public License | |||
|
13 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
|
14 | from tgext.routes import RoutedController | |||
|
15 | from kallithea.lib.base import BaseController | |||
|
16 | from kallithea.controllers.error import ErrorController | |||
|
17 | ||||
|
18 | ||||
|
19 | # With TurboGears, the RootController is the controller from which all routing | |||
|
20 | # starts from. It is 'magically' found based on the fact that a controller | |||
|
21 | # 'foo' is expected to have a class name FooController, located in a file | |||
|
22 | # foo.py, inside config['paths']['controllers']. The name 'root' for the root | |||
|
23 | # controller is the default name. The dictionary config['paths'] determines the | |||
|
24 | # directories where templates, static files and controllers are found. It is | |||
|
25 | # set up in tg.AppConfig based on AppConfig['package'] ('kallithea') and the | |||
|
26 | # respective defaults 'templates', 'public' and 'controllers'. | |||
|
27 | # Inherit from RoutedController to allow Kallithea to use regex-based routing. | |||
|
28 | class RootController(RoutedController, BaseController): | |||
|
29 | ||||
|
30 | # the following assignment hooks in error handling | |||
|
31 | error = ErrorController() |
@@ -6,3 +6,4 b' pytest-sugar>=0.7.0' | |||||
6 | pytest-catchlog |
|
6 | pytest-catchlog | |
7 | mock |
|
7 | mock | |
8 | sphinx |
|
8 | sphinx | |
|
9 | webtest < 3 |
@@ -512,7 +512,7 b' script_location = kallithea:alembic' | |||||
512 | ################################ |
|
512 | ################################ | |
513 |
|
513 | |||
514 | [loggers] |
|
514 | [loggers] | |
515 | keys = root, routes, kallithea, sqlalchemy, gearbox, beaker, templates, whoosh_indexer |
|
515 | keys = root, routes, kallithea, sqlalchemy, tg, gearbox, beaker, templates, whoosh_indexer | |
516 |
|
516 | |||
517 | [handlers] |
|
517 | [handlers] | |
518 | keys = console, console_sql |
|
518 | keys = console, console_sql | |
@@ -553,6 +553,12 b' handlers =' | |||||
553 | qualname = kallithea |
|
553 | qualname = kallithea | |
554 | propagate = 1 |
|
554 | propagate = 1 | |
555 |
|
555 | |||
|
556 | [logger_tg] | |||
|
557 | level = DEBUG | |||
|
558 | handlers = | |||
|
559 | qualname = tg | |||
|
560 | propagate = 1 | |||
|
561 | ||||
556 | [logger_gearbox] |
|
562 | [logger_gearbox] | |
557 | level = DEBUG |
|
563 | level = DEBUG | |
558 | handlers = |
|
564 | handlers = |
@@ -30,10 +30,6 b' Original author and date, and relevant c' | |||||
30 | import sys |
|
30 | import sys | |
31 | import platform |
|
31 | import platform | |
32 |
|
32 | |||
33 | # temporary aliasing to allow early introduction of imports like 'from tg import request' |
|
|||
34 | import pylons |
|
|||
35 | sys.modules['tg'] = pylons |
|
|||
36 |
|
||||
37 | VERSION = (0, 3, 99) |
|
33 | VERSION = (0, 3, 99) | |
38 | BACKENDS = { |
|
34 | BACKENDS = { | |
39 | 'hg': 'Mercurial repository', |
|
35 | 'hg': 'Mercurial repository', |
@@ -11,76 +11,112 b'' | |||||
11 | # |
|
11 | # | |
12 | # You should have received a copy of the GNU General Public License |
|
12 | # You should have received a copy of the GNU General Public License | |
13 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
13 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
|
14 | """ | |||
|
15 | Global configuration file for TurboGears2 specific settings in Kallithea. | |||
14 |
|
|
16 | ||
15 | import os |
|
17 | This file complements the .ini file. | |
16 | import kallithea |
|
18 | """ | |
|
19 | ||||
17 | import platform |
|
20 | import platform | |
18 |
|
21 | import os, sys | ||
19 | import pylons |
|
|||
20 | import mako.lookup |
|
|||
21 | import formencode |
|
|||
22 |
|
22 | |||
23 | import kallithea.lib.app_globals as app_globals |
|
23 | import tg | |
24 |
|
24 | from tg import hooks | ||
25 |
from |
|
25 | from tg.configuration import AppConfig | |
|
26 | from tg.support.converters import asbool | |||
26 |
|
27 | |||
27 |
from kallithea.lib import |
|
28 | from kallithea.lib.middleware.https_fixup import HttpsFixup | |
|
29 | from kallithea.lib.middleware.simplegit import SimpleGit | |||
|
30 | from kallithea.lib.middleware.simplehg import SimpleHg | |||
|
31 | from kallithea.config.routing import make_map | |||
28 | from kallithea.lib.auth import set_available_permissions |
|
32 | from kallithea.lib.auth import set_available_permissions | |
29 | from kallithea.lib.utils import repo2db_mapper, make_ui, set_app_settings, \ |
|
33 | from kallithea.lib.db_manage import DbManage | |
30 | load_rcextensions, check_git_version, set_vcs_config, set_indexer_config |
|
34 | from kallithea.lib.utils import load_rcextensions, make_ui, set_app_settings, set_vcs_config, \ | |
31 | from kallithea.lib.utils2 import engine_from_config, str2bool |
|
35 | set_indexer_config, check_git_version, repo2db_mapper | |
32 |
from kallithea. |
|
36 | from kallithea.lib.utils2 import str2bool | |
33 | from kallithea.model.scm import ScmModel |
|
37 | from kallithea.model.scm import ScmModel | |
34 |
|
38 | |||
35 | from routes.middleware import RoutesMiddleware |
|
39 | import formencode | |
36 | from paste.cascade import Cascade |
|
40 | import kallithea | |
37 | from paste.registry import RegistryManager |
|
41 | ||
38 | from paste.urlparser import StaticURLParser |
|
42 | ||
39 | from paste.deploy.converters import asbool |
|
43 | class KallitheaAppConfig(AppConfig): | |
|
44 | # Note: AppConfig has a misleading name, as it's not the application | |||
|
45 | # configuration, but the application configurator. The AppConfig values are | |||
|
46 | # used as a template to create the actual configuration, which might | |||
|
47 | # overwrite or extend the one provided by the configurator template. | |||
|
48 | ||||
|
49 | # To make it clear, AppConfig creates the config and sets into it the same | |||
|
50 | # values that AppConfig itself has. Then the values from the config file and | |||
|
51 | # gearbox options are loaded and merged into the configuration. Then an | |||
|
52 | # after_init_config(conf) method of AppConfig is called for any change that | |||
|
53 | # might depend on options provided by configuration files. | |||
|
54 | ||||
|
55 | def __init__(self): | |||
|
56 | super(KallitheaAppConfig, self).__init__() | |||
|
57 | ||||
|
58 | self['package'] = kallithea | |||
|
59 | ||||
|
60 | self['prefer_toscawidgets2'] = False | |||
|
61 | self['use_toscawidgets'] = False | |||
|
62 | ||||
|
63 | self['renderers'] = [] | |||
|
64 | ||||
|
65 | # Enable json in expose | |||
|
66 | self['renderers'].append('json') | |||
40 |
|
67 | |||
41 | from pylons.middleware import ErrorHandler, StatusCodeRedirect |
|
68 | # Configure template rendering | |
42 | from pylons.wsgiapp import PylonsApp |
|
69 | self['renderers'].append('mako') | |
|
70 | self['default_renderer'] = 'mako' | |||
|
71 | self['use_dotted_templatenames'] = False | |||
|
72 | ||||
|
73 | # Configure Sessions, store data as JSON to avoid pickle security issues | |||
|
74 | self['session.enabled'] = True | |||
|
75 | self['session.data_serializer'] = 'json' | |||
|
76 | ||||
|
77 | # Configure the base SQLALchemy Setup | |||
|
78 | self['use_sqlalchemy'] = True | |||
|
79 | self['model'] = kallithea.model.base | |||
|
80 | self['DBSession'] = kallithea.model.meta.Session | |||
|
81 | ||||
|
82 | # Configure App without an authentication backend. | |||
|
83 | self['auth_backend'] = None | |||
43 |
|
84 | |||
44 | from kallithea.lib.middleware.simplehg import SimpleHg |
|
85 | # Use custom error page for these errors. By default, Turbogears2 does not add | |
45 | from kallithea.lib.middleware.simplegit import SimpleGit |
|
86 | # 400 in this list. | |
46 | from kallithea.lib.middleware.https_fixup import HttpsFixup |
|
87 | # Explicitly listing all is considered more robust than appending to defaults, | |
47 | from kallithea.lib.middleware.sessionmiddleware import SecureSessionMiddleware |
|
88 | # in light of possible future framework changes. | |
48 | from kallithea.lib.middleware.wrapper import RequestWrapper |
|
89 | self['errorpage.status_codes'] = [400, 401, 403, 404] | |
49 |
|
90 | |||
50 | def setup_configuration(config, paths, app_conf, test_env, test_index): |
|
91 | # Disable transaction manager -- currently Kallithea takes care of transactions itself | |
|
92 | self['tm.enabled'] = False | |||
|
93 | ||||
|
94 | base_config = KallitheaAppConfig() | |||
|
95 | ||||
|
96 | # TODO still needed as long as we use pylonslib | |||
|
97 | sys.modules['pylons'] = tg | |||
|
98 | ||||
|
99 | def setup_configuration(app): | |||
|
100 | config = app.config | |||
51 |
|
101 | |||
52 | # store some globals into kallithea |
|
102 | # store some globals into kallithea | |
53 | kallithea.CELERY_ON = str2bool(config['app_conf'].get('use_celery')) |
|
103 | kallithea.CELERY_ON = str2bool(config['app_conf'].get('use_celery')) | |
54 | kallithea.CELERY_EAGER = str2bool(config['app_conf'].get('celery.always.eager')) |
|
104 | kallithea.CELERY_EAGER = str2bool(config['app_conf'].get('celery.always.eager')) | |
|
105 | kallithea.CONFIG = config | |||
55 |
|
106 | |||
56 | config['routes.map'] = make_map(config) |
|
107 | # Provide routes mapper to the RoutedController | |
57 | config['pylons.app_globals'] = app_globals.Globals(config) |
|
108 | root_controller = app.find_controller('root') | |
58 | config['pylons.h'] = helpers |
|
109 | root_controller.mapper = config['routes.map'] = make_map(config) | |
59 | kallithea.CONFIG = config |
|
|||
60 |
|
110 | |||
61 | load_rcextensions(root_path=config['here']) |
|
111 | load_rcextensions(root_path=config['here']) | |
62 |
|
112 | |||
63 | # Setup cache object as early as possible |
|
113 | # FIXME move test setup code out of here | |
64 | pylons.cache._push_object(config['pylons.app_globals'].cache) |
|
|||
65 |
|
||||
66 | # Create the Mako TemplateLookup, with the default auto-escaping |
|
|||
67 | config['pylons.app_globals'].mako_lookup = mako.lookup.TemplateLookup( |
|
|||
68 | directories=paths['templates'], |
|
|||
69 | strict_undefined=True, |
|
|||
70 | module_directory=os.path.join(app_conf['cache_dir'], 'templates'), |
|
|||
71 | input_encoding='utf-8', default_filters=['escape'], |
|
|||
72 | imports=['from webhelpers.html import escape']) |
|
|||
73 |
|
||||
74 | # sets the c attribute access when don't existing attribute are accessed |
|
|||
75 | config['pylons.strict_tmpl_context'] = True |
|
|||
76 | test = os.path.split(config['__file__'])[-1] == 'test.ini' |
|
114 | test = os.path.split(config['__file__'])[-1] == 'test.ini' | |
77 | if test: |
|
115 | if test: | |
78 | if test_env is None: |
|
|||
79 |
|
|
116 | test_env = not int(os.environ.get('KALLITHEA_NO_TMP_PATH', 0)) | |
80 | if test_index is None: |
|
|||
81 |
|
|
117 | test_index = not int(os.environ.get('KALLITHEA_WHOOSH_TEST_DISABLE', 0)) | |
82 | if os.environ.get('TEST_DB'): |
|
118 | if os.environ.get('TEST_DB'): | |
83 | # swap config if we pass enviroment variable |
|
119 | # swap config if we pass environment variable | |
84 | config['sqlalchemy.url'] = os.environ.get('TEST_DB') |
|
120 | config['sqlalchemy.url'] = os.environ.get('TEST_DB') | |
85 |
|
121 | |||
86 | from kallithea.tests.fixture import create_test_env, create_test_index |
|
122 | from kallithea.tests.fixture import create_test_env, create_test_index | |
@@ -93,11 +129,6 b' def setup_configuration(config, paths, a' | |||||
93 | if test_index: |
|
129 | if test_index: | |
94 | create_test_index(TESTS_TMP_PATH, config, True) |
|
130 | create_test_index(TESTS_TMP_PATH, config, True) | |
95 |
|
131 | |||
96 | # MULTIPLE DB configs |
|
|||
97 | # Setup the SQLAlchemy database engine |
|
|||
98 | sa_engine = engine_from_config(config, 'sqlalchemy.') |
|
|||
99 | init_model(sa_engine) |
|
|||
100 |
|
||||
101 | set_available_permissions(config) |
|
132 | set_available_permissions(config) | |
102 | repos_path = make_ui('db').configitems('paths')[0][1] |
|
133 | repos_path = make_ui('db').configitems('paths')[0][1] | |
103 | config['base_path'] = repos_path |
|
134 | config['base_path'] = repos_path | |
@@ -108,78 +139,37 b' def setup_configuration(config, paths, a' | |||||
108 | instance_id = '%s-%s' % (platform.uname()[1], os.getpid()) |
|
139 | instance_id = '%s-%s' % (platform.uname()[1], os.getpid()) | |
109 | kallithea.CONFIG['instance_id'] = instance_id |
|
140 | kallithea.CONFIG['instance_id'] = instance_id | |
110 |
|
141 | |||
111 | # CONFIGURATION OPTIONS HERE (note: all config options will override |
|
142 | # update kallithea.CONFIG with the meanwhile changed 'config' | |
112 | # any Pylons config options) |
|
143 | kallithea.CONFIG.update(config) | |
113 |
|
144 | |||
114 | # store config reference into our module to skip import magic of |
|
145 | # configure vcs and indexer libraries (they are supposed to be independent | |
115 | # pylons |
|
146 | # as much as possible and thus avoid importing tg.config or | |
116 |
kallithea.CONFIG. |
|
147 | # kallithea.CONFIG). | |
117 | set_vcs_config(kallithea.CONFIG) |
|
148 | set_vcs_config(kallithea.CONFIG) | |
118 | set_indexer_config(kallithea.CONFIG) |
|
149 | set_indexer_config(kallithea.CONFIG) | |
119 |
|
150 | |||
120 | #check git version |
|
|||
121 | check_git_version() |
|
151 | check_git_version() | |
122 |
|
152 | |||
123 | if str2bool(config.get('initial_repo_scan', True)): |
|
153 | if str2bool(config.get('initial_repo_scan', True)): | |
124 | repo2db_mapper(ScmModel().repo_scan(repos_path), |
|
154 | repo2db_mapper(ScmModel().repo_scan(repos_path), | |
125 | remove_obsolete=False, install_git_hooks=False) |
|
155 | remove_obsolete=False, install_git_hooks=False) | |
|
156 | ||||
126 | formencode.api.set_stdtranslation(languages=[config.get('lang')]) |
|
157 | formencode.api.set_stdtranslation(languages=[config.get('lang')]) | |
127 |
|
158 | |||
128 | return config |
|
159 | hooks.register('configure_new_app', setup_configuration) | |
129 |
|
||||
130 | def setup_application(config, global_conf, full_stack, static_files): |
|
|||
131 |
|
||||
132 | # The Pylons WSGI app |
|
|||
133 | app = PylonsApp(config=config) |
|
|||
134 |
|
160 | |||
135 | # Routing/Session/Cache Middleware |
|
|||
136 | app = RoutesMiddleware(app, config['routes.map'], use_method_override=False) |
|
|||
137 | app = SecureSessionMiddleware(app, config) |
|
|||
138 |
|
||||
139 | # CUSTOM MIDDLEWARE HERE (filtered by error handling middlewares) |
|
|||
140 | if asbool(config['pdebug']): |
|
|||
141 | from kallithea.lib.profiler import ProfilingMiddleware |
|
|||
142 | app = ProfilingMiddleware(app) |
|
|||
143 |
|
||||
144 | if asbool(full_stack): |
|
|||
145 |
|
161 | |||
146 | from kallithea.lib.middleware.sentry import Sentry |
|
162 | def setup_application(app): | |
147 | from kallithea.lib.middleware.appenlight import AppEnlight |
|
163 | config = app.config | |
148 | if AppEnlight and asbool(config['app_conf'].get('appenlight')): |
|
|||
149 | app = AppEnlight(app, config) |
|
|||
150 | elif Sentry: |
|
|||
151 | app = Sentry(app, config) |
|
|||
152 |
|
||||
153 | # Handle Python exceptions |
|
|||
154 | app = ErrorHandler(app, global_conf, **config['pylons.errorware']) |
|
|||
155 |
|
||||
156 | # Display error documents for 401, 403, 404 status codes (and |
|
|||
157 | # 500 when debug is disabled) |
|
|||
158 | # Note: will buffer the output in memory! |
|
|||
159 | if asbool(config['debug']): |
|
|||
160 | app = StatusCodeRedirect(app) |
|
|||
161 | else: |
|
|||
162 | app = StatusCodeRedirect(app, [400, 401, 403, 404, 500]) |
|
|||
163 |
|
164 | |||
164 |
|
|
165 | # we want our low level middleware to get to the request ASAP. We don't | |
165 |
|
|
166 | # need any stack middleware in them - especially no StatusCodeRedirect buffering | |
166 |
|
|
167 | app = SimpleHg(app, config) | |
167 |
|
|
168 | app = SimpleGit(app, config) | |
168 |
|
169 | |||
169 |
|
|
170 | # Enable https redirects based on HTTP_X_URL_SCHEME set by proxy | |
170 |
|
|
171 | if any(asbool(config.get(x)) for x in ['https_fixup', 'force_https', 'use_htsts']): | |
171 |
|
|
172 | app = HttpsFixup(app, config) | |
172 |
|
173 | return app | ||
173 | app = RequestWrapper(app, config) # logging |
|
|||
174 |
|
||||
175 | # Establish the Registry for this application |
|
|||
176 | app = RegistryManager(app) # thread / request-local module globals / variables |
|
|||
177 |
|
174 | |||
178 | if asbool(static_files): |
|
175 | hooks.register('before_config', setup_application) | |
179 | # Serve static files |
|
|||
180 | static_app = StaticURLParser(config['pylons.paths']['static_files']) |
|
|||
181 | app = Cascade([static_app, app]) |
|
|||
182 |
|
||||
183 | app.config = config |
|
|||
184 |
|
||||
185 | return app |
|
@@ -11,34 +11,11 b'' | |||||
11 | # |
|
11 | # | |
12 | # You should have received a copy of the GNU General Public License |
|
12 | # You should have received a copy of the GNU General Public License | |
13 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
13 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
14 | """ |
|
14 | """WSGI environment setup for Kallithea.""" | |
15 | Pylons environment configuration |
|
|||
16 | """ |
|
|||
17 |
|
15 | |||
18 | import os |
|
16 | from kallithea.config.app_cfg import base_config | |
19 | import kallithea |
|
|||
20 | import pylons |
|
|||
21 |
|
||||
22 | from kallithea.config.app_cfg import setup_configuration |
|
|||
23 |
|
17 | |||
24 | def load_environment(global_conf, app_conf, |
|
18 | __all__ = ['load_environment'] | |
25 | test_env=None, test_index=None): |
|
|||
26 | """ |
|
|||
27 | Configure the Pylons environment via the ``pylons.config`` |
|
|||
28 | object |
|
|||
29 | """ |
|
|||
30 | config = pylons.configuration.PylonsConfig() |
|
|||
31 |
|
19 | |||
32 | # Pylons paths |
|
20 | # Use base_config to setup the environment loader function | |
33 | root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) |
|
21 | load_environment = base_config.make_load_environment() | |
34 | paths = dict( |
|
|||
35 | root=root, |
|
|||
36 | controllers=os.path.join(root, 'controllers'), |
|
|||
37 | static_files=os.path.join(root, 'public'), |
|
|||
38 | templates=[os.path.join(root, 'templates')] |
|
|||
39 | ) |
|
|||
40 |
|
||||
41 | # Initialize config with the basic options |
|
|||
42 | config.init_app(global_conf, app_conf, package='kallithea', paths=paths) |
|
|||
43 |
|
||||
44 | return setup_configuration(config, paths, app_conf, test_env, test_index) |
|
@@ -11,33 +11,35 b'' | |||||
11 | # |
|
11 | # | |
12 | # You should have received a copy of the GNU General Public License |
|
12 | # You should have received a copy of the GNU General Public License | |
13 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
13 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
14 | """ |
|
14 | """WSGI middleware initialization for the Kallithea application.""" | |
15 | Pylons middleware initialization |
|
|||
16 | """ |
|
|||
17 |
|
15 | |||
18 |
from kallithea.config.app_cfg import se |
|
16 | from kallithea.config.app_cfg import base_config | |
19 | from kallithea.config.environment import load_environment |
|
17 | from kallithea.config.environment import load_environment | |
20 |
|
18 | |||
21 | def make_app(global_conf, full_stack=True, static_files=True, **app_conf): |
|
19 | __all__ = ['make_app'] | |
22 | """Create a Pylons WSGI application and return it |
|
20 | ||
|
21 | # Use base_config to setup the necessary PasteDeploy application factory. | |||
|
22 | # make_base_app will wrap the TurboGears2 app with all the middleware it needs. | |||
|
23 | make_base_app = base_config.setup_tg_wsgi_app(load_environment) | |||
23 |
|
24 | |||
24 | ``global_conf`` |
|
25 | ||
25 | The inherited configuration for this application. Normally from |
|
26 | def make_app(global_conf, full_stack=True, **app_conf): | |
26 | the [DEFAULT] section of the Paste ini file. |
|
27 | """ | |
|
28 | Set up Kallithea with the settings found in the PasteDeploy configuration | |||
|
29 | file used. | |||
27 |
|
30 | |||
28 | ``full_stack`` |
|
31 | :param global_conf: The global settings for Kallithea (those | |
29 | Whether or not this application provides a full WSGI stack (by |
|
32 | defined under the ``[DEFAULT]`` section). | |
30 | default, meaning it handles its own exceptions and errors). |
|
33 | :type global_conf: dict | |
31 | Disable full_stack when this application is "managed" by |
|
34 | :param full_stack: Should the whole TurboGears2 stack be set up? | |
32 | another WSGI middleware. |
|
35 | :type full_stack: str or bool | |
|
36 | :return: The Kallithea application with all the relevant middleware | |||
|
37 | loaded. | |||
33 |
|
38 | |||
34 | ``app_conf`` |
|
39 | This is the PasteDeploy factory for the Kallithea application. | |
35 | The application's local configuration. Normally specified in |
|
|||
36 | the [app:<name>] section of the Paste ini file (where <name> |
|
|||
37 | defaults to main). |
|
|||
38 |
|
40 | |||
|
41 | ``app_conf`` contains all the application-specific settings (those defined | |||
|
42 | under ``[app:main]``. | |||
39 | """ |
|
43 | """ | |
40 | # Configure the Pylons environment |
|
44 | app = make_base_app(global_conf, full_stack=full_stack, **app_conf) | |
41 | config = load_environment(global_conf, app_conf) |
|
45 | return app | |
42 |
|
||||
43 | return setup_application(config, global_conf, full_stack, static_files) |
|
@@ -28,7 +28,7 b" ADMIN_PREFIX = '/_admin'" | |||||
28 |
|
28 | |||
29 | def make_map(config): |
|
29 | def make_map(config): | |
30 | """Create, configure and return the routes Mapper""" |
|
30 | """Create, configure and return the routes Mapper""" | |
31 |
rmap = Mapper(directory=config[' |
|
31 | rmap = Mapper(directory=config['paths']['controllers'], | |
32 | always_scan=config['debug']) |
|
32 | always_scan=config['debug']) | |
33 | rmap.minimization = False |
|
33 | rmap.minimization = False | |
34 | rmap.explicit = False |
|
34 | rmap.explicit = False | |
@@ -90,11 +90,6 b' def make_map(config):' | |||||
90 | def check_int(environ, match_dict): |
|
90 | def check_int(environ, match_dict): | |
91 | return match_dict.get('id').isdigit() |
|
91 | return match_dict.get('id').isdigit() | |
92 |
|
92 | |||
93 | # The ErrorController route (handles 404/500 error pages); it should |
|
|||
94 | # likely stay at the top, ensuring it can always be resolved |
|
|||
95 | rmap.connect('/error/{action}', controller='error') |
|
|||
96 | rmap.connect('/error/{action}/{id}', controller='error') |
|
|||
97 |
|
||||
98 | #========================================================================== |
|
93 | #========================================================================== | |
99 | # CUSTOM ROUTES HERE |
|
94 | # CUSTOM ROUTES HERE | |
100 | #========================================================================== |
|
95 | #========================================================================== | |
@@ -426,8 +421,8 b' def make_map(config):' | |||||
426 | #========================================================================== |
|
421 | #========================================================================== | |
427 | # API V2 |
|
422 | # API V2 | |
428 | #========================================================================== |
|
423 | #========================================================================== | |
429 | with rmap.submapper(path_prefix=ADMIN_PREFIX, |
|
424 | with rmap.submapper(path_prefix=ADMIN_PREFIX, controller='api/api', | |
430 |
|
|
425 | action='_dispatch') as m: | |
431 | m.connect('api', '/api') |
|
426 | m.connect('api', '/api') | |
432 |
|
427 | |||
433 | #USER JOURNAL |
|
428 | #USER JOURNAL |
@@ -32,7 +32,7 b' import itertools' | |||||
32 |
|
32 | |||
33 | from formencode import htmlfill |
|
33 | from formencode import htmlfill | |
34 |
|
34 | |||
35 | from tg import request, tmpl_context as c |
|
35 | from tg import request, tmpl_context as c, app_globals | |
36 | from tg.i18n import ugettext as _, ungettext |
|
36 | from tg.i18n import ugettext as _, ungettext | |
37 | from webob.exc import HTTPFound, HTTPForbidden, HTTPNotFound, HTTPInternalServerError |
|
37 | from webob.exc import HTTPFound, HTTPForbidden, HTTPNotFound, HTTPInternalServerError | |
38 |
|
38 | |||
@@ -112,7 +112,7 b' class RepoGroupsController(BaseControlle' | |||||
112 | group_iter = RepoGroupList(_list, perm_level='admin') |
|
112 | group_iter = RepoGroupList(_list, perm_level='admin') | |
113 | repo_groups_data = [] |
|
113 | repo_groups_data = [] | |
114 | total_records = len(group_iter) |
|
114 | total_records = len(group_iter) | |
115 |
_tmpl_lookup = |
|
115 | _tmpl_lookup = app_globals.mako_lookup | |
116 | template = _tmpl_lookup.get_template('data_table/_dt_elements.html') |
|
116 | template = _tmpl_lookup.get_template('data_table/_dt_elements.html') | |
117 |
|
117 | |||
118 | repo_group_name = lambda repo_group_name, children_groups: ( |
|
118 | repo_group_name = lambda repo_group_name, children_groups: ( |
@@ -30,7 +30,7 b' import traceback' | |||||
30 | import formencode |
|
30 | import formencode | |
31 |
|
31 | |||
32 | from formencode import htmlfill |
|
32 | from formencode import htmlfill | |
33 | from tg import request, tmpl_context as c, config |
|
33 | from tg import request, tmpl_context as c, config, app_globals | |
34 | from tg.i18n import ugettext as _ |
|
34 | from tg.i18n import ugettext as _ | |
35 | from webob.exc import HTTPFound |
|
35 | from webob.exc import HTTPFound | |
36 |
|
36 | |||
@@ -94,7 +94,7 b' class UserGroupsController(BaseControlle' | |||||
94 | group_iter = UserGroupList(_list, perm_level='admin') |
|
94 | group_iter = UserGroupList(_list, perm_level='admin') | |
95 | user_groups_data = [] |
|
95 | user_groups_data = [] | |
96 | total_records = len(group_iter) |
|
96 | total_records = len(group_iter) | |
97 |
_tmpl_lookup = |
|
97 | _tmpl_lookup = app_globals.mako_lookup | |
98 | template = _tmpl_lookup.get_template('data_table/_dt_elements.html') |
|
98 | template = _tmpl_lookup.get_template('data_table/_dt_elements.html') | |
99 |
|
99 | |||
100 | user_group_name = lambda user_group_id, user_group_name: ( |
|
100 | user_group_name = lambda user_group_id, user_group_name: ( |
@@ -30,7 +30,7 b' import traceback' | |||||
30 | import formencode |
|
30 | import formencode | |
31 |
|
31 | |||
32 | from formencode import htmlfill |
|
32 | from formencode import htmlfill | |
33 | from tg import request, tmpl_context as c, config |
|
33 | from tg import request, tmpl_context as c, config, app_globals | |
34 | from tg.i18n import ugettext as _ |
|
34 | from tg.i18n import ugettext as _ | |
35 | from sqlalchemy.sql.expression import func |
|
35 | from sqlalchemy.sql.expression import func | |
36 | from webob.exc import HTTPFound, HTTPNotFound |
|
36 | from webob.exc import HTTPFound, HTTPNotFound | |
@@ -73,7 +73,7 b' class UsersController(BaseController):' | |||||
73 |
|
73 | |||
74 | users_data = [] |
|
74 | users_data = [] | |
75 | total_records = len(c.users_list) |
|
75 | total_records = len(c.users_list) | |
76 |
_tmpl_lookup = |
|
76 | _tmpl_lookup = app_globals.mako_lookup | |
77 | template = _tmpl_lookup.get_template('data_table/_dt_elements.html') |
|
77 | template = _tmpl_lookup.get_template('data_table/_dt_elements.html') | |
78 |
|
78 | |||
79 | grav_tmpl = '<div class="gravatar">%s</div>' |
|
79 | grav_tmpl = '<div class="gravatar">%s</div>' |
@@ -32,12 +32,9 b' import traceback' | |||||
32 | import time |
|
32 | import time | |
33 | import itertools |
|
33 | import itertools | |
34 |
|
34 | |||
35 | from paste.response import replace_header |
|
35 | from tg import Response, response, request, TGController | |
36 | from pylons.controllers import WSGIController |
|
|||
37 | from pylons.controllers.util import Response |
|
|||
38 | from tg import request |
|
|||
39 |
|
36 | |||
40 | from webob.exc import HTTPError |
|
37 | from webob.exc import HTTPError, HTTPException, WSGIHTTPException | |
41 |
|
38 | |||
42 | from kallithea.model.db import User |
|
39 | from kallithea.model.db import User | |
43 | from kallithea.model import meta |
|
40 | from kallithea.model import meta | |
@@ -59,19 +56,20 b' class JSONRPCError(BaseException):' | |||||
59 | return safe_str(self.message) |
|
56 | return safe_str(self.message) | |
60 |
|
57 | |||
61 |
|
58 | |||
62 | class JSONRPCErrorResponse(Response, Exception): |
|
59 | class JSONRPCErrorResponse(Response, HTTPException): | |
63 | """ |
|
60 | """ | |
64 | Generate a Response object with a JSON-RPC error body |
|
61 | Generate a Response object with a JSON-RPC error body | |
65 | """ |
|
62 | """ | |
66 |
|
63 | |||
67 | def __init__(self, message=None, retid=None, code=None): |
|
64 | def __init__(self, message=None, retid=None, code=None): | |
|
65 | HTTPException.__init__(self, message, self) | |||
68 | Response.__init__(self, |
|
66 | Response.__init__(self, | |
69 |
body= |
|
67 | json_body=dict(id=retid, result=None, error=message), | |
70 | status=code, |
|
68 | status=code, | |
71 | content_type='application/json') |
|
69 | content_type='application/json') | |
72 |
|
70 | |||
73 |
|
71 | |||
74 |
class JSONRPCController( |
|
72 | class JSONRPCController(TGController): | |
75 | """ |
|
73 | """ | |
76 | A WSGI-speaking JSON-RPC controller class |
|
74 | A WSGI-speaking JSON-RPC controller class | |
77 |
|
75 | |||
@@ -95,19 +93,15 b' class JSONRPCController(WSGIController):' | |||||
95 | """ |
|
93 | """ | |
96 | return self._rpc_args |
|
94 | return self._rpc_args | |
97 |
|
95 | |||
98 | def __call__(self, environ, start_response): |
|
96 | def _dispatch(self, state, remainder=None): | |
99 | """ |
|
97 | """ | |
100 | Parse the request body as JSON, look up the method on the |
|
98 | Parse the request body as JSON, look up the method on the | |
101 | controller and if it exists, dispatch to it. |
|
99 | controller and if it exists, dispatch to it. | |
102 | """ |
|
100 | """ | |
103 | try: |
|
101 | # Since we are here we should respond as JSON | |
104 | return self._handle_request(environ, start_response) |
|
102 | response.content_type = 'application/json' | |
105 | except JSONRPCErrorResponse as e: |
|
|||
106 | return e |
|
|||
107 | finally: |
|
|||
108 | meta.Session.remove() |
|
|||
109 |
|
103 | |||
110 | def _handle_request(self, environ, start_response): |
|
104 | environ = state.request.environ | |
111 | start = time.time() |
|
105 | start = time.time() | |
112 | ip_addr = request.ip_addr = self._get_ip_addr(environ) |
|
106 | ip_addr = request.ip_addr = self._get_ip_addr(environ) | |
113 | self._req_id = None |
|
107 | self._req_id = None | |
@@ -218,39 +212,26 b' class JSONRPCController(WSGIController):' | |||||
218 | ) |
|
212 | ) | |
219 |
|
213 | |||
220 | self._rpc_args = {} |
|
214 | self._rpc_args = {} | |
221 |
|
||||
222 | self._rpc_args.update(self._request_params) |
|
215 | self._rpc_args.update(self._request_params) | |
223 |
|
||||
224 | self._rpc_args['action'] = self._req_method |
|
216 | self._rpc_args['action'] = self._req_method | |
225 | self._rpc_args['environ'] = environ |
|
217 | self._rpc_args['environ'] = environ | |
226 | self._rpc_args['start_response'] = start_response |
|
|||
227 |
|
218 | |||
228 | status = [] |
|
|||
229 | headers = [] |
|
|||
230 | exc_info = [] |
|
|||
231 |
|
||||
232 | def change_content(new_status, new_headers, new_exc_info=None): |
|
|||
233 | status.append(new_status) |
|
|||
234 | headers.extend(new_headers) |
|
|||
235 | exc_info.append(new_exc_info) |
|
|||
236 |
|
||||
237 | output = WSGIController.__call__(self, environ, change_content) |
|
|||
238 | output = list(output) # expand iterator - just to ensure exact timing |
|
|||
239 | replace_header(headers, 'Content-Type', 'application/json') |
|
|||
240 | start_response(status[0], headers, exc_info[0]) |
|
|||
241 | log.info('IP: %s Request to %s time: %.3fs' % ( |
|
219 | log.info('IP: %s Request to %s time: %.3fs' % ( | |
242 | self._get_ip_addr(environ), |
|
220 | self._get_ip_addr(environ), | |
243 | safe_unicode(_get_access_path(environ)), time.time() - start) |
|
221 | safe_unicode(_get_access_path(environ)), time.time() - start) | |
244 | ) |
|
222 | ) | |
245 | return output |
|
|||
246 |
|
223 | |||
247 | def _dispatch_call(self): |
|
224 | state.set_action(self._rpc_call, []) | |
|
225 | state.set_params(self._rpc_args) | |||
|
226 | return state | |||
|
227 | ||||
|
228 | def _rpc_call(self, action, environ, **rpc_args): | |||
248 | """ |
|
229 | """ | |
249 | Implement dispatch interface specified by WSGIController |
|
230 | Call the specified RPC Method | |
250 | """ |
|
231 | """ | |
251 | raw_response = '' |
|
232 | raw_response = '' | |
252 | try: |
|
233 | try: | |
253 |
raw_response = |
|
234 | raw_response = getattr(self, action)(**rpc_args) | |
254 | if isinstance(raw_response, HTTPError): |
|
235 | if isinstance(raw_response, HTTPError): | |
255 | self._error = str(raw_response) |
|
236 | self._error = str(raw_response) | |
256 | except JSONRPCError as e: |
|
237 | except JSONRPCError as e: |
@@ -29,9 +29,8 b' import os' | |||||
29 | import cgi |
|
29 | import cgi | |
30 | import logging |
|
30 | import logging | |
31 |
|
31 | |||
32 | from tg import tmpl_context as c, request, config |
|
32 | from tg import tmpl_context as c, request, config, expose | |
33 | from tg.i18n import ugettext as _ |
|
33 | from tg.i18n import ugettext as _ | |
34 | from pylons.middleware import media_path |
|
|||
35 |
|
34 | |||
36 | from kallithea.lib.base import BaseController, render |
|
35 | from kallithea.lib.base import BaseController, render | |
37 |
|
36 | |||
@@ -52,8 +51,9 b' class ErrorController(BaseController):' | |||||
52 | # disable all base actions since we don't need them here |
|
51 | # disable all base actions since we don't need them here | |
53 | pass |
|
52 | pass | |
54 |
|
53 | |||
55 | def document(self): |
|
54 | @expose('/errors/error_document.html') | |
56 | resp = request.environ.get('pylons.original_response') |
|
55 | def document(self, *args, **kwargs): | |
|
56 | resp = request.environ.get('tg.original_response') | |||
57 | c.site_name = config.get('title') |
|
57 | c.site_name = config.get('title') | |
58 |
|
58 | |||
59 | log.debug('### %s ###', resp and resp.status or 'no response') |
|
59 | log.debug('### %s ###', resp and resp.status or 'no response') | |
@@ -70,7 +70,7 b' class ErrorController(BaseController):' | |||||
70 | c.error_message = _('No response') |
|
70 | c.error_message = _('No response') | |
71 | c.error_explanation = _('Unknown error') |
|
71 | c.error_explanation = _('Unknown error') | |
72 |
|
72 | |||
73 | return render('/errors/error_document.html') |
|
73 | return dict() | |
74 |
|
74 | |||
75 | def get_error_explanation(self, code): |
|
75 | def get_error_explanation(self, code): | |
76 | """ get the error explanations of int codes |
|
76 | """ get the error explanations of int codes |
@@ -25,9 +25,8 b' Original author and date, and relevant c' | |||||
25 | :copyright: (c) 2013 RhodeCode GmbH, and others. |
|
25 | :copyright: (c) 2013 RhodeCode GmbH, and others. | |
26 | :license: GPLv3, see LICENSE.md for more details. |
|
26 | :license: GPLv3, see LICENSE.md for more details. | |
27 | """ |
|
27 | """ | |
28 |
|
28 | import tg | ||
29 | from beaker.cache import CacheManager |
|
29 | from tg import config | |
30 | from beaker.util import parse_cache_config_options |
|
|||
31 |
|
30 | |||
32 |
|
31 | |||
33 | class Globals(object): |
|
32 | class Globals(object): | |
@@ -36,11 +35,18 b' class Globals(object):' | |||||
36 | life of the application |
|
35 | life of the application | |
37 | """ |
|
36 | """ | |
38 |
|
37 | |||
39 |
def __init__(self |
|
38 | def __init__(self): | |
40 | """One instance of Globals is created during application |
|
39 | """One instance of Globals is created during application | |
41 | initialization and is available during requests via the |
|
40 | initialization and is available during requests via the | |
42 | 'app_globals' variable |
|
41 | 'app_globals' variable | |
43 |
|
42 | |||
44 | """ |
|
43 | """ | |
45 | self.cache = CacheManager(**parse_cache_config_options(config)) |
|
|||
46 | self.available_permissions = None # propagated after init_model |
|
44 | self.available_permissions = None # propagated after init_model | |
|
45 | ||||
|
46 | @property | |||
|
47 | def cache(self): | |||
|
48 | return tg.cache | |||
|
49 | ||||
|
50 | @property | |||
|
51 | def mako_lookup(self): | |||
|
52 | return config['render_functions']['mako'].normal_loader |
@@ -41,9 +41,8 b' import paste.auth.basic' | |||||
41 | import paste.httpheaders |
|
41 | import paste.httpheaders | |
42 | from webhelpers.pylonslib import secure_form |
|
42 | from webhelpers.pylonslib import secure_form | |
43 |
|
43 | |||
44 | from tg import config, tmpl_context as c, request, response, session |
|
44 | from tg import config, tmpl_context as c, request, response, session, render_template | |
45 |
from |
|
45 | from tg import TGController | |
46 | from pylons.templating import render_mako as render # don't remove this import |
|
|||
47 | from tg.i18n import ugettext as _ |
|
46 | from tg.i18n import ugettext as _ | |
48 |
|
47 | |||
49 | from kallithea import __version__, BACKENDS |
|
48 | from kallithea import __version__, BACKENDS | |
@@ -66,6 +65,10 b' from kallithea.model.scm import ScmModel' | |||||
66 | log = logging.getLogger(__name__) |
|
65 | log = logging.getLogger(__name__) | |
67 |
|
66 | |||
68 |
|
67 | |||
|
68 | def render(template_path): | |||
|
69 | return render_template({'url': url}, 'mako', template_path) | |||
|
70 | ||||
|
71 | ||||
69 | def _filter_proxy(ip): |
|
72 | def _filter_proxy(ip): | |
70 | """ |
|
73 | """ | |
71 | HEADERS can have multiple ips inside the left-most being the original |
|
74 | HEADERS can have multiple ips inside the left-most being the original | |
@@ -101,7 +104,7 b' def _get_ip_addr(environ):' | |||||
101 |
|
104 | |||
102 | def _get_access_path(environ): |
|
105 | def _get_access_path(environ): | |
103 | path = environ.get('PATH_INFO') |
|
106 | path = environ.get('PATH_INFO') | |
104 |
org_req = environ.get(' |
|
107 | org_req = environ.get('tg.original_request') | |
105 | if org_req: |
|
108 | if org_req: | |
106 | path = org_req.environ.get('PATH_INFO') |
|
109 | path = org_req.environ.get('PATH_INFO') | |
107 | return path |
|
110 | return path | |
@@ -375,14 +378,11 b' class BaseVCSController(object):' | |||||
375 | meta.Session.remove() |
|
378 | meta.Session.remove() | |
376 |
|
379 | |||
377 |
|
380 | |||
378 |
class BaseController( |
|
381 | class BaseController(TGController): | |
379 |
|
382 | |||
380 | def _before(self, *args, **kwargs): |
|
383 | def _before(self, *args, **kwargs): | |
381 | pass |
|
|||
382 |
|
||||
383 | def __before__(self): |
|
|||
384 | """ |
|
384 | """ | |
385 |
_ |
|
385 | _before is called before controller methods and after __call__ | |
386 | """ |
|
386 | """ | |
387 | c.kallithea_version = __version__ |
|
387 | c.kallithea_version = __version__ | |
388 | rc_config = Setting.get_app_settings() |
|
388 | rc_config = Setting.get_app_settings() | |
@@ -437,13 +437,6 b' class BaseController(WSGIController):' | |||||
437 |
|
437 | |||
438 | self.scm_model = ScmModel() |
|
438 | self.scm_model = ScmModel() | |
439 |
|
439 | |||
440 | # __before__ in Pylons is called _before in TurboGears2. As preparation |
|
|||
441 | # to the migration to TurboGears2, all __before__ methods were already |
|
|||
442 | # renamed to _before. We call them from here to keep the behavior. |
|
|||
443 | # This is a temporary call that will be removed in the real TurboGears2 |
|
|||
444 | # migration commit. |
|
|||
445 | self._before() |
|
|||
446 |
|
||||
447 | @staticmethod |
|
440 | @staticmethod | |
448 | def _determine_auth_user(api_key, bearer_token, session_authuser): |
|
441 | def _determine_auth_user(api_key, bearer_token, session_authuser): | |
449 | """ |
|
442 | """ | |
@@ -530,12 +523,7 b' class BaseController(WSGIController):' | |||||
530 | log.error('%r request with payload parameters; WebOb should have stopped this', request.method) |
|
523 | log.error('%r request with payload parameters; WebOb should have stopped this', request.method) | |
531 | raise webob.exc.HTTPBadRequest() |
|
524 | raise webob.exc.HTTPBadRequest() | |
532 |
|
525 | |||
533 |
def __call__(self, environ, |
|
526 | def __call__(self, environ, context): | |
534 | """Invoke the Controller""" |
|
|||
535 |
|
||||
536 | # WSGIController.__call__ dispatches to the Controller method |
|
|||
537 | # the request is routed to. This routing information is |
|
|||
538 | # available in environ['pylons.routes_dict'] |
|
|||
539 | try: |
|
527 | try: | |
540 | request.ip_addr = _get_ip_addr(environ) |
|
528 | request.ip_addr = _get_ip_addr(environ) | |
541 | # make sure that we update permissions each time we call controller |
|
529 | # make sure that we update permissions each time we call controller | |
@@ -564,11 +552,9 b' class BaseController(WSGIController):' | |||||
564 | request.ip_addr, request.authuser, |
|
552 | request.ip_addr, request.authuser, | |
565 | safe_unicode(_get_access_path(environ)), |
|
553 | safe_unicode(_get_access_path(environ)), | |
566 | ) |
|
554 | ) | |
567 |
return |
|
555 | return super(BaseController, self).__call__(environ, context) | |
568 | except webob.exc.HTTPException as e: |
|
556 | except webob.exc.HTTPException as e: | |
569 |
return e |
|
557 | return e | |
570 | finally: |
|
|||
571 | meta.Session.remove() |
|
|||
572 |
|
558 | |||
573 |
|
559 | |||
574 | class BaseRepoController(BaseController): |
|
560 | class BaseRepoController(BaseController): |
@@ -82,12 +82,12 b' class BasePasterCommand(gearbox.command.' | |||||
82 | """ |
|
82 | """ | |
83 | Read the config file and initialize logging and the application. |
|
83 | Read the config file and initialize logging and the application. | |
84 | """ |
|
84 | """ | |
85 | from tg import config as pylonsconfig |
|
85 | from kallithea.config.middleware import make_app | |
86 |
|
86 | |||
87 | path_to_ini_file = os.path.realpath(config_file) |
|
87 | path_to_ini_file = os.path.realpath(config_file) | |
88 | conf = paste.deploy.appconfig('config:' + path_to_ini_file) |
|
88 | conf = paste.deploy.appconfig('config:' + path_to_ini_file) | |
89 | logging.config.fileConfig(path_to_ini_file) |
|
89 | logging.config.fileConfig(path_to_ini_file) | |
90 |
|
|
90 | make_app(conf.global_conf, **conf.local_conf) | |
91 |
|
91 | |||
92 | def _init_session(self): |
|
92 | def _init_session(self): | |
93 | """ |
|
93 | """ |
@@ -40,7 +40,7 b' class Command(BasePasterCommand):' | |||||
40 | "Kallithea: Create or update full text search index" |
|
40 | "Kallithea: Create or update full text search index" | |
41 |
|
41 | |||
42 | def take_action(self, args): |
|
42 | def take_action(self, args): | |
43 |
from |
|
43 | from tg import config | |
44 | index_location = config['index_dir'] |
|
44 | index_location = config['index_dir'] | |
45 | load_rcextensions(config['here']) |
|
45 | load_rcextensions(config['here']) | |
46 |
|
46 |
@@ -44,7 +44,7 b' class Command(BasePasterCommand):' | |||||
44 | takes_config_file = False |
|
44 | takes_config_file = False | |
45 |
|
45 | |||
46 | def take_action(self, args): |
|
46 | def take_action(self, args): | |
47 |
from |
|
47 | from tg import config | |
48 |
|
48 | |||
49 | here = config['here'] |
|
49 | here = config['here'] | |
50 | content = pkg_resources.resource_string( |
|
50 | content = pkg_resources.resource_string( |
@@ -516,7 +516,7 b' script_location = kallithea:alembic' | |||||
516 | <%text>################################</%text> |
|
516 | <%text>################################</%text> | |
517 |
|
517 | |||
518 | [loggers] |
|
518 | [loggers] | |
519 | keys = root, routes, kallithea, sqlalchemy, gearbox, beaker, templates, whoosh_indexer |
|
519 | keys = root, routes, kallithea, sqlalchemy, tg, gearbox, beaker, templates, whoosh_indexer | |
520 |
|
520 | |||
521 | [handlers] |
|
521 | [handlers] | |
522 | keys = console, console_sql |
|
522 | keys = console, console_sql | |
@@ -557,6 +557,12 b' handlers =' | |||||
557 | qualname = kallithea |
|
557 | qualname = kallithea | |
558 | propagate = 1 |
|
558 | propagate = 1 | |
559 |
|
559 | |||
|
560 | [logger_tg] | |||
|
561 | level = DEBUG | |||
|
562 | handlers = | |||
|
563 | qualname = tg | |||
|
564 | propagate = 1 | |||
|
565 | ||||
560 | [logger_gearbox] |
|
566 | [logger_gearbox] | |
561 | level = DEBUG |
|
567 | level = DEBUG | |
562 | handlers = |
|
568 | handlers = |
@@ -32,6 +32,7 b' import datetime' | |||||
32 | import traceback |
|
32 | import traceback | |
33 | import beaker |
|
33 | import beaker | |
34 |
|
34 | |||
|
35 | from tg import request, response | |||
35 | from webhelpers.text import collapse, remove_formatting, strip_tags |
|
36 | from webhelpers.text import collapse, remove_formatting, strip_tags | |
36 | from beaker.cache import _cache_decorate |
|
37 | from beaker.cache import _cache_decorate | |
37 |
|
38 |
@@ -29,7 +29,7 b' Original author and date, and relevant c' | |||||
29 | import logging |
|
29 | import logging | |
30 | import traceback |
|
30 | import traceback | |
31 |
|
31 | |||
32 | from tg import tmpl_context as c |
|
32 | from tg import tmpl_context as c, app_globals | |
33 | from tg.i18n import ugettext as _ |
|
33 | from tg.i18n import ugettext as _ | |
34 | from sqlalchemy.orm import joinedload, subqueryload |
|
34 | from sqlalchemy.orm import joinedload, subqueryload | |
35 |
|
35 | |||
@@ -274,8 +274,8 b' class EmailNotificationModel(object):' | |||||
274 |
|
274 | |||
275 | def __init__(self): |
|
275 | def __init__(self): | |
276 | super(EmailNotificationModel, self).__init__() |
|
276 | super(EmailNotificationModel, self).__init__() | |
277 |
self._template_root = kallithea.CONFIG[' |
|
277 | self._template_root = kallithea.CONFIG['paths']['templates'][0] | |
278 |
self._tmpl_lookup = |
|
278 | self._tmpl_lookup = app_globals.mako_lookup | |
279 | self.email_types = { |
|
279 | self.email_types = { | |
280 | self.TYPE_CHANGESET_COMMENT: 'changeset_comment', |
|
280 | self.TYPE_CHANGESET_COMMENT: 'changeset_comment', | |
281 | self.TYPE_PASSWORD_RESET: 'password_reset', |
|
281 | self.TYPE_PASSWORD_RESET: 'password_reset', |
@@ -153,10 +153,10 b' class RepoModel(object):' | |||||
153 | @classmethod |
|
153 | @classmethod | |
154 | def _render_datatable(cls, tmpl, *args, **kwargs): |
|
154 | def _render_datatable(cls, tmpl, *args, **kwargs): | |
155 | import kallithea |
|
155 | import kallithea | |
156 | from tg import tmpl_context as c, request |
|
156 | from tg import tmpl_context as c, request, app_globals | |
157 | from tg.i18n import ugettext as _ |
|
157 | from tg.i18n import ugettext as _ | |
158 |
|
158 | |||
159 |
_tmpl_lookup = |
|
159 | _tmpl_lookup = app_globals.mako_lookup | |
160 | template = _tmpl_lookup.get_template('data_table/_dt_elements.html') |
|
160 | template = _tmpl_lookup.get_template('data_table/_dt_elements.html') | |
161 |
|
161 | |||
162 | tmpl = template.get_def(tmpl) |
|
162 | tmpl = template.get_def(tmpl) |
@@ -21,14 +21,9 b' import tempfile' | |||||
21 | import time |
|
21 | import time | |
22 |
|
22 | |||
23 | from tg import config |
|
23 | from tg import config | |
24 | import pylons |
|
|||
25 | from pylons import url |
|
|||
26 | from pylons.i18n.translation import _get_translator |
|
|||
27 | from pylons.util import ContextObj |
|
|||
28 | from routes.util import URLGenerator |
|
|||
29 | from webtest import TestApp |
|
24 | from webtest import TestApp | |
30 |
|
25 | |||
31 | from kallithea import is_windows |
|
26 | from kallithea import is_windows, model | |
32 | from kallithea.model.db import Notification, User, UserNotification |
|
27 | from kallithea.model.db import Notification, User, UserNotification | |
33 | from kallithea.model.meta import Session |
|
28 | from kallithea.model.meta import Session | |
34 | from kallithea.lib.utils2 import safe_str |
|
29 | from kallithea.lib.utils2 import safe_str | |
@@ -42,6 +37,10 b' log = logging.getLogger(__name__)' | |||||
42 | skipif = pytest.mark.skipif |
|
37 | skipif = pytest.mark.skipif | |
43 | parametrize = pytest.mark.parametrize |
|
38 | parametrize = pytest.mark.parametrize | |
44 |
|
39 | |||
|
40 | # Hack: These module global values MUST be set to actual values before running any tests. This is currently done by conftest.py. | |||
|
41 | url = None | |||
|
42 | testapp = None | |||
|
43 | ||||
45 | __all__ = [ |
|
44 | __all__ = [ | |
46 | 'skipif', 'parametrize', 'environ', 'url', 'TestController', |
|
45 | 'skipif', 'parametrize', 'environ', 'url', 'TestController', | |
47 | 'ldap_lib_installed', 'pam_lib_installed', 'invalidate_all_caches', |
|
46 | 'ldap_lib_installed', 'pam_lib_installed', 'invalidate_all_caches', | |
@@ -147,17 +146,9 b' class TestController(object):' | |||||
147 |
|
146 | |||
148 | @pytest.fixture(autouse=True) |
|
147 | @pytest.fixture(autouse=True) | |
149 | def app_fixture(self): |
|
148 | def app_fixture(self): | |
150 | config = pylons.test.pylonsapp.config |
|
|||
151 | url._push_object(URLGenerator(config['routes.map'], environ)) |
|
|||
152 | pylons.app_globals._push_object(config['pylons.app_globals']) |
|
|||
153 | pylons.config._push_object(config) |
|
|||
154 | pylons.tmpl_context._push_object(ContextObj()) |
|
|||
155 | # Initialize a translator for tests that utilize i18n |
|
|||
156 | translator = _get_translator(pylons.config.get('lang')) |
|
|||
157 | pylons.translator._push_object(translator) |
|
|||
158 | h = NullHandler() |
|
149 | h = NullHandler() | |
159 | logging.getLogger("kallithea").addHandler(h) |
|
150 | logging.getLogger("kallithea").addHandler(h) | |
160 |
self.app = TestApp( |
|
151 | self.app = TestApp(testapp) | |
161 | return self.app |
|
152 | return self.app | |
162 |
|
153 | |||
163 | def remove_all_notifications(self): |
|
154 | def remove_all_notifications(self): |
@@ -1,18 +1,20 b'' | |||||
1 | import os |
|
1 | import os | |
2 | import sys |
|
2 | import sys | |
3 | import logging |
|
3 | import logging | |
|
4 | import pkg_resources | |||
4 |
|
5 | |||
5 | import pkg_resources |
|
|||
6 | from paste.deploy import loadapp |
|
6 | from paste.deploy import loadapp | |
7 | import pylons.test |
|
7 | from routes.util import URLGenerator | |
8 | from pylons.i18n.translation import _get_translator |
|
8 | from tg import config | |
|
9 | ||||
9 | import pytest |
|
10 | import pytest | |
10 | from kallithea.model.user import UserModel |
|
11 | from kallithea.model.user import UserModel | |
11 | from kallithea.model.meta import Session |
|
12 | from kallithea.model.meta import Session | |
12 | from kallithea.model.db import Setting, User, UserIpMap |
|
13 | from kallithea.model.db import Setting, User, UserIpMap | |
13 | from kallithea.tests.base import invalidate_all_caches, TEST_USER_REGULAR_LOGIN |
|
14 | from kallithea.tests.base import invalidate_all_caches, TEST_USER_REGULAR_LOGIN | |
|
15 | import kallithea.tests.base # FIXME: needed for setting testapp instance!!! | |||
14 |
|
16 | |||
15 |
from |
|
17 | from tg.util.webtest import test_context | |
16 |
|
18 | |||
17 | def pytest_configure(): |
|
19 | def pytest_configure(): | |
18 | path = os.getcwd() |
|
20 | path = os.getcwd() | |
@@ -21,14 +23,10 b' def pytest_configure():' | |||||
21 |
|
23 | |||
22 | # Disable INFO logging of test database creation, restore with NOTSET |
|
24 | # Disable INFO logging of test database creation, restore with NOTSET | |
23 | logging.disable(logging.INFO) |
|
25 | logging.disable(logging.INFO) | |
24 |
|
|
26 | kallithea.tests.base.testapp = loadapp('config:kallithea/tests/test.ini', relative_to=path) | |
25 | logging.disable(logging.NOTSET) |
|
27 | logging.disable(logging.NOTSET) | |
26 |
|
28 | |||
27 | # Initialize a translator for tests that utilize i18n |
|
29 | kallithea.tests.base.url = URLGenerator(config['routes.map'], kallithea.tests.base.environ) | |
28 | translator = _get_translator(pylons.config.get('lang')) |
|
|||
29 | pylons.translator._push_object(translator) |
|
|||
30 |
|
||||
31 | return pylons.test.pylonsapp |
|
|||
32 |
|
30 | |||
33 |
|
31 | |||
34 | @pytest.fixture |
|
32 | @pytest.fixture |
@@ -6,7 +6,7 b' from kallithea.model.notification import' | |||||
6 | from kallithea.model.meta import Session |
|
6 | from kallithea.model.meta import Session | |
7 | from kallithea.lib import helpers as h |
|
7 | from kallithea.lib import helpers as h | |
8 |
|
8 | |||
9 |
from |
|
9 | from tg.util.webtest import test_context | |
10 |
|
10 | |||
11 | class TestNotificationsController(TestController): |
|
11 | class TestNotificationsController(TestController): | |
12 | def setup_method(self, method): |
|
12 | def setup_method(self, method): |
@@ -5,7 +5,7 b' from kallithea.model.user import UserMod' | |||||
5 | from kallithea.model.meta import Session |
|
5 | from kallithea.model.meta import Session | |
6 | from kallithea.tests.base import * |
|
6 | from kallithea.tests.base import * | |
7 |
|
7 | |||
8 |
from |
|
8 | from tg.util.webtest import test_context | |
9 |
|
9 | |||
10 |
|
10 | |||
11 | class TestAdminPermissionsController(TestController): |
|
11 | class TestAdminPermissionsController(TestController): |
@@ -17,7 +17,6 b' from sqlalchemy.orm.exc import NoResultF' | |||||
17 | import pytest |
|
17 | import pytest | |
18 | from kallithea.tests.base import * |
|
18 | from kallithea.tests.base import * | |
19 | from kallithea.tests.fixture import Fixture |
|
19 | from kallithea.tests.fixture import Fixture | |
20 | from kallithea.tests.test_context import test_context |
|
|||
21 | from kallithea.controllers.admin.users import UsersController |
|
20 | from kallithea.controllers.admin.users import UsersController | |
22 | from kallithea.model.db import User, Permission, UserIpMap, UserApiKeys |
|
21 | from kallithea.model.db import User, Permission, UserIpMap, UserApiKeys | |
23 | from kallithea.lib.auth import check_password |
|
22 | from kallithea.lib.auth import check_password | |
@@ -27,6 +26,8 b' from kallithea.lib import helpers as h' | |||||
27 | from kallithea.model.meta import Session |
|
26 | from kallithea.model.meta import Session | |
28 | from webob.exc import HTTPNotFound |
|
27 | from webob.exc import HTTPNotFound | |
29 |
|
28 | |||
|
29 | from tg.util.webtest import test_context | |||
|
30 | ||||
30 | fixture = Fixture() |
|
31 | fixture = Fixture() | |
31 |
|
32 | |||
32 | @pytest.fixture |
|
33 | @pytest.fixture |
@@ -16,7 +16,7 b' from kallithea.model.db import User, Not' | |||||
16 | from kallithea.model.meta import Session |
|
16 | from kallithea.model.meta import Session | |
17 | from kallithea.model.user import UserModel |
|
17 | from kallithea.model.user import UserModel | |
18 |
|
18 | |||
19 |
from |
|
19 | from tg.util.webtest import test_context | |
20 |
|
20 | |||
21 | fixture = Fixture() |
|
21 | fixture = Fixture() | |
22 |
|
22 |
@@ -7,7 +7,7 b' from kallithea.lib import helpers as h' | |||||
7 | from kallithea.model.user import UserModel |
|
7 | from kallithea.model.user import UserModel | |
8 | from kallithea.model.meta import Session |
|
8 | from kallithea.model.meta import Session | |
9 |
|
9 | |||
10 |
from |
|
10 | from tg.util.webtest import test_context | |
11 |
|
11 | |||
12 | fixture = Fixture() |
|
12 | fixture = Fixture() | |
13 |
|
13 |
@@ -1,7 +1,7 b'' | |||||
1 | import re |
|
1 | import re | |
2 | import pytest |
|
2 | import pytest | |
3 |
|
3 | |||
4 |
from |
|
4 | from tg.util.webtest import test_context | |
5 |
|
5 | |||
6 | from kallithea.tests.base import * |
|
6 | from kallithea.tests.base import * | |
7 | from kallithea.tests.fixture import Fixture |
|
7 | from kallithea.tests.fixture import Fixture |
@@ -14,7 +14,7 b' from kallithea.model.notification import' | |||||
14 | import kallithea.lib.celerylib |
|
14 | import kallithea.lib.celerylib | |
15 | import kallithea.lib.celerylib.tasks |
|
15 | import kallithea.lib.celerylib.tasks | |
16 |
|
16 | |||
17 |
from |
|
17 | from tg.util.webtest import test_context | |
18 |
|
18 | |||
19 | class TestNotifications(TestController): |
|
19 | class TestNotifications(TestController): | |
20 |
|
20 |
@@ -31,7 +31,7 b' import mock' | |||||
31 | from kallithea.tests.base import * |
|
31 | from kallithea.tests.base import * | |
32 | from kallithea.lib.utils2 import AttributeDict |
|
32 | from kallithea.lib.utils2 import AttributeDict | |
33 | from kallithea.model.db import Repository |
|
33 | from kallithea.model.db import Repository | |
34 |
from |
|
34 | from tg.util.webtest import test_context | |
35 |
|
35 | |||
36 | proto = 'http' |
|
36 | proto = 'http' | |
37 | TEST_URLS = [ |
|
37 | TEST_URLS = [ | |
@@ -224,7 +224,7 b' class TestLibs(TestController):' | |||||
224 | from kallithea.lib.helpers import gravatar_url |
|
224 | from kallithea.lib.helpers import gravatar_url | |
225 | _md5 = lambda s: hashlib.md5(s).hexdigest() |
|
225 | _md5 = lambda s: hashlib.md5(s).hexdigest() | |
226 |
|
226 | |||
227 |
# |
|
227 | # mock tg.tmpl_context | |
228 | def fake_tmpl_context(_url): |
|
228 | def fake_tmpl_context(_url): | |
229 | _c = AttributeDict() |
|
229 | _c = AttributeDict() | |
230 | _c.visual = AttributeDict() |
|
230 | _c.visual = AttributeDict() | |
@@ -236,31 +236,31 b' class TestLibs(TestController):' | |||||
236 | fake_url = FakeUrlGenerator(current_url='https://example.com') |
|
236 | fake_url = FakeUrlGenerator(current_url='https://example.com') | |
237 | with mock.patch('kallithea.config.routing.url', fake_url): |
|
237 | with mock.patch('kallithea.config.routing.url', fake_url): | |
238 | fake = fake_tmpl_context(_url='http://example.com/{email}') |
|
238 | fake = fake_tmpl_context(_url='http://example.com/{email}') | |
239 |
with mock.patch(' |
|
239 | with mock.patch('tg.tmpl_context', fake): | |
240 | from kallithea.config.routing import url |
|
240 | from kallithea.config.routing import url | |
241 | assert url.current() == 'https://example.com' |
|
241 | assert url.current() == 'https://example.com' | |
242 | grav = gravatar_url(email_address='test@example.com', size=24) |
|
242 | grav = gravatar_url(email_address='test@example.com', size=24) | |
243 | assert grav == 'http://example.com/test@example.com' |
|
243 | assert grav == 'http://example.com/test@example.com' | |
244 |
|
244 | |||
245 | fake = fake_tmpl_context(_url='http://example.com/{email}') |
|
245 | fake = fake_tmpl_context(_url='http://example.com/{email}') | |
246 |
with mock.patch(' |
|
246 | with mock.patch('tg.tmpl_context', fake): | |
247 | grav = gravatar_url(email_address='test@example.com', size=24) |
|
247 | grav = gravatar_url(email_address='test@example.com', size=24) | |
248 | assert grav == 'http://example.com/test@example.com' |
|
248 | assert grav == 'http://example.com/test@example.com' | |
249 |
|
249 | |||
250 | fake = fake_tmpl_context(_url='http://example.com/{md5email}') |
|
250 | fake = fake_tmpl_context(_url='http://example.com/{md5email}') | |
251 |
with mock.patch(' |
|
251 | with mock.patch('tg.tmpl_context', fake): | |
252 | em = 'test@example.com' |
|
252 | em = 'test@example.com' | |
253 | grav = gravatar_url(email_address=em, size=24) |
|
253 | grav = gravatar_url(email_address=em, size=24) | |
254 | assert grav == 'http://example.com/%s' % (_md5(em)) |
|
254 | assert grav == 'http://example.com/%s' % (_md5(em)) | |
255 |
|
255 | |||
256 | fake = fake_tmpl_context(_url='http://example.com/{md5email}/{size}') |
|
256 | fake = fake_tmpl_context(_url='http://example.com/{md5email}/{size}') | |
257 |
with mock.patch(' |
|
257 | with mock.patch('tg.tmpl_context', fake): | |
258 | em = 'test@example.com' |
|
258 | em = 'test@example.com' | |
259 | grav = gravatar_url(email_address=em, size=24) |
|
259 | grav = gravatar_url(email_address=em, size=24) | |
260 | assert grav == 'http://example.com/%s/%s' % (_md5(em), 24) |
|
260 | assert grav == 'http://example.com/%s/%s' % (_md5(em), 24) | |
261 |
|
261 | |||
262 | fake = fake_tmpl_context(_url='{scheme}://{netloc}/{md5email}/{size}') |
|
262 | fake = fake_tmpl_context(_url='{scheme}://{netloc}/{md5email}/{size}') | |
263 |
with mock.patch(' |
|
263 | with mock.patch('tg.tmpl_context', fake): | |
264 | em = 'test@example.com' |
|
264 | em = 'test@example.com' | |
265 | grav = gravatar_url(email_address=em, size=24) |
|
265 | grav = gravatar_url(email_address=em, size=24) | |
266 | assert grav == 'https://example.com/%s/%s' % (_md5(em), 24) |
|
266 | assert grav == 'https://example.com/%s/%s' % (_md5(em), 24) |
@@ -517,7 +517,7 b' script_location = kallithea:alembic' | |||||
517 | ################################ |
|
517 | ################################ | |
518 |
|
518 | |||
519 | [loggers] |
|
519 | [loggers] | |
520 | keys = root, routes, kallithea, sqlalchemy, gearbox, beaker, templates, whoosh_indexer |
|
520 | keys = root, routes, kallithea, sqlalchemy, tg, gearbox, beaker, templates, whoosh_indexer | |
521 |
|
521 | |||
522 | [handlers] |
|
522 | [handlers] | |
523 | keys = console, console_sql |
|
523 | keys = console, console_sql | |
@@ -558,6 +558,12 b' handlers =' | |||||
558 | qualname = kallithea |
|
558 | qualname = kallithea | |
559 | propagate = 1 |
|
559 | propagate = 1 | |
560 |
|
560 | |||
|
561 | [logger_tg] | |||
|
562 | level = DEBUG | |||
|
563 | handlers = | |||
|
564 | qualname = tg | |||
|
565 | propagate = 1 | |||
|
566 | ||||
561 | [logger_gearbox] |
|
567 | [logger_gearbox] | |
562 | level = DEBUG |
|
568 | level = DEBUG | |
563 | handlers = |
|
569 | handlers = |
@@ -39,7 +39,9 b' requirements = [' | |||||
39 | "GearBox<1", |
|
39 | "GearBox<1", | |
40 | "waitress>=0.8.8,<1.0", |
|
40 | "waitress>=0.8.8,<1.0", | |
41 | "webob>=1.7,<2", |
|
41 | "webob>=1.7,<2", | |
42 | "Pylons>=1.0.0,<=1.0.2", |
|
42 | "backlash >= 0.1.1, < 1.0.0", | |
|
43 | "TurboGears2 >= 2.3.10, < 3.0.0", | |||
|
44 | "tgext.routes >= 0.2.0, < 1.0.0", | |||
43 | "Beaker>=1.7.0,<2", |
|
45 | "Beaker>=1.7.0,<2", | |
44 | "WebHelpers==1.3", |
|
46 | "WebHelpers==1.3", | |
45 | "formencode>=1.2.4,<=1.2.6", |
|
47 | "formencode>=1.2.4,<=1.2.6", | |
@@ -56,6 +58,8 b' requirements = [' | |||||
56 | "Routes==1.13", |
|
58 | "Routes==1.13", | |
57 | "dulwich>=0.14.1", |
|
59 | "dulwich>=0.14.1", | |
58 | "mercurial>=2.9,<4.2", |
|
60 | "mercurial>=2.9,<4.2", | |
|
61 | "decorator >= 3.3.2", | |||
|
62 | "Paste >= 2.0.3, < 3.0", | |||
59 | ] |
|
63 | ] | |
60 |
|
64 | |||
61 | if sys.version_info < (2, 7): |
|
65 | if sys.version_info < (2, 7): | |
@@ -151,9 +155,6 b' setuptools.setup(' | |||||
151 | [paste.app_factory] |
|
155 | [paste.app_factory] | |
152 | main = kallithea.config.middleware:make_app |
|
156 | main = kallithea.config.middleware:make_app | |
153 |
|
157 | |||
154 | [paste.app_install] |
|
|||
155 | main = pylons.util:PylonsInstaller |
|
|||
156 |
|
||||
157 | [gearbox.commands] |
|
158 | [gearbox.commands] | |
158 | make-config=kallithea.lib.paster_commands.make_config:Command |
|
159 | make-config=kallithea.lib.paster_commands.make_config:Command | |
159 | setup-db=kallithea.lib.paster_commands.setup_db:Command |
|
160 | setup-db=kallithea.lib.paster_commands.setup_db:Command |
General Comments 0
You need to be logged in to leave comments.
Login now