mgoblin_section.get, not app_config.get (doh)
[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 urllib
18
19 import routes
20 import mongokit
21 from paste.deploy.converters import asbool
22 from webob import Request, exc
23
24 from mediagoblin import routing, util, models, storage, staticdirect
25 from mediagoblin.globals import setup_globals
26 from mediagoblin.celery_setup import setup_celery_from_config
27
28
29 class Error(Exception): pass
30 class ImproperlyConfigured(Error): pass
31
32
33 class MediaGoblinApp(object):
34 """
35 Really basic wsgi app using routes and WebOb.
36 """
37 def __init__(self, connection, database_path,
38 public_store, queue_store,
39 staticdirector,
40 email_sender_address, email_debug_mode,
41 user_template_path=None):
42 # Get the template environment
43 self.template_env = util.get_jinja_env(user_template_path)
44
45 # Set up storage systems
46 self.public_store = public_store
47 self.queue_store = queue_store
48
49 # Set up database
50 self.connection = connection
51 self.db = connection[database_path]
52 models.register_models(connection)
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.template_env = self.template_env
107 request.db = self.db
108 request.staticdirect = self.staticdirector
109
110 util.setup_user_in_request(request)
111
112 return controller(request)(environ, start_response)
113
114
115 def paste_app_factory(global_config, **app_config):
116 # Get the database connection
117 connection = mongokit.Connection(
118 app_config.get('db_host'), app_config.get('db_port'))
119
120 # Set up the storage systems.
121 public_store = storage.storage_system_from_paste_config(
122 app_config, 'publicstore')
123 queue_store = storage.storage_system_from_paste_config(
124 app_config, 'queuestore')
125
126 # Set up the staticdirect system
127 if app_config.has_key('direct_remote_path'):
128 staticdirector = staticdirect.RemoteStaticDirect(
129 app_config['direct_remote_path'].strip())
130 elif app_config.has_key('direct_remote_paths'):
131 direct_remote_path_lines = app_config[
132 'direct_remote_paths'].strip().splitlines()
133 staticdirector = staticdirect.MultiRemoteStaticDirect(
134 dict([line.strip().split(' ', 1)
135 for line in direct_remote_path_lines]))
136 else:
137 raise ImproperlyConfigured(
138 "One of direct_remote_path or direct_remote_paths must be provided")
139
140 setup_celery_from_config(app_config, global_config)
141
142 mgoblin_app = MediaGoblinApp(
143 connection, app_config.get('db_name', 'mediagoblin'),
144 public_store=public_store, queue_store=queue_store,
145 staticdirector=staticdirector,
146 email_sender_address=app_config.get(
147 'email_sender_address', 'notice@mediagoblin.example.org'),
148 email_debug_mode=app_config.get('email_debug_mode'),
149 user_template_path=app_config.get('local_templates'))
150
151 return mgoblin_app