Use new migrations system in ./bin/gmg migrate
[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
421129b6 24from mediagoblin.init.config import (
3f5cf663 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
073b61fe 28from mediagoblin.init.celery import setup_celery_from_config
42ef819c 29from mediagoblin.init import get_jinja_loader
3f5cf663 30from mediagoblin.workbench import WorkbenchManager
31a8ff42
CAW
31
32
33class Error(Exception): pass
34class ImproperlyConfigured(Error): pass
35
36
8e1e744d 37class MediaGoblinApp(object):
31a8ff42 38 """
3f5cf663
CAW
39 WSGI application of MediaGoblin
40
41 ... this is the heart of the program!
31a8ff42 42 """
3f5cf663
CAW
43 def __init__(self, config_path, setup_celery=True):
44 """
45 Initialize the application based on a configuration file.
46
47 Arguments:
48 - config_path: path to the configuration file we're opening.
49 - setup_celery: whether or not to setup celery during init.
50 (Note: setting 'celery_setup_elsewhere' also disables
51 setting up celery.)
52 """
53 ##############
54 # Setup config
55 ##############
56
57 # Open and setup the config
58 global_config, validation_result = read_mediagoblin_config(config_path)
59 app_config = global_config['mediagoblin']
60 # report errors if necessary
61 validation_report = generate_validation_report(
62 global_config, validation_result)
63 if validation_report:
64 raise ImproperlyConfigured(validation_report)
65
66 ##########################################
67 # Setup other connections / useful objects
68 ##########################################
69
70 # Set up the database
71 self.connection, self.db = setup_connection_and_db_from_config(
72 app_config)
73
5afdd7a1 74 # Get the template environment
42ef819c 75 self.template_loader = get_jinja_loader(
3f5cf663 76 app_config.get('user_template_path'))
5afdd7a1
CAW
77
78 # Set up storage systems
3c7d11ff 79 self.public_store = storage.storage_system_from_config(
3f5cf663 80 app_config, 'publicstore')
3c7d11ff 81 self.queue_store = storage.storage_system_from_config(
3f5cf663 82 app_config, 'queuestore')
5afdd7a1
CAW
83
84 # set up routing
0f63a944 85 self.routing = routing.get_mapper()
31a8ff42 86
582c4d5f 87 # set up staticdirector tool
3f5cf663
CAW
88 if app_config.has_key('direct_remote_path'):
89 self.staticdirector = staticdirect.RemoteStaticDirect(
90 app_config['direct_remote_path'].strip())
91 elif app_config.has_key('direct_remote_paths'):
92 direct_remote_path_lines = app_config[
93 'direct_remote_paths'].strip().splitlines()
94 self.staticdirector = staticdirect.MultiRemoteStaticDirect(
95 dict([line.strip().split(' ', 1)
96 for line in direct_remote_path_lines]))
97 else:
98 raise ImproperlyConfigured(
99 "One of direct_remote_path or "
100 "direct_remote_paths must be provided")
101
102 # Setup celery, if appropriate
103 if setup_celery and not app_config.get('celery_setup_elsewhere'):
104 if os.environ.get('CELERY_ALWAYS_EAGER'):
105 setup_celery_from_config(
106 app_config, global_config,
107 force_celery_always_eager=True)
108 else:
109 setup_celery_from_config(app_config, global_config)
110
111 #######################################################
112 # Insert appropriate things into mediagoblin.mg_globals
113 #
df9809c2
CAW
114 # certain properties need to be accessed globally eg from
115 # validators, etc, which might not access to the request
116 # object.
3f5cf663
CAW
117 #######################################################
118
df9809c2 119 setup_globals(
3f5cf663
CAW
120 app_config=app_config,
121 global_config=global_config,
122
123 # TODO: No need to set these two up as globals, we could
124 # just read them out of mg_globals.app_config
125 email_sender_address=app_config['email_sender_address'],
126 email_debug_mode=app_config['email_debug_mode'],
127
128 # Actual, useful to everyone objects
becb77ee 129 app=self,
3f5cf663 130 db_connection=self.connection,
df9809c2
CAW
131 database=self.db,
132 public_store=self.public_store,
7ecc58cc 133 queue_store=self.queue_store,
3f5cf663 134 workbench_manager=WorkbenchManager(app_config['workbench_path']))
df9809c2 135
31a8ff42
CAW
136 def __call__(self, environ, start_response):
137 request = Request(environ)
138 path_info = request.path_info
582c4d5f
CAW
139
140 ## Routing / controller loading stuff
0f63a944 141 route_match = self.routing.match(path_info)
31a8ff42
CAW
142
143 # No matching page?
144 if route_match is None:
145 # Try to do see if we have a match with a trailing slash
146 # added and if so, redirect
147 if not path_info.endswith('/') \
148 and request.method == 'GET' \
0f63a944 149 and self.routing.match(path_info + '/'):
31a8ff42
CAW
150 new_path_info = path_info + '/'
151 if request.GET:
152 new_path_info = '%s?%s' % (
153 new_path_info, urllib.urlencode(request.GET))
1bb0fdf2 154 redirect = exc.HTTPFound(location=new_path_info)
31a8ff42
CAW
155 return request.get_response(redirect)(environ, start_response)
156
157 # Okay, no matches. 404 time!
158 return exc.HTTPNotFound()(environ, start_response)
159
cb8ea0fe 160 controller = util.import_component(route_match['controller'])
31a8ff42
CAW
161 request.start_response = start_response
162
582c4d5f 163 ## Attach utilities to the request object
31a8ff42 164 request.matchdict = route_match
0f63a944 165 request.urlgen = routes.URLGenerator(self.routing, environ)
7846e406 166 # Do we really want to load this via middleware? Maybe?
14ba9383 167 request.session = request.environ['beaker.session']
0dd65945
CAW
168 # Attach self as request.app
169 # Also attach a few utilities from request.app for convenience?
170 request.app = self
0e0e3d9a
CAW
171 request.locale = util.get_locale_from_request(request)
172
173 request.template_env = util.get_jinja_env(
174 self.template_loader, request.locale)
0dd65945
CAW
175 request.db = self.db
176 request.staticdirect = self.staticdirector
31a8ff42 177
ddff7cce
CAW
178 util.setup_user_in_request(request)
179
31a8ff42
CAW
180 return controller(request)(environ, start_response)
181
182
5784c4e9 183def paste_app_factory(global_config, **app_config):
3f5cf663 184 mgoblin_app = MediaGoblinApp(app_config['config'])
b61874b2 185
c4d71564 186 return mgoblin_app