Move entire app structure over to using the new config system.
[mediagoblin.git] / mediagoblin / app.py
CommitLineData
8e1e744d 1# GNU MediaGoblin -- federated, autonomous media hosting
e5572c60
ML
2# Copyright (C) 2011 Free Software Foundation, Inc
3#
4# This program is free software: you can redistribute it and/or modify
5# it under the terms of the GNU Affero General Public License as published by
6# the Free Software Foundation, either version 3 of the License, or
7# (at your option) any later version.
8#
9# This program is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12# GNU Affero General Public License for more details.
13#
14# You should have received a copy of the GNU Affero General Public License
15# along with this program. If not, see <http://www.gnu.org/licenses/>.
16
571198c9 17import os
31a8ff42
CAW
18import urllib
19
31a8ff42 20import routes
b61874b2 21from webob import Request, exc
31a8ff42 22
0f18ed8f 23from mediagoblin import routing, util, storage, staticdirect
3f5cf663
CAW
24from mediagoblin.config import (
25 read_mediagoblin_config, generate_validation_report)
a67fec81 26from mediagoblin.db.open import setup_connection_and_db_from_config
6e7ce8d1 27from mediagoblin.mg_globals import setup_globals
5784c4e9 28from mediagoblin.celery_setup import setup_celery_from_config
3f5cf663 29from mediagoblin.workbench import WorkbenchManager
31a8ff42
CAW
30
31
32class Error(Exception): pass
33class ImproperlyConfigured(Error): pass
34
35
8e1e744d 36class MediaGoblinApp(object):
31a8ff42 37 """
3f5cf663
CAW
38 WSGI application of MediaGoblin
39
40 ... this is the heart of the program!
31a8ff42 41 """
3f5cf663
CAW
42 def __init__(self, config_path, setup_celery=True):
43 """
44 Initialize the application based on a configuration file.
45
46 Arguments:
47 - config_path: path to the configuration file we're opening.
48 - setup_celery: whether or not to setup celery during init.
49 (Note: setting 'celery_setup_elsewhere' also disables
50 setting up celery.)
51 """
52 ##############
53 # Setup config
54 ##############
55
56 # Open and setup the config
57 global_config, validation_result = read_mediagoblin_config(config_path)
58 app_config = global_config['mediagoblin']
59 # report errors if necessary
60 validation_report = generate_validation_report(
61 global_config, validation_result)
62 if validation_report:
63 raise ImproperlyConfigured(validation_report)
64
65 ##########################################
66 # Setup other connections / useful objects
67 ##########################################
68
69 # Set up the database
70 self.connection, self.db = setup_connection_and_db_from_config(
71 app_config)
72
5afdd7a1 73 # Get the template environment
3f5cf663
CAW
74 self.template_loader = util.get_jinja_loader(
75 app_config.get('user_template_path'))
5afdd7a1
CAW
76
77 # Set up storage systems
3f5cf663
CAW
78 self.public_store = storage.storage_system_from_paste_config(
79 app_config, 'publicstore')
80 self.queue_store = storage.storage_system_from_paste_config(
81 app_config, 'queuestore')
5afdd7a1
CAW
82
83 # set up routing
0f63a944 84 self.routing = routing.get_mapper()
31a8ff42 85
582c4d5f 86 # set up staticdirector tool
3f5cf663
CAW
87 if app_config.has_key('direct_remote_path'):
88 self.staticdirector = staticdirect.RemoteStaticDirect(
89 app_config['direct_remote_path'].strip())
90 elif app_config.has_key('direct_remote_paths'):
91 direct_remote_path_lines = app_config[
92 'direct_remote_paths'].strip().splitlines()
93 self.staticdirector = staticdirect.MultiRemoteStaticDirect(
94 dict([line.strip().split(' ', 1)
95 for line in direct_remote_path_lines]))
96 else:
97 raise ImproperlyConfigured(
98 "One of direct_remote_path or "
99 "direct_remote_paths must be provided")
100
101 # Setup celery, if appropriate
102 if setup_celery and not app_config.get('celery_setup_elsewhere'):
103 if os.environ.get('CELERY_ALWAYS_EAGER'):
104 setup_celery_from_config(
105 app_config, global_config,
106 force_celery_always_eager=True)
107 else:
108 setup_celery_from_config(app_config, global_config)
109
110 #######################################################
111 # Insert appropriate things into mediagoblin.mg_globals
112 #
df9809c2
CAW
113 # certain properties need to be accessed globally eg from
114 # validators, etc, which might not access to the request
115 # object.
3f5cf663
CAW
116 #######################################################
117
df9809c2 118 setup_globals(
3f5cf663
CAW
119 app_config=app_config,
120 global_config=global_config,
121
122 # TODO: No need to set these two up as globals, we could
123 # just read them out of mg_globals.app_config
124 email_sender_address=app_config['email_sender_address'],
125 email_debug_mode=app_config['email_debug_mode'],
126
127 # Actual, useful to everyone objects
128 db_connection=self.connection,
df9809c2
CAW
129 database=self.db,
130 public_store=self.public_store,
7ecc58cc 131 queue_store=self.queue_store,
3f5cf663 132 workbench_manager=WorkbenchManager(app_config['workbench_path']))
df9809c2 133
31a8ff42
CAW
134 def __call__(self, environ, start_response):
135 request = Request(environ)
136 path_info = request.path_info
582c4d5f
CAW
137
138 ## Routing / controller loading stuff
0f63a944 139 route_match = self.routing.match(path_info)
31a8ff42
CAW
140
141 # No matching page?
142 if route_match is None:
143 # Try to do see if we have a match with a trailing slash
144 # added and if so, redirect
145 if not path_info.endswith('/') \
146 and request.method == 'GET' \
0f63a944 147 and self.routing.match(path_info + '/'):
31a8ff42
CAW
148 new_path_info = path_info + '/'
149 if request.GET:
150 new_path_info = '%s?%s' % (
151 new_path_info, urllib.urlencode(request.GET))
1bb0fdf2 152 redirect = exc.HTTPFound(location=new_path_info)
31a8ff42
CAW
153 return request.get_response(redirect)(environ, start_response)
154
155 # Okay, no matches. 404 time!
156 return exc.HTTPNotFound()(environ, start_response)
157
cb8ea0fe 158 controller = util.import_component(route_match['controller'])
31a8ff42
CAW
159 request.start_response = start_response
160
582c4d5f 161 ## Attach utilities to the request object
31a8ff42 162 request.matchdict = route_match
0f63a944 163 request.urlgen = routes.URLGenerator(self.routing, environ)
7846e406 164 # Do we really want to load this via middleware? Maybe?
14ba9383 165 request.session = request.environ['beaker.session']
0dd65945
CAW
166 # Attach self as request.app
167 # Also attach a few utilities from request.app for convenience?
168 request.app = self
0e0e3d9a
CAW
169 request.locale = util.get_locale_from_request(request)
170
171 request.template_env = util.get_jinja_env(
172 self.template_loader, request.locale)
0dd65945
CAW
173 request.db = self.db
174 request.staticdirect = self.staticdirector
31a8ff42 175
ddff7cce
CAW
176 util.setup_user_in_request(request)
177
31a8ff42
CAW
178 return controller(request)(environ, start_response)
179
180
5784c4e9 181def paste_app_factory(global_config, **app_config):
3f5cf663 182 mgoblin_app = MediaGoblinApp(app_config['config'])
b61874b2 183
c4d71564 184 return mgoblin_app