I shouldn't have removed the .save() entirely :)
[mediagoblin.git] / mediagoblin / app.py
1 # GNU MediaGoblin -- federated, autonomous media hosting
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
17 import os
18 import urllib
19
20 import routes
21 from paste.deploy.converters import asbool
22 from webob import Request, exc
23
24 from mediagoblin import routing, util, storage, staticdirect
25 from mediagoblin.db.open import setup_connection_and_db_from_config
26 from mediagoblin.globals import setup_globals
27 from mediagoblin.celery_setup import setup_celery_from_config
28
29
30 class Error(Exception): pass
31 class ImproperlyConfigured(Error): pass
32
33
34 class MediaGoblinApp(object):
35 """
36 Really basic wsgi app using routes and WebOb.
37 """
38 def __init__(self, connection, db,
39 public_store, queue_store,
40 staticdirector,
41 email_sender_address, email_debug_mode,
42 user_template_path=None):
43 # Get the template environment
44 self.template_loader = util.get_jinja_loader(user_template_path)
45
46 # Set up storage systems
47 self.public_store = public_store
48 self.queue_store = queue_store
49
50 # Set up database
51 self.connection = connection
52 self.db = db
53
54 # set up routing
55 self.routing = routing.get_mapper()
56
57 # set up staticdirector tool
58 self.staticdirector = staticdirector
59
60 # certain properties need to be accessed globally eg from
61 # validators, etc, which might not access to the request
62 # object.
63 setup_globals(
64 email_sender_address=email_sender_address,
65 email_debug_mode=email_debug_mode,
66 db_connection=connection,
67 database=self.db,
68 public_store=self.public_store,
69 queue_store=self.queue_store)
70
71 def __call__(self, environ, start_response):
72 request = Request(environ)
73 path_info = request.path_info
74
75 ## Routing / controller loading stuff
76 route_match = self.routing.match(path_info)
77
78 # No matching page?
79 if route_match is None:
80 # Try to do see if we have a match with a trailing slash
81 # added and if so, redirect
82 if not path_info.endswith('/') \
83 and request.method == 'GET' \
84 and self.routing.match(path_info + '/'):
85 new_path_info = path_info + '/'
86 if request.GET:
87 new_path_info = '%s?%s' % (
88 new_path_info, urllib.urlencode(request.GET))
89 redirect = exc.HTTPFound(location=new_path_info)
90 return request.get_response(redirect)(environ, start_response)
91
92 # Okay, no matches. 404 time!
93 return exc.HTTPNotFound()(environ, start_response)
94
95 controller = util.import_component(route_match['controller'])
96 request.start_response = start_response
97
98 ## Attach utilities to the request object
99 request.matchdict = route_match
100 request.urlgen = routes.URLGenerator(self.routing, environ)
101 # Do we really want to load this via middleware? Maybe?
102 request.session = request.environ['beaker.session']
103 # Attach self as request.app
104 # Also attach a few utilities from request.app for convenience?
105 request.app = self
106 request.locale = util.get_locale_from_request(request)
107
108 request.template_env = util.get_jinja_env(
109 self.template_loader, request.locale)
110 request.db = self.db
111 request.staticdirect = self.staticdirector
112
113 util.setup_user_in_request(request)
114
115 return controller(request)(environ, start_response)
116
117
118 def paste_app_factory(global_config, **app_config):
119 # Get the database connection
120 connection, db = setup_connection_and_db_from_config(app_config)
121
122 # Set up the storage systems.
123 public_store = storage.storage_system_from_paste_config(
124 app_config, 'publicstore')
125 queue_store = storage.storage_system_from_paste_config(
126 app_config, 'queuestore')
127
128 # Set up the staticdirect system
129 if app_config.has_key('direct_remote_path'):
130 staticdirector = staticdirect.RemoteStaticDirect(
131 app_config['direct_remote_path'].strip())
132 elif app_config.has_key('direct_remote_paths'):
133 direct_remote_path_lines = app_config[
134 'direct_remote_paths'].strip().splitlines()
135 staticdirector = staticdirect.MultiRemoteStaticDirect(
136 dict([line.strip().split(' ', 1)
137 for line in direct_remote_path_lines]))
138 else:
139 raise ImproperlyConfigured(
140 "One of direct_remote_path or direct_remote_paths must be provided")
141
142 if asbool(os.environ.get('CELERY_ALWAYS_EAGER')):
143 setup_celery_from_config(
144 app_config, global_config,
145 force_celery_always_eager=True)
146 else:
147 setup_celery_from_config(app_config, global_config)
148
149 mgoblin_app = MediaGoblinApp(
150 connection, db,
151 public_store=public_store, queue_store=queue_store,
152 staticdirector=staticdirector,
153 email_sender_address=app_config.get(
154 'email_sender_address', 'notice@mediagoblin.example.org'),
155 email_debug_mode=asbool(app_config.get('email_debug_mode')),
156 user_template_path=app_config.get('local_templates'))
157
158 return mgoblin_app