Refactor and add signal layout_text_variables

This commit is contained in:
Raphael Michel
2017-09-27 13:15:18 +02:00
parent 39061b659a
commit 1f889be07a
7 changed files with 177 additions and 103 deletions

View File

@@ -64,3 +64,9 @@ Dashboards
.. automodule:: pretix.control.signals .. automodule:: pretix.control.signals
:members: event_dashboard_widgets, user_dashboard_widgets :members: event_dashboard_widgets, user_dashboard_widgets
Ticket designs
""""""""""""""
.. automodule:: pretix.plugins.ticketoutputpdf.signals
:members: layout_text_variables

View File

@@ -3,7 +3,7 @@ from django.template.loader import get_template
from django.urls import resolve from django.urls import resolve
from pretix.base.signals import ( from pretix.base.signals import (
register_data_exporters, register_ticket_outputs, EventPluginSignal, register_data_exporters, register_ticket_outputs,
) )
from pretix.control.signals import html_head from pretix.control.signals import html_head
from pretix.presale.style import ( # NOQA: legacy import from pretix.presale.style import ( # NOQA: legacy import
@@ -33,3 +33,22 @@ def html_head_presale(sender, request=None, **kwargs):
}) })
else: else:
return "" return ""
layout_text_variables = EventPluginSignal()
"""
This signal is sent out to collect variables that can be used to display text in PDF ticket layouts.
Receivers are expected to return a dictionary with globally unique identifiers as keys and more
dictionaries as values that contain keys like in the following example::
return {
"product": {
"label": _("Product name"),
"editor_sample": _("Sample product"),
"evaluate": lambda orderposition, order, event: str(orderposition.item)
}
}
The evaluate member will be called with the order position, order and event as arguments. The event might
also be a subevent, if applicable.
"""

View File

