Merge branch 'master' of github.com:pretix/pretix

This commit is contained in:
Raphael Michel
2015-05-21 14:56:32 +02:00
23 changed files with 384 additions and 117 deletions

1
.gitignore vendored
View File

@@ -15,3 +15,4 @@ htmlcov/
__pycache__/
_static/
.idea
.secret

View File

@@ -5,12 +5,14 @@ python:
- "3.4"
install:
- pip install -q -r src/requirements.txt
- npm install -g less@2.5.0
before_script:
- cd src
- flake8 --ignore=E123,F403,F401,N802,C901,W503 .
- python manage.py check
- python manage.py validate
script:
- make
- make compress
- coverage run manage.py test -v 2
after_success:
- coveralls

View File

@@ -0,0 +1,40 @@
FROM debian:jessie
RUN apt-get update && apt-get install -y supervisor python3 git python3-pip \
libxml2-dev libxslt1-dev python-dev python-virtualenv locales libffi-dev \
build-essential python3-dev zlib1g-dev libssl-dev npm gettext git \
libpq-dev libmysqlclient-dev \
--no-install-recommends
WORKDIR /
RUN npm install -g less@2.5.0
RUN ln -s /usr/bin/nodejs /usr/bin/node
RUN ln -s /node_modules/.bin/lessc /usr/bin/lessc
RUN dpkg-reconfigure locales && \
locale-gen C.UTF-8 && \
/usr/sbin/update-locale LANG=C.UTF-8
ENV LC_ALL C.UTF-8
RUN apt-get clean && rm -rf /var/lib/apt/lists/*
ADD supervisord.conf /etc/supervisor/conf.d/supervisord.conf
ADD gunicorn_starter.bash /gunicorn_starter.bash
RUN git clone --recursive --depth 1 https://github.com/pretix/pretix.git /pretix
WORKDIR /pretix/src
RUN pip3 install -r requirements.txt
RUN pip3 install -r requirements/mysql.txt
RUN pip3 install -r requirements/postgres.txt
RUN pip3 install gunicorn
RUN make
RUN mkdir /etc/pretix
RUN mkdir /data
VOLUME /etc/pretix
EXPOSE 80
CMD ["/usr/bin/supervisord"]

View File

@@ -0,0 +1,3 @@
main:
#tar ch --exclude=_static --exclude=htmlcov --exclude=db.sqlite3 . |
docker build -t pretix/standalone .

View File

@@ -0,0 +1,10 @@
#!/bin/bash
cd /pretix/src
export DJANGO_SETTINGS_MODULE=pretix.settings
export MEDIA_ROOT=/data/
python3 manage.py migrate
python3 manage.py compress
gunicorn \
-b '0.0.0.0:80' \
-w 3 --max-requests 1000 --max-requests-jitter 50 \
pretix.wsgi

View File

@@ -0,0 +1,5 @@
[supervisord]
nodaemon=true
[program:gunicorn]
command=/bin/bash /gunicorn_starter.bash

177
doc/admin/config.rst Normal file
View File

@@ -0,0 +1,177 @@
.. highlight:: ini
Configuration file
==================
Pretix reads its configuration from a configuration file. It tries to find this file
at the following locations. It will try to read the file from the specified paths in
the following order. The file that is found *last* will override the settings from
the files found before.
1. ``/etc/pretix/pretix.cfg``
2. ``~/.pretix.cfg``
3. ``pretix.cfg`` in the current working directory
The file is expected to be in the INI format as specified in the `Python documentation`_.
The config file may contain the following sections (all settings are optional and have default values).
pretix settings
---------------
Example::
[pretix]
instance_name=pretix.de
global_registration=off
site_url=http://localhost
currency=EUR
cookiedomain=.pretix.de
securecookie=on
``instance_name``
The name of this installation. Default: ``pretix.de``
``global_registration``
Whether or not this installation supports global user accounts (in addition to
event-bound accounts). Defaults to ``True``.
``site_url``
The installation's full URL, without a trailing slash.
``currency``
The default currency as a three-letter code. Defaults to ``EUR``.
``cookiedomain``
The domain to be used for session cookies, csrf protection cookies and locale cookies.
Empty by default.
``securecookie``
Set the ``secure`` and ``httponly`` flags on session cookies. Off by default.
Locale settings
---------------
Example::
[locale]
default=de
timezone=Europe/Berlin
``default``
The system's default locale. Default: ``en``
``timezone``
The system's default timezone as a ``pytz`` name. Default: ``UTC``
Database settings
-----------------
Example::
[database]
backend=mysql
name=pretix
user=pretix
password=abcd
host=localhost
port=3306
``backend``
One of ``mysql``, ``sqlite3``, ``oracle`` and ``postgresql_psycopg2``.
Default: ``sqlite3``.
``name``
The database's name. Default: ``db.sqlite3``.
``user``, ``password``, ``host``, ``port``
Connection details for the database connection. Empty by default.
Uploaded files
--------------
Example::
[media]
url=/media/
root=media
``root``
The filesystem location to store user-uploaded content at. By default, this takes
the value of the environment variable ``MEDIA_ROOT``, if present, or ``media`` if not.
``url``
The URL to be used to serve user-uploaded content. You should not need to modify
this. Default: ``/media/``
Email
-----
Example::
[mail]
from=hello@localhost
host=127.0.0.71
user=pretix
password=foobar
port=1025
tls=on
ssl=off
``host``, ``port``
The SMTP Host to connect to. Defaults to ``localhost`` and ``25``.
``user``, ``password``
The SMTP user data to use for the connection. Empty by default.
``from``
The email address to set as ``From`` header in outgoing emails by the system.
Default: ``pretix@localhost``
``tls``, ``ssl``
Use STARTTLS or SSL for the SMTP connection. Off by default.
Django settings
---------------
Example::
[django]
hosts=localhost
secret=j1kjps5a5&4ilpn912s7a1!e2h!duz^i3&idu@_907s$wrz@x-
debug=off
``hosts``
Comma-seperated list of allowed host names for this installation.
Default: ``localhost``
``secret``
The secret to be used by Django for signing and verification purposes. If this
setting is not provided, pretix will generate a random secret on the first start
and store it in the filesystem for later usage.
``debug``
Whether or not to run in debug mode. Default is ``False``.
.. WARNING:: Never set this to ``True`` in production!
Static files
------------
You should *not* need to modify these settings as logn as you don't want to use a
custom delivery method for static files such as an external CDN.
Example::
[static]
url=/static/
root=_static
``url``
The URL to be used to serve static files. Default: ``/static/``.
``root``
The filesystem path to be used for static file storage. Default: ``_static``
.. _Python documentation: https://docs.python.org/3/library/configparser.html?highlight=configparser#supported-ini-file-structure

11
doc/admin/index.rst Normal file
View File

@@ -0,0 +1,11 @@
Administrator documentation
===========================
This documentation is for everyone who wants to install pretix on a server.
Contents:
.. toctree::
:maxdepth: 2
config

View File

@@ -11,5 +11,6 @@ Contents:
.. toctree::
:maxdepth: 2
admin/index
development/index

View File

@@ -1,7 +1,14 @@
all: localecompile
all: localecompile staticfiles
production: localecompile staticfiles compress
localecompile:
django-admin compilemessages
./manage.py compilemessages
localegen:
django-admin makemessages --all
./manage.py makemessages --all
staticfiles:
./manage.py collectstatic --noinput
compress:
./manage.py compress

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/env python3
import os
import sys

View File

@@ -1,5 +1,5 @@
from django.conf import settings
from django.core.urlresolvers import resolve
from django.core.urlresolvers import resolve, get_script_prefix
from .signals import html_head, nav_event
@@ -8,7 +8,7 @@ def contextprocessor(request):
Adds data to all template contexts
"""
url = resolve(request.path_info)
if not request.path.startswith('/control'):
if not request.path.startswith(get_script_prefix() + 'control'):
return {}
ctx = {
'url_name': url.url_name,

View File

@@ -1,7 +1,7 @@
from urllib.parse import urlparse
from django.conf import settings
from django.core.urlresolvers import resolve
from django.core.urlresolvers import resolve, get_script_prefix
from django.utils.encoding import force_str
from django.utils.six.moves.urllib.parse import urlparse
from django.shortcuts import resolve_url
from django.contrib.auth import REDIRECT_FIELD_NAME
from django.http import HttpResponseNotFound
@@ -25,7 +25,7 @@ class PermissionMiddleware:
def process_request(self, request):
url = resolve(request.path_info)
url_name = url.url_name
if not request.path.startswith('/control') or url_name in self.EXCEPTIONS:
if not request.path.startswith(get_script_prefix() + 'control') or url_name in self.EXCEPTIONS:
return
if not request.user.is_authenticated():
# Taken from django/contrib/auth/decorators.py

View File

@@ -52,9 +52,8 @@ class CategoryDelete(EventPermissionRequiredMixin, DeleteView):
context_object_name = 'category'
def get_object(self, queryset=None) -> ItemCategory:
url = resolve(self.request.path_info)
return self.request.event.categories.current.get(
identity=url.kwargs['category']
identity=self.kwargs['category']
)
def delete(self, request, *args, **kwargs):
@@ -199,17 +198,15 @@ class PropertyUpdate(EventPermissionRequiredMixin, UpdateView):
context_object_name = 'property'
def get_object(self, queryset=None) -> Property:
url = resolve(self.request.path_info)
return self.request.event.properties.current.get(
identity=url.kwargs['property']
identity=self.kwargs['property']
)
def get_success_url(self) -> str:
url = resolve(self.request.path_info)
return reverse('control:event.items.properties.edit', kwargs={
'organizer': self.request.event.organizer.slug,
'event': self.request.event.slug,
'property': url.kwargs['property']
'property': self.kwargs['property']
}) + '?success=true'
def get_formset(self):
@@ -322,9 +319,8 @@ class PropertyDelete(EventPermissionRequiredMixin, DeleteView):
def get_object(self, queryset=None) -> Property:
if not hasattr(self, 'object') or not self.object:
url = resolve(self.request.path_info)
self.object = self.request.event.properties.current.get(
identity=url.kwargs['property']
identity=self.kwargs['property']
)
return self.object
@@ -371,9 +367,8 @@ class QuestionDelete(EventPermissionRequiredMixin, DeleteView):
context_object_name = 'question'
def get_object(self, queryset=None) -> Question:
url = resolve(self.request.path_info)
return self.request.event.questions.current.get(
identity=url.kwargs['question']
identity=self.kwargs['question']
)
def get_context_data(self, *args, **kwargs) -> dict:
@@ -402,9 +397,8 @@ class QuestionUpdate(EventPermissionRequiredMixin, UpdateView):
context_object_name = 'question'
def get_object(self, queryset=None) -> Question:
url = resolve(self.request.path_info)
return self.request.event.questions.current.get(
identity=url.kwargs['question']
identity=self.kwargs['question']
)
def get_success_url(self) -> str:
@@ -559,9 +553,8 @@ class QuotaUpdate(EventPermissionRequiredMixin, QuotaEditorMixin, UpdateView):
context_object_name = 'quota'
def get_object(self, queryset=None) -> Quota:
url = resolve(self.request.path_info)
return self.request.event.quotas.current.get(
identity=url.kwargs['quota']
identity=self.kwargs['quota']
)
def get_success_url(self) -> str:
@@ -578,9 +571,8 @@ class QuotaDelete(EventPermissionRequiredMixin, DeleteView):
context_object_name = 'quota'
def get_object(self, queryset=None) -> Quota:
url = resolve(self.request.path_info)
return self.request.event.quotas.current.get(
identity=url.kwargs['quota']
identity=self.kwargs['quota']
)
def get_context_data(self, *args, **kwargs) -> dict:
@@ -607,9 +599,8 @@ class ItemDetailMixin(SingleObjectMixin):
def get_object(self, queryset=None) -> Item:
if not hasattr(self, 'object') or not self.object:
url = resolve(self.request.path_info)
self.item = self.request.event.items.current.get(
identity=url.kwargs['item']
identity=self.kwargs['item']
)
self.object = self.item
return self.object

View File

@@ -1,2 +0,0 @@
EMAIL_PORT = 1025
EMAIL_HOST = '127.0.0.1'

View File

@@ -1,34 +1,76 @@
"""
Django settings for pretix project.
For more information on this file, see
https://docs.djangoproject.com/en/dev/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/dev/ref/settings/
"""
import configparser
import os
from django.utils.crypto import get_random_string
config = configparser.ConfigParser()
config.read(['/etc/pretix/pretix.cfg', os.path.expanduser('~/.pretix.cfg'), 'pretix.cfg'],
encoding='utf-8')
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
if config.has_option('django', 'secret'):
SECRET_KEY = config.get('django', 'secret')
else:
SECRET_FILE = os.path.join(BASE_DIR, '.secret')
if os.path.exists(SECRET_FILE):
with open(SECRET_FILE, 'r') as f:
SECRET_KEY = f.read().strip()
else:
chars = 'abcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*(-_=+)'
SECRET_KEY = get_random_string(50, chars)
with open(SECRET_FILE, 'w') as f:
f.write(SECRET_KEY)
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/dev/howto/deployment/checklist/
# Adjustable settings
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = '0ro^46+8k#dv3ej=oen-2ww)i30#$$^&x&eajyj&_&h)$nc6@5'
DEBUG = TEMPLATE_DEBUG = config.getboolean('django', 'debug', fallback=False)
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.' + config.get('database', 'backend', fallback='sqlite3'),
'NAME': config.get('database', 'name', fallback=os.path.join(BASE_DIR, 'db.sqlite3')),
'USER': config.get('database', 'user', fallback=''),
'PASSWORD': config.get('database', 'password', fallback=''),
'HOST': config.get('database', 'host', fallback=''),
'PORT': config.get('database', 'port', fallback='')
}
}
TEMPLATE_DEBUG = True
STATIC_URL = config.get('static', 'url', fallback='/static/')
STATIC_ROOT = config.get('static', 'root', fallback='_static')
ALLOWED_HOSTS = []
MEDIA_URL = config.get('media', 'url', fallback=os.environ.get('MEDIA_ROOT', '/media/'))
MEDIA_ROOT = config.get('media', 'root', fallback='media')
PRETIX_INSTANCE_NAME = config.get('pretix', 'instance_name', fallback='pretix.de')
PRETIX_GLOBAL_REGISTRATION = config.getboolean('pretix', 'global_registration', fallback=True)
# Application definition
SITE_URL = config.get('pretix', 'url', fallback='http://localhost')
DEFAULT_CURRENCY = config.get('pretix', 'currency', fallback='EUR')
ALLOWED_HOSTS = config.get('django', 'hosts', fallback='localhost').split(',')
LANGUAGE_CODE = config.get('locale', 'default', fallback='en')
TIME_ZONE = config.get('locale', 'timezone', fallback='UTC')
MAIL_FROM = SERVER_EMAIL = DEFAULT_FROM_EMAIL = config.get(
'mail', 'from', fallback='pretix@localhost')
EMAIL_HOST = config.get('mail', 'host', fallback='localhost')
EMAIL_PORT = config.getint('mail', 'port', fallback=25)
EMAIL_HOST_USER = config.get('mail', 'user', fallback='')
EMAIL_HOST_PASSWORD = config.get('mail', 'password', fallback='')
SESSION_COOKIE_SECURE = SESSION_COOKIE_HTTPONLY = config.getboolean(
'pretix', 'securecookie', fallback=False)
LANGUAGE_COOKIE_DOMAIN = SESSION_COOKIE_DOMAIN = CSRF_COOKIE_DOMAIN = config.get(
'pretix', 'cookiedomain', fallback=None)
# Internal settings
SESSION_COOKIE_NAME = 'pretix_session'
LANGUAGE_COOKIE_NAME = 'pretix_language'
CSRF_COOKIE_NAME = 'pretix_csrftoken'
INSTALLED_APPS = (
'django.contrib.admin',
@@ -66,45 +108,12 @@ MIDDLEWARE_CLASSES = (
'pretix.base.middleware.LocaleMiddleware',
)
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.request",
"django.core.context_processors.static",
"django.core.context_processors.tz",
"django.contrib.messages.context_processors.messages",
'pretix.control.context.contextprocessor',
'pretix.presale.context.contextprocessor',
)
ROOT_URLCONF = 'pretix.urls'
WSGI_APPLICATION = 'pretix.wsgi.application'
# Database
# https://docs.djangoproject.com/en/dev/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
# Internationalization
# https://docs.djangoproject.com/en/dev/topics/i18n/
LANGUAGE_CODE = 'en'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_L10N = True
USE_TZ = True
LOCALE_PATHS = (
@@ -117,20 +126,42 @@ LANGUAGES = (
('de', _('German')),
)
# Authentication
AUTH_USER_MODEL = 'pretixbase.User'
LOGIN_URL = '/login' # global login does not yet exist
LOGIN_URL_CONTROL = 'control:auth.login'
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/dev/howto/static-files/
template_loaders = (
'django.template.loaders.filesystem.Loader',
'django.template.loaders.app_directories.Loader',
)
if DEBUG:
template_loaders = (
('django.template.loaders.cached.Loader', template_loaders),
)
STATIC_URL = '/static/'
STATIC_ROOT = '_static'
MEDIA_ROOT = 'media'
MEDIA_URL = '/media/'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [
os.path.join(BASE_DIR, 'templates')
],
'OPTIONS': {
'context_processors': [
'django.contrib.auth.context_processors.auth',
'django.template.context_processors.debug',
'django.template.context_processors.i18n',
'django.template.context_processors.media',
"django.template.context_processors.request",
'django.template.context_processors.static',
'django.template.context_processors.tz',
'django.contrib.messages.context_processors.messages',
'pretix.control.context.contextprocessor',
'pretix.presale.context.contextprocessor',
],
'loaders': template_loaders
},
},
]
STATICFILES_FINDERS = (
'django.contrib.staticfiles.finders.FileSystemFinder',
@@ -142,7 +173,7 @@ COMPRESS_PRECOMPILERS = (
('text/less', 'pretix.helpers.lessabsolutefilter.LessFilter'),
)
COMPRESS_OFFLINE = not DEBUG
COMPRESS_ENABLED = COMPRESS_OFFLINE = not DEBUG
COMPRESS_CSS_FILTERS = (
'compressor.filters.css_default.CssAbsoluteFilter',
@@ -155,13 +186,6 @@ DEBUG_TOOLBAR_CONFIG = {
'JQUERY_URL': ''
}
# Pretix specific settings
PRETIX_INSTANCE_NAME = 'pretix.de'
PRETIX_GLOBAL_REGISTRATION = True
DEFAULT_CURRENCY = 'EUR'
INTERNAL_IPS = ('127.0.0.1', '::1')
from django.contrib.messages import constants as messages # NOQA
@@ -196,11 +220,3 @@ LOGGING = {
},
},
}
MAIL_FROM = 'pretix@localhost'
SITE_URL = 'http://localhost'
try:
from .local_settings import * # NOQA
except ImportError:
pass

View File

@@ -1,2 +1,2 @@
chardet
chardet>=2.3,<3

View File

@@ -1 +1 @@
django-debug-toolbar>=1.3.0
django-debug-toolbar>=1.3.0,<2.0

View File

@@ -0,0 +1,2 @@
mysqlclient

View File

@@ -1,2 +1,2 @@
paypalrestsdk
paypalrestsdk>=1.9,<1.10,<2.0

View File

@@ -0,0 +1,2 @@
psycopg2

View File

@@ -1,12 +1,12 @@
# Functional requirements
Django==1.8.1
python-dateutil
python-dateutil>=2.4,<2.5
pytz
django-bootstrap3
django-bootstrap3>=5.4,<5.5
-e git+https://github.com/pretix/django-formset-js.git@master#egg=django-formset-js
-e git+https://github.com/pretix/cleanerversion.git@pretix#egg=cleanerversion
django-compressor
reportlab
django-compressor>=1.5,<2.0
reportlab>=3.1.44,<3.2
-e git+https://github.com/pretix/PyPDF2.git@pretix#egg=PyPDF2
# Deployment / static file compilation requirements
@@ -14,4 +14,5 @@ BeautifulSoup4
html5lib
slimit
lxml
dj-static
static3==0.6.1
-e git+https://github.com/pretix/dj-static.git@script-path#egg=dj-static

View File

@@ -1,2 +1,2 @@
stripe
stripe>=1.22,<1.23