diff --git a/.gitignore b/.gitignore index fc6eb78d0..07f4c0882 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ htmlcov/ __pycache__/ _static/ .idea +.secret diff --git a/.travis.yml b/.travis.yml index 7ab7743ca..fceef1322 100644 --- a/.travis.yml +++ b/.travis.yml @@ -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 diff --git a/deployment/docker/standalone/Dockerfile b/deployment/docker/standalone/Dockerfile new file mode 100644 index 000000000..fa975afc0 --- /dev/null +++ b/deployment/docker/standalone/Dockerfile @@ -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"] + diff --git a/deployment/docker/standalone/Makefile b/deployment/docker/standalone/Makefile new file mode 100644 index 000000000..a912853c5 --- /dev/null +++ b/deployment/docker/standalone/Makefile @@ -0,0 +1,3 @@ +main: + #tar ch --exclude=_static --exclude=htmlcov --exclude=db.sqlite3 . | + docker build -t pretix/standalone . diff --git a/deployment/docker/standalone/gunicorn_starter.bash b/deployment/docker/standalone/gunicorn_starter.bash new file mode 100644 index 000000000..225990978 --- /dev/null +++ b/deployment/docker/standalone/gunicorn_starter.bash @@ -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 diff --git a/deployment/docker/standalone/supervisord.conf b/deployment/docker/standalone/supervisord.conf new file mode 100644 index 000000000..72e2c7805 --- /dev/null +++ b/deployment/docker/standalone/supervisord.conf @@ -0,0 +1,5 @@ +[supervisord] +nodaemon=true + +[program:gunicorn] +command=/bin/bash /gunicorn_starter.bash diff --git a/doc/admin/config.rst b/doc/admin/config.rst new file mode 100644 index 000000000..edeb60ad1 --- /dev/null +++ b/doc/admin/config.rst @@ -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 \ No newline at end of file diff --git a/doc/admin/index.rst b/doc/admin/index.rst new file mode 100644 index 000000000..570fb7084 --- /dev/null +++ b/doc/admin/index.rst @@ -0,0 +1,11 @@ +Administrator documentation +=========================== + +This documentation is for everyone who wants to install pretix on a server. + +Contents: + +.. toctree:: + :maxdepth: 2 + + config diff --git a/doc/index.rst b/doc/index.rst index 387b86ee8..eadea83f7 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -11,5 +11,6 @@ Contents: .. toctree:: :maxdepth: 2 + admin/index development/index diff --git a/src/Makefile b/src/Makefile index 75067417f..d79277f04 100644 --- a/src/Makefile +++ b/src/Makefile @@ -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 diff --git a/src/manage.py b/src/manage.py index 4dbadaca8..ff13d7d95 100755 --- a/src/manage.py +++ b/src/manage.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 import os import sys diff --git a/src/pretix/control/context.py b/src/pretix/control/context.py index c34a9d679..aa3cbeb1d 100644 --- a/src/pretix/control/context.py +++ b/src/pretix/control/context.py @@ -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, diff --git a/src/pretix/control/middleware.py b/src/pretix/control/middleware.py index 512ba5a6f..e5aea9e81 100644 --- a/src/pretix/control/middleware.py +++ b/src/pretix/control/middleware.py @@ -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 diff --git a/src/pretix/control/views/item.py b/src/pretix/control/views/item.py index 5d7d1cc9a..76415975b 100644 --- a/src/pretix/control/views/item.py +++ b/src/pretix/control/views/item.py @@ -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 diff --git a/src/pretix/local_settings.py b/src/pretix/local_settings.py deleted file mode 100644 index bb2104295..000000000 --- a/src/pretix/local_settings.py +++ /dev/null @@ -1,2 +0,0 @@ -EMAIL_PORT = 1025 -EMAIL_HOST = '127.0.0.1' diff --git a/src/pretix/settings.py b/src/pretix/settings.py index a5eb72f54..365402a0d 100644 --- a/src/pretix/settings.py +++ b/src/pretix/settings.py @@ -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 diff --git a/src/requirements/banktransfer.txt b/src/requirements/banktransfer.txt index c60139b83..9bbf199d3 100644 --- a/src/requirements/banktransfer.txt +++ b/src/requirements/banktransfer.txt @@ -1,2 +1,2 @@ -chardet +chardet>=2.3,<3 diff --git a/src/requirements/dev.txt b/src/requirements/dev.txt index 2f51e53d2..1b891edfb 100644 --- a/src/requirements/dev.txt +++ b/src/requirements/dev.txt @@ -1 +1 @@ -django-debug-toolbar>=1.3.0 +django-debug-toolbar>=1.3.0,<2.0 diff --git a/src/requirements/mysql.txt b/src/requirements/mysql.txt new file mode 100644 index 000000000..93c8d852b --- /dev/null +++ b/src/requirements/mysql.txt @@ -0,0 +1,2 @@ +mysqlclient + diff --git a/src/requirements/paypal.txt b/src/requirements/paypal.txt index 19b767208..5bf92ed3b 100644 --- a/src/requirements/paypal.txt +++ b/src/requirements/paypal.txt @@ -1,2 +1,2 @@ -paypalrestsdk +paypalrestsdk>=1.9,<1.10,<2.0 diff --git a/src/requirements/postgres.txt b/src/requirements/postgres.txt new file mode 100644 index 000000000..6ccfa4b1a --- /dev/null +++ b/src/requirements/postgres.txt @@ -0,0 +1,2 @@ +psycopg2 + diff --git a/src/requirements/production.txt b/src/requirements/production.txt index dc6132474..368288571 100644 --- a/src/requirements/production.txt +++ b/src/requirements/production.txt @@ -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 diff --git a/src/requirements/stripe.txt b/src/requirements/stripe.txt index 599f08002..51f26a42a 100644 --- a/src/requirements/stripe.txt +++ b/src/requirements/stripe.txt @@ -1,2 +1,2 @@ -stripe +stripe>=1.22,<1.23