# GNU MediaGoblin -- federated, autonomous media hosting
-# Copyright (C) 2011 Free Software Foundation, Inc
+# Copyright (C) 2011 MediaGoblin contributors. See AUTHORS.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+import os
import urllib
import routes
-from paste.deploy.converters import asbool, asint
from webob import Request, exc
-from mediagoblin import routing, util, storage, staticdirect
-from mediagoblin.db import models
-from mediagoblin.db.util import connect_database
-from mediagoblin.globals import setup_globals
-from mediagoblin.celery_setup import setup_celery_from_config
-
-
-class Error(Exception): pass
-class ImproperlyConfigured(Error): pass
+from mediagoblin import routing, middleware
+from mediagoblin.tools import common, translate, template
+from mediagoblin.tools.response import render_404
+from mediagoblin.tools import request as mg_request
+from mediagoblin.mg_globals import setup_globals
+from mediagoblin.init.celery import setup_celery_from_config
+from mediagoblin.init import (get_jinja_loader, get_staticdirector,
+ setup_global_and_app_config, setup_workbench, setup_database,
+ setup_storage, setup_beaker_cache)
class MediaGoblinApp(object):
"""
- Really basic wsgi app using routes and WebOb.
+ WSGI application of MediaGoblin
+
+ ... this is the heart of the program!
"""
- def __init__(self, connection, database_path,
- public_store, queue_store,
- staticdirector,
- email_sender_address, email_debug_mode,
- user_template_path=None):
+ def __init__(self, config_path, setup_celery=True):
+ """
+ Initialize the application based on a configuration file.
+
+ Arguments:
+ - config_path: path to the configuration file we're opening.
+ - setup_celery: whether or not to setup celery during init.
+ (Note: setting 'celery_setup_elsewhere' also disables
+ setting up celery.)
+ """
+ ##############
+ # Setup config
+ ##############
+
+ # Open and setup the config
+ global_config, app_config = setup_global_and_app_config(config_path)
+
+ ##########################################
+ # Setup other connections / useful objects
+ ##########################################
+
+ # Set up the database
+ self.connection, self.db = setup_database()
+
# Get the template environment
- self.template_loader = util.get_jinja_loader(user_template_path)
-
- # Set up storage systems
- self.public_store = public_store
- self.queue_store = queue_store
+ self.template_loader = get_jinja_loader(
+ app_config.get('user_template_path'))
- # Set up database
- self.connection = connection
- self.db = connection[database_path]
- models.register_models(connection)
+ # Set up storage systems
+ self.public_store, self.queue_store = setup_storage()
# set up routing
self.routing = routing.get_mapper()
# set up staticdirector tool
- self.staticdirector = staticdirector
-
+ self.staticdirector = get_staticdirector(app_config)
+
+ # set up caching
+ self.cache = setup_beaker_cache()
+
+ # Setup celery, if appropriate
+ if setup_celery and not app_config.get('celery_setup_elsewhere'):
+ if os.environ.get('CELERY_ALWAYS_EAGER'):
+ setup_celery_from_config(
+ app_config, global_config,
+ force_celery_always_eager=True)
+ else:
+ setup_celery_from_config(app_config, global_config)
+
+ #######################################################
+ # Insert appropriate things into mediagoblin.mg_globals
+ #
# certain properties need to be accessed globally eg from
# validators, etc, which might not access to the request
# object.
- setup_globals(
- email_sender_address=email_sender_address,
- email_debug_mode=email_debug_mode,
- db_connection=connection,
- database=self.db,
- public_store=self.public_store,
- queue_store=self.queue_store)
+ #######################################################
+
+ setup_globals(app=self)
+
+ # Workbench *currently* only used by celery, so this only
+ # matters in always eager mode :)
+ setup_workbench()
+
+ # instantiate application middleware
+ self.middleware = [common.import_component(m)(self)
+ for m in middleware.ENABLED_MIDDLEWARE]
def __call__(self, environ, start_response):
request = Request(environ)
- path_info = request.path_info
+
+ # pass the request through our middleware classes
+ for m in self.middleware:
+ response = m.process_request(request)
+ if response is not None:
+ return response(environ, start_response)
## Routing / controller loading stuff
+ path_info = request.path_info
route_match = self.routing.match(path_info)
+ # By using fcgi, mediagoblin can run under a base path
+ # like /mediagoblin/. request.path_info contains the
+ # path inside mediagoblin. If the something needs the
+ # full path of the current page, that should include
+ # the basepath.
+ # Note: urlgen and routes are fine!
+ request.full_path = environ["SCRIPT_NAME"] + request.path_info
+ # python-routes uses SCRIPT_NAME. So let's use that too.
+ # The other option would be:
+ # request.full_path = environ["SCRIPT_URL"]
+
+ ## Attach utilities to the request object
+ request.matchdict = route_match
+ request.urlgen = routes.URLGenerator(self.routing, environ)
+ # Do we really want to load this via middleware? Maybe?
+ request.session = request.environ['beaker.session']
+ # Attach self as request.app
+ # Also attach a few utilities from request.app for convenience?
+ request.app = self
+ request.locale = translate.get_locale_from_request(request)
+
+ request.template_env = template.get_jinja_env(
+ self.template_loader, request.locale)
+ request.db = self.db
+ request.staticdirect = self.staticdirector
+
+ mg_request.setup_user_in_request(request)
+
# No matching page?
if route_match is None:
# Try to do see if we have a match with a trailing slash
return request.get_response(redirect)(environ, start_response)
# Okay, no matches. 404 time!
- return exc.HTTPNotFound()(environ, start_response)
+ request.matchdict = {} # in case our template expects it
+ return render_404(request)(environ, start_response)
- controller = util.import_component(route_match['controller'])
+ controller = common.import_component(route_match['controller'])
request.start_response = start_response
- ## Attach utilities to the request object
- request.matchdict = route_match
- request.urlgen = routes.URLGenerator(self.routing, environ)
- # Do we really want to load this via middleware? Maybe?
- request.session = request.environ['beaker.session']
- # Attach self as request.app
- # Also attach a few utilities from request.app for convenience?
- request.app = self
- request.locale = util.get_locale_from_request(request)
-
- request.template_env = util.get_jinja_env(
- self.template_loader, request.locale)
- request.db = self.db
- request.staticdirect = self.staticdirector
+ # get the response from the controller
+ response = controller(request)
- util.setup_user_in_request(request)
+ # pass the response through the middleware
+ for m in self.middleware[::-1]:
+ m.process_response(request, response)
- return controller(request)(environ, start_response)
+ return response(environ, start_response)
def paste_app_factory(global_config, **app_config):
- # Get the database connection
- connection = connect_database(app_config)
-
- # Set up the storage systems.
- public_store = storage.storage_system_from_paste_config(
- app_config, 'publicstore')
- queue_store = storage.storage_system_from_paste_config(
- app_config, 'queuestore')
-
- # Set up the staticdirect system
- if app_config.has_key('direct_remote_path'):
- staticdirector = staticdirect.RemoteStaticDirect(
- app_config['direct_remote_path'].strip())
- elif app_config.has_key('direct_remote_paths'):
- direct_remote_path_lines = app_config[
- 'direct_remote_paths'].strip().splitlines()
- staticdirector = staticdirect.MultiRemoteStaticDirect(
- dict([line.strip().split(' ', 1)
- for line in direct_remote_path_lines]))
- else:
- raise ImproperlyConfigured(
- "One of direct_remote_path or direct_remote_paths must be provided")
-
- setup_celery_from_config(app_config, global_config)
-
- mgoblin_app = MediaGoblinApp(
- connection, app_config.get('db_name', 'mediagoblin'),
- public_store=public_store, queue_store=queue_store,
- staticdirector=staticdirector,
- email_sender_address=app_config.get(
- 'email_sender_address', 'notice@mediagoblin.example.org'),
- email_debug_mode=asbool(app_config.get('email_debug_mode')),
- user_template_path=app_config.get('local_templates'))
+ configs = app_config['config'].split()
+ mediagoblin_config = None
+ for config in configs:
+ if os.path.exists(config) and os.access(config, os.R_OK):
+ mediagoblin_config = config
+ break
+
+ if not mediagoblin_config:
+ raise IOError("Usable mediagoblin config not found.")
+
+ mgoblin_app = MediaGoblinApp(mediagoblin_config)
return mgoblin_app