mirror of
https://github.com/pretix/pretix.git
synced 2026-06-26 03:46:14 +00:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7faf6cfd80 | |||
| cdeb3609f3 | |||
| 53d8aa5679 | |||
| 80601cc013 | |||
| e2661f304d | |||
| 71f6ade5d6 | |||
| e087dd39b8 | |||
| f3a8ce39f1 | |||
| a6c417eb2f | |||
| 5d449ea313 |
@@ -19,4 +19,4 @@
|
||||
# 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/>.
|
||||
#
|
||||
__version__ = "2026.5.0"
|
||||
__version__ = "2026.5.2"
|
||||
|
||||
@@ -61,18 +61,23 @@ class ReusableMediaExporter(OrganizerLevelExportMixin, ListExporter):
|
||||
yield headers
|
||||
yield self.ProgressSetTotal(total=media.count())
|
||||
|
||||
can_read_giftcards = self.permission_holder.has_organizer_permission(self.organizer, 'organizer.giftcards:read')
|
||||
|
||||
for medium in media.iterator(chunk_size=1000):
|
||||
row = [
|
||||
giftcard_secret = medium.linked_giftcard.secret if medium.linked_giftcard_id else ''
|
||||
if giftcard_secret and not can_read_giftcards:
|
||||
giftcard_secret = giftcard_secret[:3] + "…"
|
||||
|
||||
yield [
|
||||
medium.type,
|
||||
medium.identifier,
|
||||
_('Yes') if medium.active else _('No'),
|
||||
date_format(medium.expires, 'SHORT_DATETIME_FORMAT') if medium.expires else '',
|
||||
medium.customer.identifier if medium.customer_id else '',
|
||||
f"{medium.linked_orderposition.order.code}-{medium.linked_orderposition.positionid}" if medium.linked_orderposition_id else '',
|
||||
medium.linked_giftcard.secret if medium.linked_giftcard_id else '',
|
||||
giftcard_secret,
|
||||
medium.notes,
|
||||
]
|
||||
yield row
|
||||
|
||||
def get_filename(self):
|
||||
return f'{self.organizer.slug}_media'
|
||||
|
||||
@@ -22,9 +22,7 @@
|
||||
import datetime
|
||||
import logging
|
||||
import math
|
||||
import re
|
||||
import textwrap
|
||||
import unicodedata
|
||||
from collections import defaultdict
|
||||
from decimal import Decimal
|
||||
from io import BytesIO
|
||||
@@ -58,8 +56,8 @@ from pretix.base.services.currencies import SOURCE_NAMES
|
||||
from pretix.base.signals import register_invoice_renderers
|
||||
from pretix.base.templatetags.money import money_filter
|
||||
from pretix.helpers.reportlab import (
|
||||
FontFallbackParagraph, ThumbnailingImageReader, register_ttf_font_if_new,
|
||||
reshaper,
|
||||
FontFallbackParagraph, PlainTextParagraph, ThumbnailingImageReader,
|
||||
normalize_text, register_ttf_font_if_new, reshaper,
|
||||
)
|
||||
from pretix.presale.style import get_fonts
|
||||
|
||||
@@ -259,18 +257,8 @@ class BaseReportlabInvoiceRenderer(BaseInvoiceRenderer):
|
||||
register_ttf_font_if_new(family + ' B I', finders.find(styles['bolditalic']['truetype']))
|
||||
|
||||
def _normalize(self, text):
|
||||
# reportlab does not support unicode combination characters
|
||||
# It's important we do this before we use ArabicReshaper
|
||||
text = unicodedata.normalize("NFKC", text)
|
||||
|
||||
# reportlab does not support RTL, ligature-heavy scripts like Arabic. Therefore, we use ArabicReshaper
|
||||
# to resolve all ligatures and python-bidi to switch RTL texts.
|
||||
try:
|
||||
text = "<br />".join(get_display(reshaper.reshape(l)) for l in re.split("<br ?/>", text))
|
||||
except:
|
||||
logger.exception('Reshaping/Bidi fixes failed on string {}'.format(repr(text)))
|
||||
|
||||
return text
|
||||
# alias kept for plugin compatibility
|
||||
return normalize_text(text)
|
||||
|
||||
def _upper(self, val):
|
||||
# We uppercase labels, but not in every language
|
||||
@@ -351,10 +339,15 @@ class BaseReportlabInvoiceRenderer(BaseInvoiceRenderer):
|
||||
return 'invoice.pdf', 'application/pdf', buffer.read()
|
||||
|
||||
def _clean_text(self, text, tags=None):
|
||||
return self._normalize(bleach.clean(
|
||||
text,
|
||||
tags=set(tags) if tags else set()
|
||||
).strip().replace('<br>', '<br />').replace('\n', '<br />\n'))
|
||||
# For backwards compatibility with customer content, we need to support tags like <br> and <b> in a few text
|
||||
# fields. Therefore, we can't use PlainTextParagraph for these, but run bleach instead to limit the allowed
|
||||
# tags.
|
||||
return self._normalize(
|
||||
bleach.clean(
|
||||
text,
|
||||
tags=set(tags) if tags else set()
|
||||
).strip().replace('<br>', '<br />').replace('\n', '<br />\n')
|
||||
)
|
||||
|
||||
|
||||
class PaidMarker(Flowable):
|
||||
@@ -405,8 +398,7 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
||||
invoice_to_top = 52 * mm
|
||||
|
||||
def _draw_invoice_to(self, canvas):
|
||||
p = FontFallbackParagraph(self._clean_text(self.invoice.address_invoice_to),
|
||||
style=self.stylesheet['Normal'])
|
||||
p = PlainTextParagraph(self.invoice.address_invoice_to, style=self.stylesheet['Normal'])
|
||||
p.wrapOn(canvas, self.invoice_to_width, self.invoice_to_height)
|
||||
p_size = p.wrap(self.invoice_to_width, self.invoice_to_height)
|
||||
p.drawOn(canvas, self.invoice_to_left, self.pagesize[1] - p_size[1] - self.invoice_to_top)
|
||||
@@ -417,8 +409,8 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
||||
invoice_from_top = 17 * mm
|
||||
|
||||
def _draw_invoice_from(self, canvas):
|
||||
p = FontFallbackParagraph(
|
||||
self._clean_text(self.invoice.full_invoice_from),
|
||||
p = PlainTextParagraph(
|
||||
self.invoice.full_invoice_from,
|
||||
style=self.stylesheet['InvoiceFrom']
|
||||
)
|
||||
p.wrapOn(canvas, self.invoice_from_width, self.invoice_from_height)
|
||||
@@ -548,13 +540,12 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
||||
def _draw_event(self, canvas):
|
||||
def shorten(txt):
|
||||
txt = str(txt)
|
||||
txt = bleach.clean(txt, tags=set()).strip()
|
||||
p = FontFallbackParagraph(self._normalize(txt.strip().replace('\n', '<br />\n')), style=self.stylesheet['Normal'])
|
||||
p = PlainTextParagraph(txt, style=self.stylesheet['Normal'])
|
||||
p_size = p.wrap(self.event_width, self.event_height)
|
||||
|
||||
while p_size[1] > 2 * self.stylesheet['Normal'].leading:
|
||||
txt = ' '.join(txt.replace('…', '').split()[:-1]) + '…'
|
||||
p = FontFallbackParagraph(self._normalize(txt.strip().replace('\n', '<br />\n')), style=self.stylesheet['Normal'])
|
||||
p = PlainTextParagraph(txt, style=self.stylesheet['Normal'])
|
||||
p_size = p.wrap(self.event_width, self.event_height)
|
||||
return txt
|
||||
|
||||
@@ -572,7 +563,7 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
||||
else:
|
||||
p_str = shorten(self.invoice.event.name)
|
||||
|
||||
p = FontFallbackParagraph(self._normalize(p_str.strip().replace('\n', '<br />\n')), style=self.stylesheet['Normal'])
|
||||
p = PlainTextParagraph(p_str, style=self.stylesheet['Normal'])
|
||||
p.wrapOn(canvas, self.event_width, self.event_height)
|
||||
p_size = p.wrap(self.event_width, self.event_height)
|
||||
p.drawOn(canvas, self.event_left, self.pagesize[1] - self.event_top - p_size[1])
|
||||
@@ -645,39 +636,37 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
||||
|
||||
type_info_text = self.invoice.transmission_type_instance.pdf_info_text()
|
||||
if type_info_text:
|
||||
story.append(FontFallbackParagraph(
|
||||
story.append(PlainTextParagraph(
|
||||
type_info_text,
|
||||
self.stylesheet['WarningBlock']
|
||||
))
|
||||
|
||||
if self.invoice.custom_field:
|
||||
story.append(FontFallbackParagraph(
|
||||
story.append(PlainTextParagraph(
|
||||
'{}: {}'.format(
|
||||
self._clean_text(str(self.invoice.event.settings.invoice_address_custom_field)),
|
||||
self._clean_text(self.invoice.custom_field),
|
||||
str(self.invoice.event.settings.invoice_address_custom_field),
|
||||
self.invoice.custom_field,
|
||||
),
|
||||
self.stylesheet['Normal']
|
||||
))
|
||||
|
||||
if self.invoice.internal_reference:
|
||||
story.append(FontFallbackParagraph(
|
||||
self._normalize(pgettext('invoice', 'Customer reference: {reference}').format(
|
||||
reference=self._clean_text(self.invoice.internal_reference),
|
||||
)),
|
||||
story.append(PlainTextParagraph(
|
||||
pgettext('invoice', 'Customer reference: {reference}').format(
|
||||
reference=self.invoice.internal_reference,
|
||||
),
|
||||
self.stylesheet['Normal']
|
||||
))
|
||||
|
||||
if self.invoice.invoice_to_vat_id:
|
||||
story.append(FontFallbackParagraph(
|
||||
self._normalize(pgettext('invoice', 'Customer VAT ID')) + ': ' +
|
||||
self._clean_text(self.invoice.invoice_to_vat_id),
|
||||
story.append(PlainTextParagraph(
|
||||
pgettext('invoice', 'Customer VAT ID') + ': ' + self.invoice.invoice_to_vat_id,
|
||||
self.stylesheet['Normal']
|
||||
))
|
||||
|
||||
if self.invoice.invoice_to_beneficiary:
|
||||
story.append(FontFallbackParagraph(
|
||||
self._normalize(pgettext('invoice', 'Beneficiary')) + ':<br />' +
|
||||
self._clean_text(self.invoice.invoice_to_beneficiary),
|
||||
story.append(PlainTextParagraph(
|
||||
pgettext('invoice', 'Beneficiary') + ':\n' + self.invoice.invoice_to_beneficiary,
|
||||
self.stylesheet['Normal']
|
||||
))
|
||||
|
||||
@@ -707,11 +696,11 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
||||
|
||||
story = [
|
||||
NextPageTemplate('FirstPage'),
|
||||
FontFallbackParagraph(
|
||||
self._normalize(
|
||||
PlainTextParagraph(
|
||||
(
|
||||
pgettext('invoice', 'Tax Invoice') if str(self.invoice.invoice_from_country) == 'AU'
|
||||
else pgettext('invoice', 'Invoice')
|
||||
) if not self.invoice.is_cancellation else self._normalize(pgettext('invoice', 'Cancellation')),
|
||||
) if not self.invoice.is_cancellation else pgettext('invoice', 'Cancellation'),
|
||||
self.stylesheet['Heading1']
|
||||
),
|
||||
Spacer(1, 5 * mm),
|
||||
@@ -733,17 +722,17 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
||||
]
|
||||
if has_taxes:
|
||||
tdata = [(
|
||||
FontFallbackParagraph(self._normalize(pgettext('invoice', 'Description')), self.stylesheet['Bold']),
|
||||
FontFallbackParagraph(self._normalize(pgettext('invoice', 'Qty')), self.stylesheet['BoldRightNoSplit']),
|
||||
FontFallbackParagraph(self._normalize(pgettext('invoice', 'Tax rate')), self.stylesheet['BoldRightNoSplit']),
|
||||
FontFallbackParagraph(self._normalize(pgettext('invoice', 'Net')), self.stylesheet['BoldRightNoSplit']),
|
||||
FontFallbackParagraph(self._normalize(pgettext('invoice', 'Gross')), self.stylesheet['BoldRightNoSplit']),
|
||||
PlainTextParagraph(pgettext('invoice', 'Description'), self.stylesheet['Bold']),
|
||||
PlainTextParagraph(pgettext('invoice', 'Qty'), self.stylesheet['BoldRightNoSplit']),
|
||||
PlainTextParagraph(pgettext('invoice', 'Tax rate'), self.stylesheet['BoldRightNoSplit']),
|
||||
PlainTextParagraph(pgettext('invoice', 'Net'), self.stylesheet['BoldRightNoSplit']),
|
||||
PlainTextParagraph(pgettext('invoice', 'Gross'), self.stylesheet['BoldRightNoSplit']),
|
||||
)]
|
||||
else:
|
||||
tdata = [(
|
||||
FontFallbackParagraph(self._normalize(pgettext('invoice', 'Description')), self.stylesheet['Bold']),
|
||||
FontFallbackParagraph(self._normalize(pgettext('invoice', 'Qty')), self.stylesheet['BoldRightNoSplit']),
|
||||
FontFallbackParagraph(self._normalize(pgettext('invoice', 'Amount')), self.stylesheet['BoldRightNoSplit']),
|
||||
PlainTextParagraph(pgettext('invoice', 'Description'), self.stylesheet['Bold']),
|
||||
PlainTextParagraph(pgettext('invoice', 'Qty'), self.stylesheet['BoldRightNoSplit']),
|
||||
PlainTextParagraph(pgettext('invoice', 'Amount'), self.stylesheet['BoldRightNoSplit']),
|
||||
)]
|
||||
|
||||
def _group_key(line):
|
||||
@@ -780,8 +769,8 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
||||
max_height = self.stylesheet['Normal'].leading * 5
|
||||
p_style = self.stylesheet['Normal']
|
||||
for __ in range(1000):
|
||||
p = FontFallbackParagraph(
|
||||
self._clean_text(curr_description, tags=['br']),
|
||||
p = PlainTextParagraph(
|
||||
curr_description,
|
||||
p_style
|
||||
)
|
||||
h = p.wrap(max_width, doc.height)[1]
|
||||
@@ -862,7 +851,7 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
||||
# Group together at the end of the invoice
|
||||
request_show_service_date = period_line
|
||||
elif period_line:
|
||||
description_p_list.append(FontFallbackParagraph(
|
||||
description_p_list.append(PlainTextParagraph(
|
||||
period_line,
|
||||
self.stylesheet['Fineprint']
|
||||
))
|
||||
@@ -874,7 +863,7 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
||||
net_price=money_filter(net_value, self.invoice.event.currency),
|
||||
gross_price=money_filter(gross_value, self.invoice.event.currency),
|
||||
)
|
||||
description_p_list.append(FontFallbackParagraph(
|
||||
description_p_list.append(PlainTextParagraph(
|
||||
single_price_line,
|
||||
self.stylesheet['Fineprint']
|
||||
))
|
||||
@@ -883,11 +872,11 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
||||
description_p_list.pop(0),
|
||||
str(len(lines)),
|
||||
localize(tax_rate) + " %",
|
||||
FontFallbackParagraph(
|
||||
PlainTextParagraph(
|
||||
money_filter(net_value * len(lines), self.invoice.event.currency).replace('\xa0', ' '),
|
||||
self.stylesheet['NormalRight']
|
||||
),
|
||||
FontFallbackParagraph(
|
||||
PlainTextParagraph(
|
||||
money_filter(gross_value * len(lines), self.invoice.event.currency).replace('\xa0', ' '),
|
||||
self.stylesheet['NormalRight']
|
||||
),
|
||||
@@ -904,14 +893,14 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
||||
single_price_line = pgettext('invoice', 'Single price: {price}').format(
|
||||
price=money_filter(gross_value, self.invoice.event.currency),
|
||||
)
|
||||
description_p_list.append(FontFallbackParagraph(
|
||||
description_p_list.append(PlainTextParagraph(
|
||||
single_price_line,
|
||||
self.stylesheet['Fineprint']
|
||||
))
|
||||
tdata.append((
|
||||
description_p_list.pop(0),
|
||||
str(len(lines)),
|
||||
FontFallbackParagraph(
|
||||
PlainTextParagraph(
|
||||
money_filter(gross_value * len(lines), self.invoice.event.currency).replace('\xa0', ' '),
|
||||
self.stylesheet['NormalRight']
|
||||
),
|
||||
@@ -944,12 +933,12 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
||||
|
||||
if has_taxes:
|
||||
tdata.append([
|
||||
FontFallbackParagraph(self._normalize(pgettext('invoice', 'Invoice total')), self.stylesheet['Bold']), '', '', '',
|
||||
PlainTextParagraph(pgettext('invoice', 'Invoice total'), self.stylesheet['Bold']), '', '', '',
|
||||
money_filter(total, self.invoice.event.currency)
|
||||
])
|
||||
else:
|
||||
tdata.append([
|
||||
FontFallbackParagraph(self._normalize(pgettext('invoice', 'Invoice total')), self.stylesheet['Bold']), '',
|
||||
PlainTextParagraph(pgettext('invoice', 'Invoice total'), self.stylesheet['Bold']), '',
|
||||
money_filter(total, self.invoice.event.currency)
|
||||
])
|
||||
|
||||
@@ -958,12 +947,12 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
||||
pending_sum = self.invoice.order.pending_sum
|
||||
if pending_sum != total:
|
||||
tdata.append(
|
||||
[FontFallbackParagraph(self._normalize(pgettext('invoice', 'Received payments')), self.stylesheet['Normal'])] +
|
||||
[PlainTextParagraph(pgettext('invoice', 'Received payments'), self.stylesheet['Normal'])] +
|
||||
(['', '', ''] if has_taxes else ['']) +
|
||||
[money_filter(pending_sum - total, self.invoice.event.currency)]
|
||||
)
|
||||
tdata.append(
|
||||
[FontFallbackParagraph(self._normalize(pgettext('invoice', 'Outstanding payments')), self.stylesheet['Bold'])] +
|
||||
[PlainTextParagraph(pgettext('invoice', 'Outstanding payments'), self.stylesheet['Bold'])] +
|
||||
(['', '', ''] if has_taxes else ['']) +
|
||||
[money_filter(pending_sum, self.invoice.event.currency)]
|
||||
)
|
||||
@@ -980,12 +969,12 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
||||
s=Sum('amount')
|
||||
)['s'] or Decimal('0.00')
|
||||
tdata.append(
|
||||
[FontFallbackParagraph(self._normalize(pgettext('invoice', 'Paid by gift card')), self.stylesheet['Normal'])] +
|
||||
[PlainTextParagraph(pgettext('invoice', 'Paid by gift card'), self.stylesheet['Normal'])] +
|
||||
(['', '', ''] if has_taxes else ['']) +
|
||||
[money_filter(giftcard_sum, self.invoice.event.currency)]
|
||||
)
|
||||
tdata.append(
|
||||
[FontFallbackParagraph(self._normalize(pgettext('invoice', 'Remaining amount')), self.stylesheet['Bold'])] +
|
||||
[PlainTextParagraph(pgettext('invoice', 'Remaining amount'), self.stylesheet['Bold'])] +
|
||||
(['', '', ''] if has_taxes else ['']) +
|
||||
[money_filter(total - giftcard_sum, self.invoice.event.currency)]
|
||||
)
|
||||
@@ -1008,14 +997,14 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
||||
story.append(Spacer(1, 10 * mm))
|
||||
|
||||
if request_show_service_date:
|
||||
story.append(FontFallbackParagraph(
|
||||
self._normalize(pgettext('invoice', 'Invoice period: {daterange}').format(daterange=request_show_service_date)),
|
||||
story.append(PlainTextParagraph(
|
||||
pgettext('invoice', 'Invoice period: {daterange}').format(daterange=request_show_service_date),
|
||||
self.stylesheet['Normal']
|
||||
))
|
||||
|
||||
if self.invoice.payment_provider_text:
|
||||
story.append(FontFallbackParagraph(
|
||||
self._normalize(self.invoice.payment_provider_text),
|
||||
self._clean_text(self.invoice.payment_provider_text, tags=['br', 'b']),
|
||||
self.stylesheet['Normal']
|
||||
))
|
||||
|
||||
@@ -1039,10 +1028,10 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
||||
('FONTNAME', (0, 0), (-1, -1), self.font_regular),
|
||||
]
|
||||
thead = [
|
||||
FontFallbackParagraph(self._normalize(pgettext('invoice', 'Tax rate')), self.stylesheet['Fineprint']),
|
||||
FontFallbackParagraph(self._normalize(pgettext('invoice', 'Net value')), self.stylesheet['FineprintRight']),
|
||||
FontFallbackParagraph(self._normalize(pgettext('invoice', 'Gross value')), self.stylesheet['FineprintRight']),
|
||||
FontFallbackParagraph(self._normalize(pgettext('invoice', 'Tax')), self.stylesheet['FineprintRight']),
|
||||
PlainTextParagraph(pgettext('invoice', 'Tax rate'), self.stylesheet['Fineprint']),
|
||||
PlainTextParagraph(pgettext('invoice', 'Net value'), self.stylesheet['FineprintRight']),
|
||||
PlainTextParagraph(pgettext('invoice', 'Gross value'), self.stylesheet['FineprintRight']),
|
||||
PlainTextParagraph(pgettext('invoice', 'Tax'), self.stylesheet['FineprintRight']),
|
||||
''
|
||||
]
|
||||
tdata = [thead]
|
||||
@@ -1053,7 +1042,7 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
||||
continue
|
||||
tax = taxvalue_map[idx]
|
||||
tdata.append([
|
||||
FontFallbackParagraph(self._normalize(localize(rate) + " % " + name), self.stylesheet['Fineprint']),
|
||||
PlainTextParagraph(localize(rate) + " % " + name, self.stylesheet['Fineprint']),
|
||||
money_filter(gross - tax, self.invoice.event.currency),
|
||||
money_filter(gross, self.invoice.event.currency),
|
||||
money_filter(tax, self.invoice.event.currency),
|
||||
@@ -1072,7 +1061,7 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
||||
table.setStyle(TableStyle(tstyledata))
|
||||
story.append(Spacer(5 * mm, 5 * mm))
|
||||
story.append(KeepTogether([
|
||||
FontFallbackParagraph(self._normalize(pgettext('invoice', 'Included taxes')), self.stylesheet['FineprintHeading']),
|
||||
PlainTextParagraph(pgettext('invoice', 'Included taxes'), self.stylesheet['FineprintHeading']),
|
||||
table
|
||||
]))
|
||||
|
||||
@@ -1089,7 +1078,7 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
||||
net = gross - tax
|
||||
|
||||
tdata.append([
|
||||
FontFallbackParagraph(self._normalize(localize(rate) + " % " + name), self.stylesheet['Fineprint']),
|
||||
PlainTextParagraph(localize(rate) + " % " + name, self.stylesheet['Fineprint']),
|
||||
fmt(net), fmt(gross), fmt(tax), ''
|
||||
])
|
||||
|
||||
@@ -1098,13 +1087,13 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
||||
|
||||
story.append(KeepTogether([
|
||||
Spacer(1, height=2 * mm),
|
||||
FontFallbackParagraph(
|
||||
self._normalize(pgettext(
|
||||
PlainTextParagraph(
|
||||
pgettext(
|
||||
'invoice', 'Using the conversion rate of 1:{rate} as published by the {authority} on '
|
||||
'{date}, this corresponds to:'
|
||||
).format(rate=localize(self.invoice.foreign_currency_rate),
|
||||
authority=SOURCE_NAMES.get(self.invoice.foreign_currency_source, "?"),
|
||||
date=date_format(self.invoice.foreign_currency_rate_date, "SHORT_DATE_FORMAT"))),
|
||||
date=date_format(self.invoice.foreign_currency_rate_date, "SHORT_DATE_FORMAT")),
|
||||
self.stylesheet['Fineprint']
|
||||
),
|
||||
Spacer(1, height=3 * mm),
|
||||
@@ -1113,14 +1102,14 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
||||
elif self.invoice.foreign_currency_display and self.invoice.foreign_currency_rate:
|
||||
foreign_total = round_decimal(total * self.invoice.foreign_currency_rate)
|
||||
story.append(Spacer(1, 5 * mm))
|
||||
story.append(FontFallbackParagraph(self._normalize(
|
||||
story.append(PlainTextParagraph(
|
||||
pgettext(
|
||||
'invoice', 'Using the conversion rate of 1:{rate} as published by the {authority} on '
|
||||
'{date}, the invoice total corresponds to {total}.'
|
||||
).format(rate=localize(self.invoice.foreign_currency_rate),
|
||||
date=date_format(self.invoice.foreign_currency_rate_date, "SHORT_DATE_FORMAT"),
|
||||
authority=SOURCE_NAMES.get(self.invoice.foreign_currency_source, "?"),
|
||||
total=fmt(foreign_total))),
|
||||
total=fmt(foreign_total)),
|
||||
self.stylesheet['Fineprint']
|
||||
))
|
||||
|
||||
@@ -1162,11 +1151,8 @@ class Modern1Renderer(ClassicInvoiceRenderer):
|
||||
def _draw_invoice_from(self, canvas):
|
||||
if not self.invoice.address_invoice_from:
|
||||
return
|
||||
c = [
|
||||
self._clean_text(l)
|
||||
for l in self.invoice.address_invoice_from.strip().split('\n')
|
||||
]
|
||||
p = FontFallbackParagraph(self._normalize(' · '.join(c)), style=self.stylesheet['Sender'])
|
||||
c = self.invoice.address_invoice_from.strip().split('\n')
|
||||
p = PlainTextParagraph(' · '.join(c), style=self.stylesheet['Sender'])
|
||||
p.wrapOn(canvas, self.invoice_to_width, 15.7 * mm)
|
||||
p.drawOn(canvas, self.invoice_to_left, self.pagesize[1] - self.invoice_to_top + 2 * mm)
|
||||
super()._draw_invoice_from(canvas)
|
||||
@@ -1225,8 +1211,8 @@ class Modern1Renderer(ClassicInvoiceRenderer):
|
||||
_draw(pgettext('invoice', 'Order code'), self.invoice.order.full_code, value_size, self.left_margin, 45 * mm, **kwargs)
|
||||
]
|
||||
|
||||
p = FontFallbackParagraph(
|
||||
self._normalize(date_format(self.invoice.date, "DATE_FORMAT")),
|
||||
p = PlainTextParagraph(
|
||||
date_format(self.invoice.date, "DATE_FORMAT"),
|
||||
style=ParagraphStyle(name=f'Normal{value_size}', fontName=self.font_regular, fontSize=value_size, leading=value_size * 1.2)
|
||||
)
|
||||
w = stringWidth(p.text, p.frags[0].fontName, p.frags[0].fontSize)
|
||||
@@ -1283,7 +1269,7 @@ class Modern1SimplifiedRenderer(Modern1Renderer):
|
||||
i = []
|
||||
|
||||
if not self.invoice.event.has_subevents and self.invoice.event.settings.show_dates_on_frontpage:
|
||||
i.append(FontFallbackParagraph(
|
||||
i.append(PlainTextParagraph(
|
||||
pgettext('invoice', 'Event date: {date_range}').format(
|
||||
date_range=self.invoice.event.get_date_range_display(),
|
||||
),
|
||||
|
||||
@@ -1062,7 +1062,7 @@ class Renderer:
|
||||
except:
|
||||
logger.exception('Reshaping/Bidi fixes failed on string {}'.format(repr(text)))
|
||||
|
||||
p = Paragraph(text, style=style)
|
||||
p = Paragraph(text, style=style) # not using AutoEscapeParagraph is safe as we escape above
|
||||
return p, ad, lineheight
|
||||
|
||||
def _draw_textcontainer(self, canvas: Canvas, op: OrderPosition, order: Order, o: dict):
|
||||
|
||||
@@ -2,13 +2,14 @@
|
||||
{% load i18n %}
|
||||
{% load rich_text %}
|
||||
{% load static %}
|
||||
{% load wrap_in %}
|
||||
{% block title %}{% trans "Redirect" %}{% endblock %}
|
||||
{% block content %}
|
||||
<i class="fa fa-link fa-fw big-icon"></i>
|
||||
<div class="error-details">
|
||||
<h1>{% trans "Redirect" %}</h1>
|
||||
<h3>
|
||||
{% blocktrans trimmed with host="<strong>"|add:hostname|add:"</strong>"|safe %}
|
||||
{% blocktrans trimmed with host=hostname|wrap_in:'strong' %}
|
||||
The link you clicked on wants to redirect you to a destination on the website {{ host }}.
|
||||
{% endblocktrans %}
|
||||
{% blocktrans trimmed %}
|
||||
|
||||
@@ -38,6 +38,7 @@ from pretix import __version__
|
||||
from pretix.base.models import Order, OrderPayment, Transaction
|
||||
from pretix.base.plugins import get_all_plugins
|
||||
from pretix.base.templatetags.money import money_filter
|
||||
from pretix.helpers.reportlab import PlainTextParagraph
|
||||
from pretix.plugins.reports.exporters import ReportlabExportMixin
|
||||
from pretix.settings import DATA_DIR
|
||||
|
||||
@@ -79,23 +80,23 @@ class SysReport(ReportlabExportMixin):
|
||||
style_small.fontSize = 6
|
||||
|
||||
story = [
|
||||
Paragraph("System report", headlinestyle),
|
||||
PlainTextParagraph("System report", headlinestyle),
|
||||
Spacer(1, 5 * mm),
|
||||
Paragraph("Usage", subheadlinestyle),
|
||||
PlainTextParagraph("Usage", subheadlinestyle),
|
||||
Spacer(1, 5 * mm),
|
||||
self._usage_table(),
|
||||
Spacer(1, 5 * mm),
|
||||
Paragraph("Installed versions", subheadlinestyle),
|
||||
PlainTextParagraph("Installed versions", subheadlinestyle),
|
||||
Spacer(1, 5 * mm),
|
||||
self._tech_table(),
|
||||
Spacer(1, 5 * mm),
|
||||
Paragraph("Plugins", subheadlinestyle),
|
||||
PlainTextParagraph("Plugins", subheadlinestyle),
|
||||
Spacer(1, 5 * mm),
|
||||
Paragraph(self._get_plugin_versions(), style_small),
|
||||
PlainTextParagraph(self._get_plugin_versions(), style_small),
|
||||
Spacer(1, 5 * mm),
|
||||
Paragraph("Custom templates", subheadlinestyle),
|
||||
PlainTextParagraph("Custom templates", subheadlinestyle),
|
||||
Spacer(1, 5 * mm),
|
||||
Paragraph(self._get_custom_templates(), style_small),
|
||||
PlainTextParagraph(self._get_custom_templates(), style_small),
|
||||
Spacer(1, 5 * mm),
|
||||
]
|
||||
|
||||
@@ -121,13 +122,13 @@ class SysReport(ReportlabExportMixin):
|
||||
("RIGHTPADDING", (-1, 0), (-1, -1), 0),
|
||||
]
|
||||
tdata = [
|
||||
[Paragraph("Site URL:", style), Paragraph(settings.SITE_URL, style)],
|
||||
[Paragraph("pretix version:", style), Paragraph(__version__, style)],
|
||||
[Paragraph("Python version:", style), Paragraph(sys.version, style)],
|
||||
[Paragraph("Platform:", style), Paragraph(platform.platform(), style)],
|
||||
[PlainTextParagraph("Site URL:", style), Paragraph(settings.SITE_URL, style)],
|
||||
[PlainTextParagraph("pretix version:", style), Paragraph(__version__, style)],
|
||||
[PlainTextParagraph("Python version:", style), Paragraph(sys.version, style)],
|
||||
[PlainTextParagraph("Platform:", style), Paragraph(platform.platform(), style)],
|
||||
[
|
||||
Paragraph("Database engine:", style),
|
||||
Paragraph(settings.DATABASES["default"]["ENGINE"], style),
|
||||
PlainTextParagraph("Database engine:", style),
|
||||
PlainTextParagraph(settings.DATABASES["default"]["ENGINE"], style),
|
||||
],
|
||||
]
|
||||
table = Table(tdata, colWidths=colwidths, repeatRows=0)
|
||||
@@ -206,7 +207,7 @@ class SysReport(ReportlabExportMixin):
|
||||
year_last = now().year
|
||||
tdata = [
|
||||
[
|
||||
Paragraph(l, style_small_head)
|
||||
PlainTextParagraph(l, style_small_head)
|
||||
for l in (
|
||||
"Time frame",
|
||||
"Currency",
|
||||
@@ -257,19 +258,19 @@ class SysReport(ReportlabExportMixin):
|
||||
|
||||
tdata.append(
|
||||
(
|
||||
Paragraph(
|
||||
PlainTextParagraph(
|
||||
date_format(first_day, "M Y")
|
||||
+ " – "
|
||||
+ date_format(after_day - timedelta(days=1), "M Y"),
|
||||
style_small,
|
||||
),
|
||||
Paragraph(c, style_small),
|
||||
Paragraph(str(orders_count), style_small) if i == 0 else "",
|
||||
Paragraph(money_filter(revenue_data.get("s_net") or 0, c), style_small),
|
||||
Paragraph(str(testmode_count), style_small) if i == 0 else "",
|
||||
Paragraph(str(unconfirmed_count), style_small) if i == 0 else "",
|
||||
Paragraph(str(revenue_data.get("c") or 0), style_small),
|
||||
Paragraph(money_filter(revenue_data.get("s_gross") or 0, c), style_small),
|
||||
PlainTextParagraph(c, style_small),
|
||||
PlainTextParagraph(str(orders_count), style_small) if i == 0 else "",
|
||||
PlainTextParagraph(money_filter(revenue_data.get("s_net") or 0, c), style_small),
|
||||
PlainTextParagraph(str(testmode_count), style_small) if i == 0 else "",
|
||||
PlainTextParagraph(str(unconfirmed_count), style_small) if i == 0 else "",
|
||||
PlainTextParagraph(str(revenue_data.get("c") or 0), style_small),
|
||||
PlainTextParagraph(money_filter(revenue_data.get("s_gross") or 0, c), style_small),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@@ -19,9 +19,7 @@
|
||||
{% endif %}
|
||||
</h1>
|
||||
|
||||
<script type="application/json" id="editor-data">
|
||||
{{ layout|safe }}
|
||||
</script>
|
||||
{{ layout|json_script:"editor-data" }}
|
||||
<div class="row">
|
||||
<div class="col-md-9">
|
||||
<div class="panel panel-default panel-pdf-editor">
|
||||
|
||||
@@ -284,7 +284,7 @@ class BaseEditorView(EventPermissionRequiredMixin, TemplateView):
|
||||
ctx['pdf'] = self.get_current_background()
|
||||
ctx['variables'] = self.get_variables()
|
||||
ctx['images'] = self.get_images()
|
||||
ctx['layout'] = json.dumps(self.get_current_layout())
|
||||
ctx['layout'] = self.get_current_layout()
|
||||
ctx['title'] = self.title
|
||||
ctx['locales'] = [p for p in settings.LANGUAGES if p[0] in self.request.event.settings.locales]
|
||||
ctx['maxfilesize'] = self.maxfilesize
|
||||
|
||||
@@ -29,3 +29,8 @@ class PretixHelpersConfig(AppConfig):
|
||||
def ready(self):
|
||||
from .monkeypatching import monkeypatch_all_at_ready
|
||||
monkeypatch_all_at_ready()
|
||||
|
||||
# Ensure reportlab does not make any calls to the internet or the local disk
|
||||
from reportlab import rl_config
|
||||
rl_config.trustedHosts = []
|
||||
rl_config.trustedSchemes = ['data']
|
||||
|
||||
@@ -27,6 +27,7 @@ from datetime import datetime
|
||||
from http import cookies
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import SuspiciousFileOperation
|
||||
from PIL import Image
|
||||
from requests.adapters import HTTPAdapter
|
||||
from urllib3.connection import HTTPConnection, HTTPSConnection
|
||||
@@ -40,6 +41,8 @@ from urllib3.util.connection import (
|
||||
)
|
||||
from urllib3.util.timeout import _DEFAULT_TIMEOUT
|
||||
|
||||
from pretix.helpers.reportlab import ThumbnailingImageReader
|
||||
|
||||
_cgnat_net = ipaddress.ip_network('100.64.0.0/10')
|
||||
|
||||
|
||||
@@ -230,9 +233,27 @@ def monkeypatch_cookie_morsel():
|
||||
cookies.Morsel._reserved.setdefault("partitioned", "Partitioned")
|
||||
|
||||
|
||||
def monkeypatch_reportlab_imagereader():
|
||||
from reportlab.lib import utils
|
||||
old_init = utils.ImageReader.__init__
|
||||
|
||||
def new_init(self, fileName, ident=None): # noqa
|
||||
if not isinstance(fileName, Image.Image) and not hasattr(fileName, 'read') and not hasattr(fileName, 'str'):
|
||||
if not isinstance(self, ThumbnailingImageReader):
|
||||
# ThumbnailingImageReader is only used by us explicitly and not by using <img> in html, so it is safe
|
||||
raise SuspiciousFileOperation("reportlab should not be reading images from disk")
|
||||
|
||||
return types.MethodType(old_init, self)(
|
||||
fileName, ident
|
||||
)
|
||||
|
||||
utils.ImageReader.__init__ = new_init
|
||||
|
||||
|
||||
def monkeypatch_all_at_ready():
|
||||
monkeypatch_vobject_performance()
|
||||
monkeypatch_pillow_safer()
|
||||
monkeypatch_requests_timeout()
|
||||
monkeypatch_urllib3_ssrf_protection()
|
||||
monkeypatch_cookie_morsel()
|
||||
monkeypatch_reportlab_imagereader()
|
||||
|
||||
@@ -20,14 +20,19 @@
|
||||
# <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
import logging
|
||||
import re
|
||||
import unicodedata
|
||||
|
||||
from arabic_reshaper import ArabicReshaper
|
||||
from bidi import get_display
|
||||
from django.conf import settings
|
||||
from django.utils.functional import SimpleLazyObject
|
||||
from django.utils.html import escape
|
||||
from PIL import Image
|
||||
from reportlab.lib.styles import ParagraphStyle
|
||||
from reportlab.lib.utils import ImageReader
|
||||
from reportlab.pdfbase import pdfmetrics
|
||||
from reportlab.pdfbase.ttfonts import TTFont
|
||||
from reportlab.platypus import Paragraph
|
||||
|
||||
from pretix.presale.style import get_fonts
|
||||
@@ -70,6 +75,20 @@ reshaper = SimpleLazyObject(lambda: ArabicReshaper(configuration={
|
||||
}))
|
||||
|
||||
|
||||
def normalize_text(text: str) -> str:
|
||||
# reportlab does not support unicode combination characters
|
||||
# It's important we do this before we use ArabicReshaper
|
||||
text = unicodedata.normalize("NFKC", text)
|
||||
|
||||
# reportlab does not support RTL, ligature-heavy scripts like Arabic. Therefore, we use ArabicReshaper
|
||||
# to resolve all ligatures and python-bidi to switch RTL texts.
|
||||
try:
|
||||
text = "\n".join(get_display(reshaper.reshape(l)) for l in re.split("\n", text))
|
||||
except:
|
||||
logger.exception('Reshaping/Bidi fixes failed on string {}'.format(repr(text)))
|
||||
return text
|
||||
|
||||
|
||||
class FontFallbackParagraph(Paragraph):
|
||||
def __init__(self, text, style=None, *args, **kwargs):
|
||||
if style is None:
|
||||
@@ -87,6 +106,8 @@ class FontFallbackParagraph(Paragraph):
|
||||
if not text:
|
||||
return True
|
||||
font = pdfmetrics.getFont(font_name)
|
||||
if not isinstance(font, TTFont):
|
||||
return True
|
||||
return all(
|
||||
ord(c) in font.face.charToGlyph or not c.isprintable()
|
||||
for c in text
|
||||
@@ -102,6 +123,24 @@ class FontFallbackParagraph(Paragraph):
|
||||
return family
|
||||
|
||||
|
||||
class PlainTextParagraph(FontFallbackParagraph):
|
||||
def __init__(self, text, style=None, linebreaks=True, *args, **kwargs):
|
||||
if not isinstance(text, str):
|
||||
if hasattr(text, '__html__'):
|
||||
raise ValueError("It is contradictory to pass escaped content to PlainTextParagraph")
|
||||
text = str(text)
|
||||
|
||||
# Normalize unicode and apply reshaping
|
||||
text = normalize_text(text)
|
||||
|
||||
# Escape any HTML in the text
|
||||
text = escape(text)
|
||||
|
||||
if linebreaks:
|
||||
text = text.strip().replace("\n", "<br />\n")
|
||||
super().__init__(text, style, *args, **kwargs)
|
||||
|
||||
|
||||
def register_ttf_font_if_new(name, path):
|
||||
from reportlab.pdfbase import pdfmetrics
|
||||
from reportlab.pdfbase.ttfonts import TTFont
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
#
|
||||
# This file is part of pretix (Community Edition).
|
||||
#
|
||||
# Copyright (C) 2014-2020 Raphael Michel and contributors
|
||||
# Copyright (C) 2020-today pretix 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 django import template
|
||||
from django.utils.html import format_html
|
||||
|
||||
register = template.Library()
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@register.filter
|
||||
def wrap_in(content, tag_name):
|
||||
return format_html(f'<{tag_name}>{{}}</{tag_name}>', content)
|
||||
@@ -64,7 +64,7 @@ from pretix.base.timeframes import (
|
||||
from pretix.control.forms.widgets import Select2
|
||||
from pretix.helpers.filenames import safe_for_filename
|
||||
from pretix.helpers.iter import chunked_iterable
|
||||
from pretix.helpers.reportlab import FontFallbackParagraph
|
||||
from pretix.helpers.reportlab import PlainTextParagraph
|
||||
from pretix.helpers.templatetags.jsonfield import JSONExtract
|
||||
from pretix.plugins.reports.exporters import ReportlabExportMixin
|
||||
|
||||
@@ -343,7 +343,7 @@ class PDFCheckinList(ReportlabExportMixin, CheckInListMixin, BaseExporter):
|
||||
]
|
||||
|
||||
story = [
|
||||
FontFallbackParagraph(
|
||||
PlainTextParagraph(
|
||||
cl.name,
|
||||
headlinestyle
|
||||
),
|
||||
@@ -351,7 +351,7 @@ class PDFCheckinList(ReportlabExportMixin, CheckInListMixin, BaseExporter):
|
||||
if cl.subevent:
|
||||
story += [
|
||||
Spacer(1, 3 * mm),
|
||||
FontFallbackParagraph(
|
||||
PlainTextParagraph(
|
||||
'{} ({} {})'.format(
|
||||
cl.subevent.name,
|
||||
cl.subevent.get_date_range_display(),
|
||||
@@ -381,10 +381,10 @@ class PDFCheckinList(ReportlabExportMixin, CheckInListMixin, BaseExporter):
|
||||
headrowstyle.fontName = 'OpenSansBd'
|
||||
for q in questions:
|
||||
txt = str(q.question)
|
||||
p = FontFallbackParagraph(txt, headrowstyle)
|
||||
p = PlainTextParagraph(txt, headrowstyle)
|
||||
while p.wrap(colwidths[len(tdata[0])], 5000)[1] > 30 * mm:
|
||||
txt = txt[:len(txt) - 50] + "..."
|
||||
p = FontFallbackParagraph(txt, headrowstyle)
|
||||
p = PlainTextParagraph(txt, headrowstyle)
|
||||
tdata[0].append(p)
|
||||
|
||||
qs = self._get_queryset(cl, form_data)
|
||||
@@ -431,8 +431,8 @@ class PDFCheckinList(ReportlabExportMixin, CheckInListMixin, BaseExporter):
|
||||
CBFlowable(bool(op.last_checked_in)) if not op.blocked else '—',
|
||||
'✘' if op.order.status != Order.STATUS_PAID else '✔',
|
||||
op.order.code,
|
||||
FontFallbackParagraph(name, self.get_style()),
|
||||
FontFallbackParagraph(bleach.clean(str(item), tags={'br'}).strip().replace('<br>', '<br/>'), self.get_style()),
|
||||
PlainTextParagraph(name, self.get_style()),
|
||||
PlainTextParagraph(bleach.clean(str(item), tags={'br'}).strip().replace('<br>', '<br/>'), self.get_style()),
|
||||
]
|
||||
acache = {}
|
||||
if op.addon_to:
|
||||
@@ -443,10 +443,10 @@ class PDFCheckinList(ReportlabExportMixin, CheckInListMixin, BaseExporter):
|
||||
for q in questions:
|
||||
txt = acache.get(q.pk, '')
|
||||
txt = bleach.clean(txt, tags={'br'}).strip().replace('<br>', '<br/>')
|
||||
p = FontFallbackParagraph(txt, self.get_style())
|
||||
p = PlainTextParagraph(txt, self.get_style())
|
||||
while p.wrap(colwidths[len(row)], 5000)[1] > 50 * mm:
|
||||
txt = txt[:len(txt) - 50] + "..."
|
||||
p = FontFallbackParagraph(txt, self.get_style())
|
||||
p = PlainTextParagraph(txt, self.get_style())
|
||||
row.append(p)
|
||||
if op.order.status != Order.STATUS_PAID:
|
||||
tstyledata += [
|
||||
|
||||
@@ -36,7 +36,7 @@ from reportlab.lib import colors, pagesizes
|
||||
from reportlab.lib.enums import TA_CENTER, TA_RIGHT
|
||||
from reportlab.lib.units import mm
|
||||
from reportlab.platypus import (
|
||||
KeepTogether, PageTemplate, Paragraph, Spacer, Table, TableStyle,
|
||||
KeepTogether, PageTemplate, Spacer, Table, TableStyle,
|
||||
)
|
||||
|
||||
from pretix.base.exporter import BaseExporter
|
||||
@@ -49,7 +49,7 @@ from pretix.base.timeframes import (
|
||||
resolve_timeframe_to_datetime_start_inclusive_end_exclusive,
|
||||
)
|
||||
from pretix.control.forms.filter import get_all_payment_providers
|
||||
from pretix.helpers.reportlab import FontFallbackParagraph
|
||||
from pretix.helpers.reportlab import PlainTextParagraph
|
||||
from pretix.plugins.reports.exporters import ReportlabExportMixin
|
||||
|
||||
|
||||
@@ -311,13 +311,13 @@ class ReportExporter(ReportlabExportMixin, BaseExporter):
|
||||
|
||||
tdata = [
|
||||
[
|
||||
FontFallbackParagraph(self._transaction_group_header_label(), tstyle_bold),
|
||||
FontFallbackParagraph(_("Price"), tstyle_bold_right),
|
||||
FontFallbackParagraph(_("Tax rate"), tstyle_bold_right),
|
||||
FontFallbackParagraph("#", tstyle_bold_right),
|
||||
FontFallbackParagraph(_("Net total"), tstyle_bold_right),
|
||||
FontFallbackParagraph(_("Tax total"), tstyle_bold_right),
|
||||
FontFallbackParagraph(_("Gross total"), tstyle_bold_right),
|
||||
PlainTextParagraph(self._transaction_group_header_label(), tstyle_bold),
|
||||
PlainTextParagraph(_("Price"), tstyle_bold_right),
|
||||
PlainTextParagraph(_("Tax rate"), tstyle_bold_right),
|
||||
PlainTextParagraph("#", tstyle_bold_right),
|
||||
PlainTextParagraph(_("Net total"), tstyle_bold_right),
|
||||
PlainTextParagraph(_("Tax total"), tstyle_bold_right),
|
||||
PlainTextParagraph(_("Gross total"), tstyle_bold_right),
|
||||
]
|
||||
]
|
||||
|
||||
@@ -347,12 +347,12 @@ class ReportExporter(ReportlabExportMixin, BaseExporter):
|
||||
|
||||
if e != last_group:
|
||||
if last_group_head_idx > 0 and e is not None:
|
||||
tdata[last_group_head_idx][4] = Paragraph(money_filter(sum_price_by_group - sum_tax_by_group, currency), tstyle_bold_right),
|
||||
tdata[last_group_head_idx][5] = Paragraph(money_filter(sum_tax_by_group, currency), tstyle_bold_right),
|
||||
tdata[last_group_head_idx][6] = Paragraph(money_filter(sum_price_by_group, currency), tstyle_bold_right),
|
||||
tdata[last_group_head_idx][4] = PlainTextParagraph(money_filter(sum_price_by_group - sum_tax_by_group, currency), tstyle_bold_right),
|
||||
tdata[last_group_head_idx][5] = PlainTextParagraph(money_filter(sum_tax_by_group, currency), tstyle_bold_right),
|
||||
tdata[last_group_head_idx][6] = PlainTextParagraph(money_filter(sum_price_by_group, currency), tstyle_bold_right),
|
||||
tdata.append(
|
||||
[
|
||||
FontFallbackParagraph(
|
||||
PlainTextParagraph(
|
||||
e,
|
||||
tstyle_bold,
|
||||
),
|
||||
@@ -375,20 +375,20 @@ class ReportExporter(ReportlabExportMixin, BaseExporter):
|
||||
text = self._transaction_row_label(r)
|
||||
tdata.append(
|
||||
[
|
||||
FontFallbackParagraph(text, tstyle),
|
||||
Paragraph(
|
||||
PlainTextParagraph(text, tstyle),
|
||||
PlainTextParagraph(
|
||||
money_filter(r["price"], currency)
|
||||
if "price" in r and r["price"] is not None
|
||||
else "",
|
||||
tstyle_right,
|
||||
),
|
||||
Paragraph(localize(r["tax_rate"].normalize()) + " %", tstyle_right),
|
||||
Paragraph(str(r["sum_cont"]), tstyle_right),
|
||||
Paragraph(
|
||||
PlainTextParagraph(localize(r["tax_rate"].normalize()) + " %", tstyle_right),
|
||||
PlainTextParagraph(str(r["sum_cont"]), tstyle_right),
|
||||
PlainTextParagraph(
|
||||
money_filter(r["sum_price"] - r["sum_tax"], currency), tstyle_right
|
||||
),
|
||||
Paragraph(money_filter(r["sum_tax"], currency), tstyle_right),
|
||||
Paragraph(money_filter(r["sum_price"], currency), tstyle_right),
|
||||
PlainTextParagraph(money_filter(r["sum_tax"], currency), tstyle_right),
|
||||
PlainTextParagraph(money_filter(r["sum_price"], currency), tstyle_right),
|
||||
]
|
||||
)
|
||||
sum_cnt_by_tax_rate[r["tax_rate"]] += r["sum_cont"]
|
||||
@@ -398,19 +398,19 @@ class ReportExporter(ReportlabExportMixin, BaseExporter):
|
||||
sum_tax_by_group += r["sum_tax"]
|
||||
|
||||
if last_group_head_idx > 0 and last_group is not None:
|
||||
tdata[last_group_head_idx][4] = Paragraph(money_filter(sum_price_by_group - sum_tax_by_group, currency), tstyle_bold_right),
|
||||
tdata[last_group_head_idx][5] = Paragraph(money_filter(sum_tax_by_group, currency), tstyle_bold_right),
|
||||
tdata[last_group_head_idx][6] = Paragraph(money_filter(sum_price_by_group, currency), tstyle_bold_right),
|
||||
tdata[last_group_head_idx][4] = PlainTextParagraph(money_filter(sum_price_by_group - sum_tax_by_group, currency), tstyle_bold_right),
|
||||
tdata[last_group_head_idx][5] = PlainTextParagraph(money_filter(sum_tax_by_group, currency), tstyle_bold_right),
|
||||
tdata[last_group_head_idx][6] = PlainTextParagraph(money_filter(sum_price_by_group, currency), tstyle_bold_right),
|
||||
|
||||
if len(sum_tax_by_tax_rate) > 1:
|
||||
for tax_rate in sorted(sum_tax_by_tax_rate.keys(), reverse=True):
|
||||
tdata.append(
|
||||
[
|
||||
FontFallbackParagraph(_("Sum"), tstyle),
|
||||
Paragraph("", tstyle_right),
|
||||
Paragraph(localize(tax_rate.normalize()) + " %", tstyle_right),
|
||||
Paragraph("", tstyle_right),
|
||||
Paragraph(
|
||||
PlainTextParagraph(_("Sum"), tstyle),
|
||||
PlainTextParagraph("", tstyle_right),
|
||||
PlainTextParagraph(localize(tax_rate.normalize()) + " %", tstyle_right),
|
||||
PlainTextParagraph("", tstyle_right),
|
||||
PlainTextParagraph(
|
||||
money_filter(
|
||||
sum_price_by_tax_rate[tax_rate]
|
||||
- sum_tax_by_tax_rate[tax_rate],
|
||||
@@ -418,10 +418,10 @@ class ReportExporter(ReportlabExportMixin, BaseExporter):
|
||||
),
|
||||
tstyle_right,
|
||||
),
|
||||
Paragraph(
|
||||
PlainTextParagraph(
|
||||
money_filter(sum_tax_by_tax_rate[tax_rate], currency), tstyle_right
|
||||
),
|
||||
Paragraph(
|
||||
PlainTextParagraph(
|
||||
money_filter(sum_price_by_tax_rate[tax_rate], currency),
|
||||
tstyle_right,
|
||||
),
|
||||
@@ -439,11 +439,11 @@ class ReportExporter(ReportlabExportMixin, BaseExporter):
|
||||
|
||||
tdata.append(
|
||||
[
|
||||
FontFallbackParagraph(_("Sum"), tstyle_bold),
|
||||
Paragraph("", tstyle_right),
|
||||
Paragraph("", tstyle_right),
|
||||
Paragraph("", tstyle_bold_right),
|
||||
Paragraph(
|
||||
PlainTextParagraph(_("Sum"), tstyle_bold),
|
||||
PlainTextParagraph("", tstyle_right),
|
||||
PlainTextParagraph("", tstyle_right),
|
||||
PlainTextParagraph("", tstyle_bold_right),
|
||||
PlainTextParagraph(
|
||||
money_filter(
|
||||
sum(sum_price_by_tax_rate.values())
|
||||
- sum(sum_tax_by_tax_rate.values()),
|
||||
@@ -451,11 +451,11 @@ class ReportExporter(ReportlabExportMixin, BaseExporter):
|
||||
),
|
||||
tstyle_bold_right,
|
||||
),
|
||||
Paragraph(
|
||||
PlainTextParagraph(
|
||||
money_filter(sum(sum_tax_by_tax_rate.values()), currency),
|
||||
tstyle_bold_right,
|
||||
),
|
||||
Paragraph(
|
||||
PlainTextParagraph(
|
||||
money_filter(sum(sum_price_by_tax_rate.values()), currency),
|
||||
tstyle_bold_right,
|
||||
),
|
||||
@@ -493,10 +493,10 @@ class ReportExporter(ReportlabExportMixin, BaseExporter):
|
||||
|
||||
tdata = [
|
||||
[
|
||||
FontFallbackParagraph(_("Payment method"), tstyle_bold),
|
||||
FontFallbackParagraph(_("Payments"), tstyle_bold_right),
|
||||
FontFallbackParagraph(_("Refunds"), tstyle_bold_right),
|
||||
FontFallbackParagraph(_("Total"), tstyle_bold_right),
|
||||
PlainTextParagraph(_("Payment method"), tstyle_bold),
|
||||
PlainTextParagraph(_("Payments"), tstyle_bold_right),
|
||||
PlainTextParagraph(_("Refunds"), tstyle_bold_right),
|
||||
PlainTextParagraph(_("Total"), tstyle_bold_right),
|
||||
]
|
||||
]
|
||||
|
||||
@@ -537,20 +537,20 @@ class ReportExporter(ReportlabExportMixin, BaseExporter):
|
||||
for p in providers:
|
||||
tdata.append(
|
||||
[
|
||||
Paragraph(provider_names.get(p, p), tstyle),
|
||||
FontFallbackParagraph(
|
||||
PlainTextParagraph(provider_names.get(p, p), tstyle),
|
||||
PlainTextParagraph(
|
||||
money_filter(payments_by_provider[p], currency)
|
||||
if p in payments_by_provider
|
||||
else "",
|
||||
tstyle_right,
|
||||
),
|
||||
Paragraph(
|
||||
PlainTextParagraph(
|
||||
money_filter(refunds_by_provider[p], currency)
|
||||
if p in refunds_by_provider
|
||||
else "",
|
||||
tstyle_right,
|
||||
),
|
||||
Paragraph(
|
||||
PlainTextParagraph(
|
||||
money_filter(
|
||||
payments_by_provider.get(p, Decimal("0.00"))
|
||||
- refunds_by_provider.get(p, Decimal("0.00")),
|
||||
@@ -563,20 +563,20 @@ class ReportExporter(ReportlabExportMixin, BaseExporter):
|
||||
|
||||
tdata.append(
|
||||
[
|
||||
FontFallbackParagraph(_("Sum"), tstyle_bold),
|
||||
Paragraph(
|
||||
PlainTextParagraph(_("Sum"), tstyle_bold),
|
||||
PlainTextParagraph(
|
||||
money_filter(
|
||||
sum(payments_by_provider.values(), Decimal("0.00")), currency
|
||||
),
|
||||
tstyle_bold_right,
|
||||
),
|
||||
Paragraph(
|
||||
PlainTextParagraph(
|
||||
money_filter(
|
||||
sum(refunds_by_provider.values(), Decimal("0.00")), currency
|
||||
),
|
||||
tstyle_bold_right,
|
||||
),
|
||||
Paragraph(
|
||||
PlainTextParagraph(
|
||||
money_filter(
|
||||
sum(payments_by_provider.values(), Decimal("0.00"))
|
||||
- sum(refunds_by_provider.values(), Decimal("0.00")),
|
||||
@@ -641,7 +641,7 @@ class ReportExporter(ReportlabExportMixin, BaseExporter):
|
||||
open_before = tx_before - p_before + r_before
|
||||
tdata.append(
|
||||
[
|
||||
FontFallbackParagraph(
|
||||
PlainTextParagraph(
|
||||
_("Pending payments at {datetime}").format(
|
||||
datetime=date_format(
|
||||
(df_start - datetime.timedelta.resolution).astimezone(
|
||||
@@ -653,7 +653,7 @@ class ReportExporter(ReportlabExportMixin, BaseExporter):
|
||||
tstyle,
|
||||
),
|
||||
"",
|
||||
Paragraph(money_filter(open_before, currency), tstyle_right),
|
||||
PlainTextParagraph(money_filter(open_before, currency), tstyle_right),
|
||||
]
|
||||
)
|
||||
else:
|
||||
@@ -670,30 +670,30 @@ class ReportExporter(ReportlabExportMixin, BaseExporter):
|
||||
] or Decimal("0.00")
|
||||
tdata.append(
|
||||
[
|
||||
FontFallbackParagraph(_("Orders"), tstyle),
|
||||
Paragraph("+", tstyle_center),
|
||||
Paragraph(money_filter(tx_during, currency), tstyle_right),
|
||||
PlainTextParagraph(_("Orders"), tstyle),
|
||||
PlainTextParagraph("+", tstyle_center),
|
||||
PlainTextParagraph(money_filter(tx_during, currency), tstyle_right),
|
||||
]
|
||||
)
|
||||
tdata.append(
|
||||
[
|
||||
FontFallbackParagraph(_("Payments"), tstyle),
|
||||
Paragraph("-", tstyle_center),
|
||||
Paragraph(money_filter(p_during, currency), tstyle_right),
|
||||
PlainTextParagraph(_("Payments"), tstyle),
|
||||
PlainTextParagraph("-", tstyle_center),
|
||||
PlainTextParagraph(money_filter(p_during, currency), tstyle_right),
|
||||
]
|
||||
)
|
||||
tdata.append(
|
||||
[
|
||||
FontFallbackParagraph(_("Refunds"), tstyle),
|
||||
Paragraph("+", tstyle_center),
|
||||
Paragraph(money_filter(r_during, currency), tstyle_right),
|
||||
PlainTextParagraph(_("Refunds"), tstyle),
|
||||
PlainTextParagraph("+", tstyle_center),
|
||||
PlainTextParagraph(money_filter(r_during, currency), tstyle_right),
|
||||
]
|
||||
)
|
||||
|
||||
open_after = open_before + tx_during - p_during + r_during
|
||||
tdata.append(
|
||||
[
|
||||
Paragraph(
|
||||
PlainTextParagraph(
|
||||
_("Pending payments at {datetime}").format(
|
||||
datetime=date_format(
|
||||
((df_end or now()) - datetime.timedelta.resolution).astimezone(
|
||||
@@ -704,8 +704,8 @@ class ReportExporter(ReportlabExportMixin, BaseExporter):
|
||||
),
|
||||
tstyle_bold,
|
||||
),
|
||||
Paragraph("=", tstyle_center),
|
||||
Paragraph(money_filter(open_after, currency), tstyle_bold_right),
|
||||
PlainTextParagraph("=", tstyle_center),
|
||||
PlainTextParagraph(money_filter(open_after, currency), tstyle_bold_right),
|
||||
]
|
||||
)
|
||||
tstyledata += [
|
||||
@@ -752,7 +752,7 @@ class ReportExporter(ReportlabExportMixin, BaseExporter):
|
||||
)
|
||||
tdata.append(
|
||||
[
|
||||
Paragraph(
|
||||
PlainTextParagraph(
|
||||
_("Total gift card value at {datetime}").format(
|
||||
datetime=date_format(
|
||||
(df_start - datetime.timedelta.resolution).astimezone(
|
||||
@@ -763,7 +763,7 @@ class ReportExporter(ReportlabExportMixin, BaseExporter):
|
||||
),
|
||||
tstyle,
|
||||
),
|
||||
Paragraph(money_filter(tx_before, currency), tstyle_right),
|
||||
PlainTextParagraph(money_filter(tx_before, currency), tstyle_right),
|
||||
]
|
||||
)
|
||||
else:
|
||||
@@ -774,8 +774,8 @@ class ReportExporter(ReportlabExportMixin, BaseExporter):
|
||||
] or Decimal("0.00")
|
||||
tdata.append(
|
||||
[
|
||||
FontFallbackParagraph(_("Gift card transactions (credit)"), tstyle),
|
||||
Paragraph(money_filter(tx_during_pos, currency), tstyle_right),
|
||||
PlainTextParagraph(_("Gift card transactions (credit)"), tstyle),
|
||||
PlainTextParagraph(money_filter(tx_during_pos, currency), tstyle_right),
|
||||
]
|
||||
)
|
||||
|
||||
@@ -784,15 +784,15 @@ class ReportExporter(ReportlabExportMixin, BaseExporter):
|
||||
] or Decimal("0.00")
|
||||
tdata.append(
|
||||
[
|
||||
FontFallbackParagraph(_("Gift card transactions (debit)"), tstyle),
|
||||
Paragraph(money_filter(tx_during_neg, currency), tstyle_right),
|
||||
PlainTextParagraph(_("Gift card transactions (debit)"), tstyle),
|
||||
PlainTextParagraph(money_filter(tx_during_neg, currency), tstyle_right),
|
||||
]
|
||||
)
|
||||
|
||||
open_after = tx_before + tx_during_pos + tx_during_neg
|
||||
tdata.append(
|
||||
[
|
||||
Paragraph(
|
||||
PlainTextParagraph(
|
||||
_("Total gift card value at {datetime}").format(
|
||||
datetime=date_format(
|
||||
((df_end or now()) - datetime.timedelta.resolution).astimezone(
|
||||
@@ -803,7 +803,7 @@ class ReportExporter(ReportlabExportMixin, BaseExporter):
|
||||
),
|
||||
tstyle_bold,
|
||||
),
|
||||
Paragraph(money_filter(open_after, currency), tstyle_bold_right),
|
||||
PlainTextParagraph(money_filter(open_after, currency), tstyle_bold_right),
|
||||
]
|
||||
)
|
||||
tstyledata += [
|
||||
@@ -854,10 +854,10 @@ class ReportExporter(ReportlabExportMixin, BaseExporter):
|
||||
style_small.leading = 10
|
||||
|
||||
story = [
|
||||
FontFallbackParagraph(self.verbose_name, style_h1),
|
||||
PlainTextParagraph(self.verbose_name, style_h1),
|
||||
Spacer(0, 3 * mm),
|
||||
FontFallbackParagraph(
|
||||
"<br />".join(escape(f) for f in self.describe_filters(form_data)),
|
||||
PlainTextParagraph(
|
||||
"\n".join(escape(f) for f in self.describe_filters(form_data)),
|
||||
style_small,
|
||||
),
|
||||
]
|
||||
@@ -870,7 +870,7 @@ class ReportExporter(ReportlabExportMixin, BaseExporter):
|
||||
if s:
|
||||
story += [
|
||||
Spacer(0, 3 * mm),
|
||||
FontFallbackParagraph(_("Orders") + c_head, style_h2),
|
||||
PlainTextParagraph(_("Orders") + c_head, style_h2),
|
||||
Spacer(0, 3 * mm),
|
||||
*s
|
||||
]
|
||||
@@ -881,7 +881,7 @@ class ReportExporter(ReportlabExportMixin, BaseExporter):
|
||||
if s:
|
||||
story += [
|
||||
Spacer(0, 8 * mm),
|
||||
FontFallbackParagraph(_("Payments") + c_head, style_h2),
|
||||
PlainTextParagraph(_("Payments") + c_head, style_h2),
|
||||
Spacer(0, 3 * mm),
|
||||
*s
|
||||
]
|
||||
@@ -894,7 +894,7 @@ class ReportExporter(ReportlabExportMixin, BaseExporter):
|
||||
Spacer(0, 8 * mm),
|
||||
KeepTogether(
|
||||
[
|
||||
FontFallbackParagraph(_("Open items") + c_head, style_h2),
|
||||
PlainTextParagraph(_("Open items") + c_head, style_h2),
|
||||
Spacer(0, 3 * mm),
|
||||
*s
|
||||
]
|
||||
@@ -912,7 +912,7 @@ class ReportExporter(ReportlabExportMixin, BaseExporter):
|
||||
Spacer(0, 8 * mm),
|
||||
KeepTogether(
|
||||
[
|
||||
FontFallbackParagraph(_("Gift cards") + c_head, style_h2),
|
||||
PlainTextParagraph(_("Gift cards") + c_head, style_h2),
|
||||
Spacer(0, 3 * mm),
|
||||
*s,
|
||||
]
|
||||
|
||||
@@ -70,7 +70,7 @@ from pretix.base.timeframes import (
|
||||
)
|
||||
from pretix.control.forms.filter import OverviewFilterForm
|
||||
from pretix.helpers.reportlab import (
|
||||
FontFallbackParagraph, register_ttf_font_if_new,
|
||||
PlainTextParagraph, register_ttf_font_if_new,
|
||||
)
|
||||
from pretix.presale.style import get_fonts
|
||||
|
||||
@@ -282,7 +282,7 @@ class OverviewReport(Report):
|
||||
headlinestyle.fontSize = 15
|
||||
headlinestyle.fontName = 'OpenSansBd'
|
||||
story = [
|
||||
FontFallbackParagraph(_('Orders by product') + ' ' + (_('(excl. taxes)') if net else _('(incl. taxes)')), headlinestyle),
|
||||
PlainTextParagraph(_('Orders by product') + ' ' + (_('(excl. taxes)') if net else _('(incl. taxes)')), headlinestyle),
|
||||
Spacer(1, 5 * mm)
|
||||
]
|
||||
return story
|
||||
@@ -292,7 +292,7 @@ class OverviewReport(Report):
|
||||
if form_data.get('date_axis') and form_data.get('date_range'):
|
||||
d_start, d_end = resolve_timeframe_to_dates_inclusive(now(), form_data['date_range'], self.timezone)
|
||||
story += [
|
||||
FontFallbackParagraph(_('{axis} between {start} and {end}').format(
|
||||
PlainTextParagraph(_('{axis} between {start} and {end}').format(
|
||||
axis=dict(OverviewFilterForm(event=self.event).fields['date_axis'].choices)[form_data.get('date_axis')],
|
||||
start=date_format(d_start, 'SHORT_DATE_FORMAT') if d_start else '–',
|
||||
end=date_format(d_end, 'SHORT_DATE_FORMAT') if d_end else '–',
|
||||
@@ -305,13 +305,13 @@ class OverviewReport(Report):
|
||||
subevent = self.event.subevents.get(pk=self.form_data.get('subevent'))
|
||||
except SubEvent.DoesNotExist:
|
||||
subevent = self.form_data.get('subevent')
|
||||
story.append(FontFallbackParagraph(pgettext('subevent', 'Date: {}').format(subevent), self.get_style()))
|
||||
story.append(PlainTextParagraph(pgettext('subevent', 'Date: {}').format(subevent), self.get_style()))
|
||||
story.append(Spacer(1, 5 * mm))
|
||||
|
||||
if form_data.get('subevent_date_range'):
|
||||
d_start, d_end = resolve_timeframe_to_datetime_start_inclusive_end_exclusive(now(), form_data['subevent_date_range'], self.timezone)
|
||||
story += [
|
||||
FontFallbackParagraph(_('{axis} between {start} and {end}').format(
|
||||
PlainTextParagraph(_('{axis} between {start} and {end}').format(
|
||||
axis=_('Event date'),
|
||||
start=date_format(d_start, 'SHORT_DATE_FORMAT') if d_start else '–',
|
||||
end=date_format(d_end - timedelta(hours=1), 'SHORT_DATE_FORMAT') if d_end else '–',
|
||||
@@ -384,13 +384,13 @@ class OverviewReport(Report):
|
||||
tdata = [
|
||||
[
|
||||
_('Product'),
|
||||
FontFallbackParagraph(_('Canceled'), tstyle_th),
|
||||
PlainTextParagraph(_('Canceled'), tstyle_th),
|
||||
'',
|
||||
FontFallbackParagraph(_('Expired'), tstyle_th),
|
||||
PlainTextParagraph(_('Expired'), tstyle_th),
|
||||
'',
|
||||
FontFallbackParagraph(_('Approval pending'), tstyle_th),
|
||||
PlainTextParagraph(_('Approval pending'), tstyle_th),
|
||||
'',
|
||||
FontFallbackParagraph(_('Purchased'), tstyle_th),
|
||||
PlainTextParagraph(_('Purchased'), tstyle_th),
|
||||
'', '', '', '', ''
|
||||
],
|
||||
[
|
||||
@@ -421,14 +421,14 @@ class OverviewReport(Report):
|
||||
for tup in items_by_category:
|
||||
if tup[0]:
|
||||
tdata.append([
|
||||
FontFallbackParagraph(str(tup[0]), tstyle_bold)
|
||||
PlainTextParagraph(str(tup[0]), tstyle_bold)
|
||||
])
|
||||
for l, s in states:
|
||||
tdata[-1].append(str(tup[0].num[l][0]))
|
||||
tdata[-1].append(floatformat(tup[0].num[l][2 if net else 1], places))
|
||||
for item in tup[1]:
|
||||
tdata.append([
|
||||
FontFallbackParagraph(str(item), tstyle)
|
||||
PlainTextParagraph(str(item), tstyle)
|
||||
])
|
||||
for l, s in states:
|
||||
tdata[-1].append(str(item.num[l][0]))
|
||||
@@ -436,7 +436,7 @@ class OverviewReport(Report):
|
||||
if item.has_variations:
|
||||
for var in item.all_variations:
|
||||
tdata.append([
|
||||
FontFallbackParagraph(" " + str(var), tstyle)
|
||||
PlainTextParagraph(" " + str(var), tstyle)
|
||||
])
|
||||
for l, s in states:
|
||||
tdata[-1].append(str(var.num[l][0]))
|
||||
@@ -568,7 +568,7 @@ class OrderTaxListReportPDF(Report):
|
||||
tstyledata.append(('SPAN', (5 + 2 * i, 0), (6 + 2 * i, 0)))
|
||||
|
||||
story = [
|
||||
FontFallbackParagraph(_('Orders by tax rate ({currency})').format(currency=self.event.currency), headlinestyle),
|
||||
PlainTextParagraph(_('Orders by tax rate ({currency})').format(currency=self.event.currency), headlinestyle),
|
||||
Spacer(1, 5 * mm)
|
||||
]
|
||||
tdata = [
|
||||
|
||||
@@ -51,6 +51,7 @@ from django.http import HttpResponseNotAllowed, JsonResponse
|
||||
from django.shortcuts import redirect
|
||||
from django.utils import translation
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.html import conditional_escape
|
||||
from django.utils.translation import (
|
||||
get_language, gettext_lazy as _, pgettext_lazy,
|
||||
)
|
||||
@@ -1634,7 +1635,7 @@ class ConfirmStep(CartMixin, AsyncAction, TemplateFlowStep):
|
||||
meta_info = {
|
||||
'contact_form_data': self.cart_session.get('contact_form_data', {}),
|
||||
'confirm_messages': [
|
||||
str(m) for m in self.confirm_messages.values()
|
||||
conditional_escape(str(m)) for m in self.confirm_messages.values()
|
||||
]
|
||||
}
|
||||
api_meta = {}
|
||||
|
||||
@@ -144,7 +144,7 @@ checkout_confirm_messages = EventPluginSignal()
|
||||
This signal is sent out to retrieve short messages that need to be acknowledged by the user before the
|
||||
order can be completed. This is typically used for something like "accept the terms and conditions".
|
||||
Receivers are expected to return a dictionary where the keys are globally unique identifiers for the
|
||||
message and the values can be arbitrary HTML.
|
||||
message and the values can be a SafeString containing arbitrary HTML, or a string that will be HTML-escaped.
|
||||
|
||||
As with all event plugin signals, the ``sender`` keyword argument will contain the event.
|
||||
"""
|
||||
|
||||
@@ -176,7 +176,7 @@
|
||||
<div class="checkbox">
|
||||
<label for="input_confirm_{{ key }}">
|
||||
<input type="checkbox" class="checkbox" value="yes" name="confirm_{{ key }}" id="input_confirm_{{ key }}" required>
|
||||
{{ desc|safe }}
|
||||
{{ desc }}
|
||||
</label>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
{% load eventsignal %}
|
||||
{% load money %}
|
||||
{% load eventurl %}
|
||||
{% load wrap_in %}
|
||||
{% block title %}{% trans "Registration details" %}{% endblock %}
|
||||
{% block content %}
|
||||
<h2 class="h1">
|
||||
@@ -48,7 +49,7 @@
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<p>
|
||||
{% blocktrans trimmed with email="<strong>"|add:order.email|add:"</strong>"|safe %}
|
||||
{% blocktrans trimmed with email=order.email|wrap_in:"strong" %}
|
||||
This order is managed for you by {{ email }}. Please contact them for any questions regarding
|
||||
payment, cancellation or changes to this order.
|
||||
{% endblocktrans %}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
#
|
||||
# This file is part of pretix (Community Edition).
|
||||
#
|
||||
# Copyright (C) 2014-2020 Raphael Michel and contributors
|
||||
# Copyright (C) 2020-today pretix 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 pytest
|
||||
from django.core.exceptions import SuspiciousFileOperation
|
||||
from reportlab.platypus import Paragraph
|
||||
|
||||
|
||||
def test_http_access_disabled(monkeypatch):
|
||||
def guard(*args, **kwargs):
|
||||
pytest.fail("No internet wanted!")
|
||||
|
||||
monkeypatch.setattr('socket.socket', guard)
|
||||
|
||||
with pytest.raises(SuspiciousFileOperation, match="should not be reading images from disk"):
|
||||
Paragraph(
|
||||
'<img src="https://static.pretix.cloud/static/pretixeu/img/opengraph.png"/>',
|
||||
)
|
||||
|
||||
|
||||
def test_file_access_disabled_scheme(monkeypatch):
|
||||
with pytest.raises(SuspiciousFileOperation, match="should not be reading images from disk"):
|
||||
Paragraph(
|
||||
'<img src="file:///etc/passwd" />',
|
||||
)
|
||||
|
||||
|
||||
def test_file_access_disabled_direct(monkeypatch):
|
||||
with pytest.raises(SuspiciousFileOperation, match="should not be reading images from disk"):
|
||||
Paragraph(
|
||||
'<img src="/etc/passwd" />',
|
||||
)
|
||||
Reference in New Issue
Block a user