diff --git a/.gitignore b/.gitignore index 0d20b6487..b6eb99e54 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ *.pyc +local_settings.py diff --git a/INSTALL b/INSTALL index cb88c6b95..ef2228456 100644 --- a/INSTALL +++ b/INSTALL @@ -1,57 +1,4 @@ w.c.s. - Installation Instructions ================================== -Prerequisites -------------- - -- Python 2.5 -- Quixote 2.5 -- Lasso 2.2 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. - - - 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/ - - SCGIHandler off - - - # 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 - - +We recommend using .deb packages from https://deb.entrouvert.org/ diff --git a/manage.py b/manage.py new file mode 100755 index 000000000..a166b0433 --- /dev/null +++ b/manage.py @@ -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) diff --git a/tests/test_ctl.py b/tests/test_ctl.py index ca6309b1e..8ce5785f9 100644 --- a/tests/test_ctl.py +++ b/tests/test_ctl.py @@ -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() diff --git a/tests/test_publisher.py b/tests/test_publisher.py index 7f39ba661..bb525b4b3 100644 --- a/tests/test_publisher.py +++ b/tests/test_publisher.py @@ -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 '

The requested link' in response.body + assert '

The requested link' in str(response) def test_get_tenants(): pub = create_temporary_pub() diff --git a/tests/utilities.py b/tests/utilities.py index 51788936d..8ab5ffb19 100644 --- a/tests/utilities.py +++ b/tests/utilities.py @@ -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/') diff --git a/wcs/compat.py b/wcs/compat.py new file mode 100644 index 000000000..38a9944f9 --- /dev/null +++ b/wcs/compat.py @@ -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 . + +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) diff --git a/wcs/ctl/start.py b/wcs/ctl/start.py deleted file mode 100644 index 54d2facc1..000000000 --- a/wcs/ctl/start.py +++ /dev/null @@ -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 . - -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() - diff --git a/wcs/ctl/stop.py b/wcs/ctl/stop.py deleted file mode 100644 index 7203e5b84..000000000 --- a/wcs/ctl/stop.py +++ /dev/null @@ -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 . - -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() diff --git a/wcs/middleware.py b/wcs/middleware.py new file mode 100644 index 000000000..11d47be1e --- /dev/null +++ b/wcs/middleware.py @@ -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 . + +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 diff --git a/wcs/qommon/errors.py b/wcs/qommon/errors.py index eef654f01..b2ec8a13b 100644 --- a/wcs/qommon/errors.py +++ b/wcs/qommon/errors.py @@ -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 diff --git a/wcs/qommon/http_request.py b/wcs/qommon/http_request.py index dd1afdfe0..dc0e55b24 100644 --- a/wcs/qommon/http_request.py +++ b/wcs/qommon/http_request.py @@ -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 diff --git a/wcs/qommon/publisher.py b/wcs/qommon/publisher.py index b25f62cc5..9e4d18bbb 100644 --- a/wcs/qommon/publisher.py +++ b/wcs/qommon/publisher.py @@ -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')) diff --git a/wcs/qommon/scgi_server.py b/wcs/qommon/scgi_server.py deleted file mode 100644 index 24c3a9e34..000000000 --- a/wcs/qommon/scgi_server.py +++ /dev/null @@ -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 . - -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() - - diff --git a/wcs/settings.py b/wcs/settings.py new file mode 100644 index 000000000..fc83e7160 --- /dev/null +++ b/wcs/settings.py @@ -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) diff --git a/wcs/urls.py b/wcs/urls.py new file mode 100644 index 000000000..eb85bafb0 --- /dev/null +++ b/wcs/urls.py @@ -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 . + +from django.conf.urls import patterns, url + +urlpatterns = patterns('', + url(r'', 'wcs.compat.quixote', name='quixote'), +) diff --git a/wcs/views.py b/wcs/views.py new file mode 100644 index 000000000..8765d2024 --- /dev/null +++ b/wcs/views.py @@ -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 . + diff --git a/wcs/wsgi.py b/wcs/wsgi.py new file mode 100644 index 000000000..3b1705df1 --- /dev/null +++ b/wcs/wsgi.py @@ -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()