@@ -158,35 +158,11 @@ var editor = {
editor._update_toolbox_values(); editor._update_toolbox_values();
}, },
text_samples: {
"secret": "tdmruoekvkpbv1o2mv8xccvqcikvr58u",
"order": "A1B2C",
"item": gettext("Sample product"),
"variation": gettext("Sample variation"),
"itemvar": gettext("Sample product sample variation"),
"item_description": gettext("Sample product description"),
"price": gettext("123.45 EUR"),
"attendee_name": gettext("John Doe"),
"invoice_name": gettext("John Doe"),
"invoice_company": gettext("Sample company"),
"event_name": gettext("Sample event name"),
"event_date": gettext("May 31st, 2017"),
"event_date_range": gettext("May 31st June 4th, 2017"),
"event_begin_time": gettext("20:00"),
"event_admission_time": gettext("19:00"),
"event_begin": gettext("2017-05-31 20:00"),
"event_admission": gettext("2017-05-31 19:00"),
"event_location": gettext("Random City"),
"organizer": gettext("Event organizer company"),
"organizer_info_text": gettext("Event organizer info text"),
"addons": gettext("Addon 1\nAddon 2"),
},
_get_text_sample: function (key) { _get_text_sample: function (key) {
if (key.startsWith('meta:')) { if (key.startsWith('meta:')) {
return key.substr(5); return key.substr(5);
} }
return editor.text_samples[key]; return $('#toolbox-content option[value='+key+']').attr('data-sample');
}, },
_load_pdf: function (dump) { _load_pdf: function (dump) {
@@ -406,7 +382,7 @@ var editor = {
}, },
_add_text: function () { _add_text: function () {
var text = new fabric.Textarea(editor.text_samples['item'], { var text = new fabric.Textarea(editor._get_text_sample('item'), {
left: 100, left: 100,
top: 100, top: 100,
width: editor._mm2px(50), width: editor._mm2px(50),

View File

@@ -279,27 +279,9 @@
<div class="col-sm-12"> <div class="col-sm-12">
<label>{% trans "Text content" %}</label><br> <label>{% trans "Text content" %}</label><br>
<select class="input-block-level form-control" id="toolbox-content"> <select class="input-block-level form-control" id="toolbox-content">
<option value="secret">{% trans "Ticket code (barcode content)" %}</option> {% for varname, var in variables.items %}
<option value="order">{% trans "Order code" %}</option> <option data-sample="{{ var.editor_sample }}" value="{{ varname }}">{{ var.label }}</option>
<option value="item">{% trans "Product name" %}</option> {% endfor %}
<option value="variation">{% trans "Variation name" %}</option>
<option value="item_description">{% trans "Product description" %}</option>
<option value="itemvar">{% trans "Product name and variation" %}</option>
<option value="price">{% trans "Price" %}</option>
<option value="attendee_name">{% trans "Attendee name" %}</option>
<option value="event_name">{% trans "Event name" %}</option>
<option value="event_date">{% trans "Event date" %}</option>
<option value="event_date_range">{% trans "Event date range" %}</option>
<option value="event_begin">{% trans "Event begin date and time" %}</option>
<option value="event_begin_time">{% trans "Event begin time" %}</option>
<option value="event_admission">{% trans "Event admission date and time" %}</option>
<option value="event_admission_time">{% trans "Event admission time" %}</option>
<option value="event_location">{% trans "Event location" %}</option>
<option value="invoice_name">{% trans "Invoice address: name" %}</option>
<option value="invoice_company">{% trans "Invoice address: company" %}</option>
<option value="addons">{% trans "List of Add-Ons" %}</option>
<option value="organizer">{% trans "Organizer name" %}</option>
<option value="organizer_info_text">{% trans "Organizer info text" %}</option>
{% for p in request.organizer.meta_properties.all %} {% for p in request.organizer.meta_properties.all %}
<option value="meta:{{ p.name }}"> <option value="meta:{{ p.name }}">
{% trans "Event attribute:" %} {{ p.name }} {% trans "Event attribute:" %} {{ p.name }}

View File

@@ -1,6 +1,7 @@
import copy import copy
import logging import logging
import uuid import uuid
from collections import OrderedDict
from io import BytesIO from io import BytesIO
from django.contrib.staticfiles import finders from django.contrib.staticfiles import finders
@@ -26,11 +27,141 @@ from reportlab.platypus import Paragraph
from pretix.base.models import Order, OrderPosition from pretix.base.models import Order, OrderPosition
from pretix.base.ticketoutput import BaseTicketOutput from pretix.base.ticketoutput import BaseTicketOutput
from pretix.plugins.ticketoutputpdf.signals import get_fonts from pretix.plugins.ticketoutputpdf.signals import (
get_fonts, layout_text_variables,
)
logger = logging.getLogger('pretix.plugins.ticketoutputpdf') logger = logging.getLogger('pretix.plugins.ticketoutputpdf')
DEFAULT_VARIABLES = OrderedDict((
("secret", {
"label": _("Ticket code (barcode content)"),
"editor_sample": "tdmruoekvkpbv1o2mv8xccvqcikvr58u",
"evaluate": lambda orderposition, order, event: orderposition.secret
}),
("order", {
"label": _("Order code"),
"editor_sample": "A1B2C",
"evaluate": lambda orderposition, order, event: orderposition.order.code
}),
("item", {
"label": _("Product name"),
"editor_sample": _("Sample product"),
"evaluate": lambda orderposition, order, event: str(orderposition.item)
}),
("variation", {
"label": _("Variation name"),
"editor_sample": _("Sample variation"),
"evaluate": lambda op, order, event: str(op.variation) if op.variation else ''
}),
("item_description", {
"label": _("Product description"),
"editor_sample": _("Sample product sample variation"),
"evaluate": lambda orderposition, order, event: (
'{} - {}'.format(orderposition.item, orderposition.variation)
if orderposition.variation else str(orderposition.item)
)
}),
("itemvar", {
"label": _("Product name and variation"),
"editor_sample": _("Sample product description"),
"evaluate": lambda orderposition, order, event: str(orderposition.item.description)
}),
("price", {
"label": _("Price"),
"editor_sample": _("123.45 EUR"),
"evaluate": lambda op, order, event: '{} {}'.format(event.currency, localize(op.price))
}),
("attendee_name", {
"label": _("Attendee name"),
"editor_sample": _("John Doe"),
"evaluate": lambda op, order, ev: op.attendee_name or (op.addon_to.attendee_name if op.addon_to else '')
}),
("event_name", {
"label": _("Event name"),
"editor_sample": _("Sample event name"),
"evaluate": lambda op, order, ev: str(ev.name)
}),
("event_date", {
"label": _("Event date"),
"editor_sample": _("May 31st, 2017"),
"evaluate": lambda op, order, ev: ev.get_date_from_display(show_times=False)
}),
("event_date_range", {
"label": _("Event date range"),
"editor_sample": _("May 31st June 4th, 2017"),
"evaluate": lambda op, order, ev: ev.get_date_range_display()
}),
("event_begin", {
"label": _("Event begin date and time"),
"editor_sample": _("2017-05-31 20:00"),
"evaluate": lambda op, order, ev: ev.get_date_from_display(show_times=True)
}),
("event_begin_time", {
"label": _("Event begin time"),
"editor_sample": _("20:00"),
"evaluate": lambda op, order, ev: ev.get_time_from_display()
}),
("event_admission", {
"label": _("Event admission date and time"),
"editor_sample": _("2017-05-31 19:00"),
"evaluate": lambda op, order, ev: date_format(
ev.date_admission.astimezone(timezone(ev.settings.timezone)),
"SHORT_DATETIME_FORMAT"
) if ev.date_admission else ""
}),
("event_admission_time", {
"label": _("Event admission time"),
"editor_sample": _("19:00"),
"evaluate": lambda op, order, ev: date_format(
ev.date_admission.astimezone(timezone(ev.settings.timezone)),
"TIME_FORMAT"
) if ev.date_admission else ""
}),
("event_location", {
"label": _("Event location"),
"editor_sample": _("Random City"),
"evaluate": lambda op, order, ev: str(ev.location).replace("\n", "<br/>\n")
}),
("invoice_name", {
"label": _("Invoice address: name"),
"editor_sample": _("John Doe"),
"evaluate": lambda op, order, ev: order.invoice_address.name if getattr(order, 'invoice_address') else ''
}),
("invoice_company", {
"label": _("Invocie address: company"),
"editor_sample": _("Sample company"),
"evaluate": lambda op, order, ev: order.invoice_address.company if getattr(order, 'invoice_address') else ''
}),
("addons", {
"label": _("List of Add-Ons"),
"editor_sample": _("Addon 1\nAddon 2"),
"evaluate": lambda op, order, ev: "<br/>".join([
'{} - {}'.format(p.item, p.variation) if p.variation else str(p.item)
for p in op.addons.select_related('item', 'variation')
])
}),
("organizer", {
"label": _("Organizer name"),
"editor_sample": _("Event organizer company"),
"evaluate": lambda op, order, ev: str(order.event.organizer.name)
}),
("organizer_info_text", {
"label": _("Organizer info text"),
"editor_sample": _("Event organizer info text"),
"evaluate": lambda op, order, ev: str(order.event.settings.organizer_info_text)
}),
))
def get_variables(event):
v = copy.copy(DEFAULT_VARIABLES)
for recv, res in layout_text_variables.send(sender=event):
v.update(res)
return v
class PdfTicketOutput(BaseTicketOutput): class PdfTicketOutput(BaseTicketOutput):
identifier = 'pdf' identifier = 'pdf'
verbose_name = _('PDF output') verbose_name = _('PDF output')
@@ -39,6 +170,7 @@ class PdfTicketOutput(BaseTicketOutput):
def __init__(self, event, override_layout=None, override_background=None): def __init__(self, event, override_layout=None, override_background=None):
self.override_layout = override_layout self.override_layout = override_layout
self.override_background = override_background self.override_background = override_background
self.variables = get_variables(event)
super().__init__(event) super().__init__(event)
def _register_fonts(self): def _register_fonts(self):
@@ -71,61 +203,12 @@ class PdfTicketOutput(BaseTicketOutput):
return o['text'].replace("\n", "<br/>\n") return o['text'].replace("\n", "<br/>\n")
elif o['content'].startswith('meta:'): elif o['content'].startswith('meta:'):
return ev.meta_data.get(o['content'][5:]) or '' return ev.meta_data.get(o['content'][5:]) or ''
elif o['content'] == 'order': elif o['content'] in self.variables:
return order.code
elif o['content'] == 'item':
return str(op.item)
elif o['content'] == 'item_description':
return str(op.item.description)
elif o['content'] == 'organizer':
return str(order.event.organizer.name)
elif o['content'] == 'organizer_info_text':
return str(order.event.settings.organizer_info_text)
elif o['content'] == 'secret':
return op.secret
elif o['content'] == 'variation':
return str(op.variation) if op.variation else ''
elif o['content'] == 'itemvar':
return '{} - {}'.format(op.item, op.variation) if op.variation else str(op.item)
elif o['content'] == 'price':
return '{} {}'.format(order.event.currency, localize(op.price))
elif o['content'] == 'attendee_name':
return op.attendee_name or (op.addon_to.attendee_name if op.addon_to else '')
elif o['content'] == 'event_name':
return str(ev.name)
elif o['content'] == 'event_location':
return str(ev.location).replace("\n", "<br/>\n")
elif o['content'] == 'event_date':
return ev.get_date_from_display(show_times=False)
elif o['content'] == 'event_date_range':
return ev.get_date_range_display()
elif o['content'] == 'event_begin':
return ev.get_date_from_display(show_times=True)
elif o['content'] == 'event_begin_time':
return ev.get_time_from_display()
elif o['content'] == 'event_admission':
if ev.date_admission:
tz = timezone(order.event.settings.timezone)
return date_format(ev.date_admission.astimezone(tz), "SHORT_DATETIME_FORMAT")
elif o['content'] == 'event_admission_time':
if ev.date_admission:
tz = timezone(order.event.settings.timezone)
return date_format(ev.date_admission.astimezone(tz), "TIME_FORMAT")
elif o['content'] == 'invoice_name':
try: try:
return order.invoice_address.name self.variables[o['content']]['evaluate'](op, order, ev)
except: except:
return "" logger.exception('Failed to process variable.')
elif o['content'] == 'invoice_company': return '(error)'
try:
return order.invoice_address.company
except:
return ""
elif o['content'] == 'addons':
return "<br/>".join([
'{} - {}'.format(p.item, p.variation) if p.variation else str(p.item)
for p in op.addons.select_related('item', 'variation')
])
return '' return ''
def _draw_textarea(self, canvas: Canvas, op: OrderPosition, order: Order, o: dict): def _draw_textarea(self, canvas: Canvas, op: OrderPosition, order: Order, o: dict):

View File

@@ -24,7 +24,7 @@ from pretix.control.permissions import EventPermissionRequiredMixin
from pretix.helpers.database import rolledback_transaction from pretix.helpers.database import rolledback_transaction
from pretix.plugins.ticketoutputpdf.signals import get_fonts from pretix.plugins.ticketoutputpdf.signals import get_fonts
from .ticketoutput import PdfTicketOutput from .ticketoutput import PdfTicketOutput, get_variables
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -156,6 +156,7 @@ class EditorView(EventPermissionRequiredMixin, TemplateView):
if self.request.event.settings.get('ticketoutput_{}_background'.format(self.identifier)) if self.request.event.settings.get('ticketoutput_{}_background'.format(self.identifier))
else static('pretixpresale/pdf/ticket_default_a4.pdf') else static('pretixpresale/pdf/ticket_default_a4.pdf')
) )
ctx['variables'] = get_variables(self.request.event)
ctx['layout'] = json.dumps( ctx['layout'] = json.dumps(
self.request.event.settings.get('ticketoutput_{}_layout'.format(self.identifier), as_type=list) self.request.event.settings.get('ticketoutput_{}_layout'.format(self.identifier), as_type=list)
or prov._default_layout() or prov._default_layout()

View File

@@ -183,3 +183,10 @@ pre.mail-preview {
padding-top: 7px; padding-top: 7px;
} }
} }
.inline-multiple-choice {
.checkbox {
display: inline-block;
margin-top: 0;
margin-right: 10px;
}
}