diff --git a/README b/README new file mode 100644 index 00000000..15917b1e --- /dev/null +++ b/README @@ -0,0 +1,29 @@ +Initializing the database +------------------------- + +To create the database, execute the following line: + + ./passerelle_manage.py --config=config_example.py syncdb --migrate + +The new database is created inside `passerelle.sqlite3` in the current directory. + +Running +------- + +The command line for starting is: + + ./passerelle_manage.py --config=config_example.py runserver + +Passerelle is available on http://127.0.0.1:80000 + +Options +------- + + --config=/path/to/config.py + + Configuration file. MANDATORY. + + --multitenant + + Activate multi-tenant mode. The python-entrouvert package must be installed. + diff --git a/config_example.py b/config_example.py new file mode 100644 index 00000000..5402c652 --- /dev/null +++ b/config_example.py @@ -0,0 +1,14 @@ +DEBUG = True + +SECRET_KEY = 'changeme' + +MEDIA_ROOT = 'media' + +STATIC_ROOT = 'collected-static' + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': 'passerelle.sqlite3', + } +} diff --git a/passerelle/default_settings.py b/passerelle/default_settings.py new file mode 100644 index 00000000..eb368a5b --- /dev/null +++ b/passerelle/default_settings.py @@ -0,0 +1,107 @@ +# Django default settings for passerelle project. + +from django.conf import global_settings +import os +import logging +try: + from logging.handlers import NullHandler +except ImportError: + # python < 2.7 + class NullHandler(logging.Handler): + def emit(self, record): + pass + +logging.getLogger('passerelle').addHandler(NullHandler()) + +PACKAGE_PATH = os.path.dirname(__file__) + +LANGUAGE_CODE = 'fr-fr' + +# If you set this to False, Django will not format dates, numbers and +# calendars according to the current locale. +USE_L10N = True + +# If you set this to False, Django will not use timezone-aware datetimes. +USE_TZ = True + +# URL that handles the media served from MEDIA_ROOT. Make sure to use a +# trailing slash. +# Examples: "http://media.lawrence.com/media/", "http://example.com/media/" +MEDIA_URL = '/media/' + +# URL prefix for static files. +# Example: "http://media.lawrence.com/static/" +STATIC_URL = '/static/' + +# Additional locations of static files +STATICFILES_DIRS = (os.path.join(PACKAGE_PATH, 'static'),) + +# List of finder classes that know how to find static files in +# various locations. +STATICFILES_FINDERS = global_settings.STATICFILES_FINDERS + ('gadjo.finders.XStaticFinder',) + +MIDDLEWARE_CLASSES = global_settings.MIDDLEWARE_CLASSES + ( + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.locale.LocaleMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'passerelle.base.middleware.SearchApiUser' +) + +ROOT_URLCONF = 'passerelle.urls' + +# Python dotted path to the WSGI application used by Django's runserver. +WSGI_APPLICATION = 'passerelle.wsgi.application' + +LOCALE_PATHS = (os.path.join(PACKAGE_PATH, 'locale'),) + +TEMPLATE_DIRS = (os.path.join(PACKAGE_PATH, 'templates'),) + +TEMPLATE_CONTEXT_PROCESSORS = global_settings.TEMPLATE_CONTEXT_PROCESSORS + ( + 'django.core.context_processors.request', + 'passerelle.base.context_processors.template_vars', +) + +INSTALLED_APPS = ( + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + # Uncomment the next line to enable the admin: + 'django.contrib.admin', + # Uncomment the next line to enable admin documentation: + # 'django.contrib.admindocs', + 'south', + 'passerelle.base', + 'passerelle.datasources', + 'passerelle.repost', + 'passerelle.messages', + 'passerelle.register', + 'passerelle.queue', + 'clicrdv', + 'gdc', + 'solis', + 'makorepost', + 'choosit', + 'oxyd', + 'ovh', + 'mobyt', + 'pastell', + 'gadjo', +) + +LOGIN_REDIRECT_URL = 'homepage' + +CLICRDV_SERVER = None +CLICRDV_API_KEY = None +CLICRDV_API_USERNAME = None +CLICRDV_API_PASSWORD = None + +# where to find templates for makorepost objects +MAKO_TEMPLATES_DIRS = ( + os.path.join(PACKAGE_PATH, 'apps', 'makorepost', 'templates.mako'), + os.path.join(PACKAGE_PATH, 'apps', 'solis', 'templates.mako'), +) + +MAKO_TEMPLATES_MODULES = None # store generated Mako module files in this directory (optional) diff --git a/passerelle/settings.py b/passerelle/settings.py index f44fe570..c7777299 100644 --- a/passerelle/settings.py +++ b/passerelle/settings.py @@ -1,286 +1,30 @@ -# Django settings for passerelle project. - -from django.conf import global_settings +from django.conf.global_settings import * +from passerelle.default_settings import * from django.core.exceptions import ImproperlyConfigured + import os -import glob -import json -import re +import logging -DEBUG = True -TEMPLATE_DEBUG = DEBUG -DEBUG_TOOLBAR = False +if 'DJANGO_CONFIG_FILE' in os.environ: + logging.getLogger('passerelle').debug('Loading setting file %r', os.environ['DJANGO_CONFIG_FILE']) + execfile(os.environ['DJANGO_CONFIG_FILE']) -PROJECT_NAME = 'passerelle' - -PROJECT_PATH = os.path.dirname(os.path.dirname(__file__)) - -VAR_DIR = os.environ.get('PASSERELLE_VAR_DIR', os.path.join('/var/lib', PROJECT_NAME)) -ETC_DIR = os.environ.get('PASSERELLE_ETC_DIR', os.path.join('/etc', PROJECT_NAME)) - -if 'PASSERELLE_VAR_DIRS' in os.environ: - VAR_DIRS = os.environ['PASSERELLE_VAR_DIRS'].split(':') -else: - VAR_DIRS = (VAR_DIR,) - -VAR_DIRS = (x for x in VAR_DIRS if os.path.exists(x)) - -if 'PASSERELLE_CONFIG_DIRS' in os.environ: - CONFIG_DIRS = os.environ['PASSERELLE_CONFIG_DIRS'].split(':') -else: - CONFIG_DIRS = (ETC_DIR,) - -ADMINS = ( - # ('Your Name', 'your_email@example.com'), -) - -MANAGERS = ADMINS - -DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': os.path.join(PROJECT_PATH, 'passerelle.sqlite3'), - } -} - -# Local time zone for this installation. Choices can be found here: -# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name -# although not all choices may be available on all operating systems. -# In a Windows environment this must be set to your system time zone. -TIME_ZONE = 'America/Chicago' - -# Language code for this installation. All choices can be found here: -# http://www.i18nguy.com/unicode/language-identifiers.html -LANGUAGE_CODE = 'fr-fr' - -SITE_ID = 1 - -# If you set this to False, Django will make some optimizations so as not -# to load the internationalization machinery. -USE_I18N = True - -# If you set this to False, Django will not format dates, numbers and -# calendars according to the current locale. -USE_L10N = True - -# If you set this to False, Django will not use timezone-aware datetimes. -USE_TZ = True - -# Absolute filesystem path to the directory that will hold user-uploaded files. -# Example: "/home/media/media.lawrence.com/media/" -MEDIA_ROOT = os.path.join(PROJECT_PATH, 'media') - -# URL that handles the media served from MEDIA_ROOT. Make sure to use a -# trailing slash. -# Examples: "http://media.lawrence.com/media/", "http://example.com/media/" -MEDIA_URL = '/media/' - -# Absolute path to the directory static files should be collected to. -# Don't put anything in this directory yourself; store your static files -# in apps' "static/" subdirectories and in STATICFILES_DIRS. -# Example: "/home/media/media.lawrence.com/static/" -STATIC_ROOT = os.path.join(VAR_DIR, 'collected-static') - -# URL prefix for static files. -# Example: "http://media.lawrence.com/static/" -STATIC_URL = '/static/' - -# Additional locations of static files -STATICFILES_DIRS = tuple(os.path.join(var_dir, 'static') for var_dir in VAR_DIRS) + ( - os.path.join(PROJECT_PATH, 'passerelle', 'static'), -) - -# List of finder classes that know how to find static files in -# various locations. -STATICFILES_FINDERS = global_settings.STATICFILES_FINDERS + ('gadjo.finders.XStaticFinder',) - -# Make this unique, and don't share it with anybody. -SECRET_KEY = 't!sc5(jif2k5ymm_w@@j6hfiw(xj)(mn%euj#*_cgv(&cxm$q9' - -# List of callables that know how to import templates from various sources. -TEMPLATE_LOADERS = ( - 'django.template.loaders.filesystem.Loader', - 'django.template.loaders.app_directories.Loader', -# 'django.template.loaders.eggs.Loader', -) - -MIDDLEWARE_CLASSES = ( - 'django.middleware.common.CommonMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.middleware.locale.LocaleMiddleware', - 'django.middleware.csrf.CsrfViewMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', - # Uncomment the next line for simple clickjacking protection: - # 'django.middleware.clickjacking.XFrameOptionsMiddleware', - 'passerelle.base.middleware.SearchApiUser' -) - -ROOT_URLCONF = 'passerelle.urls' - -# Python dotted path to the WSGI application used by Django's runserver. -WSGI_APPLICATION = 'passerelle.wsgi.application' - -LOCALE_PATHS = (os.path.join(PROJECT_PATH, 'passerelle', 'locale'),) - -TEMPLATE_DIRS = tuple(os.path.join(var_dir, 'templates') for var_dir in VAR_DIRS) + ( - os.path.join(PROJECT_PATH, 'passerelle', 'templates'), -) - -TEMPLATE_CONTEXT_PROCESSORS = ( - 'django.contrib.auth.context_processors.auth', - 'django.core.context_processors.debug', - 'django.core.context_processors.i18n', - 'django.core.context_processors.media', - 'django.core.context_processors.static', - 'django.core.context_processors.tz', - 'django.core.context_processors.request', - 'django.contrib.messages.context_processors.messages', - 'passerelle.base.context_processors.template_vars', -) +if 'DATABASES' not in globals(): + logging.getLogger('passerelle').error('Unable to boot: You must define a DATABASES in your settings') + raise ImproperlyConfigured('You must define a DATABASES variable in your settings') -INSTALLED_APPS = ( - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.sessions', - 'django.contrib.messages', - 'django.contrib.staticfiles', - # Uncomment the next line to enable the admin: - 'django.contrib.admin', - # Uncomment the next line to enable admin documentation: - # 'django.contrib.admindocs', - 'south', - 'passerelle.base', - 'passerelle.datasources', - 'passerelle.repost', - 'passerelle.messages', - 'passerelle.register', - 'passerelle.queue', - 'clicrdv', - 'gdc', - 'solis', - 'makorepost', - 'choosit', - 'oxyd', - 'ovh', - 'mobyt', - 'pastell', - 'gadjo', -) - -# A sample logging configuration. The only tangible logging -# performed by this configuration is to send an email to -# the site admins on every HTTP 500 error when DEBUG=False. -# See http://docs.djangoproject.com/en/dev/topics/logging for -# more details on how to customize your logging configuration. -LOGGING = { - 'version': 1, - 'disable_existing_loggers': False, - 'filters': { - 'require_debug_false': { - '()': 'django.utils.log.RequireDebugFalse' - } - }, - 'handlers': { - 'mail_admins': { - 'level': 'ERROR', - 'filters': ['require_debug_false'], - 'class': 'django.utils.log.AdminEmailHandler' - } - }, - 'loggers': { - 'django.request': { - 'handlers': ['mail_admins'], - 'level': 'ERROR', - 'propagate': True, - }, - } -} - -LOGIN_REDIRECT_URL = 'homepage' -LOGIN_URL = 'login' -LOGOUT_URL = 'logout' - -CLICRDV_SERVER = None -CLICRDV_API_KEY = None -CLICRDV_API_USERNAME = None -CLICRDV_API_PASSWORD = None - -# where to find templates for makorepost objects -MAKO_TEMPLATES_DIRS = ( - os.path.join(PROJECT_PATH, 'passerelle', 'apps', 'makorepost', 'templates.mako'), -) -if 'solis' in INSTALLED_APPS: - MAKO_TEMPLATES_DIRS += ( - os.path.join(PROJECT_PATH, 'passerelle', 'apps', 'solis', 'templates.mako'), - ) - -MAKO_TEMPLATES_MODULES = None # store generated Mako module files in this directory (optional) - -PASSERELLE_QUEUE_AUTHORIZED_DIRECTORIES = () -PASSERELLE_QUEUE_DEFAULT_TMP_DIRECTORY = '/var/tmp' - -TENANT_BASE = None - -# load user settings -def load_dict_config(d): - for key in d: - if re.match('^[A-Z][_A-Z0-9]*$', key): - globals()[key] = d[key] - -for config_dir in CONFIG_DIRS: - wildcard = os.path.join(config_dir, '*.json') - for path in sorted(glob.glob(wildcard)): - json_config = json.load(file(path)) - if not isinstance(json_config, dict): - raise ImproperlyConfigured('JSON file %r is not a dictionnary' % path) - load_dict_config(json_config) - -for config_dir in CONFIG_DIRS: - config_py = os.path.join(config_dir, 'config.py') - if os.path.exists(config_py): - execfile(config_py, globals()) - -try: - from local_settings import * -except ImportError, e: - if 'local_settings' in e.args[0]: +if DEBUG: + # Load debug toolbar when available + try: + import debug_toolbar + except: pass - -TENANT_BASE = os.environ.get('TENANT_BASE', TENANT_BASE) -if TENANT_BASE: - SOUTH_TESTS_MIGRATE = False - if DATABASES['default']['ENGINE'] != 'django.db.backends.postgresql_psycopg2': - raise ImproperlyConfigured('MULTITENANT only work with a postgresql database') - DATABASES['default']['ENGINE'] = 'tenant_schemas.postgresql_backend' - TEMPLATE_LOADERS = ('entrouvert.djommon.multitenant.template_loader.FilesystemLoader',) + TEMPLATE_LOADERS - DEFAULT_FILE_STORAGE = 'entrouvert.djommon.multitenant.storage.TenantFileSystemStorage' - MIDDLEWARE_CLASSES = ( - 'entrouvert.djommon.multitenant.middleware.TenantMiddleware', - 'entrouvert.djommon.multitenant.middleware.JSONSettingsMiddleware', - 'entrouvert.djommon.multitenant.middleware.PythonSettingsMiddleware', - ) + MIDDLEWARE_CLASSES - INSTALLED_APPS = INSTALLED_APPS + ('entrouvert.djommon.multitenant',) - TENANT_APPS = INSTALLED_APPS - - TENANT_MODEL = 'multitenant.Tenant' - TENANT_TEMPLATE_DIRS = ( TENANT_BASE, ) - SOUTH_DATABASE_ADAPTERS = { - 'default': 'south.db.postgresql_psycopg2', - } - SHARED_APPS = ( - 'django.contrib.staticfiles', - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.sessions', - 'django.contrib.messages', - ) - -if DEBUG and DEBUG_TOOLBAR: - MIDDLEWARE_CLASSES += ( - 'debug_toolbar.middleware.DebugToolbarMiddleware', - ) - INSTALLED_APPS += ( - 'debug_toolbar', - ) + else: + logging.getLogger('passerelle').debug('loading debug_toolbar') + MIDDLEWARE_CLASSES += ( + 'debug_toolbar.middleware.DebugToolbarMiddleware', + ) + INSTALLED_APPS += ( + 'debug_toolbar', + ) diff --git a/passerelle/tenant_settings.py b/passerelle/tenant_settings.py new file mode 100644 index 00000000..669aa9b1 --- /dev/null +++ b/passerelle/tenant_settings.py @@ -0,0 +1,66 @@ +from django.conf.global_settings import * +from passerelle.default_settings import * +from django.core.exceptions import ImproperlyConfigured + +import os +import logging + +try: + import entrouvert +except ImportError: + raise ImproperlyConfigured('python-entrouvert MUST be installed for the multitenant mode to work') + +SOUTH_TESTS_MIGRATE = False +TEMPLATE_LOADERS = ('entrouvert.djommon.multitenant.template_loader.FilesystemLoader',) + TEMPLATE_LOADERS +DEFAULT_FILE_STORAGE = 'entrouvert.djommon.multitenant.storage.TenantFileSystemStorage' +MIDDLEWARE_CLASSES = ( + 'entrouvert.djommon.multitenant.middleware.TenantMiddleware', + 'entrouvert.djommon.multitenant.middleware.JSONSettingsMiddleware', + 'entrouvert.djommon.multitenant.middleware.PythonSettingsMiddleware', +) + MIDDLEWARE_CLASSES +INSTALLED_APPS = INSTALLED_APPS + ('entrouvert.djommon.multitenant',) +TENANT_APPS = INSTALLED_APPS + +TENANT_MODEL = 'multitenant.Tenant' +SOUTH_DATABASE_ADAPTERS = { + 'default': 'south.db.postgresql_psycopg2', +} +SHARED_APPS = ( + 'django.contrib.staticfiles', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', +) + +if 'DJANGO_CONFIG_FILE' in os.environ: + logging.getLogger('passerelle').debug('Loading setting file %r', os.environ['DJANGO_CONFIG_FILE']) + execfile(os.environ['DJANGO_CONFIG_FILE']) + +if 'TENANT_BASE' not in globals(): + logging.getLogger('passerelle').error('Unable to boot: You must define a TENANT_BASE in your settings') + raise ImproperlyConfigured('You must define a TENANT_BASE in your settings') + +if 'DATABASES' not in globals(): + logging.getLogger('passerelle').error('Unable to boot: You must define a DATABASES in your settings') + raise ImproperlyConfigured('You must define a DATABASES variable in your settings') + +if DATABASES['default']['ENGINE'] != 'django.db.backends.postgresql_psycopg2': + raise ImproperlyConfigured('MULTITENANT only work with a postgresql database') +DATABASES['default']['ENGINE'] = 'tenant_schemas.postgresql_backend' + +if DEBUG: + # Load debug toolbar when available + try: + import debug_toolbar + except: + pass + else: + logging.getLogger('passerelle').debug('loading debug_toolbar') + MIDDLEWARE_CLASSES += ( + 'debug_toolbar.middleware.DebugToolbarMiddleware', + ) + INSTALLED_APPS += ( + 'debug_toolbar', + ) + diff --git a/passerelle_manage.py b/passerelle_manage.py index e302b33e..ae7052c4 100755 --- a/passerelle_manage.py +++ b/passerelle_manage.py @@ -3,8 +3,30 @@ import os import sys if __name__ == "__main__": - os.environ.setdefault("DJANGO_SETTINGS_MODULE", "passerelle.settings") + multitenant = False + config_file = False + + for i, arg in enumerate(sys.argv[1:]): + if arg.startswith('-'): + if arg.startswith('--config='): + config_file = arg.split('=')[1] + elif arg == '--multitenant': + multitenant = True + else: + print >>sys.stderr, 'ERR: Unsupported flag', arg + sys.exit(1) + else: + break + if not config_file: + print >>sys.stderr, 'ERR: No configuration file specified, use --config=/path/to/config.py' + sys.exit(1) + + os.environ['DJANGO_CONFIG_FILE'] = config_file + if multitenant: + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "passerelle.tenant_settings") + else: + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "passerelle.settings") from django.core.management import execute_from_command_line - execute_from_command_line(sys.argv) + execute_from_command_line(sys.argv[:1] + sys.argv[i+1:])