diff --git a/doc/development/api/index.rst b/doc/development/api/index.rst index 8089265f83..424e5baa17 100644 --- a/doc/development/api/index.rst +++ b/doc/development/api/index.rst @@ -9,4 +9,5 @@ Contents: plugins restriction payment + ticketoutput general diff --git a/doc/development/api/payment.rst b/doc/development/api/payment.rst index 61fbf8df33..c2fc3b164f 100644 --- a/doc/development/api/payment.rst +++ b/doc/development/api/payment.rst @@ -20,11 +20,10 @@ that we'll soon create:: from pretix.base.signals import register_payment_providers - from .payment import Paypal - @receiver(register_payment_providers) def register_payment_provider(sender, **kwargs): + from .payment import Paypal return Paypal diff --git a/doc/development/api/ticketoutput.rst b/doc/development/api/ticketoutput.rst new file mode 100644 index 0000000000..eef1ced078 --- /dev/null +++ b/doc/development/api/ticketoutput.rst @@ -0,0 +1,70 @@ +.. highlight:: python + :linenothreshold: 5 + +Writing a ticket output plugin +============================== + +A ticket output is a method to offer a ticket (an order) for the user to download. + +In this document, we will walk through the creation of a ticket output plugin. This +is very similar to creating a payment provider. + +Please read :ref:`Creating a plugin ` first, if you haven't already. + +Output registration +------------------- + +The payment provider API does not make a lot of usage from signals, however, it +does use a signal to get a list of all available ticket outputs. Your plugin +should listen for this signal and return the subclass of ``pretix.base.ticketoutput.BaseTicketOutput`` +that we'll soon create:: + + from django.dispatch import receiver + + from pretix.base.signals import register_ticket_outputs + + + @receiver(register_ticket_outputs) + def register_ticket_output(sender, **kwargs): + from .ticketoutput import PdfTicketOutput + return PdfTicketOutput + + +The output class +---------------- + +.. class:: pretix.base.ticketoutput.BaseTicketOutput + + The central object of each ticket output is the subclass of ``BaseTicketOutput`` + we already mentioned above. In this section, we will discuss it's interface in detail. + + .. py:attribute:: BaseTicketOutput.event + + The default constructor sets this property to the event we are currently + working for. + + .. py:attribute:: BaseTicketOutput.settings + + The default constructor sets this property to a ``SettingsSandbox`` object. You can + use this object to store settings using its ``get`` and ``set`` methods. All settings + you store are transparently prefixed, so you get your very own settings namespace. + + .. autoattribute:: identifier + + This is an abstract attribute, you **must** override this! + + .. autoattribute:: verbose_name + + This is an abstract attribute, you **must** override this! + + .. autoattribute:: is_enabled + + .. autoattribute:: settings_form_fields + + .. automethod:: settings_content_render + + .. automethod:: generate + + .. autoattribute:: download_button_text + + .. autoattribute:: download_button_icon diff --git a/src/pretix/base/payment.py b/src/pretix/base/payment.py index 886ed7f038..40e29f9c40 100644 --- a/src/pretix/base/payment.py +++ b/src/pretix/base/payment.py @@ -123,6 +123,7 @@ class BasePaymentProvider: page, this method is called. It may return HTML containing additional information that is displayed below the form fields configured in ``settings_form_fields``. """ + pass @property def checkout_form_fields(self) -> dict: diff --git a/src/pretix/base/signals.py b/src/pretix/base/signals.py index 28e650a081..8d26d0c61f 100644 --- a/src/pretix/base/signals.py +++ b/src/pretix/base/signals.py @@ -60,3 +60,11 @@ subclass of pretix.base.payment.BasePaymentProvider register_payment_providers = EventPluginSignal( providing_args=[] ) + +""" +This signal is sent out to get all known ticket outputs. Receivers should return a +subclass of pretix.base.ticketoutput.BaseTicketOutput +""" +register_ticket_outputs = EventPluginSignal( + providing_args=[] +) diff --git a/src/pretix/base/ticketoutput.py b/src/pretix/base/ticketoutput.py new file mode 100644 index 0000000000..752675a102 --- /dev/null +++ b/src/pretix/base/ticketoutput.py @@ -0,0 +1,116 @@ +from collections import OrderedDict +from django import forms + +from django.http import HttpRequest, HttpResponse +from django.utils.translation import ugettext_lazy as _ +from pretix.base.forms import SettingsForm +from pretix.base.models import Order + +from pretix.base.settings import SettingsSandbox + + +class BaseTicketOutput: + """ + This is the base class for all ticket outputs. + """ + + def __init__(self, event): + self.event = event + self.settings = SettingsSandbox('ticketoutput', self.identifier, event) + + def __str__(self): + return self.identifier + + @property + def is_enabled(self) -> bool: + """ + Returns, whether or whether not this output is enabled. + By default, this is determined by the value of the ``_enabled`` setting. + """ + return self.settings.get('_enabled', as_type=bool) + + def generate(self, request: HttpRequest, order: Order) -> HttpResponse: + """ + This method should generate the download file and return it to the user. + """ + raise NotImplementedError() + + @property + def verbose_name(self) -> str: + """ + A human-readable name for this ticket output. This should + be short but self-explaining. Good examples include 'PDF tickets' + and 'Passbook'. + """ + raise NotImplementedError() # NOQA + + @property + def identifier(self) -> str: + """ + A short and unique identifier for this ticket output. + This should only contain lowercase letters and in most + cases will be the same as your packagename. + """ + raise NotImplementedError() # NOQA + + @property + def settings_form_fields(self) -> dict: + """ + When the event's administrator administrator visits the event configuration + page, this method is called to return the configuration fields available. + + It should therefore return a dictionary where the keys should be (unprefixed) + settings keys and the values should be corresponding Django form fields. + + The default implementation returns the appropriate fields for the ``_enabled`` + setting mentioned above. + + We suggest that you return an ``OrderedDict`` object instead of a dictionary + and make use of the default implementation. Your implementation could look + like this:: + + @property + def settings_form_fields(self): + return OrderedDict( + list(super().settings_form_fields.items()) + [ + ('paper_size', + forms.CharField( + label=_('Paper size'), + required=False + )) + ] + ) + + .. WARNING:: It is highly discouraged to alter the ``_enabled`` field of the default + implementation. + """ + return OrderedDict([ + ('_enabled', + forms.ChoiceField( + label=_('Enable output'), + required=False, + choices=SettingsForm.BOOL_CHOICES, + )), + ]) + + def settings_content_render(self, request: HttpRequest) -> str: + """ + When the event's administrator administrator visits the event configuration + page, this method is called. It may return HTML containing additional information + that is displayed below the form fields configured in ``settings_form_fields``. + """ + pass + + @property + def download_button_text(self) -> str: + """ + The text on the download button in the frontend. + """ + return _('Download ticket') + + @property + def download_button_icon(self) -> str: + """ + The name of the icon on the download button in the frontend + """ + return None diff --git a/src/pretix/control/templates/pretixcontrol/event/tickets.html b/src/pretix/control/templates/pretixcontrol/event/tickets.html index 827ac631e9..91a4d511f3 100644 --- a/src/pretix/control/templates/pretixcontrol/event/tickets.html +++ b/src/pretix/control/templates/pretixcontrol/event/tickets.html @@ -13,6 +13,27 @@ {% trans "Ticket download" %} {% bootstrap_field form.ticket_download layout="horizontal" %} {% bootstrap_field form.ticket_download_date layout="horizontal" %} + {% for provider in providers %} +
+
+
+
+

{{ provider.verbose_name }}

+
+
+
+
+ {% bootstrap_form provider.form layout='horizontal' %} + {% with c=provider.settings_content %} + {% if c %}{{ c|safe }}{% endif %} + {% endwith %} +
+
+ {% empty %} +
+ {% trans "There are no ticket outputs available. Please go to the plugin settings and activate one or more ticket output plugins." %} +
+ {% endfor %}