switch to django request/response handling (#6735)

This commit is contained in:
Frédéric Péters 2013-12-07 17:24:11 +01:00
parent fa5d08658b
commit 0482cdcd7d
18 changed files with 375 additions and 415 deletions

1
.gitignore vendored
View File

@ -1 +1,2 @@
*.pyc
local_settings.py

55
INSTALL
View File

@ -1,57 +1,4 @@
w.c.s. - Installation Instructions
==================================
Prerequisites
-------------
- Python 2.5 <http://www.python.org/>
- Quixote 2.5 <http://www.mems-exchange.org/software/quixote/>
- Lasso 2.2 <http://lasso.entrouvert.org/> for Liberty/SAML support
Installation
------------
w.c.s. uses the standard distutils Python package; just run::
python setup.py install
and you're done.
Configuration
-------------
w.c.s. has been tested with Quixote configured through SCGI with Apache and 2
The recommended configuration is to use SCGI version 1.8 or later (as it has
the SCGIMount directive).
SCGI usage requires an additional server to run, 'wcsctl.py start', the Debian
package installs a init.d script automatically.
<VirtualHost *>
ServerAdmin webmaster@example.com
ServerName www.example.com
DocumentRoot /usr/share/wcs/web/
SCGIMount / 127.0.0.1:3001
# this part allows serving static files directly from Apache, but it
# requires to run wcsctl.py collectstatic first.
Alias /static/ /var/lib/wcs/collectstatic/
<LocationMatch "^/static">
SCGIHandler off
</LocationMatch>
# this part allows serving the themes directly from Apache, but it
# requires mod_rewrite
Alias /themes/ /usr/share/wcs/themes/
RewriteEngine On
RewriteCond /usr/share/wcs/%{REQUEST_URI} !-f
RewriteCond /usr/share/wcs/%{REQUEST_URI} !-d
RewriteRule ^/themes/(.*)$ /var/lib/wcs/%{HTTP_HOST}/themes/$1
CustomLog /var/log/apache2/wcs-access.log combined
ErrorLog /var/log/apache2/wcs-error.log
</VirtualHost>
We recommend using .deb packages from https://deb.entrouvert.org/

10
manage.py Executable file
View File

@ -0,0 +1,10 @@
#!/usr/bin/env python
import os
import sys
if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "wcs.settings")
from django.core.management import execute_from_command_line
execute_from_command_line(sys.argv)

View File

@ -29,7 +29,7 @@ def teardown_module(module):
def test_loading():
ctl = wcs.qommon.ctl.Ctl(cmd_prefixes=['wcs.ctl'])
ctl.load_all_commands(ignore_errors=False)
assert 'start' in ctl.get_commands().keys()
assert 'export_settings' in ctl.get_commands().keys()
# call all __init__() methods
for cmd in ctl.get_commands().values():
cmd()

View File

@ -89,7 +89,7 @@ def test_finish_interrupted_request():
'CONTENT_LENGTH': 'aaa',
})
response = pub.process_request(req)
assert 'invalid content-length header' in response.body
assert 'invalid content-length header' in str(response)
req = HTTPRequest(StringIO.StringIO(''), {
'SERVER_NAME': 'example.net',
'SCRIPT_NAME': '',
@ -97,7 +97,7 @@ def test_finish_interrupted_request():
'CONTENT_LENGTH': '1',
})
response = pub.process_request(req)
assert 'Invalid request: unexpected end of request body' in response.body
assert 'Invalid request: unexpected end of request body' in str(response)
req = HTTPRequest(StringIO.StringIO(''), {
'SERVER_NAME': 'example.net',
'SCRIPT_NAME': '',
@ -105,14 +105,14 @@ def test_finish_interrupted_request():
'CONTENT_LENGTH': '1',
})
response = pub.process_request(req)
assert 'Invalid request: multipart/form-data missing boundary' in response.body
assert 'Invalid request: multipart/form-data missing boundary' in str(response)
req = HTTPRequest(StringIO.StringIO(''), {
'SERVER_NAME': 'example.net',
'SCRIPT_NAME': '',
'PATH_INFO': '/gloubiboulga',
})
response = pub.process_request(req)
assert '<p>The requested link' in response.body
assert '<p>The requested link' in str(response)
def test_get_tenants():
pub = create_temporary_pub()

