Compare commits

..

2 Commits

Author SHA1 Message Date
Raphael Michel
e721f370c4 Bump to 4.16.1 2023-03-06 14:50:09 +01:00
Raphael Michel
19af03c5aa [SECURITY] Enforce session validation on oauth authorize endpoint 2023-03-06 14:49:55 +01:00
531 changed files with 180458 additions and 371539 deletions

View File

@@ -6,7 +6,7 @@
version: 2 version: 2
updates: updates:
- package-ecosystem: "pip" - package-ecosystem: "pip"
directory: "/" directory: "/src"
schedule: schedule:
interval: "daily" interval: "daily"
versioning-strategy: increase versioning-strategy: increase

View File

@@ -1,49 +0,0 @@
name: Build
on:
push:
branches: [ master ]
paths-ignore:
- 'doc/**'
- 'src/pretix/locale/**'
pull_request:
branches: [ master ]
paths-ignore:
- 'doc/**'
- 'src/pretix/locale/**'
permissions:
contents: read # to fetch code (actions/checkout)
env:
FORCE_COLOR: 1
jobs:
test:
runs-on: ubuntu-22.04
name: Packaging
strategy:
matrix:
python-version: ["3.11"]
steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v1
with:
python-version: ${{ matrix.python-version }}
- uses: actions/cache@v1
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
restore-keys: |
${{ runner.os }}-pip-
- name: Install system dependencies
run: sudo apt update && sudo apt install gettext unzip
- name: Install Python dependencies
run: pip3 install -U setuptools build pip check-manifest
- name: Run check-manifest
run: check-manifest
- name: Run build
run: python -m build
- name: Check files
run: unzip -l dist/pretix*whl | grep node_modules || exit 1

View File

@@ -26,10 +26,10 @@ jobs:
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Set up Python 3.11 - name: Set up Python 3.9
uses: actions/setup-python@v1 uses: actions/setup-python@v1
with: with:
python-version: 3.11 python-version: 3.9
- uses: actions/cache@v1 - uses: actions/cache@v1
with: with:
path: ~/.cache/pip path: ~/.cache/pip

View File

@@ -24,10 +24,10 @@ jobs:
name: Check gettext syntax name: Check gettext syntax
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Set up Python 3.11 - name: Set up Python 3.9
uses: actions/setup-python@v1 uses: actions/setup-python@v1
with: with:
python-version: 3.11 python-version: 3.9
- uses: actions/cache@v1 - uses: actions/cache@v1
with: with:
path: ~/.cache/pip path: ~/.cache/pip
@@ -38,6 +38,7 @@ jobs:
run: sudo apt update && sudo apt install gettext run: sudo apt update && sudo apt install gettext
- name: Install Dependencies - name: Install Dependencies
run: pip3 install -e ".[dev]" run: pip3 install -e ".[dev]"
working-directory: ./src
- name: Compile messages - name: Compile messages
run: python manage.py compilemessages run: python manage.py compilemessages
working-directory: ./src working-directory: ./src
@@ -49,10 +50,10 @@ jobs:
name: Spellcheck name: Spellcheck
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Set up Python 3.11 - name: Set up Python 3.9
uses: actions/setup-python@v1 uses: actions/setup-python@v1
with: with:
python-version: 3.11 python-version: 3.9
- uses: actions/cache@v1 - uses: actions/cache@v1
with: with:
path: ~/.cache/pip path: ~/.cache/pip
@@ -63,6 +64,7 @@ jobs:
run: sudo apt update && sudo apt install enchant-2 hunspell hunspell-de-de aspell-en aspell-de run: sudo apt update && sudo apt install enchant-2 hunspell hunspell-de-de aspell-en aspell-de
- name: Install Dependencies - name: Install Dependencies
run: pip3 install -e ".[dev]" run: pip3 install -e ".[dev]"
working-directory: ./src
- name: Spellcheck translations - name: Spellcheck translations
run: potypo run: potypo
working-directory: ./src working-directory: ./src

View File

@@ -24,10 +24,10 @@ jobs:
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Set up Python 3.11 - name: Set up Python 3.9
uses: actions/setup-python@v1 uses: actions/setup-python@v1
with: with:
python-version: 3.11 python-version: 3.9
- uses: actions/cache@v1 - uses: actions/cache@v1
with: with:
path: ~/.cache/pip path: ~/.cache/pip
@@ -36,6 +36,7 @@ jobs:
${{ runner.os }}-pip- ${{ runner.os }}-pip-
- name: Install Dependencies - name: Install Dependencies
run: pip3 install -e ".[dev]" mysqlclient psycopg2-binary run: pip3 install -e ".[dev]" mysqlclient psycopg2-binary
working-directory: ./src
- name: Run isort - name: Run isort
run: isort -c . run: isort -c .
working-directory: ./src working-directory: ./src
@@ -44,10 +45,10 @@ jobs:
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Set up Python 3.11 - name: Set up Python 3.9
uses: actions/setup-python@v1 uses: actions/setup-python@v1
with: with:
python-version: 3.11 python-version: 3.9
- uses: actions/cache@v1 - uses: actions/cache@v1
with: with:
path: ~/.cache/pip path: ~/.cache/pip
@@ -56,6 +57,7 @@ jobs:
${{ runner.os }}-pip- ${{ runner.os }}-pip-
- name: Install Dependencies - name: Install Dependencies
run: pip3 install -e ".[dev]" mysqlclient psycopg2-binary run: pip3 install -e ".[dev]" mysqlclient psycopg2-binary
working-directory: ./src
- name: Run flake8 - name: Run flake8
run: flake8 . run: flake8 .
working-directory: ./src working-directory: ./src
@@ -64,10 +66,10 @@ jobs:
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Set up Python 3.11 - name: Set up Python 3.9
uses: actions/setup-python@v1 uses: actions/setup-python@v1
with: with:
python-version: 3.11 python-version: 3.9
- name: Install Dependencies - name: Install Dependencies
run: pip3 install licenseheaders run: pip3 install licenseheaders
- name: Run licenseheaders - name: Run licenseheaders

View File

@@ -24,22 +24,22 @@ jobs:
name: Tests name: Tests
strategy: strategy:
matrix: matrix:
python-version: ["3.9", "3.10", "3.11"] python-version: ["3.7", "3.9", "3.10"]
database: [sqlite, postgres, mysql] database: [sqlite, postgres, mysql]
exclude: exclude:
- database: mysql - database: mysql
python-version: "3.9" python-version: "3.10"
- database: mysql - database: mysql
python-version: "3.11"
- database: sqlite
python-version: "3.9" python-version: "3.9"
- database: sqlite
python-version: "3.7"
- database: sqlite - database: sqlite
python-version: "3.10" python-version: "3.10"
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- uses: getong/mariadb-action@v1.1 - uses: getong/mariadb-action@v1.1
with: with:
mariadb version: '10.10' mariadb version: '10.4'
mysql database: 'pretix' mysql database: 'pretix'
mysql root password: '' mysql root password: ''
if: matrix.database == 'mysql' if: matrix.database == 'mysql'
@@ -64,6 +64,7 @@ jobs:
run: sudo apt update && sudo apt install gettext mariadb-client run: sudo apt update && sudo apt install gettext mariadb-client
- name: Install Python dependencies - name: Install Python dependencies
run: pip3 install --ignore-requires-python -e ".[dev]" mysqlclient psycopg2-binary # We ignore that flake8 needs newer python as we don't run flake8 during tests run: pip3 install --ignore-requires-python -e ".[dev]" mysqlclient psycopg2-binary # We ignore that flake8 needs newer python as we don't run flake8 during tests
working-directory: ./src
- name: Run checks - name: Run checks
run: python manage.py check run: python manage.py check
working-directory: ./src working-directory: ./src
@@ -82,4 +83,4 @@ jobs:
file: src/coverage.xml file: src/coverage.xml
token: ${{ secrets.CODECOV_TOKEN }} token: ${{ secrets.CODECOV_TOKEN }}
fail_ci_if_error: true fail_ci_if_error: true
if: matrix.database == 'postgres' && matrix.python-version == '3.11' if: matrix.database == 'postgres' && matrix.python-version == '3.10'

2
.gitignore vendored
View File

@@ -1,6 +1,4 @@
env/ env/
build/
dist/
.coverage .coverage
htmlcov/ htmlcov/
.ropeproject .ropeproject

View File

