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