View File

@ -14,9 +14,11 @@ from wcs import sql
from webtest import TestApp
from quixote import cleanup, get_publisher
from django.conf import settings
import wcs
from wcs import publisher
import wcs.wsgi
from wcs import publisher, compat
from wcs.qommon.http_request import HTTPRequest
from wcs.users import User
from wcs.tracking_code import TrackingCode
@ -24,28 +26,6 @@ import wcs.qommon.sms
import qommon.sms
from qommon.errors import ConnectionError
class QWIP:
# copy of quixote original QWIP code, adapted to use our own HTTPRequest
# object and to process after jobs.
request_class = HTTPRequest
def __init__(self, publisher):
self.publisher = publisher
def __call__(self, env, start_response):
"""I am called for each request."""
if 'REQUEST_URI' not in env:
env['REQUEST_URI'] = env['SCRIPT_NAME'] + env['PATH_INFO']
input = env['wsgi.input']
request = self.request_class(input, env)
response = self.publisher.process_request(request)
status = "%03d %s" % (response.status_code, response.reason_phrase)
headers = response.generate_headers()
start_response(status, headers)
result = list(response.generate_body_chunks()) # Iterable object.
response.process_after_jobs()
return result
class KnownElements(object):
pickle_app_dir = None
sql_app_dir = None
@ -72,11 +52,11 @@ def create_temporary_pub(sql_mode=False):
elif sql_mode is False:
known_elements.pickle_app_dir = APP_DIR
publisher.WcsPublisher.APP_DIR = APP_DIR
publisher.WcsPublisher.DATA_DIR = os.path.abspath(
compat.CompatWcsPublisher.APP_DIR = APP_DIR
compat.CompatWcsPublisher.DATA_DIR = os.path.abspath(
os.path.join(os.path.dirname(wcs.__file__), '..', 'data'))
publisher.WcsPublisher.cronjobs = None
pub = publisher.WcsPublisher.create_publisher()
compat.CompatWcsPublisher.cronjobs = None
pub = compat.CompatWcsPublisher.create_publisher()
# allow saving the user
pub.app_dir = os.path.join(APP_DIR, 'example.net')
pub.site_charset = 'utf-8'
@ -163,8 +143,11 @@ def clean_temporary_pub():
def get_app(pub, https=False):
extra_environ = {'HTTP_HOST': 'example.net', 'REMOTE_ADDR': '127.0.0.1'}
if https:
settings.SECURE_PROXY_SSL_HEADER = ('HTTPS', 'on')
extra_environ['HTTPS'] = 'on'
return TestApp(QWIP(pub), extra_environ=extra_environ)
else:
extra_environ['HTTPS'] = 'off'
return TestApp(wcs.wsgi.application, extra_environ=extra_environ)
def login(app, username='admin', password='admin'):
login_page = app.get('/login/')

141
wcs/compat.py Normal file
View File

@ -0,0 +1,141 @@
# w.c.s. - web application for online forms
# Copyright (C) 2005-2013 Entr'ouvert
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, see <http://www.gnu.org/licenses/>.
import ConfigParser
import os
from threading import Lock
from quixote import get_publisher
from quixote.errors import PublishError
from quixote.http_request import Upload
from django.http import HttpResponse
from django.conf import settings
from .qommon import template
from .qommon.publisher import get_cfg, set_publisher_class
from .publisher import WcsPublisher
from .qommon.http_request import HTTPRequest
from .qommon.http_response import HTTPResponse
def init_publisher_if_needed():
if get_publisher() is not None:
return
# initialize publisher in first request
config = ConfigParser.ConfigParser()
if settings.WCS_LEGACY_CONFIG_FILE:
config.read(settings.WCS_LEGACY_CONFIG_FILE)
if hasattr(settings, 'WCS_EXTRA_MODULES') and settings.WCS_EXTRA_MODULES:
if not config.has_section('extra'):
config.add_section('extra')
for i, extra in enumerate(settings.WCS_EXTRA_MODULES):
config.set('extra', 'cmd_line_extra_%d' % i, extra)
CompatWcsPublisher.configure(config)
class CompatHTTPRequest(HTTPRequest):
def __init__(self, request):
self.django_request = request
self.response = None
request.environ['SCRIPT_NAME'] = str(request.environ['SCRIPT_NAME'])
request.environ['PATH_INFO'] = str(request.environ['PATH_INFO'])
self.META = self.django_request.META
HTTPRequest.__init__(self, None, request.environ)
self.scheme = str(self.django_request.scheme)
def _process_urlencoded(self, length, params):
return self._process_multipart(length, params)
def _process_multipart(self, length, params):
# Make sure request.form doesn't contain unicode strings, converting
# them all to strings in the site charset; it would contain unicode
# strings when the user agent specifies a charset in a mime content
# part, such a behaviour appears with some Nokia phones (6020, 6300)
site_charset = get_publisher().site_charset
# parse multipart data with the charset of the website
if 'charset' not in params:
params['charset'] = site_charset
if not self.form:
self.form = {}
for k, v in self.django_request.POST.items():
if type(v) is unicode:
self.form[str(k)] = v.encode(site_charset)
else:
self.form[str(k)] = v
for k, upload_file in self.django_request.FILES.items():
upload = Upload(upload_file.name.encode('utf-8'),
upload_file.content_type.encode('utf-8'),
upload_file.charset)
upload.fp = upload_file.file
self.form[str(k)] = upload
def build_absolute_uri(self):
return self.django_request.build_absolute_uri()
class CompatWcsPublisher(WcsPublisher):
def filter_output(self, request, output):
response = self.get_request().response
if response.status_code == 304:
# clients don't like to receive content with a 304
return ''
if response.content_type != 'text/html':
return output
if not hasattr(response, 'filter') or not response.filter:
return output
return self.render_response(output)
def process_request(self, request):
self._set_request(request)
try:
self.parse_request(request)
output = self.try_publish(request)
except PublishError, exc:
output = self.finish_interrupted_request(exc)
except Exception, exc:
output = self.finish_failed_request()
response = request.response
output = self.filter_output(request, output)
content = output
django_response = HttpResponse(content,
content_type=response.content_type,
status=response.status_code,
reason=response.reason_phrase)
for name, value in response.generate_headers():
if name == 'Content-Length':
continue
django_response[name] = value
self._clear_request()
return django_response
# keep a lock during quixote processing as it's not meant to work with threads;
# the publisher instance can't be shared for concurrent requests.
quixote_lock = Lock()
def quixote(request):
with quixote_lock:
pub = get_publisher()
compat_request = CompatHTTPRequest(request)
return pub.process_request(compat_request)
set_publisher_class(CompatWcsPublisher)

View File

@ -1,152 +0,0 @@
# w.c.s. - web application for online forms
# Copyright (C) 2005-2010 Entr'ouvert
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, see <http://www.gnu.org/licenses/>.
import os
import socket
import sys
import qommon.scgi_server
import quixote
import quixote.server.simple_server
from qommon.ctl import Command, make_option
class CmdStart(Command):
name = 'start'
def __init__(self):
Command.__init__(self, [
make_option('--port', metavar='PORT', action='store',
dest='port', default=3001),
make_option('--handler-connection-limit', metavar='LIMIT',
action='store',
dest='handler_connection_limit', default=None),
make_option('--script-name', metavar='NAME', action='store',
dest='script_name', default=None),
make_option('--http', action='store_true',
dest='http', default=False),
make_option('--silent', action='store_true',
dest='silent', default=False),
make_option('--daemonize', action='store_true',
dest='daemonize', default=False),
make_option('--pidfile', metavar='FILENAME',
dest='pidfile'),
make_option('--max-children', metavar='MAX_CHILDREN',
type='int', action='store', dest='max_children')
])
def make_silent(self):
sys.stdout.flush()
sys.stderr.flush()
si = file('/dev/null', 'r')
so = file('/dev/null', 'a+')
se = file('/dev/null', 'a+', 0)
os.dup2(si.fileno(), sys.stdin.fileno())
os.dup2(so.fileno(), sys.stdout.fileno())
os.dup2(se.fileno(), sys.stderr.fileno())
def del_pid(self):
if not hasattr(self, 'pidfile'):
return
try:
os.remove(self.pidfile)
except OSError:
pass
def execute(self, base_options, sub_options, args):
import publisher
run_kwargs = {}
run_kwargs['port'] = int(sub_options.port)
run_kwargs['spawn_cron'] = True
run_function = qommon.scgi_server.run
publisher.WcsPublisher.configure(self.config)
if sub_options.handler_connection_limit:
run_kwargs['handler_connection_limit'] = int(sub_options.handler_connection_limit)
elif self.config.has_option('main', 'handler_connection_limit'):
run_kwargs['handler_connection_limit'] = self.config.getint('main', 'handler_connection_limit')
if sub_options.script_name:
run_kwargs['script_name'] = sub_options.script_name
if sub_options.max_children:
run_kwargs['max_children'] = sub_options.max_children
elif self.config.has_option('main', 'max_children'):
run_kwargs['max_children'] = self.config.getint('main', 'max_children')
if sub_options.http:
run_function = qommon.scgi_server.http_run
if sub_options.silent:
self.make_silent()
# iterate over all tenants to execute required SQL migrations
# beforehand.
pub = publisher.WcsPublisher.create_publisher(register_cron=False)
quixote.cleanup()
base_app_dir = pub.app_dir
for hostname in publisher.WcsPublisher.get_tenants():
tenant_path = os.path.join(base_app_dir, hostname)
if not os.path.exists(os.path.join(tenant_path, 'config.pck')):
continue
pub = publisher.WcsPublisher.create_publisher(register_cron=False)
pub.app_dir = tenant_path
pub.set_config()
if pub.is_using_postgresql():
pub.migrate_sql()
pub.cleanup()
quixote.cleanup()
if sub_options.daemonize:
try:
pid = os.fork()
if pid > 0: # exit first parent
sys.exit(0)
except OSError, e:
print >> sys.stderr, 'failure in first fork (%s)' % e.strerror
sys.exit(1)
os.chdir("/")
os.setsid()
os.umask(0)
# do second fork
try:
pid = os.fork()
if pid > 0: # exit from second parent
sys.exit(0)
except OSError, e:
print >> sys.stderr, 'failure in second fork (%s)' % e.strerror
sys.exit(1)
if not sub_options.silent:
self.make_silent()
if sub_options.pidfile:
self.pidfile = sub_options.pidfile
file(self.pidfile, 'w+').write("%s\n" % os.getpid())
try:
run_function(publisher.WcsPublisher.create_publisher, **run_kwargs)
except socket.error, e:
self.del_pid()
if e[0] == 98:
print >> sys.stderr, 'address already in use'
sys.exit(1)
raise
except KeyboardInterrupt:
self.del_pid()
sys.exit(1)
self.del_pid()
CmdStart.register()

View File

@ -1,63 +0,0 @@
# w.c.s. - web application for online forms
# Copyright (C) 2005-2013 Entr'ouvert
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, see <http://www.gnu.org/licenses/>.
import errno
import os
import signal
import sys
import time
from qommon.ctl import Command, make_option
class CmdStop(Command):
name = 'stop'
def __init__(self):
Command.__init__(self, [
make_option('--pidfile', metavar='FILENAME',
dest='pidfile')
])
def execute(self, base_options, sub_options, args):
self.pidfile = sub_options.pidfile
if not self.pidfile:
print >> sys.stderr, 'you must specificy --pidfile'
sys.exit(1)
try:
pf = file(self.pidfile, 'r')
pid = int(pf.read().strip())
pf.close()
except IOError:
pid = None
if not pid:
print >> sys.stderr, 'pidfile does not exist. Daemon not running?'
sys.exit(1)
# Try killing the daemon process...
try:
while 1:
os.kill(pid, signal.SIGTERM)
time.sleep(0.1)
except OSError, err:
if err.errno == errno.ESRCH:
if os.path.exists(self.pidfile):
os.remove(self.pidfile)
else:
print str(err)
sys.exit(1)
CmdStop.register()

26
wcs/middleware.py Normal file
View File

@ -0,0 +1,26 @@
# w.c.s. - web application for online forms
# Copyright (C) 2005-2013 Entr'ouvert
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, see <http://www.gnu.org/licenses/>.
from .compat import init_publisher_if_needed, CompatHTTPRequest
class PublisherInitialisationMiddleware(object):
'''Initializes the publisher according to the request server name.'''
def process_request(self, request):
pub = init_publisher_if_needed()
compat_request = CompatHTTPRequest(request)
pub.init_publish(compat_request)
pub._set_request(compat_request)
request._publisher = pub

View File

@ -56,7 +56,7 @@ class AccessUnauthorizedError(AccessForbiddenError):
session.message = ('error', self.public_msg)
login_url = get_publisher().get_root_url() + 'login/'
login_url += '?' + urllib.urlencode({'next': request.get_frontoffice_url()})
quixote.redirect(login_url)
return quixote.redirect(login_url)
class EmailError(Exception):
pass

View File

@ -111,7 +111,7 @@ class HTTPRequest(quixote.http_request.HTTPRequest):
if ctype == 'application/json':
from .misc import json_loads
length = int(self.environ.get('CONTENT_LENGTH') or '0')
payload = self.stdin.read(length)
payload = self.django_request.read(length)
try:
self.json = json_loads(payload)
except ValueError, e:
@ -147,3 +147,7 @@ class HTTPRequest(quixote.http_request.HTTPRequest):
def is_in_backoffice(self):
return self.get_path().startswith('/backoffice/')
@property
def META(self):
return self.environ

View File

@ -1112,14 +1112,12 @@ def get_cfg(key, default = None):
def get_logger():
return get_publisher().get_app_logger()
_publisher_class = None
def set_publisher_class(klass):
global _publisher_class
_publisher_class = klass
__builtin__.__dict__['__publisher_class'] = klass
def get_publisher_class():
return _publisher_class
return __builtin__.__dict__.get('__publisher_class')
Substitutions.register('site_name', category=N_('General'), comment=N_('Site Name'))

View File

@ -1,107 +0,0 @@
# w.c.s. - web application for online forms
# Copyright (C) 2005-2010 Entr'ouvert
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, see <http://www.gnu.org/licenses/>.
import sys
import os
import signal
from scgi import scgi_server
import quixote.server.scgi_server
import cron
import quixote.server.simple_server as simple_server
class QommonHandler(quixote.server.scgi_server.QuixoteHandler):
connection_limit = -1
number_of_connection_handled = 0
def handle_connection(self, conn):
self.number_of_connection_handled = self.number_of_connection_handled + 1
quixote.server.scgi_server.QuixoteHandler.handle_connection(self, conn)
# input, output and conn are closed, long running jobs could be done
# here.
self.publisher.response.process_after_jobs()
if self.number_of_connection_handled == self.connection_limit:
raise SystemExit
class SCGIServer(scgi_server.SCGIServer):
def reap_children(self):
while self.children:
(pid, status) = os.waitpid(-1, os.WNOHANG)
if pid <= 0:
break
try:
os.close(self.children[pid])
except KeyError:
print >> sys.stderr, 'Closing connection to unknown pid:', pid
continue
del self.children[pid]
def restart():
'''Tell the server process to stop all worker process and start again'''
ppid = os.getppid()
os.kill(ppid, signal.SIGHUP)
def run(create_publisher, host='localhost', port=3000, script_name=None,
max_children = 5, spawn_cron = False, handler_connection_limit = -1):
if spawn_cron:
cron.spawn_cron(create_publisher)
def create_handler(parent_fd):
h = QommonHandler(parent_fd, create_publisher, script_name)
h.connection_limit = handler_connection_limit
return h
s = SCGIServer(create_handler, host=host, port=port,
max_children=max_children)
s.serve()
class QommonHTTPRequestHandler(simple_server.HTTPRequestHandler):
def process(self, env, include_body=True):
simple_server.HTTPRequestHandler.process(self, env, include_body=include_body)
self.publisher.response.process_after_jobs()
def http_run(create_publisher, spawn_cron = False,
handler_connection_limit = -1, host = '', port = 80, https = False):
"""Runs a simple, single threaded, synchronous HTTP server that
publishes a Quixote application.
"""
if spawn_cron:
cron.spawn_cron(create_publisher)
if https:
QommonHTTPRequestHandler.required_cgi_environment['HTTPS'] = 'on'
httpd = simple_server.HTTPServer((host, port), QommonHTTPRequestHandler)
def handle_error(request, client_address):
simple_server.HTTPServer.handle_error(httpd, request, client_address)
if sys.exc_info()[0] is SystemExit:
raise sys.exc_info()[0]
httpd.handle_error = handle_error
QommonHTTPRequestHandler.publisher = create_publisher()
try:
httpd.serve_forever()
finally:
httpd.server_close()

127
wcs/settings.py Normal file
View File

@ -0,0 +1,127 @@
# Django settings for wcs project.
import os
DEBUG = True
TEMPLATE_DEBUG = DEBUG
PROJECT_PATH = os.path.dirname(os.path.dirname(__file__))
ADMINS = (
# ('Your Name', 'your_email@example.com'),
)
MANAGERS = ADMINS
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(PROJECT_PATH, 'wcs.sqlite3'),
}
}
# Hosts/domain names that are valid for this site; required if DEBUG is False
# See https://docs.djangoproject.com/en/1.5/ref/settings/#allowed-hosts
ALLOWED_HOSTS = []
# 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 = 'Europe/Brussels'
# Language code for this installation. All choices can be found here:
# http://www.i18nguy.com/unicode/language-identifiers.html
LANGUAGE_CODE = 'en-us'
SITE_ID = 1
# If you set this to False, Django will make some optimizations so as not
# to load the internationalization machinery.
USE_I18N = False
# If you set this to False, Django will not format dates, numbers and
# calendars according to the current locale.
USE_L10N = False
# 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: "/var/www/example.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://example.com/media/", "http://media.example.com/"
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: "/var/www/example.com/static/"
STATIC_ROOT = os.path.join(PROJECT_PATH, 'static')
# URL prefix for static files.
# Example: "http://example.com/static/", "http://static.example.com/"
STATIC_URL = '/static/'
# Additional locations of static files
STATICFILES_DIRS = (
os.path.join(PROJECT_PATH, 'wcs', 'static'),
)
# List of finder classes that know how to find static files in
# various locations.
STATICFILES_FINDERS = (
'django.contrib.staticfiles.finders.FileSystemFinder',
'django.contrib.staticfiles.finders.AppDirectoriesFinder',
# 'django.contrib.staticfiles.finders.DefaultStorageFinder',
)
# Make this unique, and don't share it with anybody.
SECRET_KEY = 'k16cal%1fnochq4xbxqgdns-21lt9lxeof5*%j(0ief3=db32&'
# 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',
'wcs.middleware.PublisherInitialisationMiddleware',
#'django.contrib.sessions.middleware.SessionMiddleware',
#'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',
)
ROOT_URLCONF = 'wcs.urls'
# Python dotted path to the WSGI application used by Django's runserver.
WSGI_APPLICATION = 'wcs.wsgi.application'
TEMPLATE_DIRS = (
os.path.join(PROJECT_PATH, 'wcs', 'templates'),
)
INSTALLED_APPS = (
#'django.contrib.auth',
#'django.contrib.contenttypes',
#'django.contrib.sessions',
#'django.contrib.sites',
#'django.contrib.messages',
#'django.contrib.staticfiles',
#'django.contrib.admin',
)
WCS_LEGACY_CONFIG_FILE = None
local_settings_file = os.environ.get('WCS_SETTINGS_FILE',
os.path.join(os.path.dirname(__file__), 'local_settings.py'))
if os.path.exists(local_settings_file):
execfile(local_settings_file)

21
wcs/urls.py Normal file
View File

@ -0,0 +1,21 @@
# w.c.s. - web application for online forms
# Copyright (C) 2005-2013 Entr'ouvert
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, see <http://www.gnu.org/licenses/>.
from django.conf.urls import patterns, url
urlpatterns = patterns('',
url(r'', 'wcs.compat.quixote', name='quixote'),
)

16
wcs/views.py Normal file
View File

@ -0,0 +1,16 @@
# w.c.s. - web application for online forms
# Copyright (C) 2005-2013 Entr'ouvert
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, see <http://www.gnu.org/licenses/>.

8
wcs/wsgi.py Normal file
View File

@ -0,0 +1,8 @@
"""
WSGI config for wcs project.
"""
import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "wcs.settings")
from django.core.wsgi import get_wsgi_application
application = get_wsgi_application()