@@ -5,8 +5,8 @@ tests:
- virtualenv env - virtualenv env
- source env/bin/activate - source env/bin/activate
- pip install -U pip wheel setuptools - pip install -U pip wheel setuptools
- XDG_CACHE_HOME=/cache pip3 install -e ".[dev]"
- cd src - cd src
- XDG_CACHE_HOME=/cache pip3 install -e ".[dev]"
- python manage.py check - python manage.py check
- make all compress - make all compress
- py.test --reruns 3 -n 3 tests - py.test --reruns 3 -n 3 tests
@@ -21,16 +21,15 @@ pypi:
- virtualenv env - virtualenv env
- source env/bin/activate - source env/bin/activate
- pip install -U pip wheel setuptools check-manifest twine - pip install -U pip wheel setuptools check-manifest twine
- cd src
- XDG_CACHE_HOME=/cache pip3 install -e ".[dev]" - XDG_CACHE_HOME=/cache pip3 install -e ".[dev]"
- python setup.py sdist - python setup.py sdist
- pip install dist/pretix-*.tar.gz - pip install dist/pretix-*.tar.gz
- python -m pretix migrate - python -m pretix migrate
- python -m pretix check - python -m pretix check
- cd src
- make npminstall
- cd ..
- check-manifest - check-manifest
- python -m build - make npminstall
- python setup.py sdist bdist_wheel
- twine check dist/* - twine check dist/*
- twine upload dist/* - twine upload dist/*
tags: tags:

View File

@@ -1,4 +1,4 @@
FROM python:3.11-bullseye FROM python:3.9-bullseye
RUN apt-get update && \ RUN apt-get update && \
apt-get install -y --no-install-recommends \ apt-get install -y --no-install-recommends \
@@ -19,8 +19,6 @@ RUN apt-get update && \
python3-dev \ python3-dev \
sudo \ sudo \
supervisor \ supervisor \
libmaxminddb0 \
libmaxminddb-dev \
zlib1g-dev && \ zlib1g-dev && \
apt-get clean && \ apt-get clean && \
rm -rf /var/lib/apt/lists/* && \ rm -rf /var/lib/apt/lists/* && \
@@ -41,6 +39,18 @@ RUN apt-get update && \
ENV LC_ALL=C.UTF-8 \ ENV LC_ALL=C.UTF-8 \
DJANGO_SETTINGS_MODULE=production_settings DJANGO_SETTINGS_MODULE=production_settings
# To copy only the requirements files needed to install from PIP
COPY src/setup.py /pretix/src/setup.py
RUN pip3 install -U \
pip \
setuptools \
wheel && \
cd /pretix/src && \
PRETIX_DOCKER_BUILD=TRUE pip3 install \
-e ".[memcached,mysql]" \
gunicorn django-extensions ipython && \
rm -rf ~/.cache/pip
COPY deployment/docker/pretix.bash /usr/local/bin/pretix COPY deployment/docker/pretix.bash /usr/local/bin/pretix
COPY deployment/docker/supervisord /etc/supervisord COPY deployment/docker/supervisord /etc/supervisord
COPY deployment/docker/supervisord.all.conf /etc/supervisord.all.conf COPY deployment/docker/supervisord.all.conf /etc/supervisord.all.conf
@@ -48,19 +58,9 @@ COPY deployment/docker/supervisord.web.conf /etc/supervisord.web.conf
COPY deployment/docker/nginx.conf /etc/nginx/nginx.conf COPY deployment/docker/nginx.conf /etc/nginx/nginx.conf
COPY deployment/docker/nginx-max-body-size.conf /etc/nginx/conf.d/nginx-max-body-size.conf COPY deployment/docker/nginx-max-body-size.conf /etc/nginx/conf.d/nginx-max-body-size.conf
COPY deployment/docker/production_settings.py /pretix/src/production_settings.py COPY deployment/docker/production_settings.py /pretix/src/production_settings.py
COPY pyproject.toml /pretix/pyproject.toml
COPY _build /pretix/_build
COPY src /pretix/src COPY src /pretix/src
RUN pip3 install -U \ RUN cd /pretix/src && python setup.py install
pip \
setuptools \
wheel && \
cd /pretix && \
PRETIX_DOCKER_BUILD=TRUE pip3 install \
-e ".[memcached,mysql]" \
gunicorn django-extensions ipython && \
rm -rf ~/.cache/pip
RUN chmod +x /usr/local/bin/pretix && \ RUN chmod +x /usr/local/bin/pretix && \
rm /etc/nginx/sites-enabled/default && \ rm /etc/nginx/sites-enabled/default && \

View File

@@ -1,48 +0,0 @@
include LICENSE
include README.rst
include src/Makefile
include _build/backend.py
global-include *.proto
recursive-include src/pretix/static *
recursive-include src/pretix/static.dist *
recursive-include src/pretix/locale *
recursive-include src/pretix/helpers/locale *
recursive-include src/pretix/base/templates *
recursive-include src/pretix/control/templates *
recursive-include src/pretix/presale/templates *
recursive-include src/pretix/plugins/banktransfer/templates *
recursive-include src/pretix/plugins/banktransfer/static *
recursive-include src/pretix/plugins/manualpayment/templates *
recursive-include src/pretix/plugins/manualpayment/static *
recursive-include src/pretix/plugins/paypal/templates *
recursive-include src/pretix/plugins/paypal/static *
recursive-include src/pretix/plugins/paypal2/templates *
recursive-include src/pretix/plugins/paypal2/static *
recursive-include src/pretix/plugins/src/pretixdroid/templates *
recursive-include src/pretix/plugins/src/pretixdroid/static *
recursive-include src/pretix/plugins/sendmail/templates *
recursive-include src/pretix/plugins/statistics/templates *
recursive-include src/pretix/plugins/statistics/static *
recursive-include src/pretix/plugins/stripe/templates *
recursive-include src/pretix/plugins/stripe/static *
recursive-include src/pretix/plugins/ticketoutputpdf/templates *
recursive-include src/pretix/plugins/ticketoutputpdf/static *
recursive-include src/pretix/plugins/badges/templates *
recursive-include src/pretix/plugins/badges/static *
recursive-include src/pretix/plugins/returnurl/templates *
recursive-include src/pretix/plugins/returnurl/static *
recursive-include src/pretix/plugins/webcheckin/templates *
recursive-include src/pretix/plugins/webcheckin/static *
recursive-include src *.cfg
recursive-include src *.csv
recursive-include src *.gitkeep
recursive-include src *.jpg
recursive-include src *.json
recursive-include src *.py
recursive-include src *.svg
recursive-include src *.txt
recursive-include src Makefile
recursive-exclude doc *
recursive-exclude deployment *
recursive-exclude res *

View File

@@ -1,12 +0,0 @@
import tomli
from setuptools import build_meta as _orig
from setuptools.build_meta import *
def get_requires_for_build_wheel(config_settings=None):
with open("pyproject.toml", "rb") as f:
p = tomli.load(f)
return [
*_orig.get_requires_for_build_wheel(config_settings),
*p['project']['dependencies']
]

View File

@@ -18,82 +18,67 @@
<title>{{ title|striptags|e }}{{ titlesuffix }}</title> <title>{{ title|striptags|e }}{{ titlesuffix }}</title>
{% endblock %} {% endblock %}
{# FAVICON #}
{#- CSS #} {% if favicon %}
{%- for css in css_files %} <link rel="shortcut icon" href="{{ pathto('_static/' + favicon, 1) }}"/>
{%- if css|attr("rel") %} {% endif %}
<link rel="{{ css.rel }}" href="{{ pathto(css.filename, 1) }}" type="text/css"{% if css.title is not none %} title="{{ css.title }}"{% endif %} /> {# CANONICAL URL #}
{%- else %} {% if theme_canonical_url %}
<link rel="stylesheet" href="{{ pathto(css, 1) }}" type="text/css" />
{%- endif %}
{%- endfor %}
{%- for cssfile in extra_css_files %}
<link rel="stylesheet" href="{{ pathto(cssfile, 1) }}" type="text/css" />
{%- endfor -%}
{#- FAVICON
favicon_url is the only context var necessary since Sphinx 4.
In Sphinx<4, we use favicon but need to prepend path info.
#}
{%- set _favicon_url = favicon_url | default(pathto('_static/' + (favicon or ""), 1)) %}
{%- if favicon_url or favicon %}
<link rel="shortcut icon" href="{{ _favicon_url }}"/>
{%- endif %}
{#- CANONICAL URL (deprecated) #}
{%- if theme_canonical_url and not pageurl %}
<link rel="canonical" href="{{ theme_canonical_url }}{{ pagename }}.html"/> <link rel="canonical" href="{{ theme_canonical_url }}{{ pagename }}.html"/>
{%- endif -%} {% endif %}
{#- CANONICAL URL #} {# CSS #}
{%- if pageurl %}
<link rel="canonical" href="{{ pageurl|e }}" />
{%- endif -%}
{#- JAVASCRIPTS #} {# OPENSEARCH #}
{%- block scripts %} {% if not embedded %}
<!--[if lt IE 9]> {% if use_opensearch %}
<script src="{{ pathto('_static/js/html5shiv.min.js', 1) }}"></script> <link rel="search" type="application/opensearchdescription+xml" title="{% trans docstitle=docstitle|e %}Search within {{ docstitle }}{% endtrans %}" href="{{ pathto('_static/opensearch.xml', 1) }}"/>
<![endif]--> {% endif %}
{%- if not embedded %}
{# XXX Sphinx 1.8.0 made this an external js-file, quick fix until we refactor the template to inherert more blocks directly from sphinx #}
{%- for scriptfile in script_files %}
{{ js_tag(scriptfile) }}
{%- endfor %}
<script src="{{ pathto('_static/js/theme.js', 1) }}"></script>
{#- OPENSEARCH #} {% endif %}
{%- if use_opensearch %}
<link rel="search" type="application/opensearchdescription+xml" {# RTD hosts this file, so just load on non RTD builds #}
title="{% trans docstitle=docstitle|e %}Search within {{ docstitle }}{% endtrans %}" <link rel="stylesheet" href="{{ pathto('_static/' + style, 1) }}" type="text/css" />
href="{{ pathto('_static/opensearch.xml', 1) }}"/>
{%- endif %} {% for cssfile in css_files %}
{%- endif %} <link rel="stylesheet" href="{{ pathto(cssfile, 1) }}" type="text/css" />
{%- endblock %} {% endfor %}
{% for cssfile in extra_css_files %}
<link rel="stylesheet" href="{{ pathto(cssfile, 1) }}" type="text/css" />
{% endfor %}
{%- block linktags %} {%- block linktags %}
{%- if hasdoc('about') %} {%- if hasdoc('about') %}
<link rel="author" title="{{ _('About these documents') }}" href="{{ pathto('about') }}" /> <link rel="author" title="{{ _('About these documents') }}"
href="{{ pathto('about') }}"/>
{%- endif %} {%- endif %}
{%- if hasdoc('genindex') %} {%- if hasdoc('genindex') %}
<link rel="index" title="{{ _('Index') }}" href="{{ pathto('genindex') }}" /> <link rel="index" title="{{ _('Index') }}"
href="{{ pathto('genindex') }}"/>
{%- endif %} {%- endif %}
{%- if hasdoc('search') %} {%- if hasdoc('search') %}
<link rel="search" title="{{ _('Search') }}" href="{{ pathto('search') }}" /> <link rel="search" title="{{ _('Search') }}" href="{{ pathto('search') }}"/>
{%- endif %} {%- endif %}
{%- if hasdoc('copyright') %} {%- if hasdoc('copyright') %}
<link rel="copyright" title="{{ _('Copyright') }}" href="{{ pathto('copyright') }}" /> <link rel="copyright" title="{{ _('Copyright') }}" href="{{ pathto('copyright') }}"/>
{%- endif %}
<link rel="top" title="{{ docstitle|e }}" href="{{ pathto('index') }}"/>
{%- if parents %}
<link rel="up" title="{{ parents[-1].title|striptags|e }}" href="{{ parents[-1].link|e }}"/>
{%- endif %} {%- endif %}
{%- if next %} {%- if next %}
<link rel="next" title="{{ next.title|striptags|e }}" href="{{ next.link|e }}" /> <link rel="next" title="{{ next.title|striptags|e }}" href="{{ next.link|e }}"/>
{%- endif %} {%- endif %}
{%- if prev %} {%- if prev %}
<link rel="prev" title="{{ prev.title|striptags|e }}" href="{{ prev.link|e }}" /> <link rel="prev" title="{{ prev.title|striptags|e }}" href="{{ prev.link|e }}"/>
{%- endif %} {%- endif %}
{%- endblock %} {%- endblock %}
{%- block extrahead %} {% endblock %} {%- block extrahead %} {% endblock %}
{# Keep modernizr in head - http://modernizr.com/docs/#installing #}
<script src="{{ pathto('_static/js/modernizr.min.js', 1) }}"></script>
</head> </head>
<body class="wy-body-for-nav" role="document"> <body class="wy-body-for-nav" role="document">
@@ -107,14 +92,16 @@
<div class="wy-side-nav-search"> <div class="wy-side-nav-search">
{% block sidebartitle %} {% block sidebartitle %}
{# the logo helper function was removed in Sphinx 6 and deprecated since Sphinx 4 #} {% if logo and theme_logo_only %}
{# the master_doc variable was renamed to root_doc in Sphinx 4 (master_doc still exists in later Sphinx versions) #} <a href="{{ pathto('index') }}">
{%- set _logo_url = logo_url|default(pathto('_static/' + (logo or ""), 1)) %} {% else %}
{%- set _root_doc = root_doc|default(master_doc) %} <a href="{{ pathto('index') }}" class="icon icon-home"> {{ project }}
<a href="{{ pathto(_root_doc) }}"{% if not theme_logo_only %} class="icon icon-home"{% endif %}> {% endif %}
{%- if logo or logo_url %}
<img src="{{ _logo_url }}" class="logo" alt="{{ _('Logo') }}"/> {% if logo %}
{%- endif %} {# Not strictly valid HTML, but it's the only way to display/scale it properly, without weird scripting or heaps of work #}
<img src="{{ pathto('_static/' + logo, 1) }}" class="logo" />
{% endif %}
</a> </a>
{% include "searchbox.html" %} {% include "searchbox.html" %}

View File

@@ -5,37 +5,31 @@
Template for the search page. Template for the search page.
:copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS. :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see https://github.com/sphinx-doc/sphinx/blob/master/LICENSE for details. :license: BSD, see LICENSE for details.
#} #}
{%- extends "layout.html" %} {%- extends "layout.html" %}
{% set title = _('Search') %} {% set title = _('Search') %}
{% set display_vcs_links = False %} {% set script_files = script_files + ['_static/searchtools.js'] %}
{%- block scripts %}
{{ super() }}
<script src="{{ pathto('_static/searchtools.js', 1) }}"></script>
<script src="{{ pathto('_static/language_data.js', 1) }}"></script>
{%- endblock %}
{% block footer %} {% block footer %}
<script> <script type="text/javascript">
jQuery(function() { Search.loadIndex("{{ pathto('searchindex.js', 1) }}"); }); jQuery(function() { Search.loadIndex("{{ pathto('searchindex.js', 1) }}"); });
</script> </script>
{# this is used when loading the search index using $.ajax fails, {# this is used when loading the search index using $.ajax fails,
such as on Chrome for documents on localhost #} such as on Chrome for documents on localhost #}
<script id="searchindexloader"></script> <script type="text/javascript" id="searchindexloader"></script>
{{ super() }} {{ super() }}
{% endblock %} {% endblock %}
{% block body %} {% block body %}
<noscript> <noscript>
<div id="fallback" class="admonition warning"> <div id="fallback" class="admonition warning">
<p class="last"> <p class="last">
{% trans trimmed %}Please activate JavaScript to enable the search {% trans %}Please activate JavaScript to enable the search
functionality.{% endtrans %} functionality.{% endtrans %}
</p> </p>
</div> </div>
</noscript> </noscript>
{% if search_performed %} {% if search_performed %}
{# Translators: Search is a noun, not a verb #}
<h2>{{ _('Search Results') }}</h2> <h2>{{ _('Search Results') }}</h2>
{% if not search_results %} {% if not search_results %}
<p>{{ _('Your search did not match any documents. Please make sure that all words are spelled correctly and that you\'ve selected enough categories.') }}</p> <p>{{ _('Your search did not match any documents. Please make sure that all words are spelled correctly and that you\'ve selected enough categories.') }}</p>

View File

@@ -2,7 +2,7 @@
.. _`config`: .. _`config`:
.. spelling:word-list:: Galera .. spelling:: Galera
Configuration file Configuration file
================== ==================
@@ -84,7 +84,7 @@ Example::
Enables or disables the "keep me logged in" button. Defaults to ``on``. Enables or disables the "keep me logged in" button. Defaults to ``on``.
``ecb_rates`` ``ecb_rates``
By default, pretix periodically downloads currency rates from the European Central Bank as well as other authorities By default, pretix periodically downloads a XML file from the European Central Bank to retrieve exchange rates
that are used to print tax amounts in the customer currency on invoices for some currencies. Set to ``off`` to that are used to print tax amounts in the customer currency on invoices for some currencies. Set to ``off`` to
disable this feature. Defaults to ``on``. disable this feature. Defaults to ``on``.
@@ -106,11 +106,6 @@ Example::
proxy that actively removes and re-adds the header to make sure the correct value is set. proxy that actively removes and re-adds the header to make sure the correct value is set.
Defaults to ``off``. Defaults to ``off``.
``trust_x_forwarded_host``
Specifies whether the ``X-Forwarded-Host`` header can be trusted. Only set to ``on`` if you have a reverse
proxy that actively removes and re-adds the header to make sure the correct value is set.
Defaults to ``off``.
``csp_log`` ``csp_log``
Log violations of the Content Security Policy (CSP). Defaults to ``on``. Log violations of the Content Security Policy (CSP). Defaults to ``on``.
@@ -481,18 +476,3 @@ You can configure the maximum file size for uploading various files::
; Max upload size for other files in MiB, defaults to 10 MiB ; Max upload size for other files in MiB, defaults to 10 MiB
; This includes all file upload type order questions ; This includes all file upload type order questions
max_size_other = 100 max_size_other = 100
GeoIP
-----
pretix can optionally make use of a GeoIP database for some features. It needs a file in ``mmdb`` format, for example
`GeoLite2`_ or `GeoAcumen`_::
[geoip]
path=/var/geoipdata/
filename_country=GeoLite2-Country.mmdb
.. _GeoAcumen: https://github.com/geoacumen/geoacumen-country
.. _GeoLite2: https://dev.maxmind.com/geoip/geolite2-free-geolocation-data

View File

@@ -45,8 +45,8 @@ Here is the currently recommended set of commands::
CREATE INDEX CONCURRENTLY pretix_addidx_order_comment CREATE INDEX CONCURRENTLY pretix_addidx_order_comment
ON pretixbase_order ON pretixbase_order
USING gin (upper("comment") gin_trgm_ops); USING gin (upper("comment") gin_trgm_ops);
CREATE INDEX CONCURRENTLY pretix_addidx_order_event_date_id CREATE INDEX CONCURRENTLY pretix_addidx_order_event_date
ON public.pretixbase_order (event_id, datetime, id); ON public.pretixbase_order (event_id, datetime);
CREATE INDEX CONCURRENTLY pretix_addidx_orderpos_name CREATE INDEX CONCURRENTLY pretix_addidx_orderpos_name
ON pretixbase_orderposition ON pretixbase_orderposition
USING gin (upper("attendee_name_cached") gin_trgm_ops); USING gin (upper("attendee_name_cached") gin_trgm_ops);
@@ -66,10 +66,10 @@ Here is the currently recommended set of commands::
ON public.pretixbase_orderposition (upper((attendee_email)::text)); ON public.pretixbase_orderposition (upper((attendee_email)::text));
CREATE INDEX CONCURRENTLY pretix_addidx_voucher_code_upper CREATE INDEX CONCURRENTLY pretix_addidx_voucher_code_upper
ON public.pretixbase_voucher (upper((code)::text)); ON public.pretixbase_voucher (upper((code)::text));
CREATE INDEX CONCURRENTLY pretix_addidx_logentry_event_date_id CREATE INDEX CONCURRENTLY pretix_addidx_logentry_event_date
ON public.pretixbase_logentry (event_id, datetime, id); ON public.pretixbase_logentry (event_id, datetime);
CREATE INDEX CONCURRENTLY pretix_addidx_logentry_event_cid_date_id CREATE INDEX CONCURRENTLY pretix_addidx_logentry_event_cid_date
ON public.pretixbase_logentry (event_id, content_type_id, datetime, id); ON public.pretixbase_logentry (event_id, content_type_id, datetime);
Also, if you use our ``pretix-shipping`` plugin:: Also, if you use our ``pretix-shipping`` plugin::

View File

@@ -16,7 +16,7 @@ Manual installation
You can use ``pip`` to update pretix directly to the development branch. Then, upgrade as usual:: You can use ``pip`` to update pretix directly to the development branch. Then, upgrade as usual::
$ source /var/pretix/venv/bin/activate $ source /var/pretix/venv/bin/activate
(venv)$ pip3 install -U "git+https://github.com/pretix/pretix.git#egg=pretix" (venv)$ pip3 install -U "git+https://github.com/pretix/pretix.git#egg=pretix&subdirectory=src"
(venv)$ python -m pretix migrate (venv)$ python -m pretix migrate
(venv)$ python -m pretix rebuild (venv)$ python -m pretix rebuild
(venv)$ python -m pretix updatestyles (venv)$ python -m pretix updatestyles

View File

@@ -1,6 +1,6 @@
.. highlight:: ini .. highlight:: ini
.. spelling:word-list:: SQL .. spelling:: SQL
General remarks General remarks
=============== ===============

View File

@@ -23,7 +23,7 @@ installation guides):
* A SMTP server to send out mails, e.g. `Postfix`_ on your machine or some third-party server you have credentials for * A SMTP server to send out mails, e.g. `Postfix`_ on your machine or some third-party server you have credentials for
* A HTTP reverse proxy, e.g. `nginx`_ or Apache to allow HTTPS connections * A HTTP reverse proxy, e.g. `nginx`_ or Apache to allow HTTPS connections
* A `PostgreSQL`_ 11+ database server * A `PostgreSQL`_ 9.6+ database server
* A `redis`_ server * A `redis`_ server
* A `nodejs`_ installation * A `nodejs`_ installation
@@ -127,7 +127,7 @@ We now install pretix, its direct dependencies and gunicorn::
(venv)$ pip3 install pretix gunicorn (venv)$ pip3 install pretix gunicorn
Note that you need Python 3.9 or newer. You can find out your Python version using ``python -V``. Note that you need Python 3.7 or newer. You can find out your Python version using ``python -V``.
We also need to create a data directory:: We also need to create a data directory::

View File

@@ -51,7 +51,7 @@ For our standard docker installation, create the database and user like this::
# sudo -u postgres createuser -P pretix # sudo -u postgres createuser -P pretix
# sudo -u postgres createdb -O pretix pretix # sudo -u postgres createdb -O pretix pretix
Make sure that your database listens on the network. If PostgreSQL on the same same host as docker, but not inside a docker container, we recommend that you listen on the Docker interface by changing the following line in ``/etc/postgresql/<version>/main/postgresql.conf``:: Make sure that your database listens on the network. If PostgreSQL on the same same host as docker, but not inside a docker container, we recommend that you just listen on the Docker interface by changing the following line in ``/etc/postgresql/<version>/main/postgresql.conf``::
listen_addresses = 'localhost,172.17.0.1' listen_addresses = 'localhost,172.17.0.1'
@@ -104,12 +104,6 @@ Install ``pgloader``::
# apt install pgloader # apt install pgloader
.. note::
If you are using Ubuntu 20.04, the ``pgloader`` version from the repositories seems to be incompatible with PostgreSQL
12+. You can install ``pgloader`` from the `PostgreSQL repositories`_ instead.
See also `this discussion <https://github.com/pretix/pretix/issues/3090>`_.
Create a new file ``/tmp/pretix.load``, replacing the MySQL and PostgreSQL connection strings with the correct user names, passwords, and/or database names:: Create a new file ``/tmp/pretix.load``, replacing the MySQL and PostgreSQL connection strings with the correct user names, passwords, and/or database names::
LOAD DATABASE LOAD DATABASE
@@ -152,90 +146,3 @@ Now, restart pretix. Maybe stop your MySQL server as a verification step that yo
And you're done! After you've verified everything has been copied correctly, you can delete the old MySQL database. And you're done! After you've verified everything has been copied correctly, you can delete the old MySQL database.
.. note:: Don't forget to update your backup process to back up your PostgreSQL database instead of your MySQL database now. .. note:: Don't forget to update your backup process to back up your PostgreSQL database instead of your MySQL database now.
Troubleshooting
---------------
Peer authentication failed
""""""""""""""""""""""""""
Sometimes you might see an error message like this::
django.db.utils.OperationalError: connection to server on socket "/var/run/postgresql/.s.PGSQL.5432" failed: FATAL: Peer authentication failed for user "pretix"
It is important to understand that PostgreSQL by default offers two types of authentication:
- **Peer authentication**, which works automatically based on the Linux user you are working as. This requires that
the connection is made through a local socket (empty ``host=`` in ``pretix.cfg``) and the name of the PostgreSQL user
and the Linux user are identical.
- Typically, you might run into this error if you accidentally execute ``python -m pretix`` commands as root instead
of the ``pretix`` user.
- **Password authentication**, which requires a username and password and works over network connections. To force
password authentication instead of peer authentication, set ``host=127.0.0.1`` in ``pretix.cfg``.
- You can alter the password on a PostgreSQL shell using the command ``ALTER USER pretix WITH PASSWORD '***';``.
When creating a user with the ``createuser`` command, pass option ``-P`` to set a new password.
- Even with password authentication, PostgreSQL by default only allows local connections. To allow remote connections,
you need to adjust both the ``listen_address`` configuration parameter as well as the ``pg_hba.conf`` file (see above
for an example with the docker networking setup).
Database error: relation does not exist
"""""""""""""""""""""""""""""""""""""""
If you see an error like this::
2023-04-17T19:20:47.744023Z ERROR Database error 42P01: relation "public.pretix_foobar" does not exist
QUERY: ALTER TABLE public.pretix_foobar DROP CONSTRAINT IF EXISTS pretix_foobar_order_id_57e2cb41_fk_pretixbas CASCADE;
2023-04-17T19:20:47.744023Z FATAL Failed to create the schema, see above.
The reason is most likely that in the past, you installed a pretix plugin that you no longer have installed. However,
the database still contains tables of that plugin. If you want to keep the data, reinstall the plugin and re-run the
``migrate`` step from above. If you want to get rid of the data, manually drop the table mentioned in the error message
from your MySQL database::
# mysql -u root pretix
mysql> DROP TABLE pretix_foobar;
Then, retry. You might see a new error message with a new table, which you can handle the same way.
Cleaning out a failed attempt
"""""""""""""""""""""""""""""
You might want to clean your PostgreSQL database before you try again after an error. You can do so like this::
# sudo -u postgres psql pretix
pretix=# DROP SCHEMA public CASCADE;
pretix=# CREATE SCHEMA public;
pretix=# ALTER SCHEMA public OWNER TO pretix;
``pgloader`` crashes with heap exhaustion error
"""""""""""""""""""""""""""""""""""""""""""""""
On some larger databases, we've seen ``pgloader`` crash with error messages similar to this::
Heap exhausted during garbage collection: 16 bytes available, 48 requested.
Or this::
2021-01-04T21:31:17.367000Z ERROR A SB-KERNEL::HEAP-EXHAUSTED-ERROR condition without bindings for heap statistics. (If
you did not expect to see this message, please report it.
2021-01-04T21:31:17.382000Z ERROR The value
NIL
is not of type
NUMBER
when binding SB-KERNEL::X
The ``pgloader`` version distributed for Debian and Ubuntu is compiled with the ``SBCL`` compiler. If compiled with
``CCL``, these bugs go away. Unfortunately, it is pretty hard to compile ``pgloader`` manually with ``CCL``. If you
run into this, we therefore recommend using the docker container provided by the ``pgloader`` maintainers::
sudo docker run --rm -v /tmp:/tmp --network host -it dimitri/pgloader:ccl.latest pgloader /tmp/pretix.load
As peer authentication is not available from inside the container, this requires you to use password-based authentication
in PostgreSQL (see above).
.. _PostgreSQL repositories: https://wiki.postgresql.org/wiki/Apt

View File

@@ -25,7 +25,7 @@ and what you should think of.
Scaling reasons Scaling reasons
--------------- ---------------
There are two main reasons for scaling up a pretix installation beyond a single server: There's mainly two reasons to scale up a pretix installation beyond a single server:
* **Availability:** Distributing pretix over multiple servers can allow you to survive failure of one or more single machines, leading to a higher uptime and reliability of your system. * **Availability:** Distributing pretix over multiple servers can allow you to survive failure of one or more single machines, leading to a higher uptime and reliability of your system.
@@ -92,7 +92,7 @@ them from a different URL <config-urls>`.
pretix-web pretix-web
"""""""""" """"""""""
The ``pretix-web`` process does not carry any internal state and can be easily started on as many machines as you like, and you can The ``pretix-web`` process does not carry any internal state can be easily started on as many machines as you like, and you can
use the load balancing features of your frontend web server to redirect to all of them. use the load balancing features of your frontend web server to redirect to all of them.
You can adjust the number of processes in the ``gunicorn`` command line, and we recommend choosing roughly two times the number You can adjust the number of processes in the ``gunicorn`` command line, and we recommend choosing roughly two times the number
@@ -154,7 +154,7 @@ files, otherwise you **will** run into errors with the user interface.
The easiest solution for this is probably to store them on a NFS server that you mount The easiest solution for this is probably to store them on a NFS server that you mount
on each of the other servers. on each of the other servers.
Since we use Django's file storage mechanism internally, you can in theory also use an object-storage solution like Amazon S3, Ceph, or Minio to store these files, although we currently do not expose this through pretix' configuration file and this would require you to ship your own variant of ``pretix/settings.py`` and reference it through the ``DJANGO_SETTINGS_MODULE`` environment variable. Since we use Django's file storage mechanism internally, you can in theory also use a object-storage solution like Amazon S3, Ceph, or Minio to store these files, although we currently do not expose this through pretix' configuration file and this would require you to ship your own variant of ``pretix/settings.py`` and reference it through the ``DJANGO_SETTINGS_MODULE`` environment variable.
At pretix.eu, we use a custom-built `object storage cluster`_. At pretix.eu, we use a custom-built `object storage cluster`_.
@@ -171,12 +171,12 @@ you configure, so make sure to set this memory usage as high as you can afford.
memory available allows your database to make more use of caching, which is usually good. memory available allows your database to make more use of caching, which is usually good.
Scaling your database to multiple machines needs to be treated with great caution. It's a Scaling your database to multiple machines needs to be treated with great caution. It's a
good idea to have a replica of your database for availability reasons. In case your primary good to have a replica of your database for availability reasons. In case your primary
database server fails, you can easily switch over to the replica and continue working. database server fails, you can easily switch over to the replica and continue working.
However, using database replicas for performance gain is much more complicated. When using However, using database replicas for performance gains is much more complicated. When using
replicated database systems, you are always trading in consistency or availability to get replicated database systems, you are always trading in consistency or availability to get
additional performance and the consequences of this can be subtle. It is important additional performance and the consequences of this can be subtle and it is important
that you have a deep understanding of the semantics of your replication mechanism. that you have a deep understanding of the semantics of your replication mechanism.
.. warning:: .. warning::
@@ -187,7 +187,7 @@ that you have a deep understanding of the semantics of your replication mechanis
As an example, if you buy a ticket, pretix first needs to calculate how many tickets As an example, if you buy a ticket, pretix first needs to calculate how many tickets
are left to sell. If this calculation is done on a database replica that lags behind are left to sell. If this calculation is done on a database replica that lags behind
even for fractions of a second, the decision to allow selling the ticket will be made even for fractions of a second, the decision to allow selling the ticket will be made
on stale data and you can end up with more tickets sold than configured. Similarly, on out-of-data data and you can end up with more tickets sold than configured. Similarly,
you could imagine situations leading to double payments etc. you could imagine situations leading to double payments etc.
If you do have a replica, you *can* tell pretix about it :ref:`in your configuration <config-replica>`. If you do have a replica, you *can* tell pretix about it :ref:`in your configuration <config-replica>`.
@@ -204,9 +204,9 @@ redis
While redis is a very important part that glues together some of the components, it isn't used While redis is a very important part that glues together some of the components, it isn't used
heavily and can usually handle a fairly large pretix installation easily on a single modern heavily and can usually handle a fairly large pretix installation easily on a single modern
CPU core. CPU core.
Having some memory available is good, e.g. if lots of tasks queue up during a traffic peak, but we wouldn't expect ever needing more than a gigabyte of it. Having some memory available is good in case of e.g. lots of tasks queuing up during a traffic peak, but we wouldn't expect ever needing more than a gigabyte of it.
Feel free to set up a redis cluster for availability but you probably won't need it for performance. Feel free to set up a redis cluster for availability but you won't need it for performance in a long time.
The limitations The limitations
--------------- ---------------
@@ -228,7 +228,7 @@ if you add more hardware.
If you have an unlimited number of tickets, we can apply fewer locking and we've reached **approx. If you have an unlimited number of tickets, we can apply fewer locking and we've reached **approx.
1500 orders per minute per event** in benchmarks, although even more should be possible. 1500 orders per minute per event** in benchmarks, although even more should be possible.
We're working on reducing the number of cases in which this is relevant and thereby improve the possible We're working to reduce the number of cases in which this is relevant and thereby improve the possible
throughput. If you want to use pretix for an event with 10,000+ tickets that are likely to be sold out throughput. If you want to use pretix for an event with 10,000+ tickets that are likely to be sold out
within minutes, please get in touch to discuss possible solutions. We'll work something out for you! within minutes, please get in touch to discuss possible solutions. We'll work something out for you!

View File

@@ -225,3 +225,4 @@ You can get three response codes:
"subevent": 23, "subevent": 23,
"checkinlist": 5 "checkinlist": 5
} }

View File

@@ -107,9 +107,9 @@ You can supply a valid access token as a ``Bearer``-type token in the ``Authoriz
.. sourcecode:: http .. sourcecode:: http
:emphasize-lines: 3 :emphasize-lines: 3
GET /api/v1/organizers/ HTTP/1.1 GET /api/v1/organizers/ HTTP/1.1
Host: pretix.eu Host: pretix.eu
Authorization: Bearer i3ytqTSRWsKp16fqjekHXa4tdM4qNC Authorization: Bearer i3ytqTSRWsKp16fqjekHXa4tdM4qNC
Refreshing an access token Refreshing an access token
-------------------------- --------------------------

View File

@@ -1,4 +1,4 @@
.. spelling:word-list:: checkin .. spelling:: checkin
.. _rest-checkin: .. _rest-checkin:
@@ -13,10 +13,6 @@ failed scans.
The endpoints listed on this page have been added. The endpoints listed on this page have been added.
.. versionchanged:: 4.18
The ``source_type`` parameter has been added.
.. _`rest-checkin-redeem`: .. _`rest-checkin-redeem`:
Checking a ticket in Checking a ticket in
@@ -32,7 +28,6 @@ Checking a ticket in
passed needs to be from a distinct event. passed needs to be from a distinct event.
:<json string secret: Scanned QR code corresponding to the ``secret`` attribute of a ticket. :<json string secret: Scanned QR code corresponding to the ``secret`` attribute of a ticket.
:<json string source_type: Type of source the ``secret`` was obtained form. Defaults to ``"barcode"``.
:<json array lists: List of check-in list IDs to search on. No two check-in lists may be from the same event. :<json array lists: List of check-in list IDs to search on. No two check-in lists may be from the same event.
:<json string type: Send ``"exit"`` for an exit and ``"entry"`` (default) for an entry. :<json string type: Send ``"exit"`` for an exit and ``"entry"`` (default) for an entry.
:<json datetime datetime: Specifies the datetime of the check-in. If not supplied, the current time will be used. :<json datetime datetime: Specifies the datetime of the check-in. If not supplied, the current time will be used.
@@ -77,7 +72,6 @@ Checking a ticket in
{ {
"secret": "M5BO19XmFwAjLd4nDYUAL9ISjhti0e9q", "secret": "M5BO19XmFwAjLd4nDYUAL9ISjhti0e9q",
"source_type": "barcode",
"lists": [1], "lists": [1],
"force": false, "force": false,
"ignore_unpaid": false, "ignore_unpaid": false,
@@ -209,8 +203,6 @@ Checking a ticket in
* ``invalid`` - Ticket is not known. * ``invalid`` - Ticket is not known.
* ``unpaid`` - Ticket is not paid for. * ``unpaid`` - Ticket is not paid for.
* ``blocked`` - Ticket has been blocked.
* ``invalid_time`` - Ticket is not valid at this time.
* ``canceled`` Ticket is canceled or expired. * ``canceled`` Ticket is canceled or expired.
* ``already_redeemed`` - Ticket already has been redeemed. * ``already_redeemed`` - Ticket already has been redeemed.
* ``product`` - Tickets with this product may not be scanned at this device. * ``product`` - Tickets with this product may not be scanned at this device.
@@ -219,8 +211,8 @@ Checking a ticket in
* ``revoked`` - Ticket code has been revoked. * ``revoked`` - Ticket code has been revoked.
* ``error`` - Internal error. * ``error`` - Internal error.
In case of reason ``rules`` and ``invalid_time``, there might be an additional response field ``reason_explanation`` In case of reason ``rules``, there might be an additional response field ``reason_explanation`` with a human-readable
with a human-readable description of the violated rules. However, that field can also be missing or be ``null``. description of the violated rules. However, that field can also be missing or be ``null``.
:param organizer: The ``slug`` field of the organizer to fetch :param organizer: The ``slug`` field of the organizer to fetch
:statuscode 201: no error :statuscode 201: no error

View File

@@ -1,4 +1,4 @@
.. spelling:word-list:: checkin .. spelling:: checkin
.. _rest-checkinlists: .. _rest-checkinlists:
@@ -364,7 +364,7 @@ Endpoints
Stores a failed check-in. Only necessary for statistical purposes if you perform scan validation offline. Stores a failed check-in. Only necessary for statistical purposes if you perform scan validation offline.
:<json boolean error_reason: One of ``canceled``, ``invalid``, ``unpaid``, ``product``, ``rules``, ``revoked``, :<json boolean error_reason: One of ``canceled``, ``invalid``, ``unpaid``, ``product``, ``rules``, ``revoked``,
``incomplete``, ``already_redeemed``, ``blocked``, ``invalid_time``, or ``error``. Required. ``incomplete``, ``already_redeemed``, or ``error``. Required.
:<json raw_barcode: The raw barcode you scanned. Required. :<json raw_barcode: The raw barcode you scanned. Required.
:<json datetime: Date and time of the scan. Optional. :<json datetime: Date and time of the scan. Optional.
:<json type: Type of scan, defaults to ``"entry"``. :<json type: Type of scan, defaults to ``"entry"``.
@@ -743,8 +743,6 @@ Order position endpoints
* ``invalid`` - Ticket code not known. * ``invalid`` - Ticket code not known.
* ``unpaid`` - Ticket is not paid for. * ``unpaid`` - Ticket is not paid for.
* ``blocked`` - Ticket has been blocked.
* ``invalid_time`` - Ticket is not valid at this time.
* ``canceled`` Ticket is canceled or expired. This reason is only sent when your request sets. * ``canceled`` Ticket is canceled or expired. This reason is only sent when your request sets.
``canceled_supported`` to ``true``, otherwise these orders return ``unpaid``. ``canceled_supported`` to ``true``, otherwise these orders return ``unpaid``.
* ``already_redeemed`` - Ticket already has been redeemed. * ``already_redeemed`` - Ticket already has been redeemed.
@@ -753,8 +751,8 @@ Order position endpoints
* ``ambiguous`` - Multiple tickets match scan, rejected. * ``ambiguous`` - Multiple tickets match scan, rejected.
* ``revoked`` - Ticket code has been revoked. * ``revoked`` - Ticket code has been revoked.
In case of reason ``rules`` or ``invalid_time``, there might be an additional response field ``reason_explanation`` In case of reason ``rules``, there might be an additional response field ``reason_explanation`` with a human-readable
with a human-readable description of the violated rules. However, that field can also be missing or be ``null``. description of the violated rules. However, that field can also be missing or be ``null``.
:param organizer: The ``slug`` field of the organizer to fetch :param organizer: The ``slug`` field of the organizer to fetch
:param event: The ``slug`` field of the event to fetch :param event: The ``slug`` field of the event to fetch

View File

@@ -1,4 +1,4 @@
.. spelling:word-list:: fullname .. spelling:: fullname
.. _`rest-devices`: .. _`rest-devices`:

View File

@@ -1,4 +1,4 @@
.. spelling:word-list:: .. spelling::
geo geo
lat lat
@@ -49,7 +49,6 @@ valid_keys object Cryptographic k
only contained in detail views. Value can be cached. only contained in detail views. Value can be cached.
sales_channels list A list of sales channels this event is available for sales_channels list A list of sales channels this event is available for
sale on. sale on.
public_url string The public, customer-facing URL of the event (read-only).
===================================== ========================== ======================================================= ===================================== ========================== =======================================================
@@ -66,10 +65,6 @@ Endpoints
The ``search`` query parameter has been added to filter events by their slug, name, or location in any language. The ``search`` query parameter has been added to filter events by their slug, name, or location in any language.
.. versionchanged:: 4.17
The ``public_url`` field has been added.
.. http:get:: /api/v1/organizers/(organizer)/events/ .. http:get:: /api/v1/organizers/(organizer)/events/
Returns a list of all events within a given organizer the authenticated user/token has access to. Returns a list of all events within a given organizer the authenticated user/token has access to.
@@ -128,8 +123,7 @@ Endpoints
"web", "web",
"pretixpos", "pretixpos",
"resellers" "resellers"
], ]
"public_url": "https://pretix.eu/bigevents/sampleconf/"
} }
] ]
} }
@@ -137,7 +131,6 @@ Endpoints
:query page: The page number in case of a multi-page result set, default is 1 :query page: The page number in case of a multi-page result set, default is 1
:query is_public: If set to ``true``/``false``, only events with a matching value of ``is_public`` are returned. :query is_public: If set to ``true``/``false``, only events with a matching value of ``is_public`` are returned.
:query live: If set to ``true``/``false``, only events with a matching value of ``live`` are returned. :query live: If set to ``true``/``false``, only events with a matching value of ``live`` are returned.
:query testmode: If set to ``true``/``false``, only events with a matching value of ``testmode`` are returned.
:query has_subevents: If set to ``true``/``false``, only events with a matching value of ``has_subevents`` are returned. :query has_subevents: If set to ``true``/``false``, only events with a matching value of ``has_subevents`` are returned.
:query is_future: If set to ``true`` (``false``), only events that happen currently or in the future are (not) returned. Event series are never (always) returned. :query is_future: If set to ``true`` (``false``), only events that happen currently or in the future are (not) returned. Event series are never (always) returned.
:query is_past: If set to ``true`` (``false``), only events that are over are (not) returned. Event series are never (always) returned. :query is_past: If set to ``true`` (``false``), only events that are over are (not) returned. Event series are never (always) returned.
@@ -218,8 +211,7 @@ Endpoints
"web", "web",
"pretixpos", "pretixpos",
"resellers" "resellers"
], ]
"public_url": "https://pretix.eu/bigevents/sampleconf/"
} }
:param organizer: The ``slug`` field of the organizer to fetch :param organizer: The ``slug`` field of the organizer to fetch
@@ -315,8 +307,7 @@ Endpoints
"web", "web",
"pretixpos", "pretixpos",
"resellers" "resellers"
], ]
"public_url": "https://pretix.eu/bigevents/sampleconf/"
} }
:param organizer: The ``slug`` field of the organizer of the event to create. :param organizer: The ``slug`` field of the organizer of the event to create.
@@ -420,8 +411,7 @@ Endpoints
"web", "web",
"pretixpos", "pretixpos",
"resellers" "resellers"
], ]
"public_url": "https://pretix.eu/bigevents/sampleconf/"
} }
:param organizer: The ``slug`` field of the organizer of the event to create. :param organizer: The ``slug`` field of the organizer of the event to create.
@@ -495,8 +485,7 @@ Endpoints
"web", "web",
"pretixpos", "pretixpos",
"resellers" "resellers"
], ]
"public_url": "https://pretix.eu/bigevents/sampleconf/"
} }
:param organizer: The ``slug`` field of the organizer of the event to update :param organizer: The ``slug`` field of the organizer of the event to update
@@ -547,9 +536,6 @@ Therefore, we're also not including a list of the options here, but instead reco
to see available options. The ``explain=true`` flag enables a verbose mode that provides you with human-readable to see available options. The ``explain=true`` flag enables a verbose mode that provides you with human-readable
information about the properties. information about the properties.
Note that some settings are read-only, e.g. because they can be read on event level but currently only be changed on
organizer level.
.. note:: Please note that this is not a complete representation of all event settings. You will find more settings .. note:: Please note that this is not a complete representation of all event settings. You will find more settings
in the web interface. in the web interface.
@@ -596,7 +582,6 @@ organizer level.
{ {
"value": "https://pretix.eu", "value": "https://pretix.eu",
"label": "Imprint URL", "label": "Imprint URL",
"readonly": false,
"help_text": "This should point e.g. to a part of your website that has your contact details and legal information." "help_text": "This should point e.g. to a part of your website that has your contact details and legal information."
} }
}, },
@@ -610,10 +595,6 @@ organizer level.
:statuscode 401: Authentication failure :statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to view this resource. :statuscode 403: The requested organizer/event does not exist **or** you have no permission to view this resource.
.. versionchanged:: 4.18
The ``readonly`` flag has been added.
.. http:patch:: /api/v1/organizers/(organizer)/events/(event)/settings/ .. http:patch:: /api/v1/organizers/(organizer)/events/(event)/settings/
Updates event settings. Note that ``PUT`` is not allowed here, only ``PATCH``. Updates event settings. Note that ``PUT`` is not allowed here, only ``PATCH``.

View File

@@ -1,4 +1,4 @@
.. spelling:word-list:: checkin .. spelling:: checkin
Data exporters Data exporters
============== ==============

View File

@@ -20,12 +20,6 @@ currency string Currency of the
testmode boolean Whether this is a test gift card testmode boolean Whether this is a test gift card
expires datetime Expiry date (or ``null``) expires datetime Expiry date (or ``null``)
conditions string Special terms and conditions for this card (or ``null``) conditions string Special terms and conditions for this card (or ``null``)
owner_ticket integer Internal ID of an order position that is the "owner" of
this gift card and can view all transactions. When setting
this field, you can also give the ``secret`` of an order
position.
issuer string Organizer slug of the organizer who created this gift
card and is responsible for it.
===================================== ========================== ======================================================= ===================================== ========================== =======================================================
The gift card transaction resource contains the following public fields: The gift card transaction resource contains the following public fields:
@@ -41,17 +35,8 @@ value money (string) Transaction amo
event string Event slug, if the gift card was used in the web shop (or ``null``) event string Event slug, if the gift card was used in the web shop (or ``null``)
order string Order code, if the gift card was used in the web shop (or ``null``) order string Order code, if the gift card was used in the web shop (or ``null``)
text string Custom text of the transaction (or ``null``) text string Custom text of the transaction (or ``null``)
info object Additional data about the transaction (or ``null``)
acceptor string Organizer slug of the organizer who created this transaction
(can be ``null`` for all transactions performed before
this field was added.)
===================================== ========================== ======================================================= ===================================== ========================== =======================================================
.. versionchanged:: 4.20
The ``owner_ticket`` and ``issuer`` attributes of the gift card and the ``info`` and ``acceptor`` attributes of the
gift card transaction resource have been added.
Endpoints Endpoints
--------- ---------
@@ -87,8 +72,6 @@ Endpoints
"testmode": false, "testmode": false,
"expires": null, "expires": null,
"conditions": null, "conditions": null,
"owner_ticket": null,
"issuer": "bigevents",
"value": "13.37" "value": "13.37"
} }
] ]
@@ -98,10 +81,6 @@ Endpoints
:query string secret: Only show gift cards with the given secret. :query string secret: Only show gift cards with the given secret.
:query boolean testmode: Filter for gift cards that are (not) in test mode. :query boolean testmode: Filter for gift cards that are (not) in test mode.
:query boolean include_accepted: Also show gift cards issued by other organizers that are accepted by this organizer. :query boolean include_accepted: Also show gift cards issued by other organizers that are accepted by this organizer.
:query string expand: If you pass ``"owner_ticket"``, the respective field will be shown as a nested value instead of just an ID.
The nested objects are identical to the respective resources, except that the ``owner_ticket``
will have an attribute of the format ``"order": {"code": "ABCDE", "event": "eventslug"}`` to make
matching easier. The parameter can be given multiple times.
:param organizer: The ``slug`` field of the organizer to fetch :param organizer: The ``slug`` field of the organizer to fetch
:statuscode 200: no error :statuscode 200: no error
:statuscode 401: Authentication failure :statuscode 401: Authentication failure
@@ -134,8 +113,6 @@ Endpoints
"testmode": false, "testmode": false,
"expires": null, "expires": null,
"conditions": null, "conditions": null,
"owner_ticket": null,
"issuer": "bigevents",
"value": "13.37" "value": "13.37"
} }
@@ -180,16 +157,10 @@ Endpoints
"currency": "EUR", "currency": "EUR",
"expires": null, "expires": null,
"conditions": null, "conditions": null,
"owner_ticket": null,
"issuer": "bigevents",
"value": "13.37" "value": "13.37"
} }
:param organizer: The ``slug`` field of the organizer to create a gift card for :param organizer: The ``slug`` field of the organizer to create a gift card for
:query string expand: If you pass ``"owner_ticket"``, the respective field will be shown as a nested value instead of just an ID.
The nested objects are identical to the respective resources, except that the ``owner_ticket``
will have an attribute of the format ``"order": {"code": "ABCDE", "event": "eventslug"}`` to make
matching easier. The parameter can be given multiple times.
:statuscode 201: no error :statuscode 201: no error
:statuscode 400: The gift card could not be created due to invalid submitted data. :statuscode 400: The gift card could not be created due to invalid submitted data.
:statuscode 401: Authentication failure :statuscode 401: Authentication failure
@@ -234,8 +205,6 @@ Endpoints
"currency": "EUR", "currency": "EUR",
"expires": null, "expires": null,
"conditions": null, "conditions": null,
"owner_ticket": null,
"issuer": "bigevents",
"value": "14.00" "value": "14.00"
} }
@@ -281,8 +250,6 @@ Endpoints
"testmode": false, "testmode": false,
"expires": null, "expires": null,
"conditions": null, "conditions": null,
"owner_ticket": null,
"issuer": "bigevents",
"value": "15.37" "value": "15.37"
} }
@@ -326,11 +293,7 @@ Endpoints
"value": "50.00", "value": "50.00",
"event": "democon", "event": "democon",
"order": "FXQYW", "order": "FXQYW",
"text": null, "text": null
"acceptor": "bigevents",
"info": {
"created_by": "plugin1"
}
} }
] ]
} }

View File

@@ -18,7 +18,6 @@ at :ref:`plugin-docs`.
item_variations item_variations
item_bundles item_bundles
item_add-ons item_add-ons
item_meta_properties
questions questions
question_options question_options
quotas quotas
@@ -33,7 +32,6 @@ at :ref:`plugin-docs`.
membershiptypes membershiptypes
memberships memberships
giftcards giftcards
reusablemedia
carts carts
teams teams
devices devices

View File

@@ -42,7 +42,6 @@ introductory_text string Text to be prin
additional_text string Text to be printed below the product list additional_text string Text to be printed below the product list
payment_provider_text string Text to be printed below the product list with payment_provider_text string Text to be printed below the product list with
payment information payment information
payment_provider_stamp string Short text to be visibly printed to indicate payment status
footer_text string Text to be printed in the page footer area footer_text string Text to be printed in the page footer area
lines list of objects The actual invoice contents lines list of objects The actual invoice contents
├ position integer Number of the line within an invoice. ├ position integer Number of the line within an invoice.
@@ -179,7 +178,6 @@ Endpoints
"internal_reference": "", "internal_reference": "",
"additional_text": "We are looking forward to see you on our conference!", "additional_text": "We are looking forward to see you on our conference!",
"payment_provider_text": "Please transfer the money to our account ABC…", "payment_provider_text": "Please transfer the money to our account ABC…",
"payment_provider_stamp": null,
"footer_text": "Big Events LLC - Registration No. 123456 - VAT ID: EU0987654321", "footer_text": "Big Events LLC - Registration No. 123456 - VAT ID: EU0987654321",
"lines": [ "lines": [
{ {
@@ -270,7 +268,6 @@ Endpoints
"internal_reference": "", "internal_reference": "",
"additional_text": "We are looking forward to see you on our conference!", "additional_text": "We are looking forward to see you on our conference!",
"payment_provider_text": "Please transfer the money to our account ABC…", "payment_provider_text": "Please transfer the money to our account ABC…",
"payment_provider_stamp": null,
"footer_text": "Big Events LLC - Registration No. 123456 - VAT ID: EU0987654321", "footer_text": "Big Events LLC - Registration No. 123456 - VAT ID: EU0987654321",
"lines": [ "lines": [
{ {

View File

@@ -1,211 +0,0 @@
Item Meta Properties
====================
Resource description
--------------------
An Item Meta Property is used to include (event internally relevant) meta information with every item (product). This
could be internal categories like booking positions.
The Item Meta Properties resource contains the following public fields:
.. rst-class:: rest-resource-table
===================================== ========================== =======================================================
Field Type Description
===================================== ========================== =======================================================
id integer Unique ID for this property
name string Name of the property
default string Value of the default option
required boolean If ``true``, this property will have to be assigned a
value in all items of the related event
allowed_values list List of all permitted values for this property,
or ``null`` for no limitation
===================================== ========================== =======================================================
Endpoints
---------
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/item_meta_properties/
Returns a list of all Item Meta Properties within a given event.
**Example request**:
.. sourcecode:: http
GET /api/v1/organizers/bigevents/events/sampleconf/item_meta_properties/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
{
"count": 1,
"next": null,
"previous": null,
"results": [
{
"id": 1,
"name": "Color",
"default": "red",
"required": true,
"allowed_values": ["red", "green", "blue"]
}
]
}
:param organizer: The ``slug`` field of the organizer
:param event: The ``slug`` field of the event
:statuscode 200: no error
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer does not exist **or** you have no permission to view this resource.
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/item_meta_properties/(id)/
Returns information on one property, identified by its id.
**Example request**:
.. sourcecode:: http
GET /api/v1/organizers/bigevents/events/sampleconf/item_meta_properties/1/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
{
"id": 1,
"name": "Color",
"default": "red",
"required": true,
"allowed_values": ["red", "green", "blue"]
}
:param organizer: The ``slug`` field of the organizer
:param event: The ``slug`` field of the event
:param id: The ``id`` field of the item meta property to retrieve
:statuscode 200: no error
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer does not exist **or** you have no permission to view this resource.
.. http:post:: /api/v1/organizers/(organizer)/events/(event)/item_meta_properties/
Creates a new item meta property
**Example request**:
.. sourcecode:: http
POST /api/v1/organizers/bigevents/events/sampleconf/item_meta_properties/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content-Type: application/json
{
"name": "ref-code",
"default": "abcde",
"required": true,
"allowed_values": null
}
**Example response**:
.. sourcecode:: http
{
"id": 2,
"name": "ref-code",
"default": "abcde",
"required": true,
"allowed_values": null
}
:param organizer: The ``slug`` field of the organizer
:param event: The ``slug`` field of the event
:statuscode 201: no error
:statuscode 400: The item meta property could not be created due to invalid submitted data.
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer does not exist **or** you have no permission to create this resource.
.. http:patch:: /api/v1/organizers/(organizer)/events/(event)/item_meta_properties/(id)/
Update an item meta property. You can also use ``PUT`` instead of ``PATCH``. With ``PUT``, you have to provide
all fields of the resource, other fields will be reset to default. With ``PATCH``, you only need to provide the
fields that you want to change.
You can change all fields of the resource except the ``id`` field.
**Example request**:
.. sourcecode:: http
PATCH /api/v1/organizers/bigevents/events/sampleconf/item_meta_properties/2/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content-Type: application/json
Content-Length: 94
{
"required": false
}
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
{
"id": 2,
"name": "ref-code",
"default": "abcde",
"required": false,
"allowed_values": []
}
:param organizer: The ``slug`` field of the organizer
:param event: The ``slug`` field of the event
:param id: The ``id`` field of the item meta property to modify
:statuscode 200: no error
:statuscode 400: The property could not be modified due to invalid submitted data
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer does not exist **or** you have no permission to change this resource.
.. http:delete:: /api/v1/organizers/(organizer)/events/(event)/item_meta_properties/(id)/
Delete an item meta property.
**Example request**:
.. sourcecode:: http
DELETE /api/v1/organizers/bigevents/events/sampleconf/item_meta_properties/1/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 204 No Content
Vary: Accept
:param organizer: The ``slug`` field of the organizer
:param event: The ``slug`` field of the event
:param id: The ``id`` field of the item meta property to delete
:statuscode 204: no error
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer does not exist **or** you have no permission to delete this resource.

View File

@@ -24,9 +24,6 @@ active boolean If ``false``, t
description multi-lingual string A public description of the variation. May contain description multi-lingual string A public description of the variation. May contain
Markdown syntax or can be ``null``. Markdown syntax or can be ``null``.
position integer An integer, used for sorting position integer An integer, used for sorting
checkin_attention boolean If ``true``, the check-in app should show a warning
that this ticket requires special attention if such
a variation is being scanned.
require_approval boolean If ``true``, orders with this variation will need to be require_approval boolean If ``true``, orders with this variation will need to be
approved by the event organizer before they can be approved by the event organizer before they can be
paid. paid.
@@ -51,7 +48,7 @@ meta_data object Values set for
.. versionchanged:: 4.16 .. versionchanged:: 4.16
The ``meta_data`` and ``checkin_attention`` attributes have been added. The ``meta_data`` attribute has been added.
Endpoints Endpoints
--------- ---------
@@ -87,7 +84,6 @@ Endpoints
"en": "S" "en": "S"
}, },
"active": true, "active": true,
"checkin_attention": false,
"require_approval": false, "require_approval": false,
"require_membership": false, "require_membership": false,
"require_membership_hidden": false, "require_membership_hidden": false,
@@ -111,7 +107,6 @@ Endpoints
"en": "L" "en": "L"
}, },
"active": true, "active": true,
"checkin_attention": false,
"require_approval": false, "require_approval": false,
"require_membership": false, "require_membership": false,
"require_membership_hidden": false, "require_membership_hidden": false,
@@ -164,7 +159,6 @@ Endpoints
"price": "10.00", "price": "10.00",
"original_price": null, "original_price": null,
"active": true, "active": true,
"checkin_attention": false,
"require_approval": false, "require_approval": false,
"require_membership": false, "require_membership": false,
"require_membership_hidden": false, "require_membership_hidden": false,
@@ -203,7 +197,6 @@ Endpoints
"value": {"en": "Student"}, "value": {"en": "Student"},
"default_price": "10.00", "default_price": "10.00",
"active": true, "active": true,
"checkin_attention": false,
"require_approval": false, "require_approval": false,
"require_membership": false, "require_membership": false,
"require_membership_hidden": false, "require_membership_hidden": false,
@@ -232,7 +225,6 @@ Endpoints
"price": "10.00", "price": "10.00",
"original_price": null, "original_price": null,
"active": true, "active": true,
"checkin_attention": false,
"require_approval": false, "require_approval": false,
"require_membership": false, "require_membership": false,
"require_membership_hidden": false, "require_membership_hidden": false,
@@ -292,7 +284,6 @@ Endpoints
"price": "10.00", "price": "10.00",
"original_price": null, "original_price": null,
"active": false, "active": false,
"checkin_attention": false,
"require_approval": false, "require_approval": false,
"require_membership": false, "require_membership": false,
"require_membership_hidden": false, "require_membership_hidden": false,

View File

@@ -11,168 +11,147 @@ The item resource contains the following public fields:
.. rst-class:: rest-resource-table .. rst-class:: rest-resource-table
======================================= ========================== ======================================================= ===================================== ========================== =======================================================
Field Type Description Field Type Description
======================================= ========================== ======================================================= ===================================== ========================== =======================================================
id integer Internal ID of the item id integer Internal ID of the item
name multi-lingual string The item's visible name name multi-lingual string The item's visible name
internal_name string An optional name that is only used in the backend internal_name string An optional name that is only used in the backend
default_price money (string) The item price that is applied if the price is not default_price money (string) The item price that is applied if the price is not
overwritten by variations or other options. overwritten by variations or other options.
category integer The ID of the category this item belongs to category integer The ID of the category this item belongs to
(or ``null``). (or ``null``).
active boolean If ``false``, the item is hidden from all public lists active boolean If ``false``, the item is hidden from all public lists
and will not be sold. and will not be sold.
description multi-lingual string A public description of the item. May contain Markdown description multi-lingual string A public description of the item. May contain Markdown
syntax or can be ``null``. syntax or can be ``null``.
free_price boolean If ``true``, customers can change the price at which free_price boolean If ``true``, customers can change the price at which
they buy the product (however, the price can't be set they buy the product (however, the price can't be set
lower than the price defined by ``default_price`` or lower than the price defined by ``default_price`` or
otherwise). otherwise).
tax_rate decimal (string) The VAT rate to be applied for this item (read-only, tax_rate decimal (string) The VAT rate to be applied for this item (read-only,
set through ``tax_rule``). set through ``tax_rule``).
tax_rule integer The internal ID of the applied tax rule (or ``null``). tax_rule integer The internal ID of the applied tax rule (or ``null``).
admission boolean ``true`` for items that grant admission to the event admission boolean ``true`` for items that grant admission to the event
(such as primary tickets) and ``false`` for others (such as primary tickets) and ``false`` for others
(such as add-ons or merchandise). (such as add-ons or merchandise).
personalized boolean ``true`` for items that require personalization according personalized boolean ``true`` for items that require personalization according
to event settings. Only affects system-level fields, not to event settings. Only affects system-level fields, not
custom questions. Currently only allowed for products with custom questions. Currently only allowed for products with
``admission`` set to ``true``. For backwards compatibility, ``admission`` set to ``true``. For backwards compatibility,
when creating new items and this field is not given, it defaults when creating new items and this field is not given, it defaults
to the same value as ``admission``. to the same value as ``admission``.
position integer An integer, used for sorting position integer An integer, used for sorting
picture file A product picture to be displayed in the shop picture file A product picture to be displayed in the shop
(can be ``null``). (can be ``null``).
sales_channels list of strings Sales channels this product is available on, such as sales_channels list of strings Sales channels this product is available on, such as
``"web"`` or ``"resellers"``. Defaults to ``["web"]``. ``"web"`` or ``"resellers"``. Defaults to ``["web"]``.
available_from datetime The first date time at which this item can be bought available_from datetime The first date time at which this item can be bought
(or ``null``). (or ``null``).
available_until datetime The last date time at which this item can be bought available_until datetime The last date time at which this item can be bought
(or ``null``). (or ``null``).
hidden_if_available integer The internal ID of a quota object, or ``null``. If hidden_if_available integer The internal ID of a quota object, or ``null``. If
set, this item won't be shown publicly as long as this set, this item won't be shown publicly as long as this
quota is available. quota is available.
require_voucher boolean If ``true``, this item can only be bought using a require_voucher boolean If ``true``, this item can only be bought using a
voucher that is specifically assigned to this item. voucher that is specifically assigned to this item.
hide_without_voucher boolean If ``true``, this item is only shown during the voucher hide_without_voucher boolean If ``true``, this item is only shown during the voucher
redemption process, but not in the normal shop redemption process, but not in the normal shop
frontend. frontend.
allow_cancel boolean If ``false``, customers cannot cancel orders containing allow_cancel boolean If ``false``, customers cannot cancel orders containing
this item. this item.
min_per_order integer This product can only be bought if it is included at min_per_order integer This product can only be bought if it is included at
least this many times in the order (or ``null`` for no least this many times in the order (or ``null`` for no
limitation). limitation).
max_per_order integer This product can only be bought if it is included at max_per_order integer This product can only be bought if it is included at
most this many times in the order (or ``null`` for no most this many times in the order (or ``null`` for no
limitation). limitation).
checkin_attention boolean If ``true``, the check-in app should show a warning checkin_attention boolean If ``true``, the check-in app should show a warning
that this ticket requires special attention if such that this ticket requires special attention if such
a product is being scanned. a product is being scanned.
original_price money (string) An original price, shown for comparison, not used original_price money (string) An original price, shown for comparison, not used
for price calculations (or ``null``). for price calculations (or ``null``).
require_approval boolean If ``true``, orders with this product will need to be require_approval boolean If ``true``, orders with this product will need to be
approved by the event organizer before they can be approved by the event organizer before they can be
paid. paid.
require_bundling boolean If ``true``, this item is only available as part of bundles. require_bundling boolean If ``true``, this item is only available as part of bundles.
require_membership boolean If ``true``, booking this item requires an active membership. require_membership boolean If ``true``, booking this item requires an active membership.
require_membership_hidden boolean If ``true`` and ``require_membership`` is set, this product will require_membership_hidden boolean If ``true`` and ``require_membership`` is set, this product will
be hidden from users without a valid membership. be hidden from users without a valid membership.
require_membership_types list of integers Internal IDs of membership types valid if ``require_membership`` is ``true`` require_membership_types list of integers Internal IDs of membership types valid if ``require_membership`` is ``true``
grant_membership_type integer If set to the internal ID of a membership type, purchasing this item will grant_membership_type integer If set to the internal ID of a membership type, purchasing this item will
create a membership of the given type. create a membership of the given type.
grant_membership_duration_like_event boolean If ``true``, the membership created through ``grant_membership_type`` will derive grant_membership_duration_like_event boolean If ``true``, the membership created through ``grant_membership_type`` will derive
its term from ``date_from`` to ``date_to`` of the purchased (sub)event. its term from ``date_from`` to ``date_to`` of the purchased (sub)event.
grant_membership_duration_days integer If ``grant_membership_duration_like_event`` is ``false``, this sets the number of grant_membership_duration_days integer If ``grant_membership_duration_like_event`` is ``false``, this sets the number of
days for the membership. days for the membership.
grant_membership_duration_months integer If ``grant_membership_duration_like_event`` is ``false``, this sets the number of grant_membership_duration_months integer If ``grant_membership_duration_like_event`` is ``false``, this sets the number of
calendar months for the membership. calendar months for the membership.
validity_mode string If ``null``, tickets generated for this product do not generate_tickets boolean If ``false``, tickets are never generated for this
have special validity behavior, but follow event configuration and product, regardless of other settings. If ``true``,
can be limited e.g. through check-in rules. Other values are ``"fixed"`` and ``"dynamic"`` tickets are generated even if this is a
validity_fixed_from datetime If ``validity_mode`` is ``"fixed"``, this is the start of validity for issued tickets. non-admission or add-on product, regardless of event
validity_fixed_until datetime If ``validity_mode`` is ``"fixed"``, this is the end of validity for issued tickets. settings. If this is ``null``, regular ticketing
validity_dynamic_duration_minutes integer If ``validity_mode`` is ``"dynamic"``, this is the "minutes" component of the ticket validity duration. rules apply.
validity_dynamic_duration_hours integer If ``validity_mode`` is ``"dynamic"``, this is the "hours" component of the ticket validity duration. allow_waitinglist boolean If ``false``, no waiting list will be shown for this
validity_dynamic_duration_days integer If ``validity_mode`` is ``"dynamic"``, this is the "days" component of the ticket validity duration. product when it is sold out.
validity_dynamic_duration_months integer If ``validity_mode`` is ``"dynamic"``, this is the "months" component of the ticket validity duration. issue_giftcard boolean If ``true``, buying this product will yield a gift card.
validity_dynamic_start_choice boolean If ``validity_mode`` is ``"dynamic"`` and this is ``true``, customers can choose the start of validity. show_quota_left boolean Publicly show how many tickets are still available.
validity_dynamic_start_choice_day_limit boolean If ``validity_mode`` is ``"dynamic"`` and ``validity_dynamic_start_choice`` is ``true``, If this is ``null``, the event default is used.
this is the maximum number of days the start can be in the future. has_variations boolean Shows whether or not this item has variations.
generate_tickets boolean If ``false``, tickets are never generated for this variations list of objects A list with one object for each variation of this item.
product, regardless of other settings. If ``true``, Can be empty. Only writable during creation,
tickets are generated even if this is a use separate endpoint to modify this later.
non-admission or add-on product, regardless of event ├ id integer Internal ID of the variation
settings. If this is ``null``, regular ticketing ├ value multi-lingual string The "name" of the variation
rules apply. ├ default_price money (string) The price set directly for this variation or ``null``
allow_waitinglist boolean If ``false``, no waiting list will be shown for this ├ price money (string) The price used for this variation. This is either the
product when it is sold out. same as ``default_price`` if that value is set or equal
issue_giftcard boolean If ``true``, buying this product will yield a gift card. to the item's ``default_price``.
media_policy string Policy on how to handle reusable media (experimental feature). ├ original_price money (string) An original price, shown for comparison, not used
Possible values are ``null``, ``"new"``, ``"reuse"``, and ``"reuse_or_new"``. for price calculations (or ``null``).
media_type string Type of reusable media to work on (experimental feature). See :ref:`rest-reusablemedia` for possible choices. ├ active boolean If ``false``, this variation will not be sold or shown.
show_quota_left boolean Publicly show how many tickets are still available. ├ description multi-lingual string A public description of the variation. May contain
If this is ``null``, the event default is used. ├ require_membership boolean If ``true``, booking this variation requires an active membership.
has_variations boolean Shows whether or not this item has variations. ├ require_membership_hidden boolean If ``true`` and ``require_membership`` is set, this variation will
variations list of objects A list with one object for each variation of this item. be hidden from users without a valid membership.
Can be empty. Only writable during creation, ├ require_membership_types list of integers Internal IDs of membership types valid if ``require_membership`` is ``true``
use separate endpoint to modify this later. Markdown syntax or can be ``null``.
id integer Internal ID of the variation sales_channels list of strings Sales channels this variation is available on, such as
├ value multi-lingual string The "name" of the variation ``"web"`` or ``"resellers"``. Defaults to all existing sales channels.
├ default_price money (string) The price set directly for this variation or ``null`` The item-level list takes precedence, i.e. a sales
├ price money (string) The price used for this variation. This is either the channel needs to be on both lists for the item to be
same as ``default_price`` if that value is set or equal available.
to the item's ``default_price``. ├ available_from datetime The first date time at which this variation can be bought
├ original_price money (string) An original price, shown for comparison, not used (or ``null``).
for price calculations (or ``null``). ├ available_until datetime The last date time at which this variation can be bought
├ active boolean If ``false``, this variation will not be sold or shown. (or ``null``).
description multi-lingual string A public description of the variation. May contain hide_without_voucher boolean If ``true``, this variation is only shown during the voucher
├ checkin_attention boolean If ``true``, the check-in app should show a warning redemption process, but not in the normal shop
that this ticket requires special attention if such frontend.
a variation is being scanned. ├ meta_data object Values set for event-specific meta data parameters.
├ require_approval boolean If ``true``, orders with this variation will need to be └ position integer An integer, used for sorting
approved by the event organizer before they can be addons list of objects Definition of add-ons that can be chosen for this item.
paid. Only writable during creation,
├ require_membership boolean If ``true``, booking this variation requires an active membership. use separate endpoint to modify this later.
require_membership_hidden boolean If ``true`` and ``require_membership`` is set, this variation will addon_category integer Internal ID of the item category the add-on can be
be hidden from users without a valid membership. chosen from.
require_membership_types list of integers Internal IDs of membership types valid if ``require_membership`` is ``true`` min_count integer The minimal number of add-ons that need to be chosen.
Markdown syntax or can be ``null``. ├ max_count integer The maximal number of add-ons that can be chosen.
sales_channels list of strings Sales channels this variation is available on, such as position integer An integer, used for sorting
``"web"`` or ``"resellers"``. Defaults to all existing sales channels. ├ multi_allowed boolean Adding the same item multiple times is allowed
The item-level list takes precedence, i.e. a sales └ price_included boolean Adding this add-on to the item is free
channel needs to be on both lists for the item to be bundles list of objects Definition of bundles that are included in this item.
available. Only writable during creation,
├ available_from datetime The first date time at which this variation can be bought use separate endpoint to modify this later.
(or ``null``). ├ bundled_item integer Internal ID of the item that is included.
available_until datetime The last date time at which this variation can be bought bundled_variation integer Internal ID of the variation of the item (or ``null``).
(or ``null``). ├ count integer Number of items included
├ hide_without_voucher boolean If ``true``, this variation is only shown during the voucher └ designated_price money (string) Designated price of the bundled product. This will be
redemption process, but not in the normal shop used to split the price of the base item e.g. for mixed
frontend. taxation. This is not added to the price.
meta_data object Values set for event-specific meta data parameters. meta_data object Values set for event-specific meta data parameters.
└ position integer An integer, used for sorting ===================================== ========================== =======================================================
addons list of objects Definition of add-ons that can be chosen for this item.
Only writable during creation,
use separate endpoint to modify this later.
├ addon_category integer Internal ID of the item category the add-on can be
chosen from.
├ min_count integer The minimal number of add-ons that need to be chosen.
├ max_count integer The maximal number of add-ons that can be chosen.
├ position integer An integer, used for sorting
├ multi_allowed boolean Adding the same item multiple times is allowed
└ price_included boolean Adding this add-on to the item is free
bundles list of objects Definition of bundles that are included in this item.
Only writable during creation,
use separate endpoint to modify this later.
├ bundled_item integer Internal ID of the item that is included.
├ bundled_variation integer Internal ID of the variation of the item (or ``null``).
├ count integer Number of items included
└ designated_price money (string) Designated price of the bundled product. This will be
used to split the price of the base item e.g. for mixed
taxation. This is not added to the price.
meta_data object Values set for event-specific meta data parameters.
======================================= ========================== =======================================================
.. versionchanged:: 4.0 .. versionchanged:: 4.0
@@ -185,16 +164,7 @@ meta_data object Values set fo
.. versionchanged:: 4.16 .. versionchanged:: 4.16
The ``variations[x].meta_data`` and ``variations[x].checkin_attention`` attributes have been added. The ``variations[x].meta_data`` attribute has been added. The ``personalized`` attribute has been added.
The ``personalized`` attribute has been added.
.. versionchanged:: 4.17
The ``validity_*`` attributes have been added.
.. versionchanged:: 4.18
The ``media_policy`` and ``media_type`` attributes have been added.
Notes Notes
----- -----
@@ -251,8 +221,6 @@ Endpoints
"admission": false, "admission": false,
"personalized": false, "personalized": false,
"issue_giftcard": false, "issue_giftcard": false,
"media_policy": null,
"media_type": null,
"meta_data": {}, "meta_data": {},
"position": 0, "position": 0,
"picture": null, "picture": null,
@@ -277,14 +245,6 @@ Endpoints
"grant_membership_duration_like_event": true, "grant_membership_duration_like_event": true,
"grant_membership_duration_days": 0, "grant_membership_duration_days": 0,
"grant_membership_duration_months": 0, "grant_membership_duration_months": 0,
"validity_fixed_from": null,
"validity_fixed_until": null,
"validity_dynamic_duration_minutes": null,
"validity_dynamic_duration_hours": null,
"validity_dynamic_duration_days": null,
"validity_dynamic_duration_months": null,
"validity_dynamic_start_choice": false,
"validity_dynamic_start_choice_day_limit": null,
"variations": [ "variations": [
{ {
"value": {"en": "Student"}, "value": {"en": "Student"},
@@ -292,8 +252,6 @@ Endpoints
"price": "10.00", "price": "10.00",
"original_price": null, "original_price": null,
"active": true, "active": true,
"checkin_attention": false,
"require_approval": false,
"require_membership": false, "require_membership": false,
"require_membership_types": [], "require_membership_types": [],
"sales_channels": ["web"], "sales_channels": ["web"],
@@ -310,8 +268,6 @@ Endpoints
"price": "23.00", "price": "23.00",
"original_price": null, "original_price": null,
"active": true, "active": true,
"checkin_attention": false,
"require_approval": false,
"require_membership": false, "require_membership": false,
"require_membership_types": [], "require_membership_types": [],
"sales_channels": ["web"], "sales_channels": ["web"],
@@ -382,8 +338,6 @@ Endpoints
"admission": false, "admission": false,
"personalized": false, "personalized": false,
"issue_giftcard": false, "issue_giftcard": false,
"media_policy": null,
"media_type": null,
"meta_data": {}, "meta_data": {},
"position": 0, "position": 0,
"picture": null, "picture": null,
@@ -408,14 +362,6 @@ Endpoints
"grant_membership_duration_like_event": true, "grant_membership_duration_like_event": true,
"grant_membership_duration_days": 0, "grant_membership_duration_days": 0,
"grant_membership_duration_months": 0, "grant_membership_duration_months": 0,
"validity_fixed_from": null,
"validity_fixed_until": null,
"validity_dynamic_duration_minutes": null,
"validity_dynamic_duration_hours": null,
"validity_dynamic_duration_days": null,
"validity_dynamic_duration_months": null,
"validity_dynamic_start_choice": false,
"validity_dynamic_start_choice_day_limit": null,
"variations": [ "variations": [
{ {
"value": {"en": "Student"}, "value": {"en": "Student"},
@@ -423,8 +369,6 @@ Endpoints
"price": "10.00", "price": "10.00",
"original_price": null, "original_price": null,
"active": true, "active": true,
"checkin_attention": false,
"require_approval": false,
"require_membership": false, "require_membership": false,
"require_membership_types": [], "require_membership_types": [],
"description": null, "description": null,
@@ -441,8 +385,6 @@ Endpoints
"price": "23.00", "price": "23.00",
"original_price": null, "original_price": null,
"active": true, "active": true,
"checkin_attention": false,
"require_approval": false,
"require_membership": false, "require_membership": false,
"require_membership_types": [], "require_membership_types": [],
"sales_channels": ["web"], "sales_channels": ["web"],
@@ -494,8 +436,6 @@ Endpoints
"admission": false, "admission": false,
"personalized": false, "personalized": false,
"issue_giftcard": false, "issue_giftcard": false,
"media_policy": null,
"media_type": null,
"meta_data": {}, "meta_data": {},
"position": 0, "position": 0,
"picture": null, "picture": null,
@@ -519,14 +459,6 @@ Endpoints
"grant_membership_duration_like_event": true, "grant_membership_duration_like_event": true,
"grant_membership_duration_days": 0, "grant_membership_duration_days": 0,
"grant_membership_duration_months": 0, "grant_membership_duration_months": 0,
"validity_fixed_from": null,
"validity_fixed_until": null,
"validity_dynamic_duration_minutes": null,
"validity_dynamic_duration_hours": null,
"validity_dynamic_duration_days": null,
"validity_dynamic_duration_months": null,
"validity_dynamic_start_choice": false,
"validity_dynamic_start_choice_day_limit": null,
"variations": [ "variations": [
{ {
"value": {"en": "Student"}, "value": {"en": "Student"},
@@ -534,8 +466,6 @@ Endpoints
"price": "10.00", "price": "10.00",
"original_price": null, "original_price": null,
"active": true, "active": true,
"checkin_attention": false,
"require_approval": false,
"require_membership": false, "require_membership": false,
"require_membership_types": [], "require_membership_types": [],
"sales_channels": ["web"], "sales_channels": ["web"],
@@ -552,8 +482,6 @@ Endpoints
"price": "23.00", "price": "23.00",
"original_price": null, "original_price": null,
"active": true, "active": true,
"checkin_attention": false,
"require_approval": false,
"require_membership": false, "require_membership": false,
"require_membership_types": [], "require_membership_types": [],
"sales_channels": ["web"], "sales_channels": ["web"],
@@ -593,8 +521,6 @@ Endpoints
"admission": false, "admission": false,
"personalized": false, "personalized": false,
"issue_giftcard": false, "issue_giftcard": false,
"media_policy": null,
"media_type": null,
"meta_data": {}, "meta_data": {},
"position": 0, "position": 0,
"picture": null, "picture": null,
@@ -619,14 +545,6 @@ Endpoints
"grant_membership_duration_like_event": true, "grant_membership_duration_like_event": true,
"grant_membership_duration_days": 0, "grant_membership_duration_days": 0,
"grant_membership_duration_months": 0, "grant_membership_duration_months": 0,
"validity_fixed_from": null,
"validity_fixed_until": null,
"validity_dynamic_duration_minutes": null,
"validity_dynamic_duration_hours": null,
"validity_dynamic_duration_days": null,
"validity_dynamic_duration_months": null,
"validity_dynamic_start_choice": false,
"validity_dynamic_start_choice_day_limit": null,
"variations": [ "variations": [
{ {
"value": {"en": "Student"}, "value": {"en": "Student"},
@@ -634,8 +552,6 @@ Endpoints
"price": "10.00", "price": "10.00",
"original_price": null, "original_price": null,
"active": true, "active": true,
"checkin_attention": false,
"require_approval": false,
"require_membership": false, "require_membership": false,
"require_membership_types": [], "require_membership_types": [],
"sales_channels": ["web"], "sales_channels": ["web"],
@@ -652,8 +568,6 @@ Endpoints
"price": "23.00", "price": "23.00",
"original_price": null, "original_price": null,
"active": true, "active": true,
"checkin_attention": false,
"require_approval": false,
"require_membership": false, "require_membership": false,
"require_membership_types": [], "require_membership_types": [],
"sales_channels": ["web"], "sales_channels": ["web"],
@@ -724,8 +638,6 @@ Endpoints
"admission": false, "admission": false,
"personalized": false, "personalized": false,
"issue_giftcard": false, "issue_giftcard": false,
"media_policy": null,
"media_type": null,
"meta_data": {}, "meta_data": {},
"position": 0, "position": 0,
"picture": null, "picture": null,
@@ -750,14 +662,6 @@ Endpoints
"grant_membership_duration_like_event": true, "grant_membership_duration_like_event": true,
"grant_membership_duration_days": 0, "grant_membership_duration_days": 0,
"grant_membership_duration_months": 0, "grant_membership_duration_months": 0,
"validity_fixed_from": null,
"validity_fixed_until": null,
"validity_dynamic_duration_minutes": null,
"validity_dynamic_duration_hours": null,
"validity_dynamic_duration_days": null,
"validity_dynamic_duration_months": null,
"validity_dynamic_start_choice": false,
"validity_dynamic_start_choice_day_limit": null,
"variations": [ "variations": [
{ {
"value": {"en": "Student"}, "value": {"en": "Student"},
@@ -765,8 +669,6 @@ Endpoints
"price": "10.00", "price": "10.00",
"original_price": null, "original_price": null,
"active": true, "active": true,
"checkin_attention": false,
"require_approval": false,
"require_membership": false, "require_membership": false,
"require_membership_types": [], "require_membership_types": [],
"sales_channels": ["web"], "sales_channels": ["web"],
@@ -783,8 +685,6 @@ Endpoints
"price": "23.00", "price": "23.00",
"original_price": null, "original_price": null,
"active": true, "active": true,
"checkin_attention": false,
"require_approval": false,
"require_membership": false, "require_membership": false,
"require_membership_types": [], "require_membership_types": [],
"sales_channels": ["web"], "sales_channels": ["web"],

View File

@@ -1,4 +1,4 @@
.. spelling:word-list:: .. spelling::
checkins checkins
pdf pdf
@@ -91,10 +91,6 @@ require_approval boolean If ``true`` and
needs approval by an organizer before it can needs approval by an organizer before it can
continue. If ``true`` and the order is canceled, continue. If ``true`` and the order is canceled,
this order has been denied by the event organizer. this order has been denied by the event organizer.
valid_if_pending boolean If ``true`` and the order is pending, this order
is still treated like a paid order for most purposes,
such as check-in. This may be used e.g. for trusted
customers who only need to pay after the event.
url string The full URL to the order confirmation page url string The full URL to the order confirmation page
payments list of objects List of payment processes (see below) payments list of objects List of payment processes (see below)
refunds list of objects List of refund processes (see below) refunds list of objects List of refund processes (see below)
@@ -126,10 +122,6 @@ last_modified datetime Last modificati
The ``include`` query parameter has been added. The ``include`` query parameter has been added.
.. versionchanged:: 4.16
The ``valid_if_pending`` attribute has been added.
.. _order-position-resource: .. _order-position-resource:
@@ -167,9 +159,6 @@ secret string Secret code pri
addon_to integer Internal ID of the position this position is an add-on for (or ``null``) addon_to integer Internal ID of the position this position is an add-on for (or ``null``)
subevent integer ID of the date inside an event series this position belongs to (or ``null``). subevent integer ID of the date inside an event series this position belongs to (or ``null``).
discount integer ID of a discount that has been used during the creation of this position in some way (or ``null``). discount integer ID of a discount that has been used during the creation of this position in some way (or ``null``).
blocked list of strings A list of strings, or ``null``. Whenever not ``null``, the ticket may not be used (e.g. for check-in).
valid_from datetime The ticket will not be valid before this time. Can be ``null``.
valid_until datetime The ticket will not be valid after this time. Can be ``null``.
pseudonymization_id string A random ID, e.g. for use in lead scanning apps pseudonymization_id string A random ID, e.g. for use in lead scanning apps
checkins list of objects List of **successful** check-ins with this ticket checkins list of objects List of **successful** check-ins with this ticket
├ id integer Internal ID of the check-in event ├ id integer Internal ID of the check-in event
@@ -197,10 +186,6 @@ pdf_data object Data object req
``pdf_data=true`` query parameter to your request. ``pdf_data=true`` query parameter to your request.
===================================== ========================== ======================================================= ===================================== ========================== =======================================================
.. versionchanged:: 4.16
The attributes ``blocked``, ``valid_from`` and ``valid_until`` have been added.
.. _order-payment-resource: .. _order-payment-resource:
Order payment resource Order payment resource
@@ -309,7 +294,6 @@ List of all orders
"custom_followup_at": null, "custom_followup_at": null,
"checkin_attention": false, "checkin_attention": false,
"require_approval": false, "require_approval": false,
"valid_if_pending": false,
"invoice_address": { "invoice_address": {
"last_modified": "2017-12-01T10:00:00Z", "last_modified": "2017-12-01T10:00:00Z",
"is_business": true, "is_business": true,
@@ -352,9 +336,6 @@ List of all orders
"secret": "z3fsn8jyufm5kpk768q69gkbyr5f4h6w", "secret": "z3fsn8jyufm5kpk768q69gkbyr5f4h6w",
"addon_to": null, "addon_to": null,
"subevent": null, "subevent": null,
"valid_from": null,
"valid_until": null,
"blocked": null,
"discount": null, "discount": null,
"pseudonymization_id": "MQLJvANO3B", "pseudonymization_id": "MQLJvANO3B",
"seat": null, "seat": null,
@@ -486,7 +467,6 @@ Fetching individual orders
"custom_followup_at": null, "custom_followup_at": null,
"checkin_attention": false, "checkin_attention": false,
"require_approval": false, "require_approval": false,
"valid_if_pending": false,
"invoice_address": { "invoice_address": {
"last_modified": "2017-12-01T10:00:00Z", "last_modified": "2017-12-01T10:00:00Z",
"company": "Sample company", "company": "Sample company",
@@ -529,9 +509,6 @@ Fetching individual orders
"secret": "z3fsn8jyufm5kpk768q69gkbyr5f4h6w", "secret": "z3fsn8jyufm5kpk768q69gkbyr5f4h6w",
"addon_to": null, "addon_to": null,
"subevent": null, "subevent": null,
"valid_from": null,
"valid_until": null,
"blocked": null,
"discount": null, "discount": null,
"pseudonymization_id": "MQLJvANO3B", "pseudonymization_id": "MQLJvANO3B",
"seat": null, "seat": null,
@@ -663,8 +640,6 @@ Updating order fields
* ``invoice_address`` (you always need to supply the full object, or ``null`` to delete the current address) * ``invoice_address`` (you always need to supply the full object, or ``null`` to delete the current address)
* ``valid_if_pending``
**Example request**: **Example request**:
.. sourcecode:: http .. sourcecode:: http
@@ -840,7 +815,6 @@ Creating orders
* does not support or validate memberships * does not support or validate memberships
You can supply the following fields of the resource: You can supply the following fields of the resource:
* ``code`` (optional) Only ``A-Z`` and ``0-9``, but without ``O`` and ``1``. * ``code`` (optional) Only ``A-Z`` and ``0-9``, but without ``O`` and ``1``.
@@ -871,7 +845,6 @@ Creating orders
* ``custom_followup_at`` (optional) * ``custom_followup_at`` (optional)
* ``checkin_attention`` (optional) * ``checkin_attention`` (optional)
* ``require_approval`` (optional) * ``require_approval`` (optional)
* ``valid_if_pending`` (optional)
* ``invoice_address`` (optional) * ``invoice_address`` (optional)
* ``company`` * ``company``
@@ -907,10 +880,6 @@ Creating orders
* ``secret`` (optional) * ``secret`` (optional)
* ``addon_to`` (optional, see below) * ``addon_to`` (optional, see below)
* ``subevent`` (optional) * ``subevent`` (optional)
* ``valid_from`` (optional, if both ``valid_from`` and ``valid_until`` are **missing** (not ``null``) the availability will be computed from the given product)
* ``valid_until`` (optional, if both ``valid_from`` and ``valid_until`` are **missing** (not ``null``) the availability will be computed from the given product)
* ``requested_valid_from`` (optional, can be set **instead** of ``valid_from`` and ``valid_until`` to signal a user choice for the start time that may or may not be respected)
* ``use_reusable_medium`` (optional, causes the new ticket to take over the given reusable medium, identified by its ID)
* ``answers`` * ``answers``
* ``question`` * ``question``
@@ -981,7 +950,7 @@ Creating orders
"street": "Sesam Street 12", "street": "Sesam Street 12",
"zipcode": "12345", "zipcode": "12345",
"city": "Sample City", "city": "Sample City",
"country": "GB", "country": "UK",
"state": "", "state": "",
"internal_reference": "", "internal_reference": "",
"vat_id": "" "vat_id": ""
@@ -1479,9 +1448,6 @@ List of all order positions
"seat": null, "seat": null,
"addon_to": null, "addon_to": null,
"subevent": null, "subevent": null,
"valid_from": null,
"valid_until": null,
"blocked": null,
"checkins": [ "checkins": [
{ {
"list": 44, "list": 44,
@@ -1588,9 +1554,6 @@ Fetching individual positions
"secret": "z3fsn8jyufm5kpk768q69gkbyr5f4h6w", "secret": "z3fsn8jyufm5kpk768q69gkbyr5f4h6w",
"addon_to": null, "addon_to": null,
"subevent": null, "subevent": null,
"valid_from": null,
"valid_until": null,
"blocked": null,
"discount": null, "discount": null,
"pseudonymization_id": "MQLJvANO3B", "pseudonymization_id": "MQLJvANO3B",
"seat": null, "seat": null,
@@ -1696,10 +1659,6 @@ Manipulating individual positions
The ``PATCH`` method now supports changing items, variations, subevents, seats, prices, and tax rules. The ``PATCH`` method now supports changing items, variations, subevents, seats, prices, and tax rules.
The ``POST`` endpoint to add individual positions has been added. The ``POST`` endpoint to add individual positions has been added.
.. versionadded:: 4.16
The endpoints to manage blocks have been added.
.. http:patch:: /api/v1/organizers/(organizer)/events/(event)/orderpositions/(id)/ .. http:patch:: /api/v1/organizers/(organizer)/events/(event)/orderpositions/(id)/
Updates specific fields on an order position. Currently, only the following fields are supported: Updates specific fields on an order position. Currently, only the following fields are supported:
@@ -1738,10 +1697,6 @@ Manipulating individual positions
* ``tax_rule`` * ``tax_rule``
* ``valid_from``
* ``valid_until``
Changing parameters such as ``item`` or ``price`` will **not** automatically trigger creation of a new invoice, Changing parameters such as ``item`` or ``price`` will **not** automatically trigger creation of a new invoice,
you need to take care of that yourself. you need to take care of that yourself.
@@ -1816,10 +1771,6 @@ Manipulating individual positions
and ``option_identifiers`` will be ignored. As a special case, you can submit the magic value and ``option_identifiers`` will be ignored. As a special case, you can submit the magic value
``"file:keep"`` as the answer to a file question to keep the current value without re-uploading it. ``"file:keep"`` as the answer to a file question to keep the current value without re-uploading it.
* ``valid_from``
* ``valid_until``
This will **not** automatically trigger creation of a new invoice, you need to take care of that yourself. This will **not** automatically trigger creation of a new invoice, you need to take care of that yourself.
**Example request**: **Example request**:
@@ -1883,82 +1834,6 @@ Manipulating individual positions
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to view this resource. :statuscode 403: The requested organizer/event does not exist **or** you have no permission to view this resource.
:statuscode 404: The requested order position does not exist. :statuscode 404: The requested order position does not exist.
.. http:post:: /api/v1/organizers/(organizer)/events/(event)/orderpositions/(id)/add_block/
Blocks an order position from being used. The block name either needs to be ``"admin"`` or start with ``"api:"``. It
may only contain letters, numbers, dots and underscores. ``"admin"`` represents the regular block that can be set
in the backend user interface.
**Example request**:
.. sourcecode:: http
POST /api/v1/organizers/bigevents/events/sampleconf/orderpositions/23/add_block/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content-Type: application/json
{
"name": "api:block1"
}
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
(Full order position resource, see above.)
:param organizer: The ``slug`` field of the organizer of the event
:param event: The ``slug`` field of the event
:param code: The ``id`` field of the order position to update
:statuscode 200: no error
:statuscode 400: The order position could not be updated due to invalid submitted data.
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to update this order position.
.. http:post:: /api/v1/organizers/(organizer)/events/(event)/orderpositions/(id)/remove_block/
Unblocks an order position from being used. The block name either needs to be ``"admin"`` or start with ``"api:"``. It
may only contain letters, numbers, dots and underscores. ``"admin"`` represents the regular block that can be set
in the backend user interface. Blocks set by plugins cannot be lifted through this API.
**Example request**:
.. sourcecode:: http
POST /api/v1/organizers/bigevents/events/sampleconf/orderpositions/23/remove_block/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content-Type: application/json
{
"name": "api:block1"
}
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
(Full order position resource, see above.)
:param organizer: The ``slug`` field of the organizer of the event
:param event: The ``slug`` field of the event
:param code: The ``id`` field of the order position to update
:statuscode 200: no error
:statuscode 400: The order position could not be updated due to invalid submitted data.
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to update this order position.
Changing order contents Changing order contents
----------------------- -----------------------
@@ -1977,7 +1852,7 @@ otherwise, such as splitting an order or changing fees.
* ``patch_positions``: A list of objects with the two keys ``position`` specifying an order position ID and * ``patch_positions``: A list of objects with the two keys ``position`` specifying an order position ID and
``body`` specifying the desired changed values of the position (``item``, ``variation``, ``subevent``, ``seat``, ``body`` specifying the desired changed values of the position (``item``, ``variation``, ``subevent``, ``seat``,
``price``, ``tax_rule``, ``valid_from``, ``valid_until``). ``price``, ``tax_rule``).
* ``cancel_positions``: A list of objects with the single key ``position`` specifying an order position ID. * ``cancel_positions``: A list of objects with the single key ``position`` specifying an order position ID.
@@ -2666,57 +2541,3 @@ With some non-default ticket secret generation methods, a list of revoked ticket
:statuscode 200: no error :statuscode 200: no error
:statuscode 401: Authentication failure :statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to view this resource. :statuscode 403: The requested organizer/event does not exist **or** you have no permission to view this resource.
Blocked ticket secrets
----------------------
With some non-default ticket secret generation methods, a list of blocked ticket secrets is required for proper validation.
This endpoint returns all secrets that are currently blocked **or have been blocked before and are now unblocked**, so
be sure to check the ``blocked`` attribute for its actual value. The list is currently always ordered with the most
recently updated ones first.
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/blockedsecrets/
Returns a list of all blocked or historically blocked secrets within a given event.
**Example request**:
.. sourcecode:: http
GET /api/v1/organizers/bigevents/events/sampleconf/blockedsecrets/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
X-Page-Generated: 2017-12-01T10:00:00Z
{
"count": 1,
"next": null,
"previous": null,
"results": [
{
"id": 1234,
"secret": "k24fiuwvu8kxz3y1",
"blocked": true,
"updated": "2017-12-01T10:00:00Z",
}
]
}
:query integer page: The page number in case of a multi-page result set, default is 1
:query datetime updated_since: Only return records that have been updated since the given date.
:query boolean blocked: Only return blocked / non-blocked records.
:param organizer: The ``slug`` field of the organizer to fetch
:param event: The ``slug`` field of the event to fetch
:resheader X-Page-Generated: The server time at the beginning of the operation. If you're using this API to fetch
differences, this is the value you want to use as ``updated_since`` in your next call.
:statuscode 200: no error
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to view this resource.

View File

@@ -17,18 +17,12 @@ Field Type Description
name string The organizer's full name, i.e. the name of an name string The organizer's full name, i.e. the name of an
organization or company. organization or company.
slug string A short form of the name, used e.g. in URLs. slug string A short form of the name, used e.g. in URLs.
public_url string The public, customer-facing URL of the organizer, where
the list of all events can be found (read-only).
===================================== ========================== ======================================================= ===================================== ========================== =======================================================
Endpoints Endpoints
--------- ---------
.. versionchanged:: 4.17
The ``public_url`` field has been added.
.. http:get:: /api/v1/organizers/ .. http:get:: /api/v1/organizers/
Returns a list of all organizers the authenticated user/token has access to. Returns a list of all organizers the authenticated user/token has access to.
@@ -57,7 +51,6 @@ Endpoints
{ {
"name": "Big Events LLC", "name": "Big Events LLC",
"slug": "Big Events", "slug": "Big Events",
"public_url": "https://pretix.eu/bigevents/"
} }
] ]
} }
@@ -91,7 +84,6 @@ Endpoints
{ {
"name": "Big Events LLC", "name": "Big Events LLC",
"slug": "Big Events", "slug": "Big Events",
"public_url": "https://pretix.eu/bigevents/"
} }
:param organizer: The ``slug`` field of the organizer to fetch :param organizer: The ``slug`` field of the organizer to fetch
@@ -157,7 +149,6 @@ information about the properties.
{ {
"value": "calendar", "value": "calendar",
"label": "Default overview style", "label": "Default overview style",
"readonly": false,
"help_text": "If your event series has more than 50 dates in the future, only the month or week calendar can be used." "help_text": "If your event series has more than 50 dates in the future, only the month or week calendar can be used."
} }
}, },

View File

@@ -1,4 +1,4 @@
.. spelling:word-list:: .. spelling::
checkin checkin
datetime datetime
@@ -63,7 +63,6 @@ valid_date_max date Maximum value f
valid_datetime_min datetime Minimum value for date and time questions (optional) valid_datetime_min datetime Minimum value for date and time questions (optional)
valid_datetime_max datetime Maximum value for date and time questions (optional) valid_datetime_max datetime Maximum value for date and time questions (optional)
valid_file_portrait boolean Turn on file validation for portrait photos valid_file_portrait boolean Turn on file validation for portrait photos
valid_string_length_max integer Maximum length for string questions (optional)
dependency_question integer Internal ID of a different question. The current dependency_question integer Internal ID of a different question. The current
question will only be shown if the question given in question will only be shown if the question given in
this attribute is set to the value given in this attribute is set to the value given in
@@ -123,7 +122,6 @@ Endpoints
"valid_date_max": null, "valid_date_max": null,
"valid_datetime_min": null, "valid_datetime_min": null,
"valid_datetime_max": null, "valid_datetime_max": null,
"valid_string_length_max": null,
"valid_file_portrait": false, "valid_file_portrait": false,
"dependency_question": null, "dependency_question": null,
"dependency_value": null, "dependency_value": null,
@@ -203,7 +201,6 @@ Endpoints
"valid_datetime_min": null, "valid_datetime_min": null,
"valid_datetime_max": null, "valid_datetime_max": null,
"valid_file_portrait": false, "valid_file_portrait": false,
"valid_string_length_max": null,
"dependency_question": null, "dependency_question": null,
"dependency_value": null, "dependency_value": null,
"dependency_values": [], "dependency_values": [],
@@ -305,7 +302,6 @@ Endpoints
"valid_datetime_min": null, "valid_datetime_min": null,
"valid_datetime_max": null, "valid_datetime_max": null,
"valid_file_portrait": false, "valid_file_portrait": false,
"valid_string_length_max": null,
"options": [ "options": [
{ {
"id": 1, "id": 1,
@@ -388,7 +384,6 @@ Endpoints
"valid_datetime_min": null, "valid_datetime_min": null,
"valid_datetime_max": null, "valid_datetime_max": null,
"valid_file_portrait": false, "valid_file_portrait": false,
"valid_string_length_max": null,
"options": [ "options": [
{ {
"id": 1, "id": 1,

View File

@@ -1,317 +0,0 @@
.. _`rest-reusablemedia`:
Reusable media
==============
Reusable media represent things, typically physical tokens like plastic cards or NFC wristbands, which can represent
other entities inside the system. For example, a medium can link to an order position or to a gift card and can be used
in their place. Later, the medium might be reused for a different ticket.
Resource description
--------------------
The reusable medium resource contains the following public fields:
.. rst-class:: rest-resource-table
===================================== ========================== =======================================================
Field Type Description
===================================== ========================== =======================================================
id integer Internal ID of the medium
type string Type of medium, e.g. ``"barcode"`` or ``"nfc_uid"``.
identifier string Unique identifier of the medium. The format depends on the ``type``.
active boolean Whether this medium may be used.
created datetime Date of creation
updated datetime Date of last modification
expires datetime Expiry date (or ``null``)
customer string Identifier of a customer account this medium belongs to.
linked_orderposition integer Internal ID of a ticket this medium is linked to.
linked_giftcard integer Internal ID of a gift card this medium is linked to.
info object Additional data, content depends on the ``type``. Consider
this internal to the system and don't use it for your own data.
notes string Internal notes and comments (or ``null``)
===================================== ========================== =======================================================
Existing media types are:
- ``barcode``
- ``nfc_uid``
Endpoints
---------
.. http:get:: /api/v1/organizers/(organizer)/reusablemedia/
Returns a list of all media issued by a given organizer.
**Example request**:
.. sourcecode:: http
GET /api/v1/organizers/bigevents/reusablemedia/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
{
"count": 1,
"next": null,
"previous": null,
"results": [
{
"id": 1,
"identifier": "ABCDEFGH",
"created": "2021-04-06T13:44:22.809377Z",
"updated": "2021-04-06T13:44:22.809377Z",
"type": "barcode",
"active": True,
"expires": None,
"customer": None,
"linked_orderposition": None,
"linked_giftcard": None,
"notes": None,
"info": {}
}
]
}
:query integer page: The page number in case of a multi-page result set, default is 1.
:query string identifier: Only show media with the given identifier. Note that you should use the lookup endpoint described below for most use cases.
:query string type: Only show media with the given type.
:query boolean active: Only show media that are (not) active.
:query string customer: Only show media linked to the given customer.
:query string created_since: Only show media created since a given date.
:query string updated_since: Only show media updated since a given date.
:query integer linked_orderposition: Only show media linked to the given ticket.
:query integer linked_giftcard: Only show media linked to the given gift card.
:query string expand: If you pass ``"linked_giftcard"``, ``"linked_giftcard.owner_ticket"``, ``"linked_orderposition"``,
or ``"customer"``, the respective field will be shown as a nested value instead of just an ID.
The nested objects are identical to the respective resources, except that order positions
will have an attribute of the format ``"order": {"code": "ABCDE", "event": "eventslug"}`` to make
matching easier. The parameter can be given multiple times.
:param organizer: The ``slug`` field of the organizer to fetch
:statuscode 200: no error
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer does not exist **or** you have no permission to view this resource.
.. http:get:: /api/v1/organizers/(organizer)/reusablemedia/(id)/
Returns information on one medium, identified by its ID.
**Example request**:
.. sourcecode:: http
GET /api/v1/organizers/bigevents/reusablemedia/1/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
{
"id": 1,
"identifier": "ABCDEFGH",
"created": "2021-04-06T13:44:22.809377Z",
"updated": "2021-04-06T13:44:22.809377Z",
"type": "barcode",
"active": True,
"expires": None,
"customer": None,
"linked_orderposition": None,
"linked_giftcard": None,
"notes": None,
"info": {}
}
:param organizer: The ``slug`` field of the organizer to fetch
:param id: The ``id`` field of the medium to fetch
:query string expand: If you pass ``"linked_giftcard"``, ``"linked_giftcard.owner_ticket"``, ``"linked_orderposition"``,
or ``"customer"``, the respective field will be shown as a nested value instead of just an ID.
The nested objects are identical to the respective resources, except that order positions
will have an attribute of the format ``"order": {"code": "ABCDE", "event": "eventslug"}`` to make
matching easier. The parameter can be given multiple times.
:statuscode 200: no error
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer does not exist **or** you have no permission to view this resource.
.. http:post:: /api/v1/organizers/(organizer)/reusablemedia/lookup/
Look up a new reusable medium by its identifier. In some cases, this might lead to the automatic creation of a new
medium behind the scenes.
**Example request**:
.. sourcecode:: http
POST /api/v1/organizers/bigevents/reusablemedia/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content-Type: application/json
{
"identifier": "ABCDEFGH",
"type": "barcode",
}
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
{
"id": 1,
"identifier": "ABCDEFGH",
"created": "2021-04-06T13:44:22.809377Z",
"updated": "2021-04-06T13:44:22.809377Z",
"type": "barcode",
"active": True,
"expires": None,
"customer": None,
"linked_orderposition": None,
"linked_giftcard": None,
"notes": None,
"info": {}
}
:param organizer: The ``slug`` field of the organizer to look up a medium for
:query string expand: If you pass ``"linked_giftcard"``, ``"linked_orderposition"``, oder ``"customer"``, the respective
field will be shown as a nested value instead of just an ID. The nested objects are identical to
the respective resources, except that the ``linked_orderposition`` will have an attribute of the
format ``"order": {"code": "ABCDE", "event": "eventslug"}`` to make matching easier. The parameter
can be given multiple times.
:statuscode 201: no error
:statuscode 400: The medium could not be looked up due to invalid submitted data.
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer does not exist **or** you have no permission to create this resource.
.. http:post:: /api/v1/organizers/(organizer)/reusablemedia/
Creates a new reusable medium.
**Example request**:
.. sourcecode:: http
POST /api/v1/organizers/bigevents/reusablemedia/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content-Type: application/json
{
"identifier": "ABCDEFGH",
"type": "barcode",
"active": True,
"expires": None,
"customer": None,
"linked_orderposition": None,
"linked_giftcard": None,
"notes": None,
"info": {}
}
**Example response**:
.. sourcecode:: http
HTTP/1.1 201 Created
Vary: Accept
Content-Type: application/json
{
"id": 1,
"identifier": "ABCDEFGH",
"created": "2021-04-06T13:44:22.809377Z",
"updated": "2021-04-06T13:44:22.809377Z",
"type": "barcode",
"active": True,
"expires": None,
"customer": None,
"linked_orderposition": None,
"linked_giftcard": None,
"notes": None,
"info": {}
}
:param organizer: The ``slug`` field of the organizer to create a medium for
:query string expand: If you pass ``"linked_giftcard"``, ``"linked_orderposition"``, oder ``"customer"``, the respective
field will be shown as a nested value instead of just an ID. The nested objects are identical to
the respective resources, except that the ``linked_orderposition`` will have an attribute of the
format ``"order": {"code": "ABCDE", "event": "eventslug"}`` to make matching easier. The parameter
can be given multiple times.
:statuscode 201: no error
:statuscode 400: The medium could not be created due to invalid submitted data.
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer does not exist **or** you have no permission to create this resource.
.. http:patch:: /api/v1/organizers/(organizer)/reusablemedia/(id)/
Update a reusable medium. You can also use ``PUT`` instead of ``PATCH``. With ``PUT``, you have to provide all fields of
the resource, other fields will be reset to default. With ``PATCH``, you only need to provide the fields that you
want to change.
You can change all fields of the resource except the ``id``, ``identifier`` and ``type`` fields.
**Example request**:
.. sourcecode:: http
PATCH /api/v1/organizers/bigevents/reusablemedia/1/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content-Type: application/json
Content-Length: 94
{
"linked_orderposition": 13
}
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
{
"id": 1,
"identifier": "ABCDEFGH",
"created": "2021-04-06T13:44:22.809377Z",
"updated": "2021-04-06T13:44:22.809377Z",
"type": "barcode",
"active": True,
"expires": None,
"customer": None,
"linked_orderposition": 13,
"linked_giftcard": None,
"notes": None,
"info": {}
}
:param organizer: The ``slug`` field of the organizer to modify
:param id: The ``id`` field of the medium to modify
:query string expand: If you pass ``"linked_giftcard"``, ``"linked_orderposition"``, oder ``"customer"``, the respective
field will be shown as a nested value instead of just an ID. The nested objects are identical to
the respective resources, except that the ``linked_orderposition`` will have an attribute of the
format ``"order": {"code": "ABCDE", "event": "eventslug"}`` to make matching easier. The parameter
can be given multiple times.
:statuscode 200: no error
:statuscode 400: The medium could not be modified due to invalid submitted data
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer does not exist **or** you have no permission to change this resource.

View File

@@ -1,4 +1,4 @@
.. spelling:word-list:: checkin .. spelling:: checkin
Data shredders Data shredders
============== ==============

View File

@@ -1,4 +1,4 @@
.. spelling:word-list:: .. spelling::
geo geo
lat lat

View File

@@ -1,4 +1,4 @@
.. spelling:word-list:: fullname checkin .. spelling:: fullname checkin
.. _`rest-teams`: .. _`rest-teams`:
@@ -26,7 +26,6 @@ can_create_events boolean
can_change_teams boolean can_change_teams boolean
can_change_organizer_settings boolean can_change_organizer_settings boolean
can_manage_customers boolean can_manage_customers boolean
can_manage_reusable_media boolean
can_manage_gift_cards boolean can_manage_gift_cards boolean
can_change_event_settings boolean can_change_event_settings boolean
can_change_items boolean can_change_items boolean
@@ -37,10 +36,6 @@ can_change_vouchers boolean
can_checkin_orders boolean can_checkin_orders boolean
===================================== ========================== ======================================================= ===================================== ========================== =======================================================
.. versionchanged:: 4.18
The ``can_manage_reusable_media`` permission has been added.
Team member resource Team member resource
-------------------- --------------------

View File

@@ -47,8 +47,6 @@ tag string A string that i
comment string An internal comment on the voucher comment string An internal comment on the voucher
subevent integer ID of the date inside an event series this voucher belongs to (or ``null``). subevent integer ID of the date inside an event series this voucher belongs to (or ``null``).
show_hidden_items boolean Only if set to ``true``, this voucher allows to buy products with the property ``hide_without_voucher``. Defaults to ``true``. show_hidden_items boolean Only if set to ``true``, this voucher allows to buy products with the property ``hide_without_voucher``. Defaults to ``true``.
all_addons_included boolean If set to ``true``, all add-on products for the product purchased with this voucher are included in the base price.
all_bundles_included boolean If set to ``true``, all bundled products for the product purchased with this voucher are added without their designated price.
===================================== ========================== ======================================================= ===================================== ========================== =======================================================
@@ -97,9 +95,6 @@ Endpoints
"comment": "", "comment": "",
"seat": null, "seat": null,
"subevent": null, "subevent": null,
"show_hidden_items": false,
"all_addons_included": false,
"all_bundles_included": false
} }
] ]
} }
@@ -166,10 +161,7 @@ Endpoints
"tag": "testvoucher", "tag": "testvoucher",
"comment": "", "comment": "",
"seat": null, "seat": null,
"subevent": null, "subevent": null
"show_hidden_items": false,
"all_addons_included": false,
"all_bundles_included": false
} }
:param organizer: The ``slug`` field of the organizer to fetch :param organizer: The ``slug`` field of the organizer to fetch
@@ -206,10 +198,7 @@ Endpoints
"quota": null, "quota": null,
"tag": "testvoucher", "tag": "testvoucher",
"comment": "", "comment": "",
"subevent": null, "subevent": null
"show_hidden_items": false,
"all_addons_included": false,
"all_bundles_included": false
} }
**Example response**: **Example response**:
@@ -236,10 +225,7 @@ Endpoints
"tag": "testvoucher", "tag": "testvoucher",
"comment": "", "comment": "",
"seat": null, "seat": null,
"subevent": null, "subevent": null
"show_hidden_items": false,
"all_addons_included": false,
"all_bundles_included": false
} }
:param organizer: The ``slug`` field of the organizer to create a voucher for :param organizer: The ``slug`` field of the organizer to create a voucher for
@@ -278,10 +264,7 @@ Endpoints
"quota": null, "quota": null,
"tag": "testvoucher", "tag": "testvoucher",
"comment": "", "comment": "",
"subevent": null, "subevent": null
"show_hidden_items": false,
"all_addons_included": false,
"all_bundles_included": false
}, },
{ {
"code": "ASDKLJCYXCASDASD", "code": "ASDKLJCYXCASDASD",
@@ -296,10 +279,7 @@ Endpoints
"quota": null, "quota": null,
"tag": "testvoucher", "tag": "testvoucher",
"comment": "", "comment": "",
"subevent": null, "subevent": null
"show_hidden_items": false,
"all_addons_included": false,
"all_bundles_included": false
}, },
**Example response**: **Example response**:
@@ -373,10 +353,7 @@ Endpoints
"tag": "testvoucher", "tag": "testvoucher",
"comment": "", "comment": "",
"seat": null, "seat": null,
"subevent": null, "subevent": null
"show_hidden_items": false,
"all_addons_included": false,
"all_bundles_included": false
} }
:param organizer: The ``slug`` field of the organizer to modify :param organizer: The ``slug`` field of the organizer to modify

View File

@@ -26,7 +26,6 @@ limit_events list of strings If ``all_events
action_types list of strings A list of action type filters that limit the action_types list of strings A list of action type filters that limit the
notifications sent to this webhook. See below for notifications sent to this webhook. See below for
valid values valid values
comment string Internal comment on this webhook, default ``null``
===================================== ========================== ======================================================= ===================================== ========================== =======================================================
The following values for ``action_types`` are valid with pretix core: The following values for ``action_types`` are valid with pretix core:
@@ -47,7 +46,6 @@ The following values for ``action_types`` are valid with pretix core:
* ``pretix.event.order.refund.done`` * ``pretix.event.order.refund.done``
* ``pretix.event.order.refund.canceled`` * ``pretix.event.order.refund.canceled``
* ``pretix.event.order.refund.failed`` * ``pretix.event.order.refund.failed``
* ``pretix.event.order.payment.confirmed``
* ``pretix.event.order.approved`` * ``pretix.event.order.approved``
* ``pretix.event.order.denied`` * ``pretix.event.order.denied``
* ``pretix.event.checkin`` * ``pretix.event.checkin``
@@ -58,7 +56,6 @@ The following values for ``action_types`` are valid with pretix core:
* ``pretix.subevent.added`` * ``pretix.subevent.added``
* ``pretix.subevent.changed`` * ``pretix.subevent.changed``
* ``pretix.subevent.deleted`` * ``pretix.subevent.deleted``
* ``pretix.event.item.*``
* ``pretix.event.live.activated`` * ``pretix.event.live.activated``
* ``pretix.event.live.deactivated`` * ``pretix.event.live.deactivated``
* ``pretix.event.testmode.activated`` * ``pretix.event.testmode.activated``
@@ -101,8 +98,7 @@ Endpoints
"target_url": "https://httpstat.us/200", "target_url": "https://httpstat.us/200",
"all_events": false, "all_events": false,
"limit_events": ["democon"], "limit_events": ["democon"],
"action_types": ["pretix.event.order.modified", "pretix.event.order.changed.*"], "action_types": ["pretix.event.order.modified", "pretix.event.order.changed.*"]
"comment": null
} }
] ]
} }
@@ -139,8 +135,7 @@ Endpoints
"target_url": "https://httpstat.us/200", "target_url": "https://httpstat.us/200",
"all_events": false, "all_events": false,
"limit_events": ["democon"], "limit_events": ["democon"],
"action_types": ["pretix.event.order.modified", "pretix.event.order.changed.*"], "action_types": ["pretix.event.order.modified", "pretix.event.order.changed.*"]
"comment": null
} }
:param organizer: The ``slug`` field of the organizer to fetch :param organizer: The ``slug`` field of the organizer to fetch
@@ -167,8 +162,7 @@ Endpoints
"target_url": "https://httpstat.us/200", "target_url": "https://httpstat.us/200",
"all_events": false, "all_events": false,
"limit_events": ["democon"], "limit_events": ["democon"],
"action_types": ["pretix.event.order.modified", "pretix.event.order.changed.*"], "action_types": ["pretix.event.order.modified", "pretix.event.order.changed.*"]
"comment": "Called for changes"
} }
**Example response**: **Example response**:
@@ -185,8 +179,7 @@ Endpoints
"target_url": "https://httpstat.us/200", "target_url": "https://httpstat.us/200",
"all_events": false, "all_events": false,
"limit_events": ["democon"], "limit_events": ["democon"],
"action_types": ["pretix.event.order.modified", "pretix.event.order.changed.*"], "action_types": ["pretix.event.order.modified", "pretix.event.order.changed.*"]
"comment": "Called for changes"
} }
:param organizer: The ``slug`` field of the organizer to create a webhook for :param organizer: The ``slug`` field of the organizer to create a webhook for
@@ -231,8 +224,7 @@ Endpoints
"target_url": "https://httpstat.us/200", "target_url": "https://httpstat.us/200",
"all_events": false, "all_events": false,
"limit_events": ["democon"], "limit_events": ["democon"],
"action_types": ["pretix.event.order.modified", "pretix.event.order.changed.*"], "action_types": ["pretix.event.order.modified", "pretix.event.order.changed.*"]
"comment": null
} }
:param organizer: The ``slug`` field of the organizer to modify :param organizer: The ``slug`` field of the organizer to modify

View File

@@ -13,6 +13,10 @@
# All configuration values have a default; values that are commented out # All configuration values have a default; values that are commented out
# serve to show the default. # serve to show the default.
from docutils.parsers.rst.directives.admonitions import BaseAdmonition
from sphinx.util import compat
compat.make_admonition = BaseAdmonition # See https://github.com/spinus/sphinxcontrib-images/issues/41
import sys import sys
import os import os
@@ -24,13 +28,12 @@ from datetime import date
sys.path.insert(0, os.path.abspath('../src')) sys.path.insert(0, os.path.abspath('../src'))
import django import django
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "pretix.testutils.settings") os.environ.setdefault("DJANGO_SETTINGS_MODULE", "pretix.testutils.settings")
django.setup() django.setup()
try:
import enchant # noqa
try:
import enchant
HAS_PYENCHANT = True HAS_PYENCHANT = True
except: except:
HAS_PYENCHANT = False HAS_PYENCHANT = False
@@ -38,7 +41,7 @@ except:
# -- General configuration ------------------------------------------------ # -- General configuration ------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here. # If your documentation needs a minimal Sphinx version, state it here.
# needs_sphinx = '1.0' #needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be # Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
@@ -49,7 +52,6 @@ extensions = [
'sphinx.ext.coverage', 'sphinx.ext.coverage',
'sphinxcontrib.httpdomain', 'sphinxcontrib.httpdomain',
'sphinxcontrib.images', 'sphinxcontrib.images',
'sphinxcontrib.jquery',
'sphinxemoji.sphinxemoji', 'sphinxemoji.sphinxemoji',
] ]
if HAS_PYENCHANT: if HAS_PYENCHANT:
@@ -62,7 +64,7 @@ templates_path = ['_templates']
source_suffix = '.rst' source_suffix = '.rst'
# The encoding of source files. # The encoding of source files.
# source_encoding = 'utf-8-sig' #source_encoding = 'utf-8-sig'
# The master toctree document. # The master toctree document.
master_doc = 'index' master_doc = 'index'
@@ -77,20 +79,19 @@ copyright = '2014-{}, Raphael Michel'.format(date.today().year)
# #
# The short X.Y version. # The short X.Y version.
from pretix import __version__ from pretix import __version__
version = '.'.join(__version__.split('.')[:2]) version = '.'.join(__version__.split('.')[:2])
# The full version, including alpha/beta/rc tags. # The full version, including alpha/beta/rc tags.
release = __version__ release = __version__
# The language for content autogenerated by Sphinx. Refer to documentation # The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages. # for a list of supported languages.
# language = None #language = None
# There are two options for replacing |today|: either, you set today to some # There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used: # non-false value, then it is used:
# today = '' #today = ''
# Else, today_fmt is used as the format for a strftime call. # Else, today_fmt is used as the format for a strftime call.
# today_fmt = '%B %d, %Y' #today_fmt = '%B %d, %Y'
# List of patterns, relative to source directory, that match files and # List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files. # directories to ignore when looking for source files.
@@ -98,34 +99,34 @@ exclude_patterns = ['_build']
# The reST default role (used for this markup: `text`) to use for all # The reST default role (used for this markup: `text`) to use for all
# documents. # documents.
# default_role = None #default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text. # If true, '()' will be appended to :func: etc. cross-reference text.
# add_function_parentheses = True #add_function_parentheses = True
# If true, the current module name will be prepended to all description # If true, the current module name will be prepended to all description
# unit titles (such as .. function::). # unit titles (such as .. function::).
# add_module_names = True #add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the # If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default. # output. They are ignored by default.
# show_authors = False #show_authors = False
# The name of the Pygments (syntax highlighting) style to use. # The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx' pygments_style = 'sphinx'
# A list of ignored prefixes for module index sorting. # A list of ignored prefixes for module index sorting.
# modindex_common_prefix = [] #modindex_common_prefix = []
# If true, keep warnings as "system message" paragraphs in the built documents. # If true, keep warnings as "system message" paragraphs in the built documents.
# keep_warnings = False #keep_warnings = False
# -- Options for HTML output ---------------------------------------------- # -- Options for HTML output ----------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for # The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes. # a list of builtin themes.
# html_theme = "" #html_theme = ""
# Theme options are theme-specific and customize the look and feel of a theme # Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the # further. For a list of options available for each theme, see the
@@ -135,14 +136,14 @@ html_theme_options = {
} }
# Add any paths that contain custom themes here, relative to this directory. # Add any paths that contain custom themes here, relative to this directory.
# html_theme_path = [] #html_theme_path = []
# The name for this set of Sphinx documents. If None, it defaults to # The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation". # "<project> v<release> documentation".
# html_title = None #html_title = None
# A shorter title for the navigation bar. Default is the same as html_title. # A shorter title for the navigation bar. Default is the same as html_title.
# html_short_title = None #html_short_title = None
# The name of an image file (relative to this directory) to place at the top # The name of an image file (relative to this directory) to place at the top
# of the sidebar. # of the sidebar.
@@ -151,7 +152,7 @@ html_logo = 'images/logo-white.svg'
# The name of an image file (within the static path) to use as favicon of the # The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large. # pixels large.
# html_favicon = None #html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here, # Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files, # relative to this directory. They are copied after the builtin static files,
@@ -165,18 +166,18 @@ html_static_path = [
# Add any extra paths that contain custom files (such as robots.txt or # Add any extra paths that contain custom files (such as robots.txt or
# .htaccess) here, relative to this directory. These files are copied # .htaccess) here, relative to this directory. These files are copied
# directly to the root of the documentation. # directly to the root of the documentation.
# html_extra_path = [] #html_extra_path = []
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format. # using the given strftime format.
# html_last_updated_fmt = '%b %d, %Y' #html_last_updated_fmt = '%b %d, %Y'
# If true, SmartyPants will be used to convert quotes and dashes to # If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities. # typographically correct entities.
# html_use_smartypants = True #html_use_smartypants = True
# Custom sidebar templates, maps document names to template names. # Custom sidebar templates, maps document names to template names.
# html_sidebars = {} #html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to # Additional templates that should be rendered to pages, maps page names to
# template names. # template names.
@@ -191,24 +192,24 @@ html_domain_indices = False
html_use_index = False html_use_index = False
# If true, the index is split into individual pages for each letter. # If true, the index is split into individual pages for each letter.
# html_split_index = False #html_split_index = False
# If true, links to the reST sources are added to the pages. # If true, links to the reST sources are added to the pages.
html_show_sourcelink = True html_show_sourcelink = True
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. # If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
# html_show_sphinx = True #html_show_sphinx = True
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
# html_show_copyright = True #html_show_copyright = True
# If true, an OpenSearch description file will be output, and all pages will # If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it. The value of this option must be the # contain a <link> tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served. # base URL from which the finished HTML is served.
# html_use_opensearch = '' #html_use_opensearch = ''
# This is the file name suffix for HTML files (e.g. ".xhtml"). # This is the file name suffix for HTML files (e.g. ".xhtml").
# html_file_suffix = None #html_file_suffix = None
# Output file base name for HTML help builder. # Output file base name for HTML help builder.
htmlhelp_basename = 'pretixdoc' htmlhelp_basename = 'pretixdoc'
@@ -216,46 +217,47 @@ htmlhelp_basename = 'pretixdoc'
html_theme = 'pretix_theme' html_theme = 'pretix_theme'
html_theme_path = [os.path.abspath('_themes')] html_theme_path = [os.path.abspath('_themes')]
# -- Options for LaTeX output --------------------------------------------- # -- Options for LaTeX output ---------------------------------------------
latex_elements = { latex_elements = {
# The paper size ('letterpaper' or 'a4paper'). # The paper size ('letterpaper' or 'a4paper').
'papersize': 'a4paper', 'papersize': 'a4paper',
# The font size ('10pt', '11pt' or '12pt'). # The font size ('10pt', '11pt' or '12pt').
'pointsize': '10pt', 'pointsize': '10pt',
# Additional stuff for the LaTeX preamble. # Additional stuff for the LaTeX preamble.
# 'preamble': '', #'preamble': '',
} }
# Grouping the document tree into LaTeX files. List of tuples # Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, # (source start file, target name, title,
# author, documentclass [howto, manual, or own class]). # author, documentclass [howto, manual, or own class]).
latex_documents = [ latex_documents = [
('index', 'pretix.tex', 'pretix Documentation', ('index', 'pretix.tex', 'pretix Documentation',
'Raphael Michel', 'manual'), 'Raphael Michel', 'manual'),
] ]
# The name of an image file (relative to this directory) to place at the top of # The name of an image file (relative to this directory) to place at the top of
# the title page. # the title page.
# latex_logo = None #latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts, # For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters. # not chapters.
# latex_use_parts = False #latex_use_parts = False
# If true, show page references after internal links. # If true, show page references after internal links.
# latex_show_pagerefs = False #latex_show_pagerefs = False
# If true, show URL addresses after external links. # If true, show URL addresses after external links.
# latex_show_urls = False #latex_show_urls = False
# Documents to append as an appendix to all manuals. # Documents to append as an appendix to all manuals.
# latex_appendices = [] #latex_appendices = []
# If false, no module index is generated. # If false, no module index is generated.
# latex_domain_indices = True #latex_domain_indices = True
# -- Options for manual page output --------------------------------------- # -- Options for manual page output ---------------------------------------
@@ -268,7 +270,7 @@ man_pages = [
] ]
# If true, show URL addresses after external links. # If true, show URL addresses after external links.
# man_show_urls = False #man_show_urls = False
# -- Options for Texinfo output ------------------------------------------- # -- Options for Texinfo output -------------------------------------------
@@ -277,22 +279,22 @@ man_pages = [
# (source start file, target name, title, author, # (source start file, target name, title, author,
# dir menu entry, description, category) # dir menu entry, description, category)
texinfo_documents = [ texinfo_documents = [
('index', 'pretix', 'pretix Documentation', ('index', 'pretix', 'pretix Documentation',
'Raphael Michel', 'pretix', 'One line description of project.', 'Raphael Michel', 'pretix', 'One line description of project.',
'Miscellaneous'), 'Miscellaneous'),
] ]
# Documents to append as an appendix to all manuals. # Documents to append as an appendix to all manuals.
# texinfo_appendices = [] #texinfo_appendices = []
# If false, no module index is generated. # If false, no module index is generated.
# texinfo_domain_indices = True #texinfo_domain_indices = True
# How to display URL addresses: 'footnote', 'no', or 'inline'. # How to display URL addresses: 'footnote', 'no', or 'inline'.
# texinfo_show_urls = 'footnote' #texinfo_show_urls = 'footnote'
# If true, do not generate a @detailmenu in the "Top" node's menu. # If true, do not generate a @detailmenu in the "Top" node's menu.
# texinfo_no_detailmenu = False #texinfo_no_detailmenu = False
images_config = { images_config = {
@@ -312,13 +314,12 @@ if HAS_PYENCHANT:
# String specifying a file containing a list of words known to be spelled # String specifying a file containing a list of words known to be spelled
# correctly but that do not appear in the language dictionary selected by # correctly but that do not appear in the language dictionary selected by
# spelling_lang. The file should contain one word per line. # spelling_lang. The file should contain one word per line.
spelling_word_list_filename = 'spelling_wordlist.txt' spelling_word_list_filename='spelling_wordlist.txt'
# Boolean controlling whether suggestions for misspelled words are printed. # Boolean controlling whether suggestions for misspelled words are printed.
# Defaults to False. # Defaults to False.
spelling_show_suggestions = True spelling_show_suggestions=True
# List of filter classes to be added to the tokenizer that produces words to be checked. # List of filter classes to be added to the tokenizer that produces words to be checked.
from checkin_filter import CheckinFilter from checkin_filter import CheckinFilter
spelling_filters=[CheckinFilter]
spelling_filters = [CheckinFilter]

View File

@@ -13,7 +13,7 @@ Core
.. automodule:: pretix.base.signals .. automodule:: pretix.base.signals
:members: periodic_task, event_live_issues, event_copy_data, email_filter, register_notification_types, :members: periodic_task, event_live_issues, event_copy_data, email_filter, register_notification_types,
item_copy_data, register_sales_channels, register_global_settings, quota_availability, global_email_filter, item_copy_data, register_sales_channels, register_global_settings, quota_availability, global_email_filter,
register_ticket_secret_generators, gift_card_transaction_display register_ticket_secret_generators
Order events Order events
"""""""""""" """"""""""""
@@ -21,7 +21,7 @@ Order events
There are multiple signals that will be sent out in the ordering cycle: There are multiple signals that will be sent out in the ordering cycle:
.. automodule:: pretix.base.signals .. automodule:: pretix.base.signals
:members: validate_cart, validate_cart_addons, validate_order, order_valid_if_pending, order_fee_calculation, order_paid, order_placed, order_canceled, order_reactivated, order_expired, order_modified, order_changed, order_approved, order_denied, order_fee_type_name, allow_ticket_download, order_split, order_gracefully_delete, invoice_line_text :members: validate_cart, validate_cart_addons, validate_order, order_fee_calculation, order_paid, order_placed, order_canceled, order_reactivated, order_expired, order_modified, order_changed, order_approved, order_denied, order_fee_type_name, allow_ticket_download, order_split, order_gracefully_delete, invoice_line_text
Check-ins Check-ins
""""""""" """""""""
@@ -61,7 +61,7 @@ Backend
item_formsets, order_search_filter_q, order_search_forms item_formsets, order_search_filter_q, order_search_forms
.. automodule:: pretix.base.signals .. automodule:: pretix.base.signals
:members: logentry_display, logentry_object_link, requiredaction_display, timeline_events, orderposition_blocked_display :members: logentry_display, logentry_object_link, requiredaction_display, timeline_events
Vouchers Vouchers
"""""""" """"""""

View File

@@ -102,8 +102,6 @@ The provider class
.. automethod:: render_invoice_text .. automethod:: render_invoice_text
.. automethod:: render_invoice_stamp
.. automethod:: order_change_allowed .. automethod:: order_change_allowed
.. automethod:: payment_prepare .. automethod:: payment_prepare
@@ -122,8 +120,6 @@ The provider class
.. automethod:: refund_control_render .. automethod:: refund_control_render
.. automethod:: refund_control_render_short
.. automethod:: new_refund_control_form_render .. automethod:: new_refund_control_form_render
.. automethod:: new_refund_control_form_process .. automethod:: new_refund_control_form_process
@@ -134,8 +130,6 @@ The provider class
.. automethod:: matching_id .. automethod:: matching_id
.. automethod:: refund_matching_id
.. automethod:: shred_payment_info .. automethod:: shred_payment_info
.. automethod:: cancel_payment .. automethod:: cancel_payment

View File

@@ -55,6 +55,7 @@ visible boolean (optional) ``True`` by default, can hide a plugin s
restricted boolean (optional) ``False`` by default, restricts a plugin such that it can only be enabled restricted boolean (optional) ``False`` by default, restricts a plugin such that it can only be enabled
for an event by system administrators / superusers. for an event by system administrators / superusers.
experimental boolean (optional) ``False`` by default, marks a plugin as an experimental feature in the plugins list. experimental boolean (optional) ``False`` by default, marks a plugin as an experimental feature in the plugins list.
picture string (optional) Path to a picture resolvable through the static file system.
compatibility string Specifier for compatible pretix versions. compatibility string Specifier for compatible pretix versions.
================== ==================== =========================================================== ================== ==================== ===========================================================

View File

@@ -1,4 +1,4 @@
.. spelling:word-list:: Rebase rebasing .. spelling:: Rebase rebasing
Coding style and quality Coding style and quality
======================== ========================

View File

@@ -1,7 +1,7 @@
.. highlight:: python .. highlight:: python
:linenothreshold: 5 :linenothreshold: 5
.. spelling:word-list:: answ contrib .. spelling:: answ contrib
Data model Data model
========== ==========

View File

@@ -58,11 +58,11 @@ If you do not have a recent installation of ``nodejs``, install it now::
To make sure it is on your path variable, close and reopen your terminal. Now, install the Python-level dependencies of pretix:: To make sure it is on your path variable, close and reopen your terminal. Now, install the Python-level dependencies of pretix::
cd src/
pip3 install -e ".[dev]" pip3 install -e ".[dev]"
Next, you need to copy the SCSS files from the source folder to the STATIC_ROOT directory:: Next, you need to copy the SCSS files from the source folder to the STATIC_ROOT directory::
cd src/
python manage.py collectstatic --noinput python manage.py collectstatic --noinput
Then, create the local database:: Then, create the local database::
@@ -150,13 +150,6 @@ Add this to your ``src/pretix.cfg``::
Then execute ``python -m smtpd -n -c DebuggingServer localhost:1025``. Then execute ``python -m smtpd -n -c DebuggingServer localhost:1025``.
Working with periodic tasks
^^^^^^^^^^^^^^^^^^^^^^^^^^^
Periodic tasks (like sendmail rules) are run when an external scheduler (like cron)
triggers the ``runperiodic`` command.
To run periodic tasks, execute ``python manage.py runperiodic``.
Working with translations Working with translations
^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^
If you want to translate new strings that are not yet known to the translation system, If you want to translate new strings that are not yet known to the translation system,

Binary file not shown.

Before

Width:  |  Height:  |  Size: 278 KiB

After

Width:  |  Height:  |  Size: 274 KiB

View File

@@ -23,50 +23,30 @@ partition "data-based check" {
"Is the order in status PAID or PENDING\nand is the position not canceled?" --> if "" then "Is the order in status PAID or PENDING\nand is the position not canceled?" --> if "" then
-right->[no] "Return error CANCELED" -right->[no] "Return error CANCELED"
else else
-down->[yes] "Is one or more block set on the ticket?" -down->[yes] "Is the product part of the check-in list?"
--> if "" then --> if "" then
-right->[no] "Return error BLOCKED" -right->[no] "Return error PRODUCT"
else else
-down->[yes] "If this is not an exit, is the valid_from/valid_until\nconstraint on the ticket fulfilled?" -down->[yes] "Is the subevent part of the check-in list?"
--> if "" then --> if "" then
-right->[no] "Return error INVALID_TIME" -right->[no] "Return error INVALID"
note bottom: TODO\ninconsistent\nwith online\ncheck
else else
-down->[yes] "Is the product part of the check-in list?" -down->[yes] "Is the order in status PAID?"
--> if "" then --> if "" then
-right->[no] "Return error PRODUCT" -right->[no] "Does the check-in list include pending orders?"
else
-down->[yes] "Is the subevent part of the check-in list?"
--> if "" then --> if "" then
-right->[no] "Return error INVALID" -right->[no] "Return error UNPAID "
note bottom: TODO\ninconsistent\nwith online\ncheck
else else
-down->[yes] "Is the order in status PAID?" -down->[yes] "Is ignore_unpaid set?\n(Has the operator confirmed\nthe checkin?)"
--> if "" then --> if "" then
-right->[no] "Is Order.require_approval set?" -right->[no] "Return error UNPAID "
--> if "" then
-->[yes] "Return error UNPAID "
else
-right->[no] "Is Order.valid_if_pending set?"
--> if "" then
-->[yes] "Is this an entry or exit?"
else
-->[no] "Does the check-in list include pending orders?"
--> if "" then
-->[no] "Return error UNPAID "
else
-->[yes] "Is ignore_unpaid set on the request?\n(Has the operator confirmed\nthe checkin?)"
--> if "" then
-->[no] "Return error UNPAID "
else
-->[yes] "Is this an entry or exit?"
endif
endif
endif
endif
else else
-down->[yes] "Is this an entry or exit?" -down->[yes] "Is this an entry or exit?"
endif endif
endif endif
else
-down->[yes] "Is this an entry or exit?"
endif endif
endif endif
endif endif
@@ -118,26 +98,16 @@ partition "dataless check" {
--> if "" then --> if "" then
-right->[yes] "Return error REVOKED" -right->[yes] "Return error REVOKED"
else else
-down->[yes] "Is the ticket secret on the block list?" -down->[no] "Is the product part of the check-in list? "
--> if "" then --> if "" then
-right->[yes] "Return error BLOCKED " -right->[no] "Return error PRODUCT "
else else
-down->[yes] "If this is not an exit, is the valid_from/valid_until\nconstraint on the ticket fulfilled? " -down->[yes] "Is the subevent part of the check-in list? "
--> if "" then --> if "" then
-right->[no] "Return error INVALID_TIME " -right->[no] "Return error INVALID "
note bottom: TODO\ninconsistent\nwith online\ncheck
else else
-down->[no] "Is the product part of the check-in list? " --> "Is this an entry or exit? "
--> if "" then
-right->[no] "Return error PRODUCT "
else
-down->[yes] "Is the subevent part of the check-in list? "
--> if "" then
-right->[no] "Return error INVALID "
note bottom: TODO\ninconsistent\nwith online\ncheck
else
--> "Is this an entry or exit? "
endif
endif
endif endif
endif endif
endif endif

Binary file not shown.

Before

Width:  |  Height:  |  Size: 180 KiB

After

Width:  |  Height:  |  Size: 175 KiB

View File

@@ -38,51 +38,31 @@ else
endif endif
"Is the order in status PAID or PENDING\nand is the position not canceled?" --> if "" then "Is the order in status PAID or PENDING\nand is the position not canceled?" --> if "" then
-right->[no && !force] "Return error CANCELED" -right->[no] "Return error CANCELED"
else else
-down->[yes || force] "Is one or more block set on the ticket?" -down->[yes] "Is the product part of the check-in list?"
--> if "" then --> if "" then
-right->[no && !force] "Return error BLOCKED" -right->[no] "Return error PRODUCT"
else else
-down->[yes || force] "If this is not an exit, is the valid_from/valid_until\nconstraint on the ticket fulfilled?" -down->[yes] "Is the subevent part of the check-in list?"
--> if "" then --> if "" then
-right->[no && !force] "Return error INVALID_TIME" -right->[no] "Return error PRODUCT "
else else
-down->[yes || force] "Is the product part of the check-in list?" -down->[yes] "Is the order in status PAID\nor is this a forced upload?"
--> if "" then --> if "" then
-right->[no && !force] "Return error PRODUCT" -right->[no] "Does the check-in list include pending orders?"
else
-down->[yes || force] "Is the subevent part of the check-in list?"
--> if "" then --> if "" then
-right->[no && !force] "Return error PRODUCT " -right->[no] "Return error UNPAID "
else else
-down->[yes] "Is the order in status PAID?" -down->[yes] "Is ignore_unpaid set?\n(Has the operator confirmed\nthe checkin?)"
--> if "" then --> if "" then
-right->[no && !force] "Is Order.require_approval set?" -right->[no] "Return error UNPAID "
--> if "" then
-->[no] "Is Order.valid_if_pending set?"
--> if "" then
-down->[yes] "Is this an entry or exit?\nIs the upload forced?"
else
-right->[no] "Does the check-in list include pending orders?"
--> if "" then
-right->[no] "Return error UNPAID "
else
-down->[yes] "Is ignore_unpaid set on the request?\n(Has the operator confirmed\nthe checkin?)"
--> if "" then
-right->[no] "Return error UNPAID "
else
-down->[yes] "Is this an entry or exit?\nIs the upload forced?"
endif
endif
endif
else
-->[yes] "Return error UNPAID "
endif
else else
-down->[yes || force] "Is this an entry or exit?\nIs the upload forced?" -down->[yes] "Is this an entry or exit?\nIs the upload forced?"
endif endif
endif endif
else
-down->[yes] "Is this an entry or exit?\nIs the upload forced?"
endif endif
endif endif
endif endif

View File

@@ -1 +1,69 @@
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="254.156" height="109.594" version="1.1"><g transform="scale(1.9856)"><path d="M36.29 23.21c-.35 0-.61.06-.83.13v8.29c.19.06.45.13.77.13 1.73 0 2.46-1.44 2.46-4.26s-.64-4.29-2.4-4.29z" fill="#f8f8f8"/><path d="M61.22 23.15c-1.44 0-2.21 1.31-2.08 3.71l3.9-.58c.03-2.11-.58-3.14-1.82-3.14z" fill="#f8f8f8"/><path d="M127.39 17.1c.35-.03.61-.29.61-.64V.64c0-.35-.29-.64-.64-.64H75.94v2.48c0 .62-.5 1.12-1.12 1.12-.62 0-1.12-.5-1.12-1.12V0H.64C.29 0 0 .29 0 .64v15.82c0 .35.26.61.61.64 5.47.32 9.82 4.86 9.82 10.43 0 5.57-4.35 10.08-9.82 10.37-.35.03-.61.29-.61.64v15.82c0 .35.29.64.64.64h73.22-.16v-2.48c0-.63.49-1.12 1.12-1.12.63 0 1.12.5 1.12 1.12V55h-.16 51.58c.35 0 .64-.29.64-.64V38.54c0-.35-.26-.61-.61-.64-5.47-.32-9.82-4.83-9.82-10.4s4.35-10.11 9.82-10.4zM37.41 34.57c-.86 0-1.6-.1-1.95-.19v5.54H30.6v-18.5c1.31-.61 3.07-1.06 5.73-1.06 4.26 0 7.17 2.27 7.17 7.1 0 4.35-2.53 7.1-6.08 7.1zm15.58-10.78c-.9-.45-1.76-.45-2.4-.22v10.85h-4.86V21.43c1.41-.7 3.55-1.09 6.69-1.06.45 0 .93.03 1.41.06L53 23.79Zm14.56 4.26-8.03 1.12c.32 1.47 1.09 2.21 2.85 2.21 1.63 0 2.91-.35 3.68-.74l1.09 2.98c-1.22.58-2.82 1.06-5.38 1.06-4.51 0-6.88-3.04-6.88-7.17s2.21-7.1 6.53-7.1c4.35-.03 6.4 2.98 6.14 7.65zm8.38 18.56c0 .62-.5 1.12-1.12 1.12-.62 0-1.12-.5-1.12-1.12v-5.12c0-.62.5-1.12 1.12-1.12.62 0 1.12.52 1.12 1.12zm0-11.07c0 .6-.52 1.12-1.12 1.12-.6 0-1.12-.52-1.12-1.12v-5.12c0-.63.49-1.12 1.12-1.12.63 0 1.12.5 1.12 1.12zm0-11.01c0 .62-.5 1.12-1.12 1.12-.62 0-1.12-.5-1.12-1.12v-5.12c0-.62.5-1.12 1.12-1.12.62 0 1.12.52 1.12 1.12zm0-11.07c0 .6-.52 1.12-1.12 1.12-.6 0-1.12-.52-1.12-1.12V8.34c0-.63.49-1.12 1.12-1.12.63 0 1.12.5 1.12 1.12zM90.11 23.8h-2.02v6.18c0 1.02.35 1.41 1.09 1.41.35 0 .54-.06.93-.19v2.98c-.35.19-1.22.48-2.34.48-3.1 0-4.51-1.89-4.51-4.26v-6.59h-1.44v-3.17h1.44v-2.82l4.86-1.22v4.03h1.98v3.17zm7.07 10.62h-4.86V20.66h4.86zm-2.43-15.58c-1.38 0-2.5-.99-2.5-2.21s1.12-2.18 2.5-2.18 2.53.96 2.53 2.18c0 1.22-1.12 2.21-2.53 2.21zm12.35 15.58-1.76-3.81h-.06l-1.82 3.81h-4.9l4.32-7.1-3.87-6.66h5.06l1.66 3.46h.06l1.82-3.46h4.51l-4 6.37 4.38 7.42-5.41-.03z" fill="#f8f8f8"/></g></svg> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="254.15625"
height="109.59375"
viewBox="0 0 254.15625 109.59375"
version="1.1"
id="svg5"
sodipodi:docname="logo-white.svg"
inkscape:version="0.92.1 r"><metadata
id="metadata9">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1364"
inkscape:window-height="676"
id="namedview7"
showgrid="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:zoom="1"
inkscape:cx="56.462442"
inkscape:cy="54.796875"
inkscape:window-x="0"
inkscape:window-y="72"
inkscape:window-maximized="0"
inkscape:current-layer="svg5" />
id=&quot;svg2&quot;
version=&quot;1.1&quot;&gt;
<defs
id="defs4" />
<g
id="layer1"
transform="translate(-277.78125,-568.75)">
<path
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;marker:none;enable-background:accumulate"
d="m 20,20 v 34.09375 c 11.43679,0 20.71875,9.28196 20.71875,20.71875 C 40.71875,86.24928 31.43679,95.5 20,95.5 v 34.09375 h 146.6875 v -9.5 h 3 v 9.5 H 274.15625 V 95.5 c -0.0105,2e-5 -0.0208,0 -0.0312,0 -11.43678,0 -20.71875,-9.25072 -20.71875,-20.6875 0,-11.43679 9.28197,-20.71875 20.71875,-20.71875 0.0105,0 0.0208,-2e-5 0.0312,0 V 20 H 169.6875 v 9.09375 h -3 V 20 Z m 146.6875,16.09375 h 3 v 14 h -3 z m 41.44141,12.833984 c 2.79067,0 5.02343,1.92774 5.02343,4.3125 0,2.38476 -2.23276,4.363282 -5.02343,4.363282 -2.73994,0 -4.97266,-1.978522 -4.97266,-4.363282 0,-2.38476 2.23272,-4.3125 4.97266,-4.3125 z m -13.22852,4.210938 v 8.017578 h 3.95899 v 6.291016 h -3.95899 v 12.279296 c 0,2.02959 0.71015,2.791016 2.13086,2.791016 0.71035,0 1.06703,-0.10181 1.82813,-0.40625 v 5.935547 c -0.71036,0.40591 -2.38661,0.964844 -4.61915,0.964844 -6.13949,0 -8.98046,-3.753876 -8.98046,-8.472657 V 67.447266 h -2.8418 V 61.15625 h 2.8418 V 55.574219 Z M 166.6875,57.09375 h 3 v 14 h -3 z m -74.568359,3.554688 c 8.473509,0 14.207029,4.515688 14.207029,14.105468 0,8.62573 -5.02336,14.105469 -12.07617,14.105469 -1.72514,0 -3.147072,-0.20329 -3.857422,-0.40625 V 99.414062 H 80.751953 V 62.728516 c 2.58772,-1.21775 6.090268,-2.080081 11.367188,-2.080078 z m 49.863279,0 c 8.57499,0 12.63436,5.935363 12.12696,15.220703 l -15.93165,2.234375 c 0.60888,2.94289 2.18061,4.414062 5.68165,4.414062 3.24732,0 5.78445,-0.711556 7.30664,-1.472656 l 2.13086,5.886719 c -2.38476,1.16701 -5.58034,2.080078 -10.6543,2.080078 -8.93017,0 -13.64844,-6.037993 -13.64844,-14.257813 0,-8.21981 4.41329,-14.105468 12.98828,-14.105468 z m -17.92187,0.0059 c 0.8928,0.01358 1.82795,0.04496 2.80468,0.0957 l -1.67578,6.697266 c -1.77589,-0.86257 -3.50104,-0.913692 -4.76953,-0.457032 v 21.513672 h -9.64062 v -25.77539 c 2.79702,-1.376314 7.03166,-2.16926 13.28125,-2.074219 z m 79.24804,0.501953 h 9.64063 v 27.347656 h -9.64063 z m 13.23438,0 h 10.04687 l 3.29883,6.849609 h 0.10156 l 3.60157,-6.849609 h 8.98047 l -7.96485,12.632812 8.72656,14.714844 H 232.67969 L 229.17773,80.9434 h -0.10156 l -3.65234,7.560547 h -9.74219 l 8.57422,-14.105468 z m -74.9668,5.023438 c -2.84142,0 -4.41381,2.585948 -4.10937,7.355468 l 7.76367,-1.166015 c 0,-4.16064 -1.2188,-6.189454 -3.6543,-6.189453 z m -49.507811,0.09961 c -0.71035,0 -1.219131,0.101686 -1.675781,0.253906 v 16.439453 c 0.35517,0.15221 0.863828,0.253906 1.523438,0.253906 3.4503,0 4.871093,-2.840514 4.871093,-8.421874 0,-5.733571 -1.21772,-8.525391 -4.71875,-8.525391 z M 166.6875,78.09375 h 3 v 14 h -3 z m 0,21 h 3 v 14 h -3 z"
transform="translate(257.78125,548.75)"
id="rect3888"
inkscape:connector-curvature="0" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

View File

@@ -1,4 +1,4 @@
.. spelling:word-list:: .. spelling::
AGPL AGPL
AGPLv3 AGPLv3

View File

@@ -1,4 +1,4 @@
.. spelling:word-list:: .. spelling::
Analytics Analytics
List of plugins List of plugins

View File

@@ -1,5 +1,5 @@
.. highlight:: ini .. highlight:: ini
.. spelling:word-list:: .. spelling::
IdP IdP
skIDentity skIDentity

View File

@@ -1,10 +1,10 @@
sphinx==7.0.* sphinx==2.3.*
jinja2==3.1.* jinja2==3.0.*
sphinx-rtd-theme sphinx-rtd-theme
sphinxcontrib-httpdomain sphinxcontrib-httpdomain
sphinxcontrib-images sphinxcontrib-images
sphinxcontrib-jquery sphinxcontrib-spelling==4.*
sphinxcontrib-spelling==8.*
sphinxemoji sphinxemoji
pygments-markdown-lexer pygments-markdown-lexer
pyenchant==3.2.* # See https://github.com/rfk/pyenchant/pull/130
git+https://github.com/raphaelm/pyenchant.git@patch-1#egg=pyenchant

View File

@@ -1,11 +1,11 @@
-e ../ -e ../src/
sphinx==7.0.* sphinx==2.3.*
jinja2==3.1.* jinja2==3.0.*
sphinx-rtd-theme sphinx-rtd-theme
sphinxcontrib-httpdomain sphinxcontrib-httpdomain
sphinxcontrib-images sphinxcontrib-images
sphinxcontrib-jquery sphinxcontrib-spelling==4.*
sphinxcontrib-spelling==8.*
sphinxemoji sphinxemoji
pygments-markdown-lexer pygments-markdown-lexer
pyenchant==3.2.* # See https://github.com/rfk/pyenchant/pull/130
git+https://github.com/raphaelm/pyenchant.git@patch-1#egg=pyenchant

View File

@@ -201,10 +201,6 @@ record for the subdomain ``pretix._domainkey`` with the following contents::
Then, please contact support@pretix.eu and we will enable DKIM for your domain on our mail servers. Then, please contact support@pretix.eu and we will enable DKIM for your domain on our mail servers.
.. note:: Many SMTP servers impose rate limits on the sent emails, such as a maximum number of emails sent per hour.
These SMTP servers are often not suitable for use with pretix, in case you want to send an email to many
hundreds or thousands of ticket buyers. Depending on how the rate limit is implemented, emails might be lost
in this case, as pretix only retries email delivery for a certain time period.
.. _Sender Policy Framework: https://en.wikipedia.org/wiki/Sender_Policy_Framework .. _Sender Policy Framework: https://en.wikipedia.org/wiki/Sender_Policy_Framework
.. _SPF specification: http://www.open-spf.org/SPF_Record_Syntax .. _SPF specification: http://www.open-spf.org/SPF_Record_Syntax

View File

@@ -1,4 +1,4 @@
.. spelling:word-list:: .. spelling::
Warengutschein Warengutschein
Wertgutschein Wertgutschein

View File

@@ -1,7 +1,7 @@
Invoice settings Invoice settings
================ ================
.. spelling:word-list:: Inv .. spelling:: Inv
The settings at "Settings" → "Invoice" allow you to specify if and how pretix should generate invoices for your orders. The settings at "Settings" → "Invoice" allow you to specify if and how pretix should generate invoices for your orders.

View File

@@ -153,9 +153,9 @@ If you want to include all your public events, you can just reference your organ
There is an optional ``style`` parameter that let's you choose between a monthly calendar view, a week view and a list There is an optional ``style`` parameter that let's you choose between a monthly calendar view, a week view and a list
view. If you do not set it, the choice will be taken from your organizer settings:: view. If you do not set it, the choice will be taken from your organizer settings::
<pretix-widget event="https://pretix.eu/demo/series/" list-type="list"></pretix-widget> <pretix-widget event="https://pretix.eu/demo/series/" style="list"></pretix-widget>
<pretix-widget event="https://pretix.eu/demo/series/" list-type="calendar"></pretix-widget> <pretix-widget event="https://pretix.eu/demo/series/" style="calendar"></pretix-widget>
<pretix-widget event="https://pretix.eu/demo/series/" list-type="week"></pretix-widget> <pretix-widget event="https://pretix.eu/demo/series/" style="week"></pretix-widget>
If you have more than 100 events, the system might refuse to show a list view and always show a calendar for performance If you have more than 100 events, the system might refuse to show a list view and always show a calendar for performance
reasons instead. reasons instead.
@@ -164,7 +164,7 @@ You can see an example here:
.. raw:: html .. raw:: html
<pretix-widget event="https://pretix.eu/demo/series/" list-type="calendar"></pretix-widget> <pretix-widget event="https://pretix.eu/demo/series/" style="calendar"></pretix-widget>
<noscript> <noscript>
<div class="pretix-widget"> <div class="pretix-widget">
<div class="pretix-widget-info-message"> <div class="pretix-widget-info-message">
@@ -176,7 +176,7 @@ You can see an example here:
You can filter events by meta data attributes. You can create those attributes in your order profile and set their values in both event and series date You can filter events by meta data attributes. You can create those attributes in your order profile and set their values in both event and series date
settings. For example, if you set up a meta data property called "Promoted" that you set to "Yes" on some events, you can pass a filter like this:: settings. For example, if you set up a meta data property called "Promoted" that you set to "Yes" on some events, you can pass a filter like this::
<pretix-widget event="https://pretix.eu/demo/series/" list-type="list" filter="attr[Promoted]=Yes"></pretix-widget> <pretix-widget event="https://pretix.eu/demo/series/" style="list" filter="attr[Promoted]=Yes"></pretix-widget>
pretix Button pretix Button
------------- -------------
@@ -320,9 +320,6 @@ Currently, the following attributes are understood by pretix itself:
providers. All other providers will be disabled, no consent dialog will be shown. This is useful if you already providers. All other providers will be disabled, no consent dialog will be shown. This is useful if you already
asked the user for consent and don't want them to be asked again. Example: ``data-consent="facebook,google_analytics"`` asked the user for consent and don't want them to be asked again. Example: ``data-consent="facebook,google_analytics"``
When using the pretix-tracking plugin, the following values are supported::
``adform, facebook, gosquared, google_ads, google_analytics, hubspot, linkedin, matomo, twitter``
Any configured pretix plugins might understand more data fields. For example, if the appropriate plugins on pretix Any configured pretix plugins might understand more data fields. For example, if the appropriate plugins on pretix
Hosted or pretix Enterprise are active, you can pass the following fields: Hosted or pretix Enterprise are active, you can pass the following fields:

View File

@@ -1,163 +0,0 @@
[project]
name = "pretix"
dynamic = ["version"]
description = "Reinventing presales, one ticket at a time"
readme = "README.rst"
requires-python = ">=3.9"
license = {file = "LICENSE"}
keywords = ["tickets", "web", "shop", "ecommerce"]
authors = [
{name = "pretix team", email = "support@pretix.eu"},
]
maintainers = [
{name = "pretix team", email = "support@pretix.eu"},
]
classifiers = [
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
"Intended Audience :: Other Audience",
"Topic :: Internet :: WWW/HTTP :: Dynamic Content",
"Environment :: Web Environment",
"License :: OSI Approved :: GNU Affero General Public License v3",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Framework :: Django :: 3.2",
]
dependencies = [
"arabic-reshaper==3.0.0", # Support for Arabic in reportlab
"babel",
"BeautifulSoup4==4.12.*",
"bleach==5.0.*",
"celery==5.2.*",
"chardet==5.1.*",
"cryptography>=3.4.2",
"css-inline==0.8.*",
"defusedcsv>=1.1.0",
"dj-static",
"Django==3.2.*,>=3.2.18",
"django-bootstrap3==23.1.*",
"django-compressor==4.3.*",
"django-countries==7.5.*",
"django-filter==23.2",
"django-formset-js-improved==0.5.0.3",
"django-formtools==2.4.1",
"django-hierarkey==1.1.*",
"django-hijack==3.3.*",
"django-i18nfield==1.9.*,>=1.9.4",
"django-libsass==0.9",
"django-localflavor==4.0",
"django-markup",
"django-mysql",
"django-oauth-toolkit==2.2.*",
"django-otp==1.1.*",
"django-phonenumber-field==7.1.*",
"django-redis==5.2.*",
"django-scopes==2.0.*",
"django-statici18n==2.3.*",
"djangorestframework==3.14.*",
"dnspython==2.3.*",
"drf_ujson2==1.7.*",
"geoip2==4.*",
"importlib_metadata==6.6.*", # Polyfill, we can probably drop this once we require Python 3.10+
"isoweek",
"jsonschema",
"kombu==5.2.*",
"libsass==0.22.*",
"lxml",
"markdown==3.4.3", # 3.3.5 requires importlib-metadata>=4.4, but django-bootstrap3 requires importlib-metadata<3.
# We can upgrade markdown again once django-bootstrap3 upgrades or once we drop Python 3.6 and 3.7
"mt-940==4.23.*",
"oauthlib==3.2.*",
"openpyxl==3.1.*",
"packaging",
"paypalrestsdk==1.13.*",
"paypal-checkout-serversdk==1.0.*",
"PyJWT==2.6.*",
"phonenumberslite==8.13.*",
"Pillow==9.5.*",
"protobuf==4.23.*",
"psycopg2-binary",
"pycountry",
"pycparser==2.21",
"pycryptodome==3.18.*",
"pypdf==3.8.*",
"python-bidi==0.4.*", # Support for Arabic in reportlab
"python-dateutil==2.8.*",
"python-u2flib-server==4.*",
"pytz",
"pyuca",
"qrcode==7.4.*",
"redis==4.5.*,>=4.5.4",
"reportlab==4.0.*",
"requests==2.30.*",
"sentry-sdk==1.15.*",
"sepaxml==2.6.*",
"slimit",
"static3==0.7.*",
"stripe==5.4.*",
"text-unidecode==1.*",
"tlds>=2020041600",
"tqdm==4.*",
"vat_moss_forked==2020.3.20.0.11.0",
"vobject==0.9.*",
"webauthn==0.4.*",
"zeep==4.2.*"
]
[project.optional-dependencies]
memcached = ["pylibmc"]
mysql = ["mysqlclient"]
dev = [
"coverage",
"coveralls",
"flake8==6.0.*",
"freezegun",
"isort==5.12.*",
"pep8-naming==0.13.*",
"potypo",
"pycodestyle==2.10.*",
"pyflakes==3.0.*",
"pytest-cache",
"pytest-cov",
"pytest-django==4.*",
"pytest-mock==3.10.*",
"pytest-rerunfailures==11.*",
"pytest-sugar",
"pytest-xdist==3.2.*",
"pytest==7.3.*",
"responses",
]
[project.entry-points."distutils.commands"]
build = "pretix._build:CustomBuild"
build_ext = "pretix._build:CustomBuildExt"
[build-system]
build-backend = "backend"
backend-path = ["_build"]
requires = [
"setuptools",
"setuptools-rust",
"wheel",
"importlib_metadata",
"tomli",
]
[project.urls]
homepage = "https://pretix.eu"
documentation = "https://docs.pretix.eu"
repository = "https://github.com/pretix/pretix.git"
changelog = "https://pretix.eu/about/en/blog/"
[tool.setuptools]
include-package-data = true
[tool.setuptools.dynamic]
version = {attr = "pretix.__version__"}
[tool.setuptools.packages.find]
where = ["src"]
include = ["pretix*"]
namespaces = false

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

View File

@@ -1 +1,36 @@
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="159.567" height="159.567" version="1.1" viewBox="0 0 149.594 149.594"><g transform="matrix(.9375 0 0 .9375 20.413 20.413)"><path d="M45.52 48.98c-.74 0-1.28.13-1.75.27v17.43c.4.13.94.27 1.61.27 3.63 0 5.18-3.03 5.18-8.95s-1.35-9.02-5.05-9.02z" fill="#3b1c4a"/><path d="M114.72 36.13c.74-.07 1.28-.61 1.28-1.35V1.35c0-.74-.61-1.35-1.35-1.35H75.99v5.38c0 1.15-.94 2.09-2.09 2.09s-2.09-.94-2.09-2.09V0H1.35C.61 0 0 .61 0 1.35v33.44c0 .74.54 1.28 1.28 1.35 11.51.67 20.66 10.23 20.66 21.94s-9.15 21.2-20.66 21.8c-.74.07-1.28.61-1.28 1.35v33.44c0 .74.61 1.35 1.35 1.35h70.48v-5.38c0-1.19.9-2.09 2.09-2.09s2.09.94 2.09 2.09v5.38h38.66c.74 0 1.35-.61 1.35-1.35V81.23c0-.74-.54-1.28-1.28-1.35-11.51-.67-20.66-10.16-20.66-21.87s9.15-21.26 20.66-21.87zM47.87 72.87c-1.82 0-3.36-.2-4.1-.4v11.64H33.54V45.22C36.3 43.94 40 43 45.58 43c8.95 0 15.07 4.78 15.07 14.94 0 9.15-5.32 14.94-12.78 14.94zm28.12 25.3c0 1.15-.94 2.09-2.09 2.09s-2.09-.94-2.09-2.09V87.4c0-1.15.94-2.09 2.09-2.09s2.09.97 2.09 2.09zm0-23.28c0 1.11-.97 2.09-2.09 2.09-1.12 0-2.09-.97-2.09-2.09V64.12c0-1.19.9-2.09 2.09-2.09s2.09.94 2.09 2.09zm0-23.15c0 1.15-.94 2.09-2.09 2.09s-2.09-.94-2.09-2.09V40.97c0-1.15.94-2.09 2.09-2.09s2.09.97 2.09 2.09zm0-23.28c0 1.11-.97 2.09-2.09 2.09-1.12 0-2.09-.97-2.09-2.09V17.69c0-1.19.9-2.09 2.09-2.09s2.09.94 2.09 2.09z" fill="#3b1c4a"/></g></svg> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 149.59399 149.59399"
version="1.1"
id="svg2"
height="159.56693"
width="159.56693">
<defs
id="defs4" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
transform="translate(-257.78125,-548.74975)"
id="layer1">
<path
id="rect3888"
transform="matrix(0.93749999,0,0,0.93749999,257.78125,548.74975)"
d="M 21.333984 21.333984 L 21.333984 57.699219 C 33.470966 57.699219 43.320312 67.601506 43.320312 79.800781 C 43.320312 92.000035 33.470966 101.86719 21.333984 101.86719 L 21.333984 138.23438 L 94.226562 138.23438 L 94.226562 128.09961 L 97.410156 128.09961 L 97.410156 138.23438 L 138.23438 138.23438 L 138.23438 101.86719 C 138.22328 101.86721 138.21216 101.86719 138.20117 101.86719 C 126.0642 101.86719 116.21289 92.000035 116.21289 79.800781 C 116.21289 67.601506 126.0642 57.699219 138.20117 57.699219 C 138.21237 57.699219 138.22339 57.699197 138.23438 57.699219 L 138.23438 21.333984 L 97.410156 21.333984 L 97.410156 31.033203 L 94.226562 31.033203 L 94.226562 21.333984 L 21.333984 21.333984 z M 94.226562 38.5 L 97.410156 38.5 L 97.410156 53.433594 L 94.226562 53.433594 L 94.226562 38.5 z M 94.226562 60.900391 L 97.410156 60.900391 L 97.410156 75.833984 L 94.226562 75.833984 L 94.226562 60.900391 z M 67.044922 64.027344 C 76.359333 64.027344 82.662109 68.991742 82.662109 79.533203 C 82.662109 89.014942 77.139434 95.039062 69.386719 95.039062 C 67.490377 95.039062 65.927327 94.814901 65.146484 94.591797 L 65.146484 106.64062 L 54.550781 106.64062 L 54.550781 66.314453 C 57.395304 64.97585 61.244324 64.027344 67.044922 64.027344 z M 66.990234 70.216797 C 66.209392 70.216797 65.648458 70.328766 65.146484 70.496094 L 65.146484 88.568359 C 65.536906 88.735677 66.097199 88.845703 66.822266 88.845703 C 70.61497 88.845703 72.175781 85.725087 72.175781 79.589844 C 72.175781 73.287273 70.838704 70.216797 66.990234 70.216797 z M 94.226562 83.300781 L 97.410156 83.300781 L 97.410156 98.234375 L 94.226562 98.234375 L 94.226562 83.300781 z M 94.226562 105.69922 L 97.410156 105.69922 L 97.410156 120.63281 L 94.226562 120.63281 L 94.226562 105.69922 z "
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#3b1c4a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.06666672;marker:none;enable-background:accumulate" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

View File

@@ -1 +1,72 @@
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="600" height="400" version="1.1" viewBox="0 0 562.5 375"><g transform="translate(-259.031 -322.094)"><g transform="matrix(3.79525 0 0 3.79525 297.317 405.22)"><path d="M36.29 23.21c-.35 0-.61.06-.83.13v8.29c.19.06.45.13.77.13 1.73 0 2.46-1.44 2.46-4.26s-.64-4.29-2.4-4.29z" fill="#3b1c4a"/><path d="M61.22 23.15c-1.44 0-2.21 1.31-2.08 3.71l3.9-.58c.03-2.11-.58-3.14-1.82-3.14z" fill="#3b1c4a"/><path d="M127.39 17.1c.35-.03.61-.29.61-.64V.64c0-.35-.29-.64-.64-.64H75.81v2.48c0 .55-.45.99-.99.99s-.99-.45-.99-.99V0H.64C.29 0 0 .29 0 .64v15.82c0 .35.26.61.61.64 5.47.32 9.82 4.86 9.82 10.43 0 5.57-4.35 10.08-9.82 10.37-.35.03-.61.29-.61.64v15.82c0 .35.29.64.64.64h73.22-.03v-2.48c0-.57.43-.99.99-.99s.99.45.99.99V55h-.03 51.58c.35 0 .64-.29.64-.64V38.54c0-.35-.26-.61-.61-.64-5.47-.32-9.82-4.83-9.82-10.4s4.35-10.11 9.82-10.4zM37.41 34.57c-.86 0-1.6-.1-1.95-.19v5.54H30.6v-18.5c1.31-.61 3.07-1.06 5.73-1.06 4.26 0 7.17 2.27 7.17 7.1 0 4.35-2.53 7.1-6.08 7.1zm15.58-10.78c-.9-.45-1.76-.45-2.4-.22v10.85h-4.86V21.43c1.41-.7 3.55-1.09 6.69-1.06.45 0 .93.03 1.41.06L53 23.79Zm14.56 4.26-8.03 1.12c.32 1.47 1.09 2.21 2.85 2.21 1.63 0 2.91-.35 3.68-.74l1.09 2.98c-1.22.58-2.82 1.06-5.38 1.06-4.51 0-6.88-3.04-6.88-7.17s2.21-7.1 6.53-7.1c4.35-.03 6.4 2.98 6.14 7.65zm8.26 18.56c0 .55-.45.99-.99.99s-.99-.45-.99-.99v-5.12c0-.55.45-.99.99-.99s.99.46.99.99zm0-11.07c0 .53-.46.99-.99.99s-.99-.46-.99-.99v-5.12c0-.57.43-.99.99-.99s.99.45.99.99zm0-11.01c0 .55-.45.99-.99.99s-.99-.45-.99-.99v-5.12c0-.55.45-.99.99-.99s.99.46.99.99zm0-11.07c0 .53-.46.99-.99.99s-.99-.46-.99-.99V8.34c0-.57.43-.99.99-.99s.99.45.99.99zm14.3 10.34h-2.02v6.18c0 1.02.35 1.41 1.09 1.41.35 0 .54-.06.93-.19v2.98c-.35.19-1.22.48-2.34.48-3.1 0-4.51-1.89-4.51-4.26v-6.59h-1.44v-3.17h1.44v-2.82l4.86-1.22v4.03h1.98v3.17zm7.07 10.62h-4.86V20.66h4.86zm-2.43-15.58c-1.38 0-2.5-.99-2.5-2.21s1.12-2.18 2.5-2.18 2.53.96 2.53 2.18c0 1.22-1.12 2.21-2.53 2.21zm12.35 15.58-1.76-3.81h-.06l-1.82 3.81h-4.9l4.32-7.1-3.87-6.66h5.06l1.66 3.46h.06l1.82-3.46h4.51l-4 6.37 4.38 7.42-5.41-.03z" fill="#3b1c4a"/></g></g></svg> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="600"
height="400"
id="svg2"
version="1.1"
inkscape:version="0.92.1 r"
sodipodi:docname="logo.svg"
inkscape:export-filename="/tmp/LOGO.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96"
viewBox="0 0 562.50001 375.00002">
<defs
id="defs4" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.9899495"
inkscape:cx="133.36756"
inkscape:cy="276.10571"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1916"
inkscape:window-height="1041"
inkscape:window-x="1920"
inkscape:window-y="18"
inkscape:window-maximized="0"
fit-margin-top="20"
fit-margin-left="20"
fit-margin-right="20"
fit-margin-bottom="20" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Ebene 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-259.03125,-322.09374)">
<path
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#3b1c4a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.91138947;marker:none;enable-background:accumulate"
d="m 297.38548,404.85558 v 65.16643 c 21.86016,0 39.6016,17.74144 39.6016,39.60159 0,21.86015 -17.74144,39.54187 -39.6016,39.54187 v 65.16644 h 280.37693 v -18.1582 h 5.73417 v 18.1582 h 199.68046 v -65.16644 c -0.02,4e-5 -0.0397,0 -0.0596,0 -21.86015,0 -39.6016,-17.68172 -39.6016,-39.54187 0,-21.86015 17.74145,-39.60159 39.6016,-39.60159 0.02,0 0.0397,-4e-5 0.0596,0 V 404.85558 H 583.49658 v 17.38169 h -5.73417 V 404.85558 Z M 577.76241,435.617 h 5.73417 v 26.75945 h -5.73417 z m 79.21068,24.53074 c 5.33405,0 9.60172,3.68466 9.60172,8.24287 0,4.5582 -4.26767,8.33993 -9.60172,8.33993 -5.2371,0 -9.50469,-3.78173 -9.50469,-8.33993 0,-4.55821 4.26759,-8.24287 9.50469,-8.24287 z m -25.28486,8.04874 v 15.32472 h 7.56717 v 12.02458 h -7.56717 v 23.47051 c 0,3.87934 1.35737,5.33473 4.0729,5.33473 1.35775,0 2.03951,-0.19461 3.49427,-0.77651 v 11.34514 c -1.35777,0.77585 -4.56174,1.84419 -8.829,1.84419 -11.73495,0 -17.16515,-7.17511 -17.16515,-16.19455 v -25.02351 h -5.43179 V 483.5212 h 5.43179 v -10.66944 z m -53.92582,7.5597 h 5.73417 v 26.75945 h -5.73417 z m -142.52917,6.79439 c 16.19618,0 27.15517,8.63124 27.15517,26.96104 0,16.48713 -9.6016,26.96105 -23.08227,26.96105 -3.29741,0 -6.01528,-0.38857 -7.37304,-0.77651 v 20.95062 H 413.50612 V 486.5264 c 4.94614,-2.32759 11.64087,-3.97583 21.72712,-3.97583 z m 95.30815,0 c 16.39014,0 24.14917,11.34479 23.17933,29.09269 l -30.45158,4.27076 c 1.1638,5.62501 4.16799,8.437 10.85985,8.437 6.20689,0 11.05633,-1.36007 13.96583,-2.81482 l 4.0729,11.2518 c -4.5582,2.23062 -10.6662,3.97584 -20.36451,3.97584 -17.06903,0 -26.08749,-11.54096 -26.08749,-27.25223 0,-15.71126 8.43552,-26.96104 24.82567,-26.96104 z m -34.25568,0.0113 c 1.70649,0.026 3.49392,0.0859 5.36084,0.18292 l -3.20307,12.80108 c -3.39442,-1.6487 -6.69185,-1.74642 -9.11643,-0.87356 v 41.121 h -18.42698 v -49.2668 c 5.3462,-2.63068 13.44024,-4.1463 25.38564,-3.96465 z m 151.47387,0.95943 h 18.42699 v 52.27202 h -18.42699 z m 25.29605,0 h 19.20348 l 6.30535,13.09227 h 0.19412 l 6.884,-13.09227 h 17.16517 l -15.22393,24.14622 16.67986,28.1258 h -20.3645 l -6.69361,-14.45115 h -0.19412 l -6.98104,14.45115 h -18.62112 l 16.38867,-26.96104 z m -143.29075,9.60175 c -5.43106,0 -8.43651,4.94275 -7.85461,14.05916 l 14.8394,-2.22871 c 0,-7.9526 -2.3296,-11.83045 -6.98479,-11.83045 z m -94.6287,0.19038 c -1.35776,0 -2.33024,0.19437 -3.20308,0.48532 v 31.4222 c 0.67888,0.29093 1.65112,0.48531 2.91189,0.48531 6.59487,0 9.31055,-5.42933 9.31055,-16.09748 0,-10.95908 -2.32753,-16.29535 -9.01936,-16.29535 z m 142.62623,22.58194 h 5.73417 v 26.75946 h -5.73417 z m 0,40.13918 h 5.73417 v 26.75946 h -5.73417 z"
id="rect3888"
inkscape:connector-curvature="0"
inkscape:export-filename="/tmp/LOGO.png"
inkscape:export-xdpi="88"
inkscape:export-ydpi="88" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

View File

@@ -1 +1,68 @@
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="294.156" height="149.594" version="1.1"><g transform="translate(-257.781 -548.75)"><g transform="matrix(1.9856 0 0 1.9856 277.781 568.75)"><path d="M36.29 23.21c-.35 0-.61.06-.83.13v8.29c.19.06.45.13.77.13 1.73 0 2.46-1.44 2.46-4.26s-.64-4.29-2.4-4.29z" fill="#f8f8f8"/><path d="M61.22 23.15c-1.44 0-2.21 1.31-2.08 3.71l3.9-.58c.03-2.11-.58-3.14-1.82-3.14z" fill="#f8f8f8"/><path d="M127.39 17.1c.35-.03.61-.29.61-.64V.64c0-.35-.29-.64-.64-.64H75.94v2.48c0 .62-.5 1.12-1.12 1.12-.62 0-1.12-.5-1.12-1.12V0H.64C.29 0 0 .29 0 .64v15.82c0 .35.26.61.61.64 5.47.32 9.82 4.86 9.82 10.43 0 5.57-4.35 10.08-9.82 10.37-.35.03-.61.29-.61.64v15.82c0 .35.29.64.64.64h73.22-.16v-2.48c0-.63.49-1.12 1.12-1.12.63 0 1.12.5 1.12 1.12V55h-.16 51.58c.35 0 .64-.29.64-.64V38.54c0-.35-.26-.61-.61-.64-5.47-.32-9.82-4.83-9.82-10.4s4.35-10.11 9.82-10.4zM37.41 34.57c-.86 0-1.6-.1-1.95-.19v5.54H30.6v-18.5c1.31-.61 3.07-1.06 5.73-1.06 4.26 0 7.17 2.27 7.17 7.1 0 4.35-2.53 7.1-6.08 7.1zm15.58-10.78c-.9-.45-1.76-.45-2.4-.22v10.85h-4.86V21.43c1.41-.7 3.55-1.09 6.69-1.06.45 0 .93.03 1.41.06L53 23.79Zm14.56 4.26-8.03 1.12c.32 1.47 1.09 2.21 2.85 2.21 1.63 0 2.91-.35 3.68-.74l1.09 2.98c-1.22.58-2.82 1.06-5.38 1.06-4.51 0-6.88-3.04-6.88-7.17s2.21-7.1 6.53-7.1c4.35-.03 6.4 2.98 6.14 7.65zm8.38 18.56c0 .62-.5 1.12-1.12 1.12-.62 0-1.12-.5-1.12-1.12v-5.12c0-.62.5-1.12 1.12-1.12.62 0 1.12.52 1.12 1.12zm0-11.07c0 .6-.52 1.12-1.12 1.12-.6 0-1.12-.52-1.12-1.12v-5.12c0-.63.49-1.12 1.12-1.12.63 0 1.12.5 1.12 1.12zm0-11.01c0 .62-.5 1.12-1.12 1.12-.62 0-1.12-.5-1.12-1.12v-5.12c0-.62.5-1.12 1.12-1.12.62 0 1.12.52 1.12 1.12zm0-11.07c0 .6-.52 1.12-1.12 1.12-.6 0-1.12-.52-1.12-1.12V8.34c0-.63.49-1.12 1.12-1.12.63 0 1.12.5 1.12 1.12zM90.11 23.8h-2.02v6.18c0 1.02.35 1.41 1.09 1.41.35 0 .54-.06.93-.19v2.98c-.35.19-1.22.48-2.34.48-3.1 0-4.51-1.89-4.51-4.26v-6.59h-1.44v-3.17h1.44v-2.82l4.86-1.22v4.03h1.98v3.17zm7.07 10.62h-4.86V20.66h4.86zm-2.43-15.58c-1.38 0-2.5-.99-2.5-2.21s1.12-2.18 2.5-2.18 2.53.96 2.53 2.18c0 1.22-1.12 2.21-2.53 2.21zm12.35 15.58-1.76-3.81h-.06l-1.82 3.81h-4.9l4.32-7.1-3.87-6.66h5.06l1.66 3.46h.06l1.82-3.46h4.51l-4 6.37 4.38 7.42-5.41-.03z" fill="#f8f8f8"/></g></g></svg> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="294.15625"
height="149.59375"
id="svg2"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="logo_draft_white.svg"
inkscape:export-filename="/home/raphael/proj/pretix/pretix/logo_draft.png"
inkscape:export-xdpi="88.529999"
inkscape:export-ydpi="88.529999">
<defs
id="defs4" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.9899495"
inkscape:cx="121.06383"
inkscape:cy="277.43904"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="956"
inkscape:window-height="1041"
inkscape:window-x="2880"
inkscape:window-y="18"
inkscape:window-maximized="0"
fit-margin-top="20"
fit-margin-left="20"
fit-margin-right="20"
fit-margin-bottom="20" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Ebene 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-257.78125,-548.75)">
<path
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;marker:none;enable-background:accumulate"
d="M 20 20 L 20 54.09375 C 31.43679 54.09375 40.71875 63.37571 40.71875 74.8125 C 40.71875 86.24928 31.43679 95.5 20 95.5 L 20 129.59375 L 166.6875 129.59375 L 166.6875 120.09375 L 169.6875 120.09375 L 169.6875 129.59375 L 274.15625 129.59375 L 274.15625 95.5 C 274.14575 95.50002 274.1354 95.5 274.125 95.5 C 262.68822 95.5 253.40625 86.24928 253.40625 74.8125 C 253.40625 63.37571 262.68822 54.09375 274.125 54.09375 C 274.1355 54.09375 274.14585 54.09373 274.15625 54.09375 L 274.15625 20 L 169.6875 20 L 169.6875 29.09375 L 166.6875 29.09375 L 166.6875 20 L 20 20 z M 166.6875 36.09375 L 169.6875 36.09375 L 169.6875 50.09375 L 166.6875 50.09375 L 166.6875 36.09375 z M 208.12891 48.927734 C 210.91958 48.927734 213.15234 50.855474 213.15234 53.240234 C 213.15234 55.624994 210.91958 57.603516 208.12891 57.603516 C 205.38897 57.603516 203.15625 55.624994 203.15625 53.240234 C 203.15625 50.855474 205.38897 48.927734 208.12891 48.927734 z M 194.90039 53.138672 L 194.90039 61.15625 L 198.85938 61.15625 L 198.85938 67.447266 L 194.90039 67.447266 L 194.90039 79.726562 C 194.90039 81.756152 195.61054 82.517578 197.03125 82.517578 C 197.7416 82.517578 198.09828 82.415768 198.85938 82.111328 L 198.85938 88.046875 C 198.14902 88.452785 196.47277 89.011719 194.24023 89.011719 C 188.10074 89.011719 185.25977 85.257843 185.25977 80.539062 L 185.25977 67.447266 L 182.41797 67.447266 L 182.41797 61.15625 L 185.25977 61.15625 L 185.25977 55.574219 L 194.90039 53.138672 z M 166.6875 57.09375 L 169.6875 57.09375 L 169.6875 71.09375 L 166.6875 71.09375 L 166.6875 57.09375 z M 92.119141 60.648438 C 100.59265 60.648438 106.32617 65.164126 106.32617 74.753906 C 106.32617 83.379636 101.30281 88.859375 94.25 88.859375 C 92.52486 88.859375 91.102928 88.656085 90.392578 88.453125 L 90.392578 99.414062 L 80.751953 99.414062 L 80.751953 62.728516 C 83.339673 61.510766 86.842221 60.648435 92.119141 60.648438 z M 141.98242 60.648438 C 150.55741 60.648438 154.61678 66.583801 154.10938 75.869141 L 138.17773 78.103516 C 138.78661 81.046406 140.35834 82.517578 143.85938 82.517578 C 147.1067 82.517578 149.64383 81.806022 151.16602 81.044922 L 153.29688 86.931641 C 150.91212 88.098651 147.71654 89.011719 142.64258 89.011719 C 133.71241 89.011719 128.99414 82.973726 128.99414 74.753906 C 128.99414 66.534096 133.40743 60.648438 141.98242 60.648438 z M 124.06055 60.654297 C 124.95335 60.667874 125.8885 60.69926 126.86523 60.75 L 125.18945 67.447266 C 123.41356 66.584696 121.68841 66.533574 120.41992 66.990234 L 120.41992 88.503906 L 110.7793 88.503906 L 110.7793 62.728516 C 113.57632 61.352202 117.81096 60.559256 124.06055 60.654297 z M 203.30859 61.15625 L 212.94922 61.15625 L 212.94922 88.503906 L 203.30859 88.503906 L 203.30859 61.15625 z M 216.54297 61.15625 L 226.58984 61.15625 L 229.88867 68.005859 L 229.99023 68.005859 L 233.5918 61.15625 L 242.57227 61.15625 L 234.60742 73.789062 L 243.33398 88.503906 L 232.67969 88.503906 L 229.17773 80.943359 L 229.07617 80.943359 L 225.42383 88.503906 L 215.68164 88.503906 L 224.25586 74.398438 L 216.54297 61.15625 z M 141.57617 66.179688 C 138.73475 66.179688 137.16236 68.765636 137.4668 73.535156 L 145.23047 72.369141 C 145.23047 68.208501 144.01167 66.179687 141.57617 66.179688 z M 92.068359 66.279297 C 91.358009 66.279297 90.849228 66.380983 90.392578 66.533203 L 90.392578 82.972656 C 90.747748 83.124866 91.256406 83.226562 91.916016 83.226562 C 95.366316 83.226562 96.787109 80.386048 96.787109 74.804688 C 96.787109 69.071117 95.569389 66.279297 92.068359 66.279297 z M 166.6875 78.09375 L 169.6875 78.09375 L 169.6875 92.09375 L 166.6875 92.09375 L 166.6875 78.09375 z M 166.6875 99.09375 L 169.6875 99.09375 L 169.6875 113.09375 L 166.6875 113.09375 L 166.6875 99.09375 z "
transform="translate(257.78125,548.75)"
id="rect3888" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 5.8 KiB

View File

@@ -1,40 +0,0 @@
[check-manifest]
ignore =
env/**
doc/*
deployment/*
res/*
src/.update-locales
src/Makefile
src/manage.py
src/pretix/icons/*
src/pretix/static.dist/**
src/pretix/static/jsi18n/**
src/requirements.txt
src/requirements/*
src/tests/*
src/tests/api/*
src/tests/base/*
src/tests/control/*
src/tests/testdummy/*
src/tests/templates/*
src/tests/presale/*
src/tests/doc/*
src/tests/helpers/*
src/tests/media/*
src/tests/multidomain/*
src/tests/plugins/*
src/tests/plugins/badges/*
src/tests/plugins/banktransfer/*
src/tests/plugins/paypal/*
src/tests/plugins/paypal2/*
src/tests/plugins/pretixdroid/*
src/tests/plugins/stripe/*
src/tests/plugins/sendmail/*
src/tests/plugins/ticketoutputpdf/*
.*
CODE_OF_CONDUCT.md
CONTRIBUTING.md
Dockerfile
SECURITY.md

View File

@@ -1,50 +0,0 @@
#
# This file is part of pretix (Community Edition).
#
# Copyright (C) 2014-2020 Raphael Michel and contributors
# Copyright (C) 2020-2021 rami.io GmbH and contributors
#
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
# Public License as published by the Free Software Foundation in version 3 of the License.
#
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
# this file, see <https://pretix.eu/about/en/license>.
#
# 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 Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>.
#
import sys
from pathlib import Path
import setuptools
sys.path.append(str(Path.cwd() / 'src'))
def _CustomBuild(*args, **kwargs):
print(sys.path)
from pretix._build import CustomBuild
return CustomBuild(*args, **kwargs)
def _CustomBuildExt(*args, **kwargs):
from pretix._build import CustomBuildExt
return CustomBuildExt(*args, **kwargs)
cmdclass = {
'build': _CustomBuild,
'build_ext': _CustomBuildExt,
}
if __name__ == "__main__":
setuptools.setup(
cmdclass=cmdclass,
)

33
src/MANIFEST.in Normal file
View File

@@ -0,0 +1,33 @@
include LICENSE
include README.rst
global-include *.proto
recursive-include pretix/static *
recursive-include pretix/static.dist *
recursive-include pretix/locale *
recursive-include pretix/helpers/locale *
recursive-include pretix/base/templates *
recursive-include pretix/control/templates *
recursive-include pretix/presale/templates *
recursive-include pretix/plugins/banktransfer/templates *
recursive-include pretix/plugins/banktransfer/static *
recursive-include pretix/plugins/manualpayment/templates *
recursive-include pretix/plugins/manualpayment/static *
recursive-include pretix/plugins/paypal/templates *
recursive-include pretix/plugins/paypal/static *
recursive-include pretix/plugins/paypal2/templates *
recursive-include pretix/plugins/paypal2/static *
recursive-include pretix/plugins/pretixdroid/templates *
recursive-include pretix/plugins/pretixdroid/static *
recursive-include pretix/plugins/sendmail/templates *
recursive-include pretix/plugins/statistics/templates *
recursive-include pretix/plugins/statistics/static *
recursive-include pretix/plugins/stripe/templates *
recursive-include pretix/plugins/stripe/static *
recursive-include pretix/plugins/ticketoutputpdf/templates *
recursive-include pretix/plugins/ticketoutputpdf/static *
recursive-include pretix/plugins/badges/templates *
recursive-include pretix/plugins/badges/static *
recursive-include pretix/plugins/returnurl/templates *
recursive-include pretix/plugins/returnurl/static *
recursive-include pretix/plugins/webcheckin/templates *
recursive-include pretix/plugins/webcheckin/static *

View File

@@ -19,4 +19,4 @@
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see # You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>. # <https://www.gnu.org/licenses/>.
# #
__version__ = "4.21.0.dev0" __version__ = "4.16.1"

View File

@@ -1,252 +0,0 @@
#
# This file is part of pretix (Community Edition).
#
# Copyright (C) 2014-2020 Raphael Michel and contributors
# Copyright (C) 2020-2021 rami.io GmbH and contributors
#
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
# Public License as published by the Free Software Foundation in version 3 of the License.
#
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
# this file, see <https://pretix.eu/about/en/license>.
#
# 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 Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>.
#
import os
import django.conf.locale
from pycountry import currencies
from django.utils.translation import gettext_lazy as _ # NOQA
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
USE_I18N = True
USE_L10N = True
USE_TZ = True
INSTALLED_APPS = [
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'django.contrib.humanize',
'pretix.base',
'pretix.control',
'pretix.presale',
'pretix.multidomain',
'pretix.api',
'pretix.helpers',
'rest_framework',
'djangoformsetjs',
'compressor',
'bootstrap3',
'pretix.plugins.banktransfer',
'pretix.plugins.stripe',
'pretix.plugins.paypal',
'pretix.plugins.paypal2',
'pretix.plugins.ticketoutputpdf',
'pretix.plugins.sendmail',
'pretix.plugins.statistics',
'pretix.plugins.reports',
'pretix.plugins.checkinlists',
'pretix.plugins.pretixdroid',
'pretix.plugins.badges',
'pretix.plugins.manualpayment',
'pretix.plugins.returnurl',
'pretix.plugins.webcheckin',
'django_countries',
'oauth2_provider',
'phonenumber_field',
'statici18n',
]
FORMAT_MODULE_PATH = [
'pretix.helpers.formats',
]
ALL_LANGUAGES = [
('en', _('English')),
('de', _('German')),
('de-informal', _('German (informal)')),
('ar', _('Arabic')),
('zh-hans', _('Chinese (simplified)')),
('zh-hant', _('Chinese (traditional)')),
('cs', _('Czech')),
('da', _('Danish')),
('nl', _('Dutch')),
('nl-informal', _('Dutch (informal)')),
('fr', _('French')),
('fi', _('Finnish')),
('gl', _('Galician')),
('el', _('Greek')),
('it', _('Italian')),
('lv', _('Latvian')),
('pl', _('Polish')),
('pt-pt', _('Portuguese (Portugal)')),
('pt-br', _('Portuguese (Brazil)')),
('ro', _('Romanian')),
('ru', _('Russian')),
('es', _('Spanish')),
('tr', _('Turkish')),
('uk', _('Ukrainian')),
]
LANGUAGES_OFFICIAL = {
'en', 'de', 'de-informal'
}
LANGUAGES_RTL = {
'ar', 'hw'
}
LANGUAGES_INCUBATING = {
'pl', 'fi', 'pt-br', 'gl',
}
LOCALE_PATHS = [
os.path.join(os.path.dirname(__file__), 'locale'),
]
EXTRA_LANG_INFO = {
'de-informal': {
'bidi': False,
'code': 'de-informal',
'name': 'German (informal)',
'name_local': 'Deutsch',
'public_code': 'de',
},
'nl-informal': {
'bidi': False,
'code': 'nl-informal',
'name': 'Dutch (informal)',
'name_local': 'Nederlands',
'public_code': 'nl',
},
'fr': {
'bidi': False,
'code': 'fr',
'name': 'French',
'name_local': 'Français'
},
'lv': {
'bidi': False,
'code': 'lv',
'name': 'Latvian',
'name_local': 'Latviešu'
},
'pt-pt': {
'bidi': False,
'code': 'pt-pt',
'name': 'Portuguese',
'name_local': 'Português',
},
}
django.conf.locale.LANG_INFO.update(EXTRA_LANG_INFO)
template_loaders = (
'django.template.loaders.filesystem.Loader',
'pretix.helpers.template_loaders.AppLoader',
)
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.base.context.contextprocessor',
'pretix.control.context.contextprocessor',
'pretix.presale.context.contextprocessor',
],
'loaders': template_loaders
},
},
]
STATIC_ROOT = os.path.join(os.path.dirname(__file__), 'static.dist')
STATICFILES_FINDERS = (
'django.contrib.staticfiles.finders.FileSystemFinder',
'django.contrib.staticfiles.finders.AppDirectoriesFinder',
'compressor.finders.CompressorFinder',
)
STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'pretix/static')
] if os.path.exists(os.path.join(BASE_DIR, 'pretix/static')) else []
STATICI18N_ROOT = os.path.join(BASE_DIR, "pretix/static")
STATICFILES_STORAGE = 'django.contrib.staticfiles.storage.ManifestStaticFilesStorage'
# if os.path.exists(os.path.join(DATA_DIR, 'static')):
# STATICFILES_DIRS.insert(0, os.path.join(DATA_DIR, 'static'))
COMPRESS_PRECOMPILERS = (
('text/x-scss', 'django_libsass.SassCompiler'),
('text/vue', 'pretix.helpers.compressor.VueCompiler'),
)
COMPRESS_OFFLINE_CONTEXT = {
'basetpl': 'empty.html',
}
COMPRESS_ENABLED = True
COMPRESS_OFFLINE = True
COMPRESS_FILTERS = {
'css': (
# CssAbsoluteFilter is incredibly slow, especially when dealing with our _flags.scss
# However, we don't need it if we consequently use the static() function in Sass
# 'compressor.filters.css_default.CssAbsoluteFilter',
'compressor.filters.cssmin.rCSSMinFilter',
),
'js': (
'compressor.filters.jsmin.JSMinFilter',
)
}
CURRENCIES = list(currencies)
CURRENCY_PLACES = {
# default is 2
'BIF': 0,
'CLP': 0,
'DJF': 0,
'GNF': 0,
'JPY': 0,
'KMF': 0,
'KRW': 0,
'MGA': 0,
'PYG': 0,
'RWF': 0,
'VND': 0,
'VUV': 0,
'XAF': 0,
'XOF': 0,
'XPF': 0,
}
PRETIX_EMAIL_NONE_VALUE = 'none@well-known.pretix.eu'
PRETIX_PRIMARY_COLOR = '#8E44B3'
# pretix includes caching options for some special situations where full HTML responses are cached. This might be
# stressful for some cache setups so it is enabled by default and currently can't be enabled through pretix.cfg
CACHE_LARGE_VALUES_ALLOWED = False
CACHE_LARGE_VALUES_ALIAS = 'default'

View File

@@ -1,74 +0,0 @@
#
# This file is part of pretix (Community Edition).
#
# Copyright (C) 2014-2020 Raphael Michel and contributors
# Copyright (C) 2020-2021 rami.io GmbH and contributors
#
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
# Public License as published by the Free Software Foundation in version 3 of the License.
#
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
# this file, see <https://pretix.eu/about/en/license>.
#
# 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 Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>.
#
import os
import shutil
import subprocess
from setuptools.command.build import build
from setuptools.command.build_ext import build_ext
here = os.path.abspath(os.path.dirname(__file__))
npm_installed = False
def npm_install():
global npm_installed
if not npm_installed:
# keep this in sync with Makefile!
node_prefix = os.path.join(here, 'static.dist', 'node_prefix')
os.makedirs(node_prefix, exist_ok=True)
shutil.copytree(os.path.join(here, 'static', 'npm_dir'), node_prefix, dirs_exist_ok=True)
subprocess.check_call('npm install', shell=True, cwd=node_prefix)
npm_installed = True
class CustomBuild(build):
def run(self):
if "PRETIX_DOCKER_BUILD" in os.environ:
return # this is a hack to allow calling this file early in our docker build to make use of caching
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "pretix._build_settings")
os.environ.setdefault("PRETIX_IGNORE_CONFLICTS", "True")
import django
django.setup()
from django.conf import settings
from django.core import management
settings.COMPRESS_ENABLED = True
settings.COMPRESS_OFFLINE = True
npm_install()
management.call_command('compilemessages', verbosity=1)
management.call_command('compilejsi18n', verbosity=1)
management.call_command('collectstatic', verbosity=1, interactive=False)
management.call_command('compress', verbosity=1)
build.run(self)
class CustomBuildExt(build_ext):
def run(self):
if "PRETIX_DOCKER_BUILD" in os.environ:
return # this is a hack to allow calling this file early in our docker build to make use of caching
npm_install()
build_ext.run(self)

View File

@@ -1,48 +0,0 @@
#
# This file is part of pretix (Community Edition).
#
# Copyright (C) 2014-2020 Raphael Michel and contributors
# Copyright (C) 2020-2021 rami.io GmbH and contributors
#
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
# Public License as published by the Free Software Foundation in version 3 of the License.
#
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
# this file, see <https://pretix.eu/about/en/license>.
#
# 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 Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>.
#
"""
This file contains settings that we need at wheel require time. All settings that we only need at runtime are set
in settings.py.
"""
from ._base_settings import * # NOQA
ENTROPY = {
'order_code': 5,
'customer_identifier': 7,
'ticket_secret': 32,
'voucher_code': 16,
'giftcard_secret': 12,
}
MAIL_FROM_ORGANIZERS = 'invalid@invalid'
FILE_UPLOAD_MAX_SIZE_EMAIL_AUTO_ATTACHMENT = 10
FILE_UPLOAD_MAX_SIZE_EMAIL_ATTACHMENT = 10
FILE_UPLOAD_MAX_SIZE_IMAGE = 10
DEFAULT_CURRENCY = 'EUR'
SECRET_KEY = "build-time-secret-key"
HAS_REDIS = False
STATIC_URL = '/static/'
HAS_MEMCACHED = False
HAS_CELERY = False
HAS_GEOIP = False
SENTRY_ENABLED = False

View File

@@ -19,12 +19,9 @@
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see # You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>. # <https://www.gnu.org/licenses/>.
# #
import logging
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
logger = logging.getLogger(__name__)
class FullAccessSecurityProfile: class FullAccessSecurityProfile:
identifier = 'full' identifier = 'full'
@@ -39,13 +36,7 @@ class AllowListSecurityProfile:
def is_allowed(self, request): def is_allowed(self, request):
key = (request.method, f"{request.resolver_match.namespace}:{request.resolver_match.url_name}") key = (request.method, f"{request.resolver_match.namespace}:{request.resolver_match.url_name}")
if key in self.allowlist: return key in self.allowlist
return True
else:
logger.info(
f'Request {key} not allowed in profile {self.identifier}'
)
return False
class PretixScanSecurityProfile(AllowListSecurityProfile): class PretixScanSecurityProfile(AllowListSecurityProfile):
@@ -74,14 +65,12 @@ class PretixScanSecurityProfile(AllowListSecurityProfile):
('GET', 'api-v1:checkinlistpos-list'), ('GET', 'api-v1:checkinlistpos-list'),
('POST', 'api-v1:checkinlistpos-redeem'), ('POST', 'api-v1:checkinlistpos-redeem'),
('GET', 'api-v1:revokedsecrets-list'), ('GET', 'api-v1:revokedsecrets-list'),
('GET', 'api-v1:blockedsecrets-list'),
('GET', 'api-v1:order-list'), ('GET', 'api-v1:order-list'),
('GET', 'api-v1:orderposition-pdf_image'), ('GET', 'api-v1:orderposition-pdf_image'),
('GET', 'api-v1:event.settings'), ('GET', 'api-v1:event.settings'),
('POST', 'api-v1:upload'), ('POST', 'api-v1:upload'),
('POST', 'api-v1:checkinrpc.redeem'), ('POST', 'api-v1:checkinrpc.redeem'),
('GET', 'api-v1:checkinrpc.search'), ('GET', 'api-v1:checkinrpc.search'),
('GET', 'api-v1:reusablemedium-list'),
) )
@@ -110,7 +99,6 @@ class PretixScanNoSyncNoSearchSecurityProfile(AllowListSecurityProfile):
('POST', 'api-v1:checkinlist-failed_checkins'), ('POST', 'api-v1:checkinlist-failed_checkins'),
('POST', 'api-v1:checkinlistpos-redeem'), ('POST', 'api-v1:checkinlistpos-redeem'),
('GET', 'api-v1:revokedsecrets-list'), ('GET', 'api-v1:revokedsecrets-list'),
('GET', 'api-v1:blockedsecrets-list'),
('GET', 'api-v1:orderposition-pdf_image'), ('GET', 'api-v1:orderposition-pdf_image'),
('GET', 'api-v1:event.settings'), ('GET', 'api-v1:event.settings'),
('POST', 'api-v1:upload'), ('POST', 'api-v1:upload'),
@@ -145,7 +133,6 @@ class PretixScanNoSyncSecurityProfile(AllowListSecurityProfile):
('GET', 'api-v1:checkinlistpos-list'), ('GET', 'api-v1:checkinlistpos-list'),
('POST', 'api-v1:checkinlistpos-redeem'), ('POST', 'api-v1:checkinlistpos-redeem'),
('GET', 'api-v1:revokedsecrets-list'), ('GET', 'api-v1:revokedsecrets-list'),
('GET', 'api-v1:blockedsecrets-list'),
('GET', 'api-v1:orderposition-pdf_image'), ('GET', 'api-v1:orderposition-pdf_image'),
('GET', 'api-v1:event.settings'), ('GET', 'api-v1:event.settings'),
('POST', 'api-v1:upload'), ('POST', 'api-v1:upload'),
@@ -201,7 +188,6 @@ class PretixPosSecurityProfile(AllowListSecurityProfile):
('DELETE', 'api-v1:cartposition-detail'), ('DELETE', 'api-v1:cartposition-detail'),
('GET', 'api-v1:giftcard-list'), ('GET', 'api-v1:giftcard-list'),
('POST', 'api-v1:giftcard-transact'), ('POST', 'api-v1:giftcard-transact'),
('PATCH', 'api-v1:giftcard-detail'),
('GET', 'plugins:pretix_posbackend:posclosing-list'), ('GET', 'plugins:pretix_posbackend:posclosing-list'),
('POST', 'plugins:pretix_posbackend:posreceipt-list'), ('POST', 'plugins:pretix_posbackend:posreceipt-list'),
('POST', 'plugins:pretix_posbackend:posclosing-list'), ('POST', 'plugins:pretix_posbackend:posclosing-list'),
@@ -213,7 +199,6 @@ class PretixPosSecurityProfile(AllowListSecurityProfile):
('POST', 'plugins:pretix_posbackend:stripeterminal.paymentintent'), ('POST', 'plugins:pretix_posbackend:stripeterminal.paymentintent'),
('PUT', 'plugins:pretix_posbackend:file.upload'), ('PUT', 'plugins:pretix_posbackend:file.upload'),
('GET', 'api-v1:revokedsecrets-list'), ('GET', 'api-v1:revokedsecrets-list'),
('GET', 'api-v1:blockedsecrets-list'),
('GET', 'api-v1:event.settings'), ('GET', 'api-v1:event.settings'),
('GET', 'plugins:pretix_seating:event.event'), ('GET', 'plugins:pretix_seating:event.event'),
('GET', 'plugins:pretix_seating:event.event.subevent'), ('GET', 'plugins:pretix_seating:event.event.subevent'),
@@ -222,7 +207,6 @@ class PretixPosSecurityProfile(AllowListSecurityProfile):
('POST', 'api-v1:upload'), ('POST', 'api-v1:upload'),
('POST', 'api-v1:checkinrpc.redeem'), ('POST', 'api-v1:checkinrpc.redeem'),
('GET', 'api-v1:checkinrpc.search'), ('GET', 'api-v1:checkinrpc.search'),
('POST', 'api-v1:reusablemedium-lookup'),
) )

View File

@@ -20,7 +20,6 @@
# <https://www.gnu.org/licenses/>. # <https://www.gnu.org/licenses/>.
# #
import json import json
import logging
from hashlib import sha1 from hashlib import sha1
from django.conf import settings from django.conf import settings
@@ -35,8 +34,6 @@ from pretix.api.models import ApiCall
from pretix.base.models import Organizer from pretix.base.models import Organizer
from pretix.helpers import OF_SELF from pretix.helpers import OF_SELF
logger = logging.getLogger(__name__)
class IdempotencyMiddleware: class IdempotencyMiddleware:
def __init__(self, get_response): def __init__(self, get_response):
@@ -100,9 +97,6 @@ class IdempotencyMiddleware:
return resp return resp
else: else:
if call.locked: if call.locked:
logger.info(
f'Concurrent request with idempotency key {idempotency_key} blocked.'
)
r = JsonResponse( r = JsonResponse(
{'detail': 'Concurrent request with idempotency key.'}, {'detail': 'Concurrent request with idempotency key.'},
status=status.HTTP_409_CONFLICT, status=status.HTTP_409_CONFLICT,
@@ -117,7 +111,6 @@ class IdempotencyMiddleware:
content=content, content=content,
status=call.response_code, status=call.response_code,
) )
logger.info(f'API response replayed from idempotency store for key {idempotency_key} [{call.response_code}]')
for k, v in json.loads(call.response_headers).values(): for k, v in json.loads(call.response_headers).values():
r[k] = v r[k] = v
return r return r

View File

@@ -1,18 +0,0 @@
# Generated by Django 3.2.17 on 2023-02-07 12:18
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('pretixapi', '0009_auto_20221217_1847'),
]
operations = [
migrations.AddField(
model_name='webhook',
name='comment',
field=models.CharField(max_length=255, null=True),
),
]

View File

@@ -112,7 +112,6 @@ class WebHook(models.Model):
target_url = models.URLField(verbose_name=_("Target URL"), max_length=255) target_url = models.URLField(verbose_name=_("Target URL"), max_length=255)
all_events = models.BooleanField(default=True, verbose_name=_("All events (including newly created ones)")) all_events = models.BooleanField(default=True, verbose_name=_("All events (including newly created ones)"))
limit_events = models.ManyToManyField('pretixbase.Event', verbose_name=_("Limit to events"), blank=True) limit_events = models.ManyToManyField('pretixbase.Event', verbose_name=_("Limit to events"), blank=True)
comment = models.CharField(verbose_name=_("Comment"), max_length=255, null=True, blank=True)
class Meta: class Meta:
ordering = ('id',) ordering = ('id',)

View File

@@ -19,19 +19,9 @@
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see # You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>. # <https://www.gnu.org/licenses/>.
# #
from rest_framework.filters import OrderingFilter
from rest_framework.pagination import PageNumberPagination from rest_framework.pagination import PageNumberPagination
from pretix.helpers import get_deterministic_ordering
class Pagination(PageNumberPagination): class Pagination(PageNumberPagination):
page_size_query_param = 'page_size' page_size_query_param = 'page_size'
max_page_size = 50 max_page_size = 50
class TotalOrderingFilter(OrderingFilter):
def get_ordering(self, request, queryset, view):
o = super().get_ordering(request, queryset, view)
o = get_deterministic_ordering(queryset.model, o)
return o

View File

@@ -19,30 +19,3 @@
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see # You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>. # <https://www.gnu.org/licenses/>.
# #
from rest_framework import serializers
class AsymmetricField(serializers.Field):
def __init__(self, read, write, **kwargs):
self.read = read
self.write = write
super().__init__(
required=self.write.required,
default=self.write.default,
initial=self.write.initial,
source=self.write.source if self.write.source != self.write.field_name else None,
label=self.write.label,
allow_null=self.write.allow_null,
error_messages=self.write.error_messages,
validators=self.write.validators,
**kwargs
)
def to_internal_value(self, data):
return self.write.to_internal_value(data)
def to_representation(self, value):
return self.read.to_representation(value)
def run_validation(self, data=serializers.empty):
return self.write.run_validation(data)

View File

@@ -26,7 +26,6 @@ from rest_framework.exceptions import ValidationError
from pretix.api.serializers.event import SubEventSerializer from pretix.api.serializers.event import SubEventSerializer
from pretix.api.serializers.i18n import I18nAwareModelSerializer from pretix.api.serializers.i18n import I18nAwareModelSerializer
from pretix.base.channels import get_all_sales_channels from pretix.base.channels import get_all_sales_channels
from pretix.base.media import MEDIA_TYPES
from pretix.base.models import Checkin, CheckinList from pretix.base.models import Checkin, CheckinList
@@ -85,7 +84,6 @@ class CheckinRPCRedeemInputSerializer(serializers.Serializer):
lists = serializers.PrimaryKeyRelatedField(required=True, many=True, queryset=CheckinList.objects.none()) lists = serializers.PrimaryKeyRelatedField(required=True, many=True, queryset=CheckinList.objects.none())
secret = serializers.CharField(required=True, allow_null=False) secret = serializers.CharField(required=True, allow_null=False)
force = serializers.BooleanField(default=False, required=False) force = serializers.BooleanField(default=False, required=False)
source_type = serializers.ChoiceField(choices=[(k, v) for k, v in MEDIA_TYPES.items()], default='barcode')
type = serializers.ChoiceField(choices=Checkin.CHECKIN_TYPES, default=Checkin.TYPE_ENTRY) type = serializers.ChoiceField(choices=Checkin.CHECKIN_TYPES, default=Checkin.TYPE_ENTRY)
ignore_unpaid = serializers.BooleanField(default=False, required=False) ignore_unpaid = serializers.BooleanField(default=False, required=False)
questions_supported = serializers.BooleanField(default=True, required=False) questions_supported = serializers.BooleanField(default=True, required=False)

View File

@@ -50,9 +50,7 @@ from pretix.api.serializers.i18n import I18nAwareModelSerializer
from pretix.api.serializers.settings import SettingsSerializer from pretix.api.serializers.settings import SettingsSerializer
from pretix.base.models import Device, Event, TaxRule, TeamAPIToken from pretix.base.models import Device, Event, TaxRule, TeamAPIToken
from pretix.base.models.event import SubEvent from pretix.base.models.event import SubEvent
from pretix.base.models.items import ( from pretix.base.models.items import SubEventItem, SubEventItemVariation
ItemMetaProperty, SubEventItem, SubEventItemVariation,
)
from pretix.base.services.seating import ( from pretix.base.services.seating import (
SeatProtected, generate_seats, validate_plan_change, SeatProtected, generate_seats, validate_plan_change,
) )
@@ -61,7 +59,6 @@ from pretix.base.settings import (
LazyI18nStringList, validate_event_settings, LazyI18nStringList, validate_event_settings,
) )
from pretix.base.signals import api_event_settings_fields from pretix.base.signals import api_event_settings_fields
from pretix.multidomain.urlreverse import build_absolute_uri
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -167,10 +164,6 @@ class EventSerializer(I18nAwareModelSerializer):
timezone = TimeZoneField(required=False, choices=[(a, a) for a in common_timezones]) timezone = TimeZoneField(required=False, choices=[(a, a) for a in common_timezones])
valid_keys = ValidKeysField(source='*', read_only=True) valid_keys = ValidKeysField(source='*', read_only=True)
best_availability_state = serializers.IntegerField(allow_null=True, read_only=True) best_availability_state = serializers.IntegerField(allow_null=True, read_only=True)
public_url = serializers.SerializerMethodField('get_event_url', read_only=True)
def get_event_url(self, event):
return build_absolute_uri(event, 'presale:event.index')
class Meta: class Meta:
model = Event model = Event
@@ -178,7 +171,7 @@ class EventSerializer(I18nAwareModelSerializer):
'date_to', 'date_admission', 'is_public', 'presale_start', 'date_to', 'date_admission', 'is_public', 'presale_start',
'presale_end', 'location', 'geo_lat', 'geo_lon', 'has_subevents', 'meta_data', 'seating_plan', 'presale_end', 'location', 'geo_lat', 'geo_lon', 'has_subevents', 'meta_data', 'seating_plan',
'plugins', 'seat_category_mapping', 'timezone', 'item_meta_properties', 'valid_keys', 'plugins', 'seat_category_mapping', 'timezone', 'item_meta_properties', 'valid_keys',
'sales_channels', 'best_availability_state', 'public_url') 'sales_channels', 'best_availability_state')
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
@@ -669,7 +662,6 @@ class EventSettingsSerializer(SettingsSerializer):
'show_times', 'show_times',
'show_items_outside_presale_period', 'show_items_outside_presale_period',
'display_net_prices', 'display_net_prices',
'hide_prices_from_attendees',
'presale_start_show_date', 'presale_start_show_date',
'locales', 'locales',
'locale', 'locale',
@@ -685,7 +677,6 @@ class EventSettingsSerializer(SettingsSerializer):
'waiting_list_phones_asked', 'waiting_list_phones_asked',
'waiting_list_phones_required', 'waiting_list_phones_required',
'waiting_list_phones_explanation_text', 'waiting_list_phones_explanation_text',
'waiting_list_limit_per_user',
'max_items_per_order', 'max_items_per_order',
'reservation_time', 'reservation_time',
'contact_mail', 'contact_mail',
@@ -696,7 +687,6 @@ class EventSettingsSerializer(SettingsSerializer):
'frontpage_subevent_ordering', 'frontpage_subevent_ordering',
'event_list_type', 'event_list_type',
'event_list_available_only', 'event_list_available_only',
'event_calendar_future_only',
'frontpage_text', 'frontpage_text',
'event_info_text', 'event_info_text',
'attendee_names_asked', 'attendee_names_asked',
@@ -768,7 +758,6 @@ class EventSettingsSerializer(SettingsSerializer):
'invoice_footer_text', 'invoice_footer_text',
'invoice_eu_currencies', 'invoice_eu_currencies',
'invoice_logo_image', 'invoice_logo_image',
'invoice_renderer_highlight_order_code',
'cancel_allow_user', 'cancel_allow_user',
'cancel_allow_user_until', 'cancel_allow_user_until',
'cancel_allow_user_unpaid_keep', 'cancel_allow_user_unpaid_keep',
@@ -789,7 +778,6 @@ class EventSettingsSerializer(SettingsSerializer):
'change_allow_user_addons', 'change_allow_user_addons',
'change_allow_user_until', 'change_allow_user_until',
'change_allow_user_price', 'change_allow_user_price',
'change_allow_attendee',
'primary_color', 'primary_color',
'theme_color_success', 'theme_color_success',
'theme_color_danger', 'theme_color_danger',
@@ -801,21 +789,6 @@ class EventSettingsSerializer(SettingsSerializer):
'logo_show_title', 'logo_show_title',
'og_image', 'og_image',
'name_scheme', 'name_scheme',
'reusable_media_active',
'reusable_media_type_barcode',
'reusable_media_type_barcode_identifier_length',
'reusable_media_type_nfc_uid',
'reusable_media_type_nfc_uid_autocreate_giftcard',
'reusable_media_type_nfc_uid_autocreate_giftcard_currency',
]
readonly_fields = [
# These are read-only since they are currently only settable on organizers, not events
'reusable_media_active',
'reusable_media_type_barcode',
'reusable_media_type_barcode_identifier_length',
'reusable_media_type_nfc_uid',
'reusable_media_type_nfc_uid_autocreate_giftcard',
'reusable_media_type_nfc_uid_autocreate_giftcard_currency',
] ]
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
@@ -882,9 +855,6 @@ class DeviceEventSettingsSerializer(EventSettingsSerializer):
'invoice_address_from_tax_id', 'invoice_address_from_tax_id',
'invoice_address_from_vat_id', 'invoice_address_from_vat_id',
'name_scheme', 'name_scheme',
'reusable_media_type_barcode',
'reusable_media_type_nfc_uid',
'system_question_order',
] ]
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
@@ -906,23 +876,3 @@ class DeviceEventSettingsSerializer(EventSettingsSerializer):
else [] else []
) )
) )
class MultiLineStringField(serializers.Field):
def to_representation(self, value):
return [v.strip() for v in value.splitlines()]
def to_internal_value(self, data):
if isinstance(data, list) and len(data) > 0:
return "\n".join(data)
else:
raise ValidationError('Invalid data type.')
class ItemMetaPropertiesSerializer(I18nAwareModelSerializer):
allowed_values = MultiLineStringField(allow_null=True)
class Meta:
model = ItemMetaProperty
fields = ('id', 'name', 'default', 'required', 'allowed_values')

View File

@@ -20,7 +20,6 @@
# <https://www.gnu.org/licenses/>. # <https://www.gnu.org/licenses/>.
# #
from django.conf import settings from django.conf import settings
from django.core.validators import URLValidator
from i18nfield.fields import I18nCharField, I18nTextField from i18nfield.fields import I18nCharField, I18nTextField
from i18nfield.strings import LazyI18nString from i18nfield.strings import LazyI18nString
from rest_framework.exceptions import ValidationError from rest_framework.exceptions import ValidationError
@@ -70,17 +69,3 @@ class I18nAwareModelSerializer(ModelSerializer):
I18nAwareModelSerializer.serializer_field_mapping[I18nCharField] = I18nField I18nAwareModelSerializer.serializer_field_mapping[I18nCharField] = I18nField
I18nAwareModelSerializer.serializer_field_mapping[I18nTextField] = I18nField I18nAwareModelSerializer.serializer_field_mapping[I18nTextField] = I18nField
class I18nURLField(I18nField):
def to_internal_value(self, value):
value = super().to_internal_value(value)
if not value:
return value
if isinstance(value.data, dict):
for v in value.data.values():
if v:
URLValidator()(v)
else:
URLValidator()(value.data)
return value

View File

@@ -52,7 +52,7 @@ from pretix.base.models import (
class InlineItemVariationSerializer(I18nAwareModelSerializer): class InlineItemVariationSerializer(I18nAwareModelSerializer):
price = serializers.DecimalField(read_only=True, decimal_places=2, max_digits=13, price = serializers.DecimalField(read_only=True, decimal_places=2, max_digits=10,
coerce_to_string=True) coerce_to_string=True)
meta_data = MetaDataField(required=False, source='*') meta_data = MetaDataField(required=False, source='*')
@@ -60,8 +60,8 @@ class InlineItemVariationSerializer(I18nAwareModelSerializer):
model = ItemVariation model = ItemVariation
fields = ('id', 'value', 'active', 'description', fields = ('id', 'value', 'active', 'description',
'position', 'default_price', 'price', 'original_price', 'require_approval', 'position', 'default_price', 'price', 'original_price', 'require_approval',
'require_membership', 'require_membership_types', 'require_membership_hidden', 'require_membership', 'require_membership_types',
'checkin_attention', 'available_from', 'available_until', 'require_membership_hidden', 'available_from', 'available_until',
'sales_channels', 'hide_without_voucher', 'meta_data') 'sales_channels', 'hide_without_voucher', 'meta_data')
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
@@ -76,7 +76,7 @@ class InlineItemVariationSerializer(I18nAwareModelSerializer):
class ItemVariationSerializer(I18nAwareModelSerializer): class ItemVariationSerializer(I18nAwareModelSerializer):
price = serializers.DecimalField(read_only=True, decimal_places=2, max_digits=13, price = serializers.DecimalField(read_only=True, decimal_places=2, max_digits=10,
coerce_to_string=True) coerce_to_string=True)
meta_data = MetaDataField(required=False, source='*') meta_data = MetaDataField(required=False, source='*')
@@ -84,8 +84,8 @@ class ItemVariationSerializer(I18nAwareModelSerializer):
model = ItemVariation model = ItemVariation
fields = ('id', 'value', 'active', 'description', fields = ('id', 'value', 'active', 'description',
'position', 'default_price', 'price', 'original_price', 'require_approval', 'position', 'default_price', 'price', 'original_price', 'require_approval',
'require_membership', 'require_membership_types', 'require_membership_hidden', 'require_membership', 'require_membership_types',
'checkin_attention', 'available_from', 'available_until', 'require_membership_hidden', 'available_from', 'available_until',
'sales_channels', 'hide_without_voucher', 'meta_data') 'sales_channels', 'hide_without_voucher', 'meta_data')
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
@@ -242,10 +242,7 @@ class ItemSerializer(I18nAwareModelSerializer):
'show_quota_left', 'hidden_if_available', 'allow_waitinglist', 'issue_giftcard', 'meta_data', 'show_quota_left', 'hidden_if_available', 'allow_waitinglist', 'issue_giftcard', 'meta_data',
'require_membership', 'require_membership_types', 'require_membership_hidden', 'grant_membership_type', 'require_membership', 'require_membership_types', 'require_membership_hidden', 'grant_membership_type',
'grant_membership_duration_like_event', 'grant_membership_duration_days', 'grant_membership_duration_like_event', 'grant_membership_duration_days',
'grant_membership_duration_months', 'validity_mode', 'validity_fixed_from', 'validity_fixed_until', 'grant_membership_duration_months')
'validity_dynamic_duration_minutes', 'validity_dynamic_duration_hours', 'validity_dynamic_duration_days',
'validity_dynamic_duration_months', 'validity_dynamic_start_choice', 'validity_dynamic_start_choice_day_limit',
'media_policy', 'media_type')
read_only_fields = ('has_variations',) read_only_fields = ('has_variations',)
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
@@ -264,7 +261,6 @@ class ItemSerializer(I18nAwareModelSerializer):
Item.clean_per_order(data.get('min_per_order'), data.get('max_per_order')) Item.clean_per_order(data.get('min_per_order'), data.get('max_per_order'))
Item.clean_available(data.get('available_from'), data.get('available_until')) Item.clean_available(data.get('available_from'), data.get('available_until'))
Item.clean_media_settings(self.context['event'], data.get('media_policy'), data.get('media_type'), data.get('issue_giftcard'))
if data.get('personalized') and not data.get('admission'): if data.get('personalized') and not data.get('admission'):
raise ValidationError(_('Only admission products can currently be personalized.')) raise ValidationError(_('Only admission products can currently be personalized.'))
@@ -306,9 +302,9 @@ class ItemSerializer(I18nAwareModelSerializer):
if not self.instance: if not self.instance:
for addon_data in value: for addon_data in value:
ItemAddOn.clean_categories(self.context['event'], None, self.instance, addon_data['addon_category']) ItemAddOn.clean_categories(self.context['event'], None, self.instance, addon_data['addon_category'])
ItemAddOn.clean_min_count(addon_data.get('min_count', 0)) ItemAddOn.clean_min_count(addon_data['min_count'])
ItemAddOn.clean_max_count(addon_data.get('max_count', 0)) ItemAddOn.clean_max_count(addon_data['max_count'])
ItemAddOn.clean_max_min_count(addon_data.get('max_count', 0), addon_data.get('min_count', 0)) ItemAddOn.clean_max_min_count(addon_data['max_count'], addon_data['min_count'])
return value return value
@cached_property @cached_property
@@ -442,7 +438,7 @@ class QuestionSerializer(I18nAwareModelSerializer):
'ask_during_checkin', 'identifier', 'dependency_question', 'dependency_values', 'ask_during_checkin', 'identifier', 'dependency_question', 'dependency_values',
'hidden', 'dependency_value', 'print_on_invoice', 'help_text', 'valid_number_min', 'hidden', 'dependency_value', 'print_on_invoice', 'help_text', 'valid_number_min',
'valid_number_max', 'valid_date_min', 'valid_date_max', 'valid_datetime_min', 'valid_datetime_max', 'valid_number_max', 'valid_date_min', 'valid_date_max', 'valid_datetime_min', 'valid_datetime_max',
'valid_string_length_max', 'valid_file_portrait') 'valid_file_portrait')
def validate_identifier(self, value): def validate_identifier(self, value):
Question._clean_identifier(self.context['event'], value, self.instance) Question._clean_identifier(self.context['event'], value, self.instance)

View File

@@ -1,130 +0,0 @@
#
# This file is part of pretix (Community Edition).
#
# Copyright (C) 2014-2020 Raphael Michel and contributors
# Copyright (C) 2020-2021 rami.io GmbH and contributors
#
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
# Public License as published by the Free Software Foundation in version 3 of the License.
#
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
# this file, see <https://pretix.eu/about/en/license>.
#
# 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 Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>.
#
import logging
from decimal import Decimal
from django.utils.translation import gettext_lazy as _
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from pretix.api.serializers.i18n import I18nAwareModelSerializer
from pretix.api.serializers.order import OrderPositionSerializer
from pretix.api.serializers.organizer import (
CustomerSerializer, GiftCardSerializer,
)
from pretix.base.models import Order, OrderPosition, ReusableMedium
logger = logging.getLogger(__name__)
class NestedOrderMiniSerializer(I18nAwareModelSerializer):
event = serializers.SlugRelatedField(slug_field='slug', read_only=True)
class Meta:
model = Order
fields = ['code', 'event']
class NestedOrderPositionSerializer(OrderPositionSerializer):
order = NestedOrderMiniSerializer()
class NestedGiftCardSerializer(GiftCardSerializer):
def to_representation(self, instance):
d = super().to_representation(instance)
if hasattr(instance, 'cached_value'):
d['value'] = str(Decimal(instance.cached_value).quantize(Decimal("0.01")))
else:
d['value'] = str(Decimal(instance.value).quantize(Decimal("0.01")))
return d
class ReusableMediaSerializer(I18nAwareModelSerializer):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if 'linked_giftcard' in self.context['request'].query_params.getlist('expand'):
self.fields['linked_giftcard'] = NestedGiftCardSerializer(read_only=True, context=self.context)
if 'linked_giftcard.owner_ticket' in self.context['request'].query_params.getlist('expand'):
self.fields['linked_giftcard'].fields['owner_ticket'] = NestedOrderPositionSerializer(read_only=True, context=self.context)
else:
self.fields['linked_giftcard'] = serializers.PrimaryKeyRelatedField(
required=False,
allow_null=True,
queryset=self.context['organizer'].issued_gift_cards.all()
)
if 'linked_orderposition' in self.context['request'].query_params.getlist('expand'):
self.fields['linked_orderposition'] = NestedOrderPositionSerializer(read_only=True)
else:
self.fields['linked_orderposition'] = serializers.PrimaryKeyRelatedField(
required=False,
allow_null=True,
queryset=OrderPosition.all.filter(order__event__organizer=self.context['organizer']),
)
if 'customer' in self.context['request'].query_params.getlist('expand'):
self.fields['customer'] = CustomerSerializer(read_only=True)
else:
self.fields['customer'] = serializers.SlugRelatedField(
required=False,
allow_null=True,
slug_field='identifier',
queryset=self.context['organizer'].customers.all()
)
def validate(self, data):
data = super().validate(data)
if 'type' in data and 'identifier' in data:
qs = self.context['organizer'].reusable_media.filter(
identifier=data['identifier'], type=data['type']
)
if self.instance:
qs = qs.exclude(pk=self.instance.pk)
if qs.exists():
raise ValidationError(
{'identifier': _('A medium with the same identifier and type already exists in your organizer account.')}
)
return data
class Meta:
model = ReusableMedium
fields = (
'id',
'created',
'updated',
'type',
'identifier',
'active',
'expires',
'customer',
'linked_orderposition',
'linked_giftcard',
'info',
'notes',
)
class MediaLookupInputSerializer(serializers.Serializer):
type = serializers.CharField(required=True)
identifier = serializers.CharField(required=True)

View File

@@ -33,7 +33,6 @@ from django.utils.encoding import force_str
from django.utils.timezone import now from django.utils.timezone import now
from django.utils.translation import gettext_lazy from django.utils.translation import gettext_lazy
from django_countries.fields import Country from django_countries.fields import Country
from django_scopes import scopes_disabled
from rest_framework import serializers from rest_framework import serializers
from rest_framework.exceptions import ValidationError from rest_framework.exceptions import ValidationError
from rest_framework.relations import SlugRelatedField from rest_framework.relations import SlugRelatedField
@@ -49,12 +48,11 @@ from pretix.base.decimal import round_decimal
from pretix.base.i18n import language from pretix.base.i18n import language
from pretix.base.models import ( from pretix.base.models import (
CachedFile, Checkin, Customer, Invoice, InvoiceAddress, InvoiceLine, Item, CachedFile, Checkin, Customer, Invoice, InvoiceAddress, InvoiceLine, Item,
ItemVariation, Order, OrderPosition, Question, QuestionAnswer, ItemVariation, Order, OrderPosition, Question, QuestionAnswer, Seat,
ReusableMedium, Seat, SubEvent, TaxRule, Voucher, SubEvent, TaxRule, Voucher,
) )
from pretix.base.models.orders import ( from pretix.base.models.orders import (
BlockedTicketSecret, CartPosition, OrderFee, OrderPayment, OrderRefund, CartPosition, OrderFee, OrderPayment, OrderRefund, RevokedTicketSecret,
RevokedTicketSecret,
) )
from pretix.base.pdf import get_images, get_variables from pretix.base.pdf import get_images, get_variables
from pretix.base.services.cart import error_messages from pretix.base.services.cart import error_messages
@@ -302,9 +300,7 @@ class FailedCheckinSerializer(I18nAwareModelSerializer):
class OrderDownloadsField(serializers.Field): class OrderDownloadsField(serializers.Field):
def to_representation(self, instance: Order): def to_representation(self, instance: Order):
if instance.status != Order.STATUS_PAID: if instance.status != Order.STATUS_PAID:
if instance.status != Order.STATUS_PENDING or instance.require_approval or ( if instance.status != Order.STATUS_PENDING or instance.require_approval or not instance.event.settings.ticket_download_pending:
not instance.valid_if_pending and not instance.event.settings.ticket_download_pending
):
return [] return []
request = self.context['request'] request = self.context['request']
@@ -328,9 +324,7 @@ class OrderDownloadsField(serializers.Field):
class PositionDownloadsField(serializers.Field): class PositionDownloadsField(serializers.Field):
def to_representation(self, instance: OrderPosition): def to_representation(self, instance: OrderPosition):
if instance.order.status != Order.STATUS_PAID: if instance.order.status != Order.STATUS_PAID:
if instance.order.status != Order.STATUS_PENDING or instance.order.require_approval or ( if instance.order.status != Order.STATUS_PENDING or instance.order.require_approval or not instance.order.event.settings.ticket_download_pending:
not instance.order.valid_if_pending and not instance.order.event.settings.ticket_download_pending
):
return [] return []
if not instance.generate_ticket: if not instance.generate_ticket:
return [] return []
@@ -357,9 +351,6 @@ class PdfDataSerializer(serializers.Field):
def to_representation(self, instance: OrderPosition): def to_representation(self, instance: OrderPosition):
res = {} res = {}
if 'event' not in self.context:
return {}
ev = instance.subevent or instance.order.event ev = instance.subevent or instance.order.event
with language(instance.order.locale, instance.order.event.settings.region): with language(instance.order.locale, instance.order.event.settings.region):
# This needs to have some extra performance improvements to avoid creating hundreds of queries when # This needs to have some extra performance improvements to avoid creating hundreds of queries when
@@ -385,9 +376,11 @@ class PdfDataSerializer(serializers.Field):
res['meta:' + k] = v res['meta:' + k] = v
if instance.variation_id: if instance.variation_id:
print(instance, instance.variation, instance.variation_id, instance.item)
if not hasattr(instance.variation, '_cached_meta_data'): if not hasattr(instance.variation, '_cached_meta_data'):
instance.variation.item = instance.item # saves some database lookups instance.variation.item = instance.item # saves some database lookups
instance.variation._cached_meta_data = instance.variation.meta_data instance.variation._cached_meta_data = instance.variation.meta_data
print(instance.variation._cached_meta_data.items())
for k, v in instance.variation._cached_meta_data.items(): for k, v in instance.variation._cached_meta_data.items():
res['itemmeta:' + k] = v res['itemmeta:' + k] = v
else: else:
@@ -444,12 +437,11 @@ class OrderPositionSerializer(I18nAwareModelSerializer):
fields = ('id', 'order', 'positionid', 'item', 'variation', 'price', 'attendee_name', 'attendee_name_parts', fields = ('id', 'order', 'positionid', 'item', 'variation', 'price', 'attendee_name', 'attendee_name_parts',
'company', 'street', 'zipcode', 'city', 'country', 'state', 'discount', 'company', 'street', 'zipcode', 'city', 'country', 'state', 'discount',
'attendee_email', 'voucher', 'tax_rate', 'tax_value', 'secret', 'addon_to', 'subevent', 'checkins', 'attendee_email', 'voucher', 'tax_rate', 'tax_value', 'secret', 'addon_to', 'subevent', 'checkins',
'downloads', 'answers', 'tax_rule', 'pseudonymization_id', 'pdf_data', 'seat', 'canceled', 'downloads', 'answers', 'tax_rule', 'pseudonymization_id', 'pdf_data', 'seat', 'canceled')
'valid_from', 'valid_until', 'blocked')
read_only_fields = ( read_only_fields = (
'id', 'order', 'positionid', 'item', 'variation', 'price', 'voucher', 'tax_rate', 'tax_value', 'secret', 'id', 'order', 'positionid', 'item', 'variation', 'price', 'voucher', 'tax_rate', 'tax_value', 'secret',
'addon_to', 'subevent', 'checkins', 'downloads', 'answers', 'tax_rule', 'pseudonymization_id', 'pdf_data', 'addon_to', 'subevent', 'checkins', 'downloads', 'answers', 'tax_rule', 'pseudonymization_id', 'pdf_data',
'seat', 'canceled', 'discount', 'valid_from', 'valid_until', 'blocked' 'seat', 'canceled', 'discount',
) )
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
@@ -471,7 +463,7 @@ class OrderPositionSerializer(I18nAwareModelSerializer):
class RequireAttentionField(serializers.Field): class RequireAttentionField(serializers.Field):
def to_representation(self, instance: OrderPosition): def to_representation(self, instance: OrderPosition):
return instance.require_checkin_attention return instance.order.checkin_attention or instance.item.checkin_attention
class AttendeeNameField(serializers.Field): class AttendeeNameField(serializers.Field):
@@ -516,7 +508,7 @@ class CheckinListOrderPositionSerializer(OrderPositionSerializer):
'company', 'street', 'zipcode', 'city', 'country', 'state', 'company', 'street', 'zipcode', 'city', 'country', 'state',
'attendee_email', 'voucher', 'tax_rate', 'tax_value', 'secret', 'addon_to', 'subevent', 'checkins', 'attendee_email', 'voucher', 'tax_rate', 'tax_value', 'secret', 'addon_to', 'subevent', 'checkins',
'downloads', 'answers', 'tax_rule', 'pseudonymization_id', 'pdf_data', 'seat', 'require_attention', 'downloads', 'answers', 'tax_rule', 'pseudonymization_id', 'pdf_data', 'seat', 'require_attention',
'order__status', 'valid_from', 'valid_until', 'blocked') 'order__status')
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
@@ -630,7 +622,7 @@ class OrderSerializer(I18nAwareModelSerializer):
'code', 'status', 'testmode', 'secret', 'email', 'phone', 'locale', 'datetime', 'expires', 'payment_date', 'code', 'status', 'testmode', 'secret', 'email', 'phone', 'locale', 'datetime', 'expires', 'payment_date',
'payment_provider', 'fees', 'total', 'comment', 'custom_followup_at', 'invoice_address', 'positions', 'downloads', 'payment_provider', 'fees', 'total', 'comment', 'custom_followup_at', 'invoice_address', 'positions', 'downloads',
'checkin_attention', 'last_modified', 'payments', 'refunds', 'require_approval', 'sales_channel', 'checkin_attention', 'last_modified', 'payments', 'refunds', 'require_approval', 'sales_channel',
'url', 'customer', 'valid_if_pending' 'url', 'customer'
) )
read_only_fields = ( read_only_fields = (
'code', 'status', 'testmode', 'secret', 'datetime', 'expires', 'payment_date', 'code', 'status', 'testmode', 'secret', 'datetime', 'expires', 'payment_date',
@@ -685,8 +677,7 @@ class OrderSerializer(I18nAwareModelSerializer):
def update(self, instance, validated_data): def update(self, instance, validated_data):
# Even though all fields that shouldn't be edited are marked as read_only in the serializer # Even though all fields that shouldn't be edited are marked as read_only in the serializer
# (hopefully), we'll be extra careful here and be explicit about the model fields we update. # (hopefully), we'll be extra careful here and be explicit about the model fields we update.
update_fields = ['comment', 'custom_followup_at', 'checkin_attention', 'email', 'locale', 'phone', update_fields = ['comment', 'custom_followup_at', 'checkin_attention', 'email', 'locale', 'phone']
'valid_if_pending']
if 'invoice_address' in validated_data: if 'invoice_address' in validated_data:
iadata = validated_data.pop('invoice_address') iadata = validated_data.pop('invoice_address')
@@ -783,20 +774,16 @@ class OrderPositionCreateSerializer(I18nAwareModelSerializer):
attendee_name = serializers.CharField(required=False, allow_null=True) attendee_name = serializers.CharField(required=False, allow_null=True)
seat = serializers.CharField(required=False, allow_null=True) seat = serializers.CharField(required=False, allow_null=True)
price = serializers.DecimalField(required=False, allow_null=True, decimal_places=2, price = serializers.DecimalField(required=False, allow_null=True, decimal_places=2,
max_digits=13) max_digits=10)
voucher = serializers.SlugRelatedField(slug_field='code', queryset=Voucher.objects.none(), voucher = serializers.SlugRelatedField(slug_field='code', queryset=Voucher.objects.none(),
required=False, allow_null=True) required=False, allow_null=True)
country = CompatibleCountryField(source='*') country = CompatibleCountryField(source='*')
requested_valid_from = serializers.DateTimeField(required=False, allow_null=True)
use_reusable_medium = serializers.PrimaryKeyRelatedField(queryset=ReusableMedium.objects.none(),
required=False, allow_null=True)
class Meta: class Meta:
model = OrderPosition model = OrderPosition
fields = ('positionid', 'item', 'variation', 'price', 'attendee_name', 'attendee_name_parts', 'attendee_email', fields = ('positionid', 'item', 'variation', 'price', 'attendee_name', 'attendee_name_parts', 'attendee_email',
'company', 'street', 'zipcode', 'city', 'country', 'state', 'is_bundled', 'company', 'street', 'zipcode', 'city', 'country', 'state', 'is_bundled',
'secret', 'addon_to', 'subevent', 'answers', 'seat', 'voucher', 'valid_from', 'valid_until', 'secret', 'addon_to', 'subevent', 'answers', 'seat', 'voucher')
'requested_valid_from', 'use_reusable_medium')
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
@@ -805,9 +792,6 @@ class OrderPositionCreateSerializer(I18nAwareModelSerializer):
v.required = False v.required = False
v.allow_blank = True v.allow_blank = True
v.allow_null = True v.allow_null = True
with scopes_disabled():
if 'use_reusable_medium' in self.fields:
self.fields['use_reusable_medium'].queryset = ReusableMedium.objects.all()
def validate_secret(self, secret): def validate_secret(self, secret):
if secret and OrderPosition.all.filter(order__event=self.context['event'], secret=secret).exists(): if secret and OrderPosition.all.filter(order__event=self.context['event'], secret=secret).exists():
@@ -816,13 +800,6 @@ class OrderPositionCreateSerializer(I18nAwareModelSerializer):
) )
return secret return secret
def validate_use_reusable_medium(self, m):
if m.organizer_id != self.context['event'].organizer_id:
raise ValidationError(
'The specified medium does not belong to this organizer.'
)
return m
def validate_item(self, item): def validate_item(self, item):
if item.event != self.context['event']: if item.event != self.context['event']:
raise ValidationError( raise ValidationError(
@@ -964,8 +941,7 @@ class OrderCreateSerializer(I18nAwareModelSerializer):
model = Order model = Order
fields = ('code', 'status', 'testmode', 'email', 'phone', 'locale', 'payment_provider', 'fees', 'comment', 'sales_channel', fields = ('code', 'status', 'testmode', 'email', 'phone', 'locale', 'payment_provider', 'fees', 'comment', 'sales_channel',
'invoice_address', 'positions', 'checkin_attention', 'payment_info', 'payment_date', 'consume_carts', 'invoice_address', 'positions', 'checkin_attention', 'payment_info', 'payment_date', 'consume_carts',
'force', 'send_email', 'simulate', 'customer', 'custom_followup_at', 'require_approval', 'force', 'send_email', 'simulate', 'customer', 'custom_followup_at', 'require_approval')
'valid_if_pending')
def validate_payment_provider(self, pp): def validate_payment_provider(self, pp):
if pp is None: if pp is None:
@@ -1193,20 +1169,6 @@ class OrderCreateSerializer(I18nAwareModelSerializer):
elif seated: elif seated:
errs[i]['seat'] = ['The specified product requires to choose a seat.'] errs[i]['seat'] = ['The specified product requires to choose a seat.']
requested_valid_from = pos_data.pop('requested_valid_from', None)
if 'valid_from' not in pos_data and 'valid_until' not in pos_data:
valid_from, valid_until = pos_data['item'].compute_validity(
requested_start=(
max(requested_valid_from, now())
if requested_valid_from and pos_data['item'].validity_dynamic_start_choice
else now()
),
enforce_start_limit=True,
override_tz=self.context['event'].timezone,
)
pos_data['valid_from'] = valid_from
pos_data['valid_until'] = valid_until
if not force: if not force:
for i, pos_data in enumerate(positions_data): for i, pos_data in enumerate(positions_data):
if pos_data.get('voucher'): if pos_data.get('voucher'):
@@ -1280,7 +1242,7 @@ class OrderCreateSerializer(I18nAwareModelSerializer):
pos_data['attendee_name_parts'] = { pos_data['attendee_name_parts'] = {
'_legacy': attendee_name '_legacy': attendee_name
} }
pos = OrderPosition(**{k: v for k, v in pos_data.items() if k != 'answers' and k != '_quotas' and k != 'use_reusable_medium'}) pos = OrderPosition(**{k: v for k, v in pos_data.items() if k != 'answers' and k != '_quotas'})
if simulate: if simulate:
pos.order = order._wrapped pos.order = order._wrapped
else: else:
@@ -1348,7 +1310,6 @@ class OrderCreateSerializer(I18nAwareModelSerializer):
# Save instances # Save instances
for pos_data in positions_data: for pos_data in positions_data:
answers_data = pos_data.pop('answers', []) answers_data = pos_data.pop('answers', [])
use_reusable_medium = pos_data.pop('use_reusable_medium', None)
pos = pos_data['__instance'] pos = pos_data['__instance']
pos._calculate_tax() pos._calculate_tax()
@@ -1387,17 +1348,6 @@ class OrderCreateSerializer(I18nAwareModelSerializer):
answ = pos.answers.create(**answ_data) answ = pos.answers.create(**answ_data)
answ.options.add(*options) answ.options.add(*options)
if use_reusable_medium:
use_reusable_medium.linked_orderposition = pos
use_reusable_medium.save(update_fields=['linked_orderposition'])
use_reusable_medium.log_action(
'pretix.reusable_medium.linked_orderposition.changed',
data={
'by_order': order.code,
'linked_orderposition': pos.pk,
}
)
if not simulate: if not simulate:
for cp in delete_cps: for cp in delete_cps:
if cp.addon_to_id: if cp.addon_to_id:
@@ -1534,9 +1484,9 @@ class InvoiceSerializer(I18nAwareModelSerializer):
'invoice_to', 'invoice_to_company', 'invoice_to_name', 'invoice_to_street', 'invoice_to_zipcode', 'invoice_to', 'invoice_to_company', 'invoice_to_name', 'invoice_to_street', 'invoice_to_zipcode',
'invoice_to_city', 'invoice_to_state', 'invoice_to_country', 'invoice_to_vat_id', 'invoice_to_beneficiary', 'invoice_to_city', 'invoice_to_state', 'invoice_to_country', 'invoice_to_vat_id', 'invoice_to_beneficiary',
'custom_field', 'date', 'refers', 'locale', 'custom_field', 'date', 'refers', 'locale',
'introductory_text', 'additional_text', 'payment_provider_text', 'payment_provider_stamp', 'introductory_text', 'additional_text', 'payment_provider_text', 'footer_text', 'lines',
'footer_text', 'lines', 'foreign_currency_display', 'foreign_currency_rate', 'foreign_currency_display', 'foreign_currency_rate', 'foreign_currency_rate_date',
'foreign_currency_rate_date', 'internal_reference') 'internal_reference')
class OrderPaymentCreateSerializer(I18nAwareModelSerializer): class OrderPaymentCreateSerializer(I18nAwareModelSerializer):
@@ -1582,10 +1532,3 @@ class RevokedTicketSecretSerializer(I18nAwareModelSerializer):
class Meta: class Meta:
model = RevokedTicketSecret model = RevokedTicketSecret
fields = ('id', 'secret', 'created') fields = ('id', 'secret', 'created')
class BlockedTicketSecretSerializer(I18nAwareModelSerializer):
class Meta:
model = BlockedTicketSecret
fields = ('id', 'secret', 'updated', 'blocked')

View File

@@ -24,7 +24,6 @@ import os
import pycountry import pycountry
from django.core.files import File from django.core.files import File
from django.core.validators import RegexValidator
from rest_framework import serializers from rest_framework import serializers
from rest_framework.exceptions import ValidationError from rest_framework.exceptions import ValidationError
@@ -47,14 +46,14 @@ class OrderPositionCreateForExistingOrderSerializer(OrderPositionCreateSerialize
attendee_name = serializers.CharField(required=False, allow_null=True) attendee_name = serializers.CharField(required=False, allow_null=True)
seat = serializers.CharField(required=False, allow_null=True) seat = serializers.CharField(required=False, allow_null=True)
price = serializers.DecimalField(required=False, allow_null=True, decimal_places=2, price = serializers.DecimalField(required=False, allow_null=True, decimal_places=2,
max_digits=13) max_digits=10)
country = CompatibleCountryField(source='*') country = CompatibleCountryField(source='*')
class Meta: class Meta:
model = OrderPosition model = OrderPosition
fields = ('order', 'item', 'variation', 'price', 'attendee_name', 'attendee_name_parts', 'attendee_email', fields = ('order', 'item', 'variation', 'price', 'attendee_name', 'attendee_name_parts', 'attendee_email',
'company', 'street', 'zipcode', 'city', 'country', 'state', 'company', 'street', 'zipcode', 'city', 'country', 'state',
'secret', 'addon_to', 'subevent', 'answers', 'seat', 'valid_from', 'valid_until') 'secret', 'addon_to', 'subevent', 'answers', 'seat')
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
@@ -90,8 +89,6 @@ class OrderPositionCreateForExistingOrderSerializer(OrderPositionCreateSerialize
addon_to=validated_data.get('addon_to'), addon_to=validated_data.get('addon_to'),
subevent=validated_data.get('subevent'), subevent=validated_data.get('subevent'),
seat=validated_data.get('seat'), seat=validated_data.get('seat'),
valid_from=validated_data.get('valid_from'),
valid_until=validated_data.get('valid_until'),
) )
if self.context.get('commit', True): if self.context.get('commit', True):
ocm.commit() ocm.commit()
@@ -201,7 +198,7 @@ class OrderPositionChangeSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = OrderPosition model = OrderPosition
fields = ( fields = (
'item', 'variation', 'subevent', 'seat', 'price', 'tax_rule', 'valid_from', 'valid_until' 'item', 'variation', 'subevent', 'seat', 'price', 'tax_rule',
) )
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
@@ -267,8 +264,6 @@ class OrderPositionChangeSerializer(serializers.ModelSerializer):
price = validated_data.get('price', instance.price) price = validated_data.get('price', instance.price)
seat = validated_data.get('seat', current_seat) seat = validated_data.get('seat', current_seat)
tax_rule = validated_data.get('tax_rule', instance.tax_rule) tax_rule = validated_data.get('tax_rule', instance.tax_rule)
valid_from = validated_data.get('valid_from', instance.valid_from)
valid_until = validated_data.get('valid_until', instance.valid_until)
change_item = None change_item = None
if item != instance.item or variation != instance.variation: if item != instance.item or variation != instance.variation:
@@ -295,12 +290,6 @@ class OrderPositionChangeSerializer(serializers.ModelSerializer):
if tax_rule != instance.tax_rule: if tax_rule != instance.tax_rule:
ocm.change_tax_rule(instance, tax_rule) ocm.change_tax_rule(instance, tax_rule)
if valid_from != instance.valid_from:
ocm.change_valid_from(instance, valid_from)
if valid_until != instance.valid_until:
ocm.change_valid_until(instance, valid_until)
if self.context.get('commit', True): if self.context.get('commit', True):
ocm.commit() ocm.commit()
instance.refresh_from_db() instance.refresh_from_db()
@@ -434,7 +423,3 @@ class OrderChangeOperationSerializer(serializers.Serializer):
seen_positions.add(d['fee']) seen_positions.add(d['fee'])
return data return data
class BlockNameSerializer(serializers.Serializer):
name = serializers.CharField(validators=[RegexValidator('^(admin|api:[a-zA-Z0-9._]+)$')])

View File

@@ -22,14 +22,12 @@
import logging import logging
from decimal import Decimal from decimal import Decimal
from django.core.exceptions import ObjectDoesNotExist
from django.db.models import Q from django.db.models import Q
from django.utils.crypto import get_random_string from django.utils.crypto import get_random_string
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from rest_framework import serializers from rest_framework import serializers
from rest_framework.exceptions import ValidationError from rest_framework.exceptions import ValidationError
from pretix.api.serializers import AsymmetricField
from pretix.api.serializers.i18n import I18nAwareModelSerializer from pretix.api.serializers.i18n import I18nAwareModelSerializer
from pretix.api.serializers.order import CompatibleJSONField from pretix.api.serializers.order import CompatibleJSONField
from pretix.api.serializers.settings import SettingsSerializer from pretix.api.serializers.settings import SettingsSerializer
@@ -37,27 +35,21 @@ from pretix.base.auth import get_auth_backends
from pretix.base.i18n import get_language_without_region from pretix.base.i18n import get_language_without_region
from pretix.base.models import ( from pretix.base.models import (
Customer, Device, GiftCard, GiftCardTransaction, Membership, Customer, Device, GiftCard, GiftCardTransaction, Membership,
MembershipType, OrderPosition, Organizer, ReusableMedium, SeatingPlan, MembershipType, Organizer, SeatingPlan, Team, TeamAPIToken, TeamInvite,
Team, TeamAPIToken, TeamInvite, User, User,
) )
from pretix.base.models.seating import SeatingPlanLayoutValidator from pretix.base.models.seating import SeatingPlanLayoutValidator
from pretix.base.services.mail import SendMailException, mail from pretix.base.services.mail import SendMailException, mail
from pretix.base.settings import validate_organizer_settings from pretix.base.settings import validate_organizer_settings
from pretix.helpers.urls import build_absolute_uri as build_global_uri from pretix.helpers.urls import build_absolute_uri
from pretix.multidomain.urlreverse import build_absolute_uri
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class OrganizerSerializer(I18nAwareModelSerializer): class OrganizerSerializer(I18nAwareModelSerializer):
public_url = serializers.SerializerMethodField('get_organizer_url', read_only=True)
def get_organizer_url(self, organizer):
return build_absolute_uri(organizer, 'presale:organizer.index')
class Meta: class Meta:
model = Organizer model = Organizer
fields = ('name', 'slug', 'public_url') fields = ('name', 'slug')
class SeatingPlanSerializer(I18nAwareModelSerializer): class SeatingPlanSerializer(I18nAwareModelSerializer):
@@ -129,52 +121,8 @@ class MembershipSerializer(I18nAwareModelSerializer):
return super().update(instance, validated_data) return super().update(instance, validated_data)
class FlexibleTicketRelatedField(serializers.PrimaryKeyRelatedField):
def to_internal_value(self, data):
queryset = self.get_queryset()
if isinstance(data, int):
try:
return queryset.get(pk=data)
except ObjectDoesNotExist:
self.fail('does_not_exist', pk_value=data)
elif isinstance(data, str):
try:
return queryset.get(
Q(secret=data)
| Q(pseudonymization_id=data)
| Q(pk__in=ReusableMedium.objects.filter(
organizer=self.context['organizer'],
type='barcode',
identifier=data
))
)
except ObjectDoesNotExist:
self.fail('does_not_exist', pk_value=data)
self.fail('incorrect_type', data_type=type(data).__name__)
class GiftCardSerializer(I18nAwareModelSerializer): class GiftCardSerializer(I18nAwareModelSerializer):
value = serializers.DecimalField(max_digits=13, decimal_places=2, min_value=Decimal('0.00')) value = serializers.DecimalField(max_digits=10, decimal_places=2, min_value=Decimal('0.00'))
owner_ticket = FlexibleTicketRelatedField(required=False, allow_null=True, queryset=OrderPosition.all.none())
issuer = serializers.SlugRelatedField(slug_field='slug', read_only=True)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['owner_ticket'].queryset = OrderPosition.objects.filter(order__event__organizer=self.context['organizer'])
if 'owner_ticket' in self.context['request'].query_params.getlist('expand'):
from pretix.api.serializers.media import (
NestedOrderPositionSerializer,
)
self.fields['owner_ticket'] = AsymmetricField(
NestedOrderPositionSerializer(read_only=True, context=self.context),
self.fields['owner_ticket'],
)
def validate(self, data): def validate(self, data):
data = super().validate(data) data = super().validate(data)
@@ -197,8 +145,7 @@ class GiftCardSerializer(I18nAwareModelSerializer):
class Meta: class Meta:
model = GiftCard model = GiftCard
fields = ('id', 'secret', 'issuance', 'value', 'currency', 'testmode', 'expires', 'conditions', 'owner_ticket', fields = ('id', 'secret', 'issuance', 'value', 'currency', 'testmode', 'expires', 'conditions')
'issuer')
class OrderEventSlugField(serializers.RelatedField): class OrderEventSlugField(serializers.RelatedField):
@@ -209,12 +156,11 @@ class OrderEventSlugField(serializers.RelatedField):
class GiftCardTransactionSerializer(I18nAwareModelSerializer): class GiftCardTransactionSerializer(I18nAwareModelSerializer):
order = serializers.SlugRelatedField(slug_field='code', read_only=True) order = serializers.SlugRelatedField(slug_field='code', read_only=True)
acceptor = serializers.SlugRelatedField(slug_field='slug', read_only=True)
event = OrderEventSlugField(source='order', read_only=True) event = OrderEventSlugField(source='order', read_only=True)
class Meta: class Meta:
model = GiftCardTransaction model = GiftCardTransaction
fields = ('id', 'datetime', 'value', 'event', 'order', 'text', 'info', 'acceptor') fields = ('id', 'datetime', 'value', 'event', 'order', 'text')
class EventSlugField(serializers.SlugRelatedField): class EventSlugField(serializers.SlugRelatedField):
@@ -231,7 +177,7 @@ class TeamSerializer(serializers.ModelSerializer):
'id', 'name', 'all_events', 'limit_events', 'can_create_events', 'can_change_teams', 'id', 'name', 'all_events', 'limit_events', 'can_create_events', 'can_change_teams',
'can_change_organizer_settings', 'can_manage_gift_cards', 'can_change_event_settings', 'can_change_organizer_settings', 'can_manage_gift_cards', 'can_change_event_settings',
'can_change_items', 'can_view_orders', 'can_change_orders', 'can_view_vouchers', 'can_change_items', 'can_view_orders', 'can_change_orders', 'can_view_vouchers',
'can_change_vouchers', 'can_checkin_orders', 'can_manage_customers', 'can_manage_reusable_media' 'can_change_vouchers', 'can_checkin_orders', 'can_manage_customers'
) )
def validate(self, data): def validate(self, data):
@@ -281,7 +227,7 @@ class TeamInviteSerializer(serializers.ModelSerializer):
'user': self, 'user': self,
'organizer': self.context['organizer'].name, 'organizer': self.context['organizer'].name,
'team': instance.team.name, 'team': instance.team.name,
'url': build_global_uri('control:auth.invite', kwargs={ 'url': build_absolute_uri('control:auth.invite', kwargs={
'token': instance.token 'token': instance.token
}) })
}, },
@@ -381,12 +327,6 @@ class OrganizerSettingsSerializer(SettingsSerializer):
'cookie_consent_dialog_text_secondary', 'cookie_consent_dialog_text_secondary',
'cookie_consent_dialog_button_yes', 'cookie_consent_dialog_button_yes',
'cookie_consent_dialog_button_no', 'cookie_consent_dialog_button_no',
'reusable_media_active',
'reusable_media_type_barcode',
'reusable_media_type_barcode_identifier_length',
'reusable_media_type_nfc_uid',
'reusable_media_type_nfc_uid_autocreate_giftcard',
'reusable_media_type_nfc_uid_autocreate_giftcard_currency',
] ]
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):

View File

@@ -36,7 +36,6 @@ logger = logging.getLogger(__name__)
class SettingsSerializer(serializers.Serializer): class SettingsSerializer(serializers.Serializer):
default_fields = [] default_fields = []
readonly_fields = []
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
self.changed_data = [] self.changed_data = []
@@ -60,13 +59,8 @@ class SettingsSerializer(serializers.Serializer):
f.parent = self f.parent = self
self.fields[fname] = f self.fields[fname] = f
def validate(self, attrs):
return {k: v for k, v in attrs.items() if k not in self.readonly_fields}
def update(self, instance: HierarkeyProxy, validated_data): def update(self, instance: HierarkeyProxy, validated_data):
for attr, value in validated_data.items(): for attr, value in validated_data.items():
if attr in self.readonly_fields:
continue
if isinstance(value, FieldFile): if isinstance(value, FieldFile):
# Delete old file # Delete old file
fname = instance.get(attr, as_type=File) fname = instance.get(attr, as_type=File)

View File

@@ -63,8 +63,7 @@ class VoucherSerializer(I18nAwareModelSerializer):
model = Voucher model = Voucher
fields = ('id', 'code', 'max_usages', 'redeemed', 'min_usages', 'valid_until', 'block_quota', fields = ('id', 'code', 'max_usages', 'redeemed', 'min_usages', 'valid_until', 'block_quota',
'allow_ignore_quota', 'price_mode', 'value', 'item', 'variation', 'quota', 'allow_ignore_quota', 'price_mode', 'value', 'item', 'variation', 'quota',
'tag', 'comment', 'subevent', 'show_hidden_items', 'seat', 'all_addons_included', 'tag', 'comment', 'subevent', 'show_hidden_items', 'seat')
'all_bundles_included')
read_only_fields = ('id', 'redeemed') read_only_fields = ('id', 'redeemed')
list_serializer_class = VoucherListSerializer list_serializer_class = VoucherListSerializer

View File

@@ -39,7 +39,7 @@ class WaitingListSerializer(I18nAwareModelSerializer):
full_data = self.to_internal_value(self.to_representation(self.instance)) if self.instance else {} full_data = self.to_internal_value(self.to_representation(self.instance)) if self.instance else {}
full_data.update(data) full_data.update(data)
WaitingListEntry.clean_duplicate(event, full_data.get('email'), full_data.get('item'), full_data.get('variation'), WaitingListEntry.clean_duplicate(full_data.get('email'), full_data.get('item'), full_data.get('variation'),
full_data.get('subevent'), self.instance.pk if self.instance else None) full_data.get('subevent'), self.instance.pk if self.instance else None)
WaitingListEntry.clean_itemvar(event, full_data.get('item'), full_data.get('variation')) WaitingListEntry.clean_itemvar(event, full_data.get('item'), full_data.get('variation'))
WaitingListEntry.clean_subevent(event, full_data.get('subevent')) WaitingListEntry.clean_subevent(event, full_data.get('subevent'))

View File

@@ -55,7 +55,7 @@ class WebHookSerializer(I18nAwareModelSerializer):
class Meta: class Meta:
model = WebHook model = WebHook
fields = ('id', 'enabled', 'target_url', 'all_events', 'limit_events', 'action_types', 'comment') fields = ('id', 'enabled', 'target_url', 'all_events', 'limit_events', 'action_types')
def validate(self, data): def validate(self, data):
data = super().validate(data) data = super().validate(data)

View File

@@ -42,9 +42,9 @@ from rest_framework import routers
from pretix.api.views import cart from pretix.api.views import cart
from .views import ( from .views import (
checkin, device, discount, event, exporters, idempotency, item, media, checkin, device, discount, event, exporters, idempotency, item, oauth,
oauth, order, organizer, shredders, upload, user, version, voucher, order, organizer, shredders, upload, user, version, voucher, waitinglist,
waitinglist, webhooks, webhooks,
) )
router = routers.DefaultRouter() router = routers.DefaultRouter()
@@ -59,7 +59,6 @@ orga_router.register(r'giftcards', organizer.GiftCardViewSet)
orga_router.register(r'customers', organizer.CustomerViewSet) orga_router.register(r'customers', organizer.CustomerViewSet)
orga_router.register(r'memberships', organizer.MembershipViewSet) orga_router.register(r'memberships', organizer.MembershipViewSet)
orga_router.register(r'membershiptypes', organizer.MembershipTypeViewSet) orga_router.register(r'membershiptypes', organizer.MembershipTypeViewSet)
orga_router.register(r'reusablemedia', media.ReusableMediaViewSet)
orga_router.register(r'teams', organizer.TeamViewSet) orga_router.register(r'teams', organizer.TeamViewSet)
orga_router.register(r'devices', organizer.DeviceViewSet) orga_router.register(r'devices', organizer.DeviceViewSet)
orga_router.register(r'exporters', exporters.OrganizerExportersViewSet, basename='exporters') orga_router.register(r'exporters', exporters.OrganizerExportersViewSet, basename='exporters')
@@ -82,14 +81,12 @@ event_router.register(r'orders', order.OrderViewSet)
event_router.register(r'orderpositions', order.OrderPositionViewSet) event_router.register(r'orderpositions', order.OrderPositionViewSet)
event_router.register(r'invoices', order.InvoiceViewSet) event_router.register(r'invoices', order.InvoiceViewSet)
event_router.register(r'revokedsecrets', order.RevokedSecretViewSet, basename='revokedsecrets') event_router.register(r'revokedsecrets', order.RevokedSecretViewSet, basename='revokedsecrets')
event_router.register(r'blockedsecrets', order.BlockedSecretViewSet, basename='blockedsecrets')
event_router.register(r'taxrules', event.TaxRuleViewSet) event_router.register(r'taxrules', event.TaxRuleViewSet)
event_router.register(r'waitinglistentries', waitinglist.WaitingListViewSet) event_router.register(r'waitinglistentries', waitinglist.WaitingListViewSet)
event_router.register(r'checkinlists', checkin.CheckinListViewSet) event_router.register(r'checkinlists', checkin.CheckinListViewSet)
event_router.register(r'cartpositions', cart.CartPositionViewSet) event_router.register(r'cartpositions', cart.CartPositionViewSet)
event_router.register(r'exporters', exporters.EventExportersViewSet, basename='exporters') event_router.register(r'exporters', exporters.EventExportersViewSet, basename='exporters')
event_router.register(r'shredders', shredders.EventShreddersViewSet, basename='shredders') event_router.register(r'shredders', shredders.EventShreddersViewSet, basename='shredders')
event_router.register(r'item_meta_properties', event.ItemMetaPropertiesViewSet)
checkinlist_router = routers.DefaultRouter() checkinlist_router = routers.DefaultRouter()
checkinlist_router.register(r'positions', checkin.CheckinListPositionViewSet, basename='checkinlistpos') checkinlist_router.register(r'positions', checkin.CheckinListPositionViewSet, basename='checkinlistpos')

View File

@@ -24,11 +24,10 @@ from calendar import timegm
from django.db.models import Max from django.db.models import Max
from django.http import HttpResponse from django.http import HttpResponse
from django.utils.http import http_date, parse_http_date_safe from django.utils.http import http_date, parse_http_date_safe
from rest_framework.filters import OrderingFilter
from pretix.api.pagination import TotalOrderingFilter
class RichOrderingFilter(TotalOrderingFilter): class RichOrderingFilter(OrderingFilter):
def filter_queryset(self, request, queryset, view): def filter_queryset(self, request, queryset, view):
ordering = self.get_ordering(request, queryset, view) ordering = self.get_ordering(request, queryset, view)

View File

@@ -29,11 +29,11 @@ from django.utils.translation import gettext as _
from rest_framework import status, viewsets from rest_framework import status, viewsets
from rest_framework.decorators import action from rest_framework.decorators import action
from rest_framework.exceptions import ValidationError from rest_framework.exceptions import ValidationError
from rest_framework.filters import OrderingFilter
from rest_framework.mixins import CreateModelMixin, DestroyModelMixin from rest_framework.mixins import CreateModelMixin, DestroyModelMixin
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.serializers import as_serializer_error from rest_framework.serializers import as_serializer_error
from pretix.api.pagination import TotalOrderingFilter
from pretix.api.serializers.cart import ( from pretix.api.serializers.cart import (
CartPositionCreateSerializer, CartPositionSerializer, CartPositionCreateSerializer, CartPositionSerializer,
) )
@@ -47,7 +47,7 @@ from pretix.base.services.locking import NoLockManager
class CartPositionViewSet(CreateModelMixin, DestroyModelMixin, viewsets.ReadOnlyModelViewSet): class CartPositionViewSet(CreateModelMixin, DestroyModelMixin, viewsets.ReadOnlyModelViewSet):
serializer_class = CartPositionSerializer serializer_class = CartPositionSerializer
queryset = CartPosition.objects.none() queryset = CartPosition.objects.none()
filter_backends = (TotalOrderingFilter,) filter_backends = (OrderingFilter,)
ordering = ('datetime',) ordering = ('datetime',)
ordering_fields = ('datetime', 'cart_id') ordering_fields = ('datetime', 'cart_id')
lookup_field = 'id' lookup_field = 'id'

View File

@@ -59,7 +59,7 @@ from pretix.api.views.order import OrderPositionFilter
from pretix.base.i18n import language from pretix.base.i18n import language
from pretix.base.models import ( from pretix.base.models import (
CachedFile, Checkin, CheckinList, Device, Event, Order, OrderPosition, CachedFile, Checkin, CheckinList, Device, Event, Order, OrderPosition,
Question, ReusableMedium, RevokedTicketSecret, TeamAPIToken, Question, RevokedTicketSecret, TeamAPIToken,
) )
from pretix.base.services.checkin import ( from pretix.base.services.checkin import (
CheckInError, RequiredQuestionsError, SQLLogic, perform_checkin, CheckInError, RequiredQuestionsError, SQLLogic, perform_checkin,
@@ -273,13 +273,7 @@ with scopes_disabled():
def check_rules_qs(self, queryset, name, value): def check_rules_qs(self, queryset, name, value):
if not self.checkinlist.rules: if not self.checkinlist.rules:
return queryset return queryset
return queryset.filter( return queryset.filter(SQLLogic(self.checkinlist).apply(self.checkinlist.rules))
SQLLogic(self.checkinlist).apply(self.checkinlist.rules)
).filter(
Q(valid_from__isnull=True) | Q(valid_from__lte=now()),
Q(valid_until__isnull=True) | Q(valid_until__gte=now()),
blocked__isnull=True,
)
def _handle_file_upload(data, user, auth): def _handle_file_upload(data, user, auth):
@@ -331,13 +325,7 @@ def _checkin_list_position_queryset(checkinlists, ignore_status=False, ignore_pr
if checkinlist.subevent: if checkinlist.subevent:
list_q &= Q(subevent=checkinlist.subevent) list_q &= Q(subevent=checkinlist.subevent)
if not ignore_status: if not ignore_status:
if checkinlist.include_pending: list_q &= Q(order__status__in=[Order.STATUS_PAID, Order.STATUS_PENDING] if checkinlist.include_pending else [Order.STATUS_PAID])
list_q &= Q(order__status__in=[Order.STATUS_PAID, Order.STATUS_PENDING])
else:
list_q &= Q(
Q(order__status=Order.STATUS_PAID) |
Q(order__status=Order.STATUS_PENDING, order__valid_if_pending=True)
)
if not checkinlist.all_products and not ignore_products: if not checkinlist.all_products and not ignore_products:
list_q &= Q(item__in=checkinlist.limit_products.values_list('id', flat=True)) list_q &= Q(item__in=checkinlist.limit_products.values_list('id', flat=True))
lists_qs.append(list_q) lists_qs.append(list_q)
@@ -396,7 +384,7 @@ def _checkin_list_position_queryset(checkinlists, ignore_status=False, ignore_pr
def _redeem_process(*, checkinlists, raw_barcode, answers_data, datetime, force, checkin_type, ignore_unpaid, nonce, def _redeem_process(*, checkinlists, raw_barcode, answers_data, datetime, force, checkin_type, ignore_unpaid, nonce,
untrusted_input, user, auth, expand, pdf_data, request, questions_supported, canceled_supported, untrusted_input, user, auth, expand, pdf_data, request, questions_supported, canceled_supported,
source_type='barcode', legacy_url_support=False): legacy_url_support=False):
if not checkinlists: if not checkinlists:
raise ValidationError('No check-in list passed.') raise ValidationError('No check-in list passed.')
@@ -422,7 +410,6 @@ def _redeem_process(*, checkinlists, raw_barcode, answers_data, datetime, force,
common_checkin_args = dict( common_checkin_args = dict(
raw_barcode=raw_barcode, raw_barcode=raw_barcode,
raw_source_type=source_type,
type=checkin_type, type=checkin_type,
list=checkinlists[0], list=checkinlists[0],
datetime=datetime, datetime=datetime,
@@ -434,7 +421,7 @@ def _redeem_process(*, checkinlists, raw_barcode, answers_data, datetime, force,
raw_barcode_for_checkin = None raw_barcode_for_checkin = None
from_revoked_secret = False from_revoked_secret = False
# 1. Gather a list of positions that could be the one we looking for, either from their ID, secret or # 1. Gather a list of positions that could be the one we looking fore, either from their ID, secret or
# parent secret # parent secret
queryset = _checkin_list_position_queryset(checkinlists, pdf_data=pdf_data, ignore_status=True, ignore_products=True).order_by( queryset = _checkin_list_position_queryset(checkinlists, pdf_data=pdf_data, ignore_status=True, ignore_products=True).order_by(
F('addon_to').asc(nulls_first=True) F('addon_to').asc(nulls_first=True)
@@ -458,113 +445,98 @@ def _redeem_process(*, checkinlists, raw_barcode, answers_data, datetime, force,
# 2. Handle the "nothing found" case: Either it's really a bogus secret that we don't know (-> error), or it # 2. Handle the "nothing found" case: Either it's really a bogus secret that we don't know (-> error), or it
# might be a revoked one that we actually know (-> error, but with better error message and logging and # might be a revoked one that we actually know (-> error, but with better error message and logging and
# with respecting the force option), or it's a reusable medium (-> proceed with that) # with respecting the force option).
if not op_candidates: if not op_candidates:
try: revoked_matches = list(RevokedTicketSecret.objects.filter(event_id__in=list_by_event.keys(), secret=raw_barcode))
media = ReusableMedium.objects.select_related('linked_orderposition').active().get( if len(revoked_matches) == 0:
organizer_id=checkinlists[0].event.organizer_id, checkinlists[0].event.log_action('pretix.event.checkin.unknown', data={
type=source_type, 'datetime': datetime,
identifier=raw_barcode, 'type': checkin_type,
linked_orderposition__isnull=False, 'list': checkinlists[0].pk,
) 'barcode': raw_barcode,
raw_barcode_for_checkin = raw_barcode 'searched_lists': [cl.pk for cl in checkinlists]
except ReusableMedium.DoesNotExist: }, user=user, auth=auth)
revoked_matches = list(
RevokedTicketSecret.objects.filter(event_id__in=list_by_event.keys(), secret=raw_barcode))
if len(revoked_matches) == 0:
checkinlists[0].event.log_action('pretix.event.checkin.unknown', data={
'datetime': datetime,
'type': checkin_type,
'list': checkinlists[0].pk,
'barcode': raw_barcode,
'searched_lists': [cl.pk for cl in checkinlists]
}, user=user, auth=auth)
for cl in checkinlists: for cl in checkinlists:
for k, s in cl.event.ticket_secret_generators.items(): for k, s in cl.event.ticket_secret_generators.items():
try:
parsed = s.parse_secret(raw_barcode)
common_checkin_args.update({
'raw_item': parsed.item,
'raw_variation': parsed.variation,
'raw_subevent': parsed.subevent,
})
except:
pass
Checkin.objects.create(
position=None,
successful=False,
error_reason=Checkin.REASON_INVALID,
**common_checkin_args,
)
if force and legacy_url_support and isinstance(auth, Device):
# There was a bug in libpretixsync: If you scanned a ticket in offline mode that was
# valid at the time but no longer exists at time of upload, the device would retry to
# upload the same scan over and over again. Since we can't update all devices quickly,
# here's a dirty workaround to make it stop.
try: try:
brand = auth.software_brand parsed = s.parse_secret(raw_barcode)
ver = parse(auth.software_version) common_checkin_args.update({
legacy_mode = ( 'raw_item': parsed.item,
(brand == 'pretixSCANPROXY' and ver < parse('0.0.3')) or 'raw_variation': parsed.variation,
(brand == 'pretixSCAN Android' and ver < parse('1.11.2')) or 'raw_subevent': parsed.subevent,
(brand == 'pretixSCAN' and ver < parse('1.11.2')) })
) except:
if legacy_mode:
return Response({
'status': 'error',
'reason': Checkin.REASON_ALREADY_REDEEMED,
'reason_explanation': None,
'require_attention': False,
'__warning': 'Compatibility hack active due to detected old pretixSCAN version',
}, status=400)
except: # we don't care e.g. about invalid version numbers
pass pass
return Response({ Checkin.objects.create(
'detail': 'Not found.', # for backwards compatibility position=None,
'status': 'error', successful=False,
'reason': Checkin.REASON_INVALID, error_reason=Checkin.REASON_INVALID,
'reason_explanation': None, **common_checkin_args,
'require_attention': False, )
'list': MiniCheckinListSerializer(checkinlists[0]).data,
}, status=404) if force and legacy_url_support and isinstance(auth, Device):
elif revoked_matches and force: # There was a bug in libpretixsync: If you scanned a ticket in offline mode that was
op_candidates = [revoked_matches[0].position] # valid at the time but no longer exists at time of upload, the device would retry to
if list_by_event[revoked_matches[0].event_id].addon_match: # upload the same scan over and over again. Since we can't update all devices quickly,
op_candidates += list(revoked_matches[0].position.addons.all()) # here's a dirty workaround to make it stop.
raw_barcode_for_checkin = raw_barcode_for_checkin or raw_barcode try:
from_revoked_secret = True brand = auth.software_brand
else: ver = parse(auth.software_version)
op = revoked_matches[0].position legacy_mode = (
op.order.log_action('pretix.event.checkin.revoked', data={ (brand == 'pretixSCANPROXY' and ver < parse('0.0.3')) or
'datetime': datetime, (brand == 'pretixSCAN Android' and ver < parse('1.11.2')) or
'type': checkin_type, (brand == 'pretixSCAN' and ver < parse('1.11.2'))
'list': list_by_event[revoked_matches[0].event_id].pk, )
'barcode': raw_barcode if legacy_mode:
}, user=user, auth=auth) return Response({
common_checkin_args['list'] = list_by_event[revoked_matches[0].event_id] 'status': 'error',
Checkin.objects.create( 'reason': Checkin.REASON_ALREADY_REDEEMED,
position=op, 'reason_explanation': None,
successful=False, 'require_attention': False,
error_reason=Checkin.REASON_REVOKED, '__warning': 'Compatibility hack active due to detected old pretixSCAN version',
**common_checkin_args }, status=400)
) except: # we don't care e.g. about invalid version numbers
return Response({ pass
'status': 'error',
'reason': Checkin.REASON_REVOKED, return Response({
'reason_explanation': None, 'detail': 'Not found.', # for backwards compatibility
'require_attention': False, 'status': 'error',
'position': CheckinListOrderPositionSerializer(op, context=_make_context(context, revoked_matches[ 'reason': Checkin.REASON_INVALID,
0].event)).data, 'reason_explanation': None,
'list': MiniCheckinListSerializer(list_by_event[revoked_matches[0].event_id]).data, 'require_attention': False,
}, status=400) 'list': MiniCheckinListSerializer(checkinlists[0]).data,
}, status=404)
elif revoked_matches and force:
op_candidates = [revoked_matches[0].position]
if list_by_event[revoked_matches[0].event_id].addon_match:
op_candidates += list(revoked_matches[0].position.addons.all())
raw_barcode_for_checkin = raw_barcode
from_revoked_secret = True
else: else:
op_candidates = [media.linked_orderposition] op = revoked_matches[0].position
if list_by_event[media.linked_orderposition.order.event_id].addon_match: op.order.log_action('pretix.event.checkin.revoked', data={
op_candidates += list(media.linked_orderposition.addons.all()) 'datetime': datetime,
'type': checkin_type,
'list': list_by_event[revoked_matches[0].event_id].pk,
'barcode': raw_barcode
}, user=user, auth=auth)
common_checkin_args['list'] = list_by_event[revoked_matches[0].event_id]
Checkin.objects.create(
position=op,
successful=False,
error_reason=Checkin.REASON_REVOKED,
**common_checkin_args
)
return Response({
'status': 'error',
'reason': Checkin.REASON_REVOKED,
'reason_explanation': None,
'require_attention': False,
'position': CheckinListOrderPositionSerializer(op, context=_make_context(context, revoked_matches[0].event)).data,
'list': MiniCheckinListSerializer(list_by_event[revoked_matches[0].event_id]).data,
}, status=400)
# 3. Handle the "multiple options found" case: Except for the unlikely case of a secret being also a valid primary # 3. Handle the "multiple options found" case: Except for the unlikely case of a secret being also a valid primary
# key on the same list, we're probably dealing with the ``addon_match`` case here and need to figure out # key on the same list, we're probably dealing with the ``addon_match`` case here and need to figure out
@@ -610,7 +582,7 @@ def _redeem_process(*, checkinlists, raw_barcode, answers_data, datetime, force,
'status': 'error', 'status': 'error',
'reason': Checkin.REASON_AMBIGUOUS, 'reason': Checkin.REASON_AMBIGUOUS,
'reason_explanation': None, 'reason_explanation': None,
'require_attention': op.require_checkin_attention, 'require_attention': op.item.checkin_attention or op.order.checkin_attention,
'position': CheckinListOrderPositionSerializer(op, context=_make_context(context, op.order.event)).data, 'position': CheckinListOrderPositionSerializer(op, context=_make_context(context, op.order.event)).data,
'list': MiniCheckinListSerializer(list_by_event[op.order.event_id]).data, 'list': MiniCheckinListSerializer(list_by_event[op.order.event_id]).data,
}, status=400) }, status=400)
@@ -650,13 +622,12 @@ def _redeem_process(*, checkinlists, raw_barcode, answers_data, datetime, force,
auth=auth, auth=auth,
type=checkin_type, type=checkin_type,
raw_barcode=raw_barcode_for_checkin, raw_barcode=raw_barcode_for_checkin,
raw_source_type=source_type,
from_revoked_secret=from_revoked_secret, from_revoked_secret=from_revoked_secret,
) )
except RequiredQuestionsError as e: except RequiredQuestionsError as e:
return Response({ return Response({
'status': 'incomplete', 'status': 'incomplete',
'require_attention': op.require_checkin_attention, 'require_attention': op.item.checkin_attention or op.order.checkin_attention,
'position': CheckinListOrderPositionSerializer(op, context=_make_context(context, op.order.event)).data, 'position': CheckinListOrderPositionSerializer(op, context=_make_context(context, op.order.event)).data,
'questions': [ 'questions': [
QuestionSerializer(q).data for q in e.questions QuestionSerializer(q).data for q in e.questions
@@ -685,14 +656,14 @@ def _redeem_process(*, checkinlists, raw_barcode, answers_data, datetime, force,
'status': 'error', 'status': 'error',
'reason': e.code, 'reason': e.code,
'reason_explanation': e.reason, 'reason_explanation': e.reason,
'require_attention': op.require_checkin_attention, 'require_attention': op.item.checkin_attention or op.order.checkin_attention,
'position': CheckinListOrderPositionSerializer(op, context=_make_context(context, op.order.event)).data, 'position': CheckinListOrderPositionSerializer(op, context=_make_context(context, op.order.event)).data,
'list': MiniCheckinListSerializer(list_by_event[op.order.event_id]).data, 'list': MiniCheckinListSerializer(list_by_event[op.order.event_id]).data,
}, status=400) }, status=400)
else: else:
return Response({ return Response({
'status': 'ok', 'status': 'ok',
'require_attention': op.require_checkin_attention, 'require_attention': op.item.checkin_attention or op.order.checkin_attention,
'position': CheckinListOrderPositionSerializer(op, context=_make_context(context, op.order.event)).data, 'position': CheckinListOrderPositionSerializer(op, context=_make_context(context, op.order.event)).data,
'list': MiniCheckinListSerializer(list_by_event[op.order.event_id]).data, 'list': MiniCheckinListSerializer(list_by_event[op.order.event_id]).data,
}, status=201) }, status=201)
@@ -829,7 +800,6 @@ class CheckinRPCRedeemView(views.APIView):
return _redeem_process( return _redeem_process(
checkinlists=s.validated_data['lists'], checkinlists=s.validated_data['lists'],
raw_barcode=s.validated_data['secret'], raw_barcode=s.validated_data['secret'],
source_type=s.validated_data['source_type'],
answers_data=s.validated_data.get('answers'), answers_data=s.validated_data.get('answers'),
datetime=s.validated_data.get('datetime') or now(), datetime=s.validated_data.get('datetime') or now(),
force=s.validated_data['force'], force=s.validated_data['force'],

View File

@@ -36,8 +36,8 @@ from django_filters.rest_framework import DjangoFilterBackend, FilterSet
from django_scopes import scopes_disabled from django_scopes import scopes_disabled
from rest_framework import viewsets from rest_framework import viewsets
from rest_framework.exceptions import PermissionDenied from rest_framework.exceptions import PermissionDenied
from rest_framework.filters import OrderingFilter
from pretix.api.pagination import TotalOrderingFilter
from pretix.api.serializers.discount import DiscountSerializer from pretix.api.serializers.discount import DiscountSerializer
from pretix.api.views import ConditionalListView from pretix.api.views import ConditionalListView
from pretix.base.models import CartPosition, Discount from pretix.base.models import CartPosition, Discount
@@ -52,7 +52,7 @@ with scopes_disabled():
class DiscountViewSet(ConditionalListView, viewsets.ModelViewSet): class DiscountViewSet(ConditionalListView, viewsets.ModelViewSet):
serializer_class = DiscountSerializer serializer_class = DiscountSerializer
queryset = Discount.objects.none() queryset = Discount.objects.none()
filter_backends = (DjangoFilterBackend, TotalOrderingFilter) filter_backends = (DjangoFilterBackend, OrderingFilter)
filterset_class = DiscountFilter filterset_class = DiscountFilter
ordering_fields = ('id', 'position') ordering_fields = ('id', 'position')
ordering = ('position', 'id') ordering = ('position', 'id')

View File

@@ -39,21 +39,18 @@ from django.db.models import Prefetch, ProtectedError, Q
from django.utils.timezone import now from django.utils.timezone import now
from django_filters.rest_framework import DjangoFilterBackend, FilterSet from django_filters.rest_framework import DjangoFilterBackend, FilterSet
from django_scopes import scopes_disabled from django_scopes import scopes_disabled
from rest_framework import serializers, views, viewsets from rest_framework import filters, serializers, views, viewsets
from rest_framework.exceptions import PermissionDenied, ValidationError from rest_framework.exceptions import PermissionDenied, ValidationError
from rest_framework.response import Response from rest_framework.response import Response
from pretix.api.auth.permission import EventCRUDPermission from pretix.api.auth.permission import EventCRUDPermission
from pretix.api.pagination import TotalOrderingFilter
from pretix.api.serializers.event import ( from pretix.api.serializers.event import (
CloneEventSerializer, DeviceEventSettingsSerializer, EventSerializer, CloneEventSerializer, DeviceEventSettingsSerializer, EventSerializer,
EventSettingsSerializer, ItemMetaPropertiesSerializer, SubEventSerializer, EventSettingsSerializer, SubEventSerializer, TaxRuleSerializer,
TaxRuleSerializer,
) )
from pretix.api.views import ConditionalListView from pretix.api.views import ConditionalListView
from pretix.base.models import ( from pretix.base.models import (
CartPosition, Device, Event, ItemMetaProperty, SeatCategoryMapping, CartPosition, Device, Event, SeatCategoryMapping, TaxRule, TeamAPIToken,
TaxRule, TeamAPIToken,
) )
from pretix.base.models.event import SubEvent from pretix.base.models.event import SubEvent
from pretix.base.services.quotas import QuotaAvailability from pretix.base.services.quotas import QuotaAvailability
@@ -74,7 +71,7 @@ with scopes_disabled():
class Meta: class Meta:
model = Event model = Event
fields = ['is_public', 'live', 'has_subevents', 'testmode'] fields = ['is_public', 'live', 'has_subevents']
def ends_after_qs(self, queryset, name, value): def ends_after_qs(self, queryset, name, value):
expr = ( expr = (
@@ -130,7 +127,7 @@ class EventViewSet(viewsets.ModelViewSet):
lookup_url_kwarg = 'event' lookup_url_kwarg = 'event'
lookup_value_regex = '[^/]+' lookup_value_regex = '[^/]+'
permission_classes = (EventCRUDPermission,) permission_classes = (EventCRUDPermission,)
filter_backends = (DjangoFilterBackend, TotalOrderingFilter) filter_backends = (DjangoFilterBackend, filters.OrderingFilter)
ordering = ('slug',) ordering = ('slug',)
ordering_fields = ('date_from', 'slug') ordering_fields = ('date_from', 'slug')
filterset_class = EventFilter filterset_class = EventFilter
@@ -382,7 +379,7 @@ class SubEventViewSet(ConditionalListView, viewsets.ModelViewSet):
serializer_class = SubEventSerializer serializer_class = SubEventSerializer
queryset = SubEvent.objects.none() queryset = SubEvent.objects.none()
write_permission = 'can_change_event_settings' write_permission = 'can_change_event_settings'
filter_backends = (DjangoFilterBackend, TotalOrderingFilter) filter_backends = (DjangoFilterBackend, filters.OrderingFilter)
filterset_class = SubEventFilter filterset_class = SubEventFilter
ordering = ('date_from',) ordering = ('date_from',)
ordering_fields = ('id', 'date_from', 'last_modified') ordering_fields = ('id', 'date_from', 'last_modified')
@@ -524,54 +521,6 @@ class TaxRuleViewSet(ConditionalListView, viewsets.ModelViewSet):
super().perform_destroy(instance) super().perform_destroy(instance)
class ItemMetaPropertiesViewSet(viewsets.ModelViewSet):
serializer_class = ItemMetaPropertiesSerializer
queryset = ItemMetaProperty.objects.none()
write_permission = 'can_change_event_settings'
def get_queryset(self):
qs = self.request.event.item_meta_properties.all()
return qs
def get_serializer_context(self):
ctx = super().get_serializer_context()
ctx['organizer'] = self.request.organizer
ctx['event'] = self.request.event
return ctx
@transaction.atomic()
def perform_destroy(self, instance):
instance.log_action(
'pretix.event.item_meta_property.deleted',
user=self.request.user,
auth=self.request.auth,
data={'id': instance.pk}
)
instance.delete()
@transaction.atomic()
def perform_create(self, serializer):
inst = serializer.save(event=self.request.event)
serializer.instance.log_action(
'pretix.event.item_meta_property.added',
user=self.request.user,
auth=self.request.auth,
data=self.request.data,
)
return inst
@transaction.atomic()
def perform_update(self, serializer):
inst = serializer.save(event=self.request.event)
serializer.instance.log_action(
'pretix.event.item_meta_property.changed',
user=self.request.user,
auth=self.request.auth,
data=self.request.data,
)
return inst
class EventSettingsView(views.APIView): class EventSettingsView(views.APIView):
permission = None permission = None
write_permission = 'can_change_event_settings' write_permission = 'can_change_event_settings'
@@ -592,8 +541,7 @@ class EventSettingsView(views.APIView):
fname: { fname: {
'value': s.data[fname], 'value': s.data[fname],
'label': getattr(field, '_label', fname), 'label': getattr(field, '_label', fname),
'help_text': getattr(field, '_help_text', None), 'help_text': getattr(field, '_help_text', None)
'readonly': fname in s.readonly_fields,
} for fname, field in s.fields.items() } for fname, field in s.fields.items()
}) })
return Response(s.data) return Response(s.data)

Some files were not shown because too many files have changed in this diff Show More