Updating deployment guide so that it can handle the current virtualenv site-packages...
[mediagoblin.git] / mediagoblin / app.py
CommitLineData
8e1e744d 1# GNU MediaGoblin -- federated, autonomous media hosting
12a100e4 2# Copyright (C) 2011 MediaGoblin contributors. See AUTHORS.
e5572c60
ML
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
ce5ae8da 23from mediagoblin import routing, meddleware
c0022ecc
CAW
24from mediagoblin.tools import common, translate, template
25from mediagoblin.tools.response import render_404
152a3bfa 26from mediagoblin.tools import request as mg_request
6e7ce8d1 27from mediagoblin.mg_globals import setup_globals
073b61fe 28from mediagoblin.init.celery import setup_celery_from_config
50854db0
WKG
29from mediagoblin.init import (get_jinja_loader, get_staticdirector,
30 setup_global_and_app_config, setup_workbench, setup_database,
0533f117 31 setup_storage, setup_beaker_cache)
90e342f9 32
31a8ff42 33
8e1e744d 34class MediaGoblinApp(object):
31a8ff42 35 """
3f5cf663
CAW
36 WSGI application of MediaGoblin
37
38 ... this is the heart of the program!
31a8ff42 39 """
3f5cf663
CAW
40 def __init__(self, config_path, setup_celery=True):
41 """
42 Initialize the application based on a configuration file.
43
44 Arguments:
45 - config_path: path to the configuration file we're opening.
46 - setup_celery: whether or not to setup celery during init.
47 (Note: setting 'celery_setup_elsewhere' also disables
48 setting up celery.)
49 """
50 ##############
51 # Setup config
52 ##############
53
54 # Open and setup the config
fe289be4 55 global_config, app_config = setup_global_and_app_config(config_path)
3f5cf663
CAW
56
57 ##########################################
58 # Setup other connections / useful objects
59 ##########################################
60
61 # Set up the database
3f4b5e4a 62 self.connection, self.db = setup_database()
ff94114c 63
5afdd7a1 64 # Get the template environment
42ef819c 65 self.template_loader = get_jinja_loader(
1e9d1acc 66 app_config.get('local_templates'))
0c8a30e6 67
5afdd7a1 68 # Set up storage systems
dccef262 69 self.public_store, self.queue_store = setup_storage()
5afdd7a1
CAW
70
71 # set up routing
0f63a944 72 self.routing = routing.get_mapper()
31a8ff42 73
582c4d5f 74 # set up staticdirector tool
c85c9dc7 75 self.staticdirector = get_staticdirector(app_config)
3f5cf663 76
0533f117
CAW
77 # set up caching
78 self.cache = setup_beaker_cache()
79
3f5cf663
CAW
80 # Setup celery, if appropriate
81 if setup_celery and not app_config.get('celery_setup_elsewhere'):
82 if os.environ.get('CELERY_ALWAYS_EAGER'):
83 setup_celery_from_config(
84 app_config, global_config,
85 force_celery_always_eager=True)
86 else:
87 setup_celery_from_config(app_config, global_config)
88
89 #######################################################
90 # Insert appropriate things into mediagoblin.mg_globals
91 #
df9809c2
CAW
92 # certain properties need to be accessed globally eg from
93 # validators, etc, which might not access to the request
94 # object.
3f5cf663
CAW
95 #######################################################
96
243c3843 97 setup_globals(app=self)
1fd97db3
CAW
98
99 # Workbench *currently* only used by celery, so this only
100 # matters in always eager mode :)
7664b4db 101 setup_workbench()
df9809c2 102
ce5ae8da
CAW
103 # instantiate application meddleware
104 self.meddleware = [common.import_component(m)(self)
105 for m in meddleware.ENABLED_MEDDLEWARE]
0c8a30e6 106
31a8ff42
CAW
107 def __call__(self, environ, start_response):
108 request = Request(environ)
0c8a30e6 109
582c4d5f 110 ## Routing / controller loading stuff
0c8a30e6 111 path_info = request.path_info
0f63a944 112 route_match = self.routing.match(path_info)
31a8ff42 113
05788ef4
E
114 # By using fcgi, mediagoblin can run under a base path
115 # like /mediagoblin/. request.path_info contains the
116 # path inside mediagoblin. If the something needs the
117 # full path of the current page, that should include
118 # the basepath.
119 # Note: urlgen and routes are fine!
120 request.full_path = environ["SCRIPT_NAME"] + request.path_info
121 # python-routes uses SCRIPT_NAME. So let's use that too.
122 # The other option would be:
123 # request.full_path = environ["SCRIPT_URL"]
124
871fc591 125 # Fix up environ for urlgen
d23d4b23 126 # See bug: https://bitbucket.org/bbangert/routes/issue/55/cache_hostinfo-breaks-on-https-off
871fc591
E
127 if environ.get('HTTPS', '').lower() == 'off':
128 environ.pop('HTTPS')
129
3d0557bf
CAW
130 ## Attach utilities to the request object
131 request.matchdict = route_match
132 request.urlgen = routes.URLGenerator(self.routing, environ)
133 # Do we really want to load this via middleware? Maybe?
134 request.session = request.environ['beaker.session']
135 # Attach self as request.app
136 # Also attach a few utilities from request.app for convenience?
137 request.app = self
ae3bc7fa 138 request.locale = translate.get_locale_from_request(request)
0c8a30e6 139
ae3bc7fa 140 request.template_env = template.get_jinja_env(
3d0557bf
CAW
141 self.template_loader, request.locale)
142 request.db = self.db
143 request.staticdirect = self.staticdirector
144
152a3bfa 145 mg_request.setup_user_in_request(request)
3d0557bf 146
31a8ff42
CAW
147 # No matching page?
148 if route_match is None:
149 # Try to do see if we have a match with a trailing slash
150 # added and if so, redirect
151 if not path_info.endswith('/') \
152 and request.method == 'GET' \
0f63a944 153 and self.routing.match(path_info + '/'):
31a8ff42
CAW
154 new_path_info = path_info + '/'
155 if request.GET:
156 new_path_info = '%s?%s' % (
157 new_path_info, urllib.urlencode(request.GET))
1bb0fdf2 158 redirect = exc.HTTPFound(location=new_path_info)
31a8ff42
CAW
159 return request.get_response(redirect)(environ, start_response)
160
161 # Okay, no matches. 404 time!
3807e8e2 162 request.matchdict = {} # in case our template expects it
c0022ecc 163 return render_404(request)(environ, start_response)
31a8ff42 164
152a3bfa 165 controller = common.import_component(route_match['controller'])
91cf6738
NY
166
167 # pass the request through our meddleware classes
168 for m in self.meddleware:
169 response = m.process_request(request, controller)
170 if response is not None:
171 return response(environ, start_response)
172
31a8ff42
CAW
173 request.start_response = start_response
174
0c8a30e6
NY
175 # get the response from the controller
176 response = controller(request)
177
ce5ae8da
CAW
178 # pass the response through the meddleware
179 for m in self.meddleware[::-1]:
0c8a30e6
NY
180 m.process_response(request, response)
181
182 return response(environ, start_response)
31a8ff42
CAW
183
184
5784c4e9 185def paste_app_factory(global_config, **app_config):
91903aa6
CAW
186 configs = app_config['config'].split()
187 mediagoblin_config = None
188 for config in configs:
189 if os.path.exists(config) and os.access(config, os.R_OK):
190 mediagoblin_config = config
191 break
192
193 if not mediagoblin_config:
194 raise IOError("Usable mediagoblin config not found.")
195
196 mgoblin_app = MediaGoblinApp(mediagoblin_config)
b61874b2 197
c4d71564 198 return mgoblin_app