Compare commits

...
72 Commits
Author SHA1 Message Date
simon 60af7e5984 Merge pull request 'upstream/2026.5.2' (#19) from upstream/2026.5.2 into master
Reviewed-on: CGM_Public/pretix_cgo#19
2026-07-01 19:27:12 +00:00
TechuserUpdatechecker ce2704f6fd Remove gendered language from de/de_Informal translations 2026-07-01 21:26:32 +02:00
TechuserUpdatechecker 04cecedffb Merge branch 'master' into upstream/2026.5.2 2026-07-01 21:21:43 +02:00
Raphael Michel 7faf6cfd80 Bump to 2026.5.2 2026-06-25 16:40:44 +02:00
Raphael Michel cdeb3609f3 [SECURITY] Properly escape HTML tags in PDF generation (CVE-2026-57535) 2026-06-25 16:40:28 +02:00
Raphael MichelandRaphael Michel 53d8aa5679 [SECURITY] Prevent reading of any local files in reportlab (CVE-2026-57535) 2026-06-25 16:40:00 +02:00
Raphael MichelandRaphael Michel 80601cc013 [SECURITY] Disable outbound and file access for reportlab (CVE-2026-57535) 2026-06-25 16:40:00 +02:00
Mira WellerandRaphael Michel e2661f304d [SECURITY] Fix reflected XSS in redirection page (CVE-2026-57533) 2026-06-25 16:40:00 +02:00
Mira WellerandRaphael Michel 71f6ade5d6 [SECURITY] Fix stored XSS in ticket confirmation page (CVE-2026-13225) 2026-06-25 16:40:00 +02:00
Mira WellerandRaphael Michel e087dd39b8 [SECURITY] Hardening: Don't use |safe on confirm_messages 2026-06-25 16:40:00 +02:00
Mira WellerandRaphael Michel f3a8ce39f1 [SECURITY] Fix XSS in ticket layout JSON (CVE-2026-57532) 2026-06-25 16:40:00 +02:00
TechuserUpdatechecker 54479f8f32 Update pretix domain 2026-06-20 21:33:05 +02:00
simon 8142cc8b18 Merge pull request 'upstream/2026.5.1' (#18) from upstream/2026.5.1 into master
Reviewed-on: CGM_Public/pretix_cgo#18
2026-06-20 19:21:33 +00:00
TechuserUpdatechecker 87c1232a38 Merge branch 'master' into upstream/2026.5.1 2026-06-20 21:00:06 +02:00
TechuserUpdatechecker f838e29400 Add claude skill 2026-06-20 20:59:23 +02:00
simon bd1e583266 Merge branch 'feature/pretix-oidc-plugin' into master
Adds pretix-oidc plugin to Dockerfile for Keycloak SSO integration.
Plugin is only active when configured (currently cgm72 only).
2026-06-20 20:35:55 +02:00
simonandClaude Sonnet 4.6 7a9640ddca Add pretix-oidc plugin for Keycloak SSO integration
Enables OIDC authentication against Keycloak and automatic team assignment
via claim-based rules (pretix_scanner, pretix_shop, pretix_admin).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 22:10:45 +02:00
Raphael Michel a6c417eb2f Bump version to 2026.5.1 2026-06-09 13:22:17 +02:00
Richard SchreiberandRaphael Michel 5d449ea313 [SECURITY] Reusable media export: Respect giftcard permissions (CVE-2026-11764) (#6261) 2026-06-09 13:21:41 +02:00
simon f700b57d57 Merge pull request 'upstream/2026.5.0' (#17) from upstream/2026.5.0 into master
Reviewed-on: CGM_Public/pretix_cgo#17
2026-05-31 18:34:40 +00:00
TechuserUpdatecheckerandClaude Sonnet 4.6 9e2ae90b1e Merge master into upstream/2026.5.0
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 20:32:21 +02:00
simon 3ccd0f7cd5 Merge pull request 'upstream/2026.4.1' (#16) from upstream/2026.4.1 into master
Reviewed-on: CGM_Public/pretix_cgo#16
2026-05-16 19:58:34 +00:00
TechuserUpdatecheckerandClaude Sonnet 4.6 51cdb437db Remove gendered language from de/de_Informal translations
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 21:56:20 +02:00
TechuserUpdatechecker 215dc8b48b Merge branch 'master' into upstream/2026.4.1 2026-05-16 21:54:40 +02:00
Raphael Michel f88a560d5f Bump to 2026.4.1 2026-05-04 11:24:26 +02:00
Thomas GöttgensandRaphael Michel 4019f956a8 Fix Dockerfile syntax for chmod command (#6145) 2026-05-04 11:24:04 +02:00
Raphael Michel c75c847393 Settings: Fix typo in class path to mail backend (#6144) 2026-05-04 11:24:04 +02:00
luelistaandRaphael Michel 4e3d3b1f22 Fix permissions of /pretix in docker container (#6133) 2026-05-04 11:24:04 +02:00
TechuserUpdatecheckerandClaude Sonnet 4.6 e2bf94ff9f Dockerfile: Fix /pretix permissions for nginx on Debian Trixie
Debian Trixie changed the default home directory mode from 0755 to 0700,
causing nginx (www-data) to get 403 Forbidden on all static files.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-03 21:41:06 +02:00
simon a37e6c8270 Merge pull request 'upstream/2026.4.0' (#15) from upstream/2026.4.0 into master
Reviewed-on: CGM_Public/pretix_cgo#15
2026-05-03 19:11:35 +00:00
TechuserUpdatechecker dfe94e09cc Merge with Master 2026-05-03 21:08:58 +02:00
simon d87f7b0072 Merge pull request 'upstream/2026.3.1' (#14) from upstream/2026.3.1 into master
Reviewed-on: CGM_Public/pretix_cgo#14
2026-04-13 09:11:15 +00:00
TechuserUpdatechecker 393cd0235e Update Teilnehmer 2026-04-13 11:07:57 +02:00
TechuserUpdatechecker 7fdc7f3405 Merge with master 2026-04-13 11:05:15 +02:00
Raphael Michel 21d62c5078 Bump version to 2026.3.1 2026-04-08 13:58:36 +02:00
Raphael MichelandRaphael Michel 988dc112ac [SECURITY] API: Add missing event filter for check-ins 2026-04-08 13:58:23 +02:00
simon 2da8d250c8 Merge pull request 'upstream/2026.2.0' (#13) from upstream/2026.2.0 into master
Reviewed-on: CGM_Public/pretix_cgo#13
2026-03-23 20:25:57 +00:00
TechuserUpdatechecker 02e0fed4a0 Merge branch 'master' into upstream/2026.2.0 2026-03-23 21:19:51 +01:00
simon 932b646fcc Merge pull request 'upstream/v2026.1.0' (#12) from upstream/v2026.1.0 into master
Reviewed-on: CGM_Public/pretix_cgo#12
2026-02-03 21:56:31 +00:00
TechuserUpdatechecker edf1bd08f8 Merge branch 'master' into upstream/v2026.1.0
# Conflicts:
#	src/pretix/__init__.py
2026-02-03 22:56:14 +01:00
simon 09aff627bb Merge pull request 'upstream/2025.10.1' (#11) from upstream/2025.10.1 into master
Reviewed-on: CGM_Public/pretix_cgo#11
2025-12-26 19:38:03 +00:00
TechuserUpdatechecker da87c64ea0 Merge branch 'master' into upstream/2025.10.1 2025-12-26 20:35:13 +01:00
Raphael Michel 5d87f9a26f Bump to 2025.10.1 2025-12-19 13:06:58 +01:00
Raphael Michel 4b5651862c [SECURITY] Prevent access to arbitrary cached files by UUID (CVE-2025-14881) 2025-12-19 13:06:48 +01:00
simon 26f3a92d09 Merge pull request 'upstream/2025.10.0' (#10) from upstream/2025.10.0 into master
Reviewed-on: CGM_Public/pretix_cgo#10
2025-12-18 22:18:53 +00:00
TechuserUpdatechecker d468e0a2b3 Merge master 2025-12-18 23:18:30 +01:00
simon 8de80d5867 Merge pull request 'upstream/2025.9.0.1' (#9) from upstream/2025.9.0.1 into master
Reviewed-on: CGM_Public/pretix_cgo#9
2025-11-09 19:16:53 +00:00
TechuserUpdatechecker ec3272dd7c merge with master 2025-11-09 20:13:46 +01:00
TechuserUpdatechecker 72b14e04c0 Merge with 2025.8.0 2025-10-05 20:58:06 +02:00
philipp 93cb51c7de Merge pull request 'upstream/2025.7.1' (#7) from upstream/2025.7.1 into master
Reviewed-on: simon/pretix_cgo#7
2025-09-16 17:03:57 +00:00
philipp 64b56afe44 Fix language file bug 2025-09-16 19:00:58 +02:00
philipp 8e16c18060 Merge branch 'master' into upstream/2025.7.1 2025-09-16 18:36:21 +02:00
Raphael Michel 2d42c1f166 Bump to 2025.7.1 2025-08-23 10:15:29 +02:00
Raphael Michel e4b8c5da25 Bump hierarkey to 2.0.1 2025-08-23 09:14:06 +02:00
luelistaandRaphael Michel c85b496187 Organizer plugins: Do not show plugins as active if they are inactive on org-level (#5396) 2025-08-23 09:13:56 +02:00
simon b632fa620e Merge pull request 'upstream/2025.6.0' (#6) from upstream/2025.6.0 into master
Reviewed-on: simon/pretix_cgo#6
2025-07-04 20:24:59 +00:00
philipp 04684ed93f Merge pull request 'upstream/2025.5.0' (#5) from upstream/2025.5.0 into master
Reviewed-on: simon/pretix_cgo#5
2025-06-03 16:12:49 +00:00
philipp 466bf49941 Merge branch 'master' into upstream/2025.5.0 2025-06-03 17:51:30 +02:00
philipp e03ec73490 Merge pull request 'upstream/2025.4.0' (#4) from upstream/2025.4.0 into master
Reviewed-on: simon/pretix_cgo#4
2025-05-08 22:05:43 +00:00
philipp 3fda118234 Merge branch 'master' into upstream/2025.4.0 2025-05-08 23:44:59 +02:00
simon 1cb98d0837 Merge pull request 'upstream/2025.3.0' (#3) from upstream/2025.3.0 into master
Reviewed-on: simon/pretix_cgo#3
2025-03-30 20:07:31 +00:00
simon 713e3a40aa Merge pull request 'upstream/2025.2.0' (#2) from upstream/2025.2.0 into master
Reviewed-on: simon/pretix_cgo#2
2025-03-07 22:45:05 +00:00
TechuserUpdatechecker be234edc0f Merge from master 2025-03-07 23:10:21 +01:00
philipp 34efc2d953 Add relevant_orderlist exporter (including bugfixes)
This Exporter has the most useful information in the first rows of the
exported document. Specifically the product and the custom questions.
Other fields are also resorted somewhat to place very useless columns at
the end of the table. See code for details :)
- register relevant_orderlist as separate data_exporter
- sort it with the other order data exporters.
2025-02-12 13:17:21 +01:00
UpdateBot 82366dd6c9 Add debugging Information 2025-02-11 20:48:27 +01:00
UpdateBot 38abad0bf6 Update dockerbuild, use Tag if a tag was pushed, latest otherwise 2025-02-11 20:44:55 +01:00
simon 41817fe080 Merge pull request 'Furhter text udpates' (#1) from release/2025.1.0 into master
Reviewed-on: simon/pretix_cgo#1
2025-02-10 14:49:03 +00:00
simon 6cee467dbd Merge branch 'master' into release/2025.1.0 2025-02-10 14:48:25 +00:00
UpdateBot 931ba73f1d Furhter text udpates 2025-02-10 15:47:21 +01:00
UpdateBot 9179621e72 Use latest tag in master branch 2025-02-05 22:02:20 +01:00
UpdateBot 65c978558e Merge branch 'release/2025.1.0' 2025-02-05 22:01:24 +01:00
UpdateBot f4f090506b Update Texts 2025-02-04 22:11:52 +01:00
30 changed files with 1980 additions and 389 deletions
+202
View File
@@ -0,0 +1,202 @@
Führe den vollständigen Upstream-Merge-Workflow für pretix_cgo durch. Gehe dabei strikt Schritt für Schritt vor und warte an den markierten Stellen auf Bestätigung.
---
## Schritt 1 Master aktualisieren
```bash
git checkout master
git pull
```
---
## Schritt 2 Upstream-Tags holen & neuesten Tag ermitteln
```bash
git fetch upstream --tags
```
Bestimme danach den neuesten upstream-Tag (Format `vYYYY.M.P`, **ohne** das `.0`-Suffix unserer eigenen Tags):
```bash
git tag --sort=-version:refname | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | head -1
```
Merke dir diesen Tag als `UPSTREAM_TAG` (z.B. `v2026.5.0`) und leite daraus den Branch-Namen ab: `upstream/2026.5.0` (ohne führendes `v`).
---
## Schritt 3 Branch erstellen
```bash
git checkout -b upstream/<VERSION> tags/<UPSTREAM_TAG>
# Beispiel: git checkout -b upstream/2026.5.0 tags/v2026.5.0
```
---
## Schritt 4 Master mergen (immer, nicht optional)
```bash
git merge master
```
Bei Merge-Konflikten: Konflikte anzeigen und gemeinsam mit dem Nutzer lösen, bevor es weitergeht.
---
## Schritt 5 Branch pushen & MR anlegen
Push mit Push Option, damit Gitea direkt einen MR anlegt:
```bash
git push --set-upstream origin upstream/<VERSION> -o merge_request.create
```
---
## Schritt 6 Gender-Prüfung in den deutschen Übersetzungen
**Erst nach dem Merge mit master** die folgenden vier Dateien auf gegenderte Sprache prüfen:
- `src/pretix/locale/de/LC_MESSAGES/django.po`
- `src/pretix/locale/de/LC_MESSAGES/djangojs.po`
- `src/pretix/locale/de_Informal/LC_MESSAGES/django.po`
- `src/pretix/locale/de_Informal/LC_MESSAGES/djangojs.po`
Suche in den `msgstr`-Zeilen (und den zugehörigen `msgid`-Zeilen für Kontext) nach folgenden Mustern:
| Muster | Beispiele |
|--------|-----------|
| Binneni mit `*` | `Teilnehmer*in`, `Käufer*innen` |
| Binneni mit `:` | `Teilnehmer:in`, `Käufer:innen` |
| Binneni mit `_` | `Teilnehmer_in`, `Käufer_innen` |
| Binneni mit `(in)` | `Teilnehmer(in)`, `Käufer(innen)` |
| Partizip-Plural auf `-enden` | `Teilnehmenden`, `Helfenden`, `Organisierenden`, `Bestellenden`, `Buchenden`, `Zahlenden` |
**Ausgabe der Befunde:** Zeige für jeden Treffer:
1. Datei + Zeilennummer
2. Den vollständigen `msgstr`-Block (mehrzeilig wenn nötig) als Kontext
3. Den konkreten Vorschlag für die Ersetzung (z.B. `Teilnehmer*in``Teilnehmer`, `Teilnehmenden``Teilnehmer`)
Warte dann auf Freigabe durch den Nutzer, bevor Änderungen vorgenommen werden.
Nach Freigabe: Korrekturen direkt in den Dateien vornehmen und committen:
```bash
git add src/pretix/locale/de/ src/pretix/locale/de_Informal/
git commit -m "Remove gendered language from de/de_Informal translations"
git push
```
---
## Schritt 7 Warten auf Merge-Bestätigung
**Hier pausieren.** Den Nutzer auffordern zu bestätigen, sobald der MR auf Gitea gemergt wurde.
---
## Schritt 8 Master aktualisieren und neuen Tag vergeben
Nach Bestätigung:
```bash
git checkout master
git pull
```
Den neuen Tag ableiten: `<UPSTREAM_TAG>.0` **ohne führendes `v`** (z.B. `v2026.5.0``2026.5.0.0`):
```bash
git tag <VERSION>.0
# Beispiel: git tag 2026.5.0.0
git push origin <VERSION>.0
```
Ausgabe: Bestätigung mit dem neuen Tag und kurzem Status.
---
## Schritt 9 Warten auf Build-Pipeline
Nutze den Gitea MCP Server, um den Pipeline-Lauf für den soeben gepushten Tag zu überwachen:
- **Repo:** `CGM_Public` / `pretix_cgo`
- **Methode:** `mcp__gitea__actions_run_read` mit `method: list_runs`
- Warte, bis ein Run mit dem passenden Ref (`refs/tags/<NEUER_TAG>`) den Status `success` erreicht.
- Prüfe alle ~30 Sekunden erneut und zeige dem Nutzer den aktuellen Status (running / success / failure).
- Bei `failure`: sofort stoppen und dem Nutzer melden.
Erst wenn der Build erfolgreich (`success`) abgeschlossen ist, weitermachen.
---
## Schritt 10 cgm72 GitOps-Update
Im GitOps-Repo unter `../OrtlerstrasseGitOps/` (liegt immer neben dem pretix_cgo-Repo):
1. In `pretix-cgm72/pretix-deployment.yaml` den Image-Tag aktualisieren:
- Suche: `cr.ortlerstrasse.de/cgo/pretix:<ALTER_TAG>`
- Ersetze mit: `cr.ortlerstrasse.de/cgo/pretix:<NEUER_TAG>` (= der in Schritt 8 gesetzte Git-Tag, z.B. `2026.5.0.0`)
2. Committen und pushen (im OrtlerstrasseGitOps-Verzeichnis):
```bash
git -C ../OrtlerstrasseGitOps add pretix-cgm72/pretix-deployment.yaml
git -C ../OrtlerstrasseGitOps commit -m "Update pretix-cgm72 to <NEUER_TAG>"
git -C ../OrtlerstrasseGitOps push
```
Der GitOps-Operator wendet das YAML automatisch via `kubectl apply` an und rollt den Container neu aus.
---
## Schritt 11 cgm72: Container-Neustart abwarten und Loginseite prüfen
Warte, bis der Container neu gestartet ist, und prüfe dann die Loginseite:
- **URL:** `https://anmeldung.christliche-gemeinde.de/`
- **Prüfung:** HTTP 200 und die Seite enthält typische Pretix-Inhalte (z.B. Stichwort „pretix" oder ein Login-Formular im Body)
Methode: Nutze `mcp__claude_ai_Simon_Fetch__fetch_page` (oder `curl -sI`), um die URL alle ~30 Sekunden abzurufen. Erwarte einen kurzen Ausfall während des Neustarts warte danach auf eine erfolgreiche Antwort.
Zeige dem Nutzer das Ergebnis (Status-Code + kurzen Seitenausschnitt als Bestätigung).
---
## Schritt 12 Manuelle Bestätigung für cgm72
**Hier pausieren.** Den Nutzer auffordern zu bestätigen, dass cgm72 korrekt funktioniert (`https://anmeldung.christliche-gemeinde.de/`), bevor mit sola fortgefahren wird.
---
## Schritt 13 sola GitOps-Update
Im GitOps-Repo unter `../OrtlerstrasseGitOps/` (liegt immer neben dem pretix_cgo-Repo):
1. In `pretix-sola/pretix-deployment.yaml` den Image-Tag aktualisieren:
- Suche: `cr.ortlerstrasse.de/cgo/pretix:<ALTER_TAG>`
- Ersetze mit: `cr.ortlerstrasse.de/cgo/pretix:<NEUER_TAG>`
2. Committen und pushen:
```bash
git -C ../OrtlerstrasseGitOps add pretix-sola/pretix-deployment.yaml
git -C ../OrtlerstrasseGitOps commit -m "Update pretix-sola to <NEUER_TAG>"
git -C ../OrtlerstrasseGitOps push
```
---
## Schritt 14 sola: Container-Neustart abwarten und Loginseite prüfen
Warte, bis der Container neu gestartet ist, und prüfe dann die Loginseite:
- **URL:** `https://anmeldung.sola-muenchen.de`
- **Prüfung:** HTTP 200 und die Seite enthält typische Pretix-Inhalte
Methode: wie in Schritt 11.
Ausgabe: Bestätigung mit kurzem Abschluss-Status des gesamten Workflows.
+31
View File
@@ -0,0 +1,31 @@
name: Build Deploy email notification tool
run-name: ${{ gitea.actor }} building new version of the email notification tool
on:
push: # Baut bei jedem Push (Branches + Tags)
workflow_dispatch:
jobs:
Apply-Kubernetes-Resources:
runs-on: podman
steps:
- name: Check out repository code
uses: actions/checkout@v3
- name: Login to Docker Registry
run: podman login -u ${{ secrets.REGISTRY_USERNAME }} -p ${{ secrets.REGISTRY_TOKEN }} cr.ortlerstrasse.de
- name: Set Docker Image Tag
run: |
if [[ "${{ gitea.ref }}" == refs/tags/* ]]; then
echo "TAG_NAME=${{ gitea.ref_name }}" >> $GITHUB_ENV
else
echo "TAG_NAME=latest" >> $GITHUB_ENV
fi
- name: Build Docker image
run: podman build -t cr.ortlerstrasse.de/cgo/pretix:${{ env.TAG_NAME }} .
- name: Push Docker image
run: |
podman push cr.ortlerstrasse.de/cgo/pretix:${{ env.TAG_NAME }}
echo "Image pushed successfully: cr.ortlerstrasse.de/cgo/pretix:${{ env.TAG_NAME }}"
+2 -1
View File
@@ -62,7 +62,8 @@ RUN pip3 install -U \
cd /pretix && \
PRETIX_DOCKER_BUILD=TRUE pip3 install \
-e ".[memcached]" \
gunicorn django-extensions ipython && \
gunicorn django-extensions ipython \
pretix-oidc && \
rm -rf ~/.cache/pip
RUN chmod +x /usr/local/bin/pretix && \
+1 -1
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
# <https://www.gnu.org/licenses/>.
#
__version__ = "2026.5.0"
__version__ = "2026.5.2"
+1
View File
@@ -28,5 +28,6 @@ from .items import * # noqa
from .json import * # noqa
from .mail import * # noqa
from .orderlist import * # noqa
from .relevant_orderlist import * # noqa
from .reusablemedia import * # noqa
from .waitinglist import * # noqa
+1 -1
View File
@@ -89,7 +89,7 @@ class OrderListExporter(MultiSheetListExporter):
description = gettext_lazy('Download a spreadsheet of all orders. The spreadsheet will include three sheets, one '
'with a line for every order, one with a line for every order position, and one with '
'a line for every additional fee charged in an order.')
featured = True
featured = False
repeatable_read = False
@cached_property
File diff suppressed because it is too large Load Diff
+8 -3
View File
@@ -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'
+76 -90
View File
@@ -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(),
),
+1 -1
View File
@@ -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 %}
+23 -22
View File
@@ -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">
+1 -1
View File
@@ -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
+5
View File
@@ -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']
+21
View File
@@ -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()
+39
View File
@@ -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)
+89 -73
View File
@@ -891,7 +891,7 @@ msgstr "Details der Bestellposition"
#: pretix/base/datasync/sourcefields.py:129
msgid "Attendee details"
msgstr "Details Teilnehmer*in"
msgstr "Details Teilnehmer"
#: pretix/base/datasync/sourcefields.py:130 pretix/base/exporters/answers.py:66
#: pretix/base/models/items.py:1767 pretix/control/navigation.py:185
@@ -953,13 +953,13 @@ msgstr "Veranstaltungs- oder Termininformationen"
#: pretix/presale/templates/pretixpresale/organizers/customer_membership.html:50
#: pretix/presale/templates/pretixpresale/organizers/customer_memberships.html:36
msgid "Attendee name"
msgstr "Name Teilnehmer*in"
msgstr "Name Teilnehmer"
#: pretix/base/datasync/sourcefields.py:187
#: pretix/base/datasync/sourcefields.py:604
#: pretix/base/datasync/sourcefields.py:628
msgid "Attendee"
msgstr "Teilnehmer*in"
msgstr "Teilnehmer"
#: pretix/base/datasync/sourcefields.py:207
#: pretix/base/exporters/orderlist.py:648 pretix/base/forms/questions.py:713
@@ -970,11 +970,11 @@ msgstr "Teilnehmer*in"
#: pretix/presale/templates/pretixpresale/event/fragment_cart.html:172
#: pretix/presale/templates/pretixpresale/event/fragment_cart.html:175
msgid "Attendee email"
msgstr "E-Mail Teilnehmer*in"
msgstr "E-Mail Teilnehmer"
#: pretix/base/datasync/sourcefields.py:219
msgid "Attendee or order email"
msgstr "E-Mail Teilnehmer*in oder Bestellung"
msgstr "E-Mail Teilnehmer oder Bestellung"
#: pretix/base/datasync/sourcefields.py:232
#: pretix/base/exporters/orderlist.py:649 pretix/base/pdf.py:188
@@ -984,23 +984,23 @@ msgstr "E-Mail Teilnehmer*in oder Bestellung"
#: pretix/presale/templates/pretixpresale/event/fragment_cart.html:182
#: pretix/presale/templates/pretixpresale/event/fragment_cart.html:185
msgid "Attendee company"
msgstr "Teilnehmer*innen-Firma"
msgstr "Teilnehmer-Firma"
#: pretix/base/datasync/sourcefields.py:241
msgid "Attendee address street"
msgstr "Teilnehmer*innen-Adresse: Straße"
msgstr "Teilnehmer-Adresse: Straße"
#: pretix/base/datasync/sourcefields.py:250
msgid "Attendee address ZIP code"
msgstr "Teilnehmer*innen-Adresse: PLZ"
msgstr "Teilnehmer-Adresse: PLZ"
#: pretix/base/datasync/sourcefields.py:259
msgid "Attendee address city"
msgstr "Teilnehmer*innen-Adresse: Stadt"
msgstr "Teilnehmer-Adresse: Stadt"
#: pretix/base/datasync/sourcefields.py:268
msgid "Attendee address country"
msgstr "Teilnehmer*innen-Adresse: Land"
msgstr "Teilnehmer-Adresse: Land"
#: pretix/base/datasync/sourcefields.py:279
#: pretix/base/exporters/orderlist.py:691 pretix/base/pdf.py:346
@@ -1278,7 +1278,7 @@ msgid ""
"Download a ZIP file including all files that have been uploaded by your "
"customers while creating an order."
msgstr ""
"ZIP-Datei mit allen Dateien, die von Kund*innen im Bestellprozess als "
"ZIP-Datei mit allen Dateien, die von Kunden im Bestellprozess als "
"Antwort auf eine Frage hochgeladen wurden."
#: pretix/base/exporters/answers.py:76 pretix/base/exporters/orderlist.py:629
@@ -2239,6 +2239,10 @@ msgstr "Ohne gültige Mitgliedschaft verstecken"
msgid "Order data"
msgstr "Bestelldaten"
#: pretix/base/exporters/relevant_orderlist.py:86
msgid "Order data (sorted by relevance)"
msgstr "Bestelldaten (nach Relevanz sortiert)"
#: pretix/base/exporters/json.py:53
msgid ""
"Download a structured JSON representation of all orders. This might be "
@@ -2256,8 +2260,8 @@ msgid ""
"Download a text file with all email addresses collected either from buyers "
"or from ticket holders."
msgstr ""
"Textdatei mit allen E-Mail-Adressen, die von Käufer*innen und "
"Ticketinhaber*innen eingesammelt wurden."
"Textdatei mit allen E-Mail-Adressen, die von Käufern und "
"Ticketinhaber eingesammelt wurden."
#: pretix/base/exporters/mail.py:76 pretix/plugins/reports/exporters.py:502
#: pretix/plugins/reports/exporters.py:685
@@ -2275,6 +2279,18 @@ msgstr ""
"Bestellposition und das dritte eine Zeile für jede zusätzlich erhobene "
"Gebühr."
#: pretix/base/exporters/relevant_orderlist.py:88
msgid ""
"Download a spreadsheet of all orders. The spreadsheet will include three "
"sheets, one with a line for every order, one with a line for every order "
"position, and one with a line for every additional fee charged in an "
"order. The most relevant data is in the first columns of the tables."
msgstr ""
"Tabelle (Excel oder CSV) mit allen Bestellungen. Das erste Tabellenblatt "
"enthält eine Zeile für jede Bestellung, das zweite eine Zeile für jede "
"Bestellposition und das dritte eine Zeile für jede zusätzlich erhobene "
"Gebühr. Die relevantesten Daten sind in den ersten Spalten der Tabellen."
#: pretix/base/exporters/orderlist.py:102 pretix/base/models/orders.py:336
#: pretix/base/permissions.py:228 pretix/control/navigation.py:267
#: pretix/control/navigation.py:387
@@ -4238,7 +4254,7 @@ msgstr "Bitte wählen Sie einen gültigen Staat aus."
#: pretix/base/modelimport_orders.py:359 pretix/control/forms/filter.py:688
msgid "Attendee email address"
msgstr "Teilnehmer*innen-E-Mail-Adresse"
msgstr "Teilnehmer-E-Mail-Adresse"
#: pretix/base/modelimport_orders.py:375 pretix/base/modelimport_orders.py:386
#: pretix/base/modelimport_orders.py:397 pretix/base/modelimport_orders.py:408
@@ -4250,7 +4266,7 @@ msgstr "Teilnehmer*innen-E-Mail-Adresse"
#: pretix/presale/templates/pretixpresale/event/fragment_cart.html:193
#: pretix/presale/templates/pretixpresale/event/fragment_cart.html:196
msgid "Attendee address"
msgstr "Teilnehmer*innen-Adresse"
msgstr "Teilnehmer-Adresse"
#: pretix/base/modelimport_orders.py:468
msgid "Calculate from product"
@@ -8109,7 +8125,7 @@ msgstr "Musterfirma GmbH"
#: pretix/base/pdf.py:193
msgid "Full attendee address"
msgstr "Volle Teilnehmer*innen-Adresse"
msgstr "Volle Teilnehmer-Adresse"
#: pretix/base/pdf.py:194
msgid ""
@@ -8127,23 +8143,23 @@ msgstr ""
#: pretix/base/pdf.py:198
msgid "Attendee street"
msgstr "Teilnehmer*innen-Straße"
msgstr "Teilnehmer-Straße"
#: pretix/base/pdf.py:203
msgid "Attendee ZIP code"
msgstr "Teilnehmer*innen-PLZ"
msgstr "Teilnehmer-PLZ"
#: pretix/base/pdf.py:208
msgid "Attendee city"
msgstr "Teilnehmer*innen-Stadt"
msgstr "Teilnehmer-Stadt"
#: pretix/base/pdf.py:213
msgid "Attendee state"
msgstr "Teilnehmer*innen-Bundesstaat"
msgstr "Teilnehmer-Bundesstaat"
#: pretix/base/pdf.py:218
msgid "Attendee country"
msgstr "Teilnehmer*innen-Land"
msgstr "Teilnehmer-Land"
#: pretix/base/pdf.py:230
msgid "Pseudonymization ID (lead scanning)"
@@ -8441,7 +8457,7 @@ msgstr "Herr Mustermann"
#: pretix/plugins/ticketoutputpdf/exporters.py:99
#, python-brace-format
msgid "Attendee name: {part}"
msgstr "Teilnehmer*innenname: {part}"
msgstr "Teilnehmername: {part}"
#: pretix/base/pdf.py:695
msgid "Invoice address name for salutation"
@@ -9355,7 +9371,7 @@ msgstr "Veranstaltungsort: {location}"
#, python-brace-format
msgctxt "invoice"
msgid "Attendee: {name}"
msgstr "Teilnehmer*in: {name}"
msgstr "Teilnehmer: {name}"
#: pretix/base/services/invoices.py:293 pretix/plugins/reports/exporters.py:308
#, python-brace-format
@@ -10226,7 +10242,7 @@ msgstr ""
#: pretix/base/settings.py:362
msgid "Hide prices on attendee ticket page"
msgstr "Preise auf Teilnehmer*innen-Ticket-Seite verstecken"
msgstr "Preise auf Teilnehmer-Ticket-Seite verstecken"
#: pretix/base/settings.py:363
msgid ""
@@ -10236,13 +10252,13 @@ msgid ""
"price."
msgstr ""
"Wenn eine Person mehrere Tickets erwirbt und E-Mails an alle "
"Teilnehmer*innen verschickt werden, wird mit dieser Option der Ticketpreis "
"auf der Ticket-Seite der einzelnen Teilnehmer*innen versteckt. Nur die "
"Teilnehmer verschickt werden, wird mit dieser Option der Ticketpreis "
"auf der Ticket-Seite der einzelnen Teilnehmer versteckt. Nur die "
"Person, welche die Tickets kauft, sieht den Preis."
#: pretix/base/settings.py:381
msgid "Ask for attendee names"
msgstr "Namen der Teilnehmer*innen erfragen"
msgstr "Namen der Teilnehmer erfragen"
#: pretix/base/settings.py:382
msgid "Ask for a name for all personalized tickets."
@@ -10251,11 +10267,11 @@ msgstr ""
#: pretix/base/settings.py:391
msgid "Require attendee names"
msgstr "Namen der Teilnehmer*innen erfordern"
msgstr "Namen der Teilnehmer erfordern"
#: pretix/base/settings.py:392
msgid "Require customers to fill in the names of all attendees."
msgstr "Erfordere die Eingabe aller Teilnehmer*innen-Namen."
msgstr "Erfordere die Eingabe aller Teilnehmer-Namen."
#: pretix/base/settings.py:402
msgid "Ask for email addresses per ticket"
@@ -10786,8 +10802,8 @@ msgid ""
"but no indication of missing payment will be visible on the ticket pages of "
"attendees who did not buy the ticket themselves."
msgstr ""
"Die Box mit Zahlungsinstruktionen wird Ticketkäufer*innen weiter angezeigt, "
"aber Teilnehmer*innen, die ihr Ticket nicht selbst gekauft haben, werden "
"Die Box mit Zahlungsinstruktionen wird Ticketkäufern weiter angezeigt, "
"aber Teilnehmern, die ihr Ticket nicht selbst gekauft haben, werden "
"keine Anzeichen des fehlenden Zahlungseingangs sehen."
#: pretix/base/settings.py:1107
@@ -11285,7 +11301,7 @@ msgstr ""
#: pretix/base/settings.py:1750
msgid "Show number of check-ins to customer"
msgstr "Zeige Anzahl der Check-ins für Kund*innen an"
msgstr "Zeige Anzahl der Check-ins für Kunden an"
#: pretix/base/settings.py:1751
msgid ""
@@ -11296,12 +11312,12 @@ msgid ""
"failed scans will not be counted, and the user will not see the different "
"check-in lists."
msgstr ""
"Wenn diese Option aktiv ist, können Kund*innen selbst sehen, wie oft sie die "
"Wenn diese Option aktiv ist, können Kunden selbst sehen, wie oft sie die "
"Veranstaltung betreten haben. Das ist normalerweise nicht nötig, aber kann "
"nützlich sein, wenn es Tickets gibt, die eine bestimmte Anzahl an Eintritten "
"erlauben, sodass Kund*innen die bisherige Nutzung des Tickets einsehen "
"erlauben, sodass Kunden die bisherige Nutzung des Tickets einsehen "
"können. Ausgänge oder fehlgeschlagene Scans werden nicht angezeigt und die "
"Kund*innen sehen keine Aufschlüsselung verschiedener Check-in-Listen."
"Kunden sehen keine Aufschlüsselung verschiedener Check-in-Listen."
#: pretix/base/settings.py:1764
msgid "Allow users to download tickets"
@@ -11482,7 +11498,7 @@ msgstr ""
#: pretix/base/settings.py:1927 pretix/base/settings.py:1936
msgid "Both the attendee and the person who ordered can make changes"
msgstr ""
"Sowohl Besteller*in als auch Teilnehmer*innen können Änderungen vornehmen"
"Sowohl Besteller als auch Teilnehmer können Änderungen vornehmen"
#: pretix/base/settings.py:1931
msgid "Allow customers to modify their information"
@@ -11590,7 +11606,7 @@ msgstr ""
#: pretix/base/settings.py:2037
msgid "Allow individual attendees to change their ticket"
msgstr "Erlaubt einzelnen Teilnehmer*innen ihr Ticket zu ändern"
msgstr "Erlaubt einzelnen Teilnehmern ihr Ticket zu ändern"
#: pretix/base/settings.py:2038
msgid ""
@@ -11602,7 +11618,7 @@ msgid ""
msgstr ""
"Standardmäßig kann nur die Person, welche die Tickets gekauft hat, "
"Änderungen an der Bestellung vornehmen. Wenn diese Option aktiv ist, können "
"auch einzelne Teilnehmer*innen Änderungen vornehmen. Teilnehmer*innen können "
"auch einzelne Teilnehmer Änderungen vornehmen. Teilnehmer können "
"jedoch immer nur Änderungen vornehmen, welche die Gesamtkosten der "
"Bestellung nicht verändern. Solche Änderungen können nur vom Ticketkäufer "
"vorgenommen werden."
@@ -11899,7 +11915,7 @@ msgid ""
"people."
msgstr ""
"Sie können dieses Feld benutzen, um zusätzliche Informationen mit Ihren "
"Teilnehmer*innen zu teilen, wie z.B. Anreise-Informationen oder den Link zu "
"Teilnehmer zu teilen, wie z.B. Anreise-Informationen oder den Link zu "
"einer digitalen Veranstaltung. Wenn das Feld leer ist, fügen wir automatisch "
"einen Link zum Ticketshop, die Einlass-Uhrzeit und den Veranstalter hier "
"ein. Es sind keine Platzhalter mit sensiblen personenbezogenen Daten "
@@ -12140,8 +12156,8 @@ msgstr ""
"Diese Datei wird an die erste E-Mail angehängt, die wir beim Eingang einer "
"neuen Bestellung verschicken. Sie kann daher mit den Textvorlagen "
"\"Getätigte Bestellung\", \"Kostenlose Bestellung\" oder \"Erhaltene "
"Bestellung\" von oben auftreten. Sie wird ggf. sowohl an Besteller*innen als "
"auch Teilnehmer*innen verschickt. Nicht geeignet zum Versand nicht-"
"Bestellung\" von oben auftreten. Sie wird ggf. sowohl an Besteller als "
"auch Teilnehmer verschickt. Nicht geeignet zum Versand nicht-"
"öffentlicher Informationen, da die Datei unabhängig davon verschickt wird, "
"ob die Bestellung bezahlt oder freigegeben ist. Um zu vermeiden, dass diese "
"wichtige E-Mail nicht ankommt, können nur PDF-Dateien mit maximal {size} MB "
@@ -15476,7 +15492,7 @@ msgstr "Check-in-Status"
#: pretix/control/forms/filter.py:2168
#: pretix/plugins/checkinlists/exporters.py:109
msgid "All attendees"
msgstr "Alle Teilnehmer*innen"
msgstr "Alle Teilnehmer"
#: pretix/control/forms/filter.py:2169
#: pretix/control/templates/pretixcontrol/checkin/index.html:183
@@ -16060,7 +16076,7 @@ msgid ""
"people over 65. This ticket includes access to all parts of the event, "
"except the VIP area."
msgstr ""
"z.B. Dieses reduzierte Ticket ist erhältlich für Vollzeitstudent*innen, "
"z.B. Dieses reduzierte Ticket ist erhältlich für Vollzeitstudent, "
"Arbeitslose und Menschen über 65. Das Ticket enthält Zugang zu allen Teilen "
"der Veranstaltung außer des VIP-Bereiches."
@@ -16374,7 +16390,7 @@ msgstr ""
#: pretix/control/forms/orders.py:167 pretix/control/forms/orders.py:226
#: pretix/control/forms/orders.py:240
msgid "Notify customer by email"
msgstr "Kund*in per E-Mail benachrichtigen"
msgstr "Kunde per E-Mail benachrichtigen"
#: pretix/control/forms/orders.py:174
msgid "Keep a cancellation fee of"
@@ -17933,7 +17949,7 @@ msgstr "Eine individuelle E-Mail wurde verschickt."
#: pretix/control/logdisplay.py:554
msgid "A custom email has been sent to an attendee."
msgstr "Eine individuelle E-Mail wurde an eine Teilnehmer*in verschickt."
msgstr "Eine individuelle E-Mail wurde an einen Teilnehmer verschickt."
#: pretix/control/logdisplay.py:555
msgid ""
@@ -19651,7 +19667,7 @@ msgstr "Terminal-ID"
#: pretix/control/templates/pretixcontrol/boxoffice/payment.html:104
msgid "Card holder"
msgstr "Karteninhaber*in"
msgstr "Karteninhaber"
#: pretix/control/templates/pretixcontrol/boxoffice/payment.html:108
msgid "Card expiration"
@@ -19960,7 +19976,7 @@ msgstr "CSV"
#: pretix/control/templates/pretixcontrol/checkin/index.html:73
msgid "No attendee record was found."
msgstr "Keine passenden Teilnehmer*innen gefunden."
msgstr "Keine passenden Teilnehmer gefunden."
#: pretix/control/templates/pretixcontrol/checkin/index.html:91
#: pretix/control/templates/pretixcontrol/datasync/failed_jobs.html:19
@@ -22753,7 +22769,7 @@ msgid ""
"Only purchases of such products will be considered \"attendees\" for most "
"statistical purposes or within some plugins."
msgstr ""
"Nur Käufe eines solchen Produkts werden als \"Teilnehmer*innen\" gewertet, "
"Nur Käufe eines solchen Produkts werden als \"Teilnehmer\" gewertet, "
"z.B. in Statistiken oder in Funktionen von Erweiterungen."
#: pretix/control/templates/pretixcontrol/item/create.html:39
@@ -22832,7 +22848,7 @@ msgid ""
"The system will not ask for a name or other attendee details. This only "
"affects system-provided fields, you can still add your own questions."
msgstr ""
"Das System wird nicht nach einem Namen oder anderen Teilnehmer*innen-Daten "
"Das System wird nicht nach einem Namen oder anderen Teilnehmer-Daten "
"fragen. Dies betrifft nur vom System bereitgestellte Felder, eigene Fragen "
"können trotzdem hinzugefügt werden."
@@ -25169,7 +25185,7 @@ msgstr "Sonstige Datenexporte"
#: pretix/control/templates/pretixcontrol/orders/export.html:107
#: pretix/control/templates/pretixcontrol/organizers/export.html:107
msgid "Recommended for new users"
msgstr "Empfohlen für neue Benutzer*innen"
msgstr "Empfohlen für neue Benutzer"
#: pretix/control/templates/pretixcontrol/orders/export.html:120
#: pretix/control/templates/pretixcontrol/organizers/export.html:120
@@ -26127,7 +26143,7 @@ msgstr "Domains"
#: pretix/control/templates/pretixcontrol/organizers/edit.html:320
msgid "This dialog is intended for advanced users."
msgstr "Dieser Dialog ist für fortgeschrittene Anwender*innen gedacht."
msgstr "Dieser Dialog ist für fortgeschrittene Anwender gedacht."
#: pretix/control/templates/pretixcontrol/organizers/edit.html:321
msgid ""
@@ -28526,7 +28542,7 @@ msgid ""
"customers. This way, customers will not be able to discover the waiting list."
msgstr ""
"Entsprechend Ihrer Veranstaltungseinstellungen werden ausverkaufte Produkte "
"nicht angezeigt. Dies führt dazu, dass Kund*innen die Warteliste nicht "
"nicht angezeigt. Dies führt dazu, dass Kunden die Warteliste nicht "
"finden können."
#: pretix/control/templates/pretixcontrol/waitinglist/index.html:38
@@ -28837,11 +28853,11 @@ msgstr "Das ausgewählte List wurde gelöscht."
#: pretix/control/views/dashboards.py:115
msgid "Attendees (ordered)"
msgstr "Teilnehmende (bestellt)"
msgstr "Teilnehmer (bestellt)"
#: pretix/control/views/dashboards.py:125
msgid "Attendees (paid)"
msgstr "Teilnehmende (bezahlt)"
msgstr "Teilnehmer (bezahlt)"
#: pretix/control/views/dashboards.py:137
#, python-brace-format
@@ -30920,7 +30936,7 @@ msgstr "PDF-Sammlungen"
#: pretix/plugins/badges/exporters.py:431
msgid "Download all attendee badges as one large PDF for printing."
msgstr "Alle Teilnehmer*innen-Badges in einer großen PDF-Datei für den Druck."
msgstr "Alle Teilnehmer-Badges in einer großen PDF-Datei für den Druck."
#: pretix/plugins/badges/exporters.py:452
#: pretix/plugins/ticketoutputpdf/exporters.py:80
@@ -31200,7 +31216,7 @@ msgstr "Anderes Bankkonto"
#: pretix/plugins/banktransfer/payment.py:85
msgid "Name of account holder"
msgstr "Kontoinhaber*in"
msgstr "Kontoinhaber"
#: pretix/plugins/banktransfer/payment.py:87
msgid ""
@@ -31342,7 +31358,7 @@ msgstr "Bitte überweisen Sie den vollen Betrag auf das folgende Bankkonto:"
#: pretix/plugins/stripe/templates/pretixplugins/stripe/checkout_payment_confirm.html:32
#: pretix/plugins/stripe/templates/pretixplugins/stripe/checkout_payment_confirm.html:35
msgid "Account holder"
msgstr "Kontoinhaber*in"
msgstr "Kontoinhaber"
#: pretix/plugins/banktransfer/payment.py:304
#: pretix/plugins/banktransfer/templates/pretixplugins/banktransfer/checkout_payment_form.html:20
@@ -31937,7 +31953,7 @@ msgid ""
"Download a spreadsheet with all attendees that are included in a check-in "
"list."
msgstr ""
"Tabelle (Excel oder CSV) mit allen Teilnehmer*innen, die in einer Check-in-"
"Tabelle (Excel oder CSV) mit allen Teilnehmer, die in einer Check-in-"
"Liste zutrittsberechtigt sind."
#: pretix/plugins/checkinlists/exporters.py:500
@@ -33116,7 +33132,7 @@ msgstr "Geplante E-Mails"
#: pretix/plugins/sendmail/signals.py:122
msgid "Mass email was sent to customers or attendees."
msgstr "Rundmail wurde an Kunden oder Teilnehmer*innen verschickt."
msgstr "Rundmail wurde an Kunden oder Teilnehmer verschickt."
#: pretix/plugins/sendmail/signals.py:123
msgid "Mass email was sent to waiting list entries."
@@ -33148,7 +33164,7 @@ msgstr "Eine automatisierte E-Mail wurde an den Besteller verschickt"
#: pretix/plugins/sendmail/signals.py:142
msgid "A scheduled email was sent to a ticket holder"
msgstr "Eine automatisierte E-Mail wurde an eine Teilnehmer*in verschickt."
msgstr "Eine automatisierte E-Mail wurde an eine Teilnehmer verschickt."
#: pretix/plugins/sendmail/signals.py:143
msgid "An email rule was deleted"
@@ -33181,7 +33197,7 @@ msgstr "Alle nicht eingecheckten Kunden"
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/history_fragment_orders.html:23
msgid "Attendee contact addresses"
msgstr "Teilnehmer*innen-E-Mail-Adressen"
msgstr "Teilnehmer-E-Mail-Adressen"
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/history_fragment_orders.html:25
msgid "All contact addresses"
@@ -33340,14 +33356,14 @@ msgstr ""
#: pretix/plugins/sendmail/views.py:250
msgid "Orders or attendees"
msgstr "Bestellungen oder Teilnehmer*innen"
msgstr "Bestellungen oder Teilnehmer"
#: pretix/plugins/sendmail/views.py:251
msgid ""
"Send an email to every customer, or to every person a ticket has been "
"purchased for, or a combination of both."
msgstr ""
"Senden Sie eine E-Mail an alle Ticketkäufer*innen, alle Ticketinhaber*innen "
"Senden Sie eine E-Mail an alle Ticketkäufer, alle Ticketinhaber "
"oder eine Kombination aus beiden Gruppen."
#: pretix/plugins/sendmail/views.py:417
@@ -33921,23 +33937,23 @@ msgstr "SEPA-Lastschrift"
#: pretix/plugins/stripe/payment.py:1277
msgid "Account Holder Name"
msgstr "Kontoinhaber*in"
msgstr "Kontoinhaber"
#: pretix/plugins/stripe/payment.py:1282
msgid "Account Holder Street"
msgstr "Straße (Kontoinhaber*in)"
msgstr "Straße (Kontoinhaber)"
#: pretix/plugins/stripe/payment.py:1294
msgid "Account Holder Postal Code"
msgstr "PLZ (Kontoinhaber*in)"
msgstr "PLZ (Kontoinhaber)"
#: pretix/plugins/stripe/payment.py:1306
msgid "Account Holder City"
msgstr "Stadt (Kontoinhaber*in)"
msgstr "Stadt (Kontoinhaber)"
#: pretix/plugins/stripe/payment.py:1318
msgid "Account Holder Country"
msgstr "Land (Kontoinhaber*in)"
msgstr "Land (Kontoinhaber)"
#: pretix/plugins/stripe/payment.py:1362
msgid "Affirm via Stripe"
@@ -34388,7 +34404,7 @@ msgstr "MOTO"
#: pretix/plugins/stripe/templates/pretixplugins/stripe/control.html:64
#: pretix/plugins/stripe/templates/pretixplugins/stripe/control.html:67
msgid "Payer name"
msgstr "Name des Zahlenden"
msgstr "Name des Zahlers"
#: pretix/plugins/stripe/templates/pretixplugins/stripe/control.html:91
msgid "Payment receipt"
@@ -35783,7 +35799,7 @@ msgstr[1] "Das Ticket wurde %(count)s-mal eingelöst."
#: pretix/presale/templates/pretixpresale/event/fragment_cart.html:166
msgid "No attendee name provided"
msgstr "Name der teilnehmenden Person nicht angegeben"
msgstr "Name des Teilnehmenrs nicht angegeben"
#: pretix/presale/templates/pretixpresale/event/fragment_cart.html:219
msgid "The image you previously uploaded"
@@ -37545,12 +37561,12 @@ msgstr ""
#: pretix/presale/templates/pretixpresale/organizers/customer_profiles.html:11
#: pretix/presale/views/customer.py:386
msgid "Attendee profiles"
msgstr "Teilnehmer*innen-Profile"
msgstr "Teilnehmer-Profile"
#: pretix/presale/templates/pretixpresale/organizers/customer_profiles.html:37
msgid "You dont have any attendee profiles in your account yet."
msgstr ""
"In Ihrem Kundenkonto sind noch keine Teilnehmer*innen-Profile gespeichert."
"In Ihrem Kundenkonto sind noch keine Teilnehmer-Profile gespeichert."
#: pretix/presale/templates/pretixpresale/organizers/customer_registration.html:7
msgid "Registration"
@@ -39183,7 +39199,7 @@ msgstr "Kosovo"
#~ "This plugin allows you to generate badges or name tags for your attendees."
#~ msgstr ""
#~ "Diese Erweiterung erlaubt, Namensschilder oder Badges für die "
#~ "Teilnehmer*innen zu erstellen."
#~ "Teilnehmer zu erstellen."
#~ msgid "This plugin allows you to receive payments via PayPal"
#~ msgstr "Dieses Plugin erlaubt, Zahlungen über PayPal anzunehmen"
+2 -2
View File
@@ -161,12 +161,12 @@ msgstr "Bezahlte Bestellungen"
#: pretix/plugins/statistics/static/pretixplugins/statistics/statistics.js:27
#: pretix/plugins/statistics/static/pretixplugins/statistics/statistics.js:39
msgid "Attendees (ordered)"
msgstr "Teilnehmende (bestellt)"
msgstr "Teilnehmer (bestellt)"
#: pretix/plugins/statistics/static/pretixplugins/statistics/statistics.js:27
#: pretix/plugins/statistics/static/pretixplugins/statistics/statistics.js:39
msgid "Attendees (paid)"
msgstr "Teilnehmende (bezahlt)"
msgstr "Teilnehmer (bezahlt)"
#: pretix/plugins/statistics/static/pretixplugins/statistics/statistics.js:51
msgid "Total revenue"
@@ -893,7 +893,7 @@ msgstr "Details der Bestellposition"
#: pretix/base/datasync/sourcefields.py:129
msgid "Attendee details"
msgstr "Details Teilnehmer*in"
msgstr "Details Teilnehmer"
#: pretix/base/datasync/sourcefields.py:130 pretix/base/exporters/answers.py:66
#: pretix/base/models/items.py:1767 pretix/control/navigation.py:185
@@ -955,13 +955,13 @@ msgstr "Veranstaltungs- oder Termininformationen"
#: pretix/presale/templates/pretixpresale/organizers/customer_membership.html:50
#: pretix/presale/templates/pretixpresale/organizers/customer_memberships.html:36
msgid "Attendee name"
msgstr "Name Teilnehmer*in"
msgstr "Name Teilnehmer"
#: pretix/base/datasync/sourcefields.py:187
#: pretix/base/datasync/sourcefields.py:604
#: pretix/base/datasync/sourcefields.py:628
msgid "Attendee"
msgstr "Teilnehmer*in"
msgstr "Teilnehmer"
#: pretix/base/datasync/sourcefields.py:207
#: pretix/base/exporters/orderlist.py:648 pretix/base/forms/questions.py:713
@@ -972,11 +972,11 @@ msgstr "Teilnehmer*in"
#: pretix/presale/templates/pretixpresale/event/fragment_cart.html:172
#: pretix/presale/templates/pretixpresale/event/fragment_cart.html:175
msgid "Attendee email"
msgstr "E-Mail Teilnehmer*in"
msgstr "E-Mail Teilnehmer"
#: pretix/base/datasync/sourcefields.py:219
msgid "Attendee or order email"
msgstr "E-Mail Teilnehmer*in oder Bestellung"
msgstr "E-Mail Teilnehmer oder Bestellung"
#: pretix/base/datasync/sourcefields.py:232
#: pretix/base/exporters/orderlist.py:649 pretix/base/pdf.py:188
@@ -986,23 +986,23 @@ msgstr "E-Mail Teilnehmer*in oder Bestellung"
#: pretix/presale/templates/pretixpresale/event/fragment_cart.html:182
#: pretix/presale/templates/pretixpresale/event/fragment_cart.html:185
msgid "Attendee company"
msgstr "Teilnehmer*innen-Firma"
msgstr "Teilnehmer-Firma"
#: pretix/base/datasync/sourcefields.py:241
msgid "Attendee address street"
msgstr "Teilnehmer*innen-Adresse: Straße"
msgstr "Teilnehmer-Adresse: Straße"
#: pretix/base/datasync/sourcefields.py:250
msgid "Attendee address ZIP code"
msgstr "Teilnehmer*innen-Adresse: PLZ"
msgstr "Teilnehmer-Adresse: PLZ"
#: pretix/base/datasync/sourcefields.py:259
msgid "Attendee address city"
msgstr "Teilnehmer*innen-Adresse: Stadt"
msgstr "Teilnehmer-Adresse: Stadt"
#: pretix/base/datasync/sourcefields.py:268
msgid "Attendee address country"
msgstr "Teilnehmer*innen-Adresse: Land"
msgstr "Teilnehmer-Adresse: Land"
#: pretix/base/datasync/sourcefields.py:279
#: pretix/base/exporters/orderlist.py:691 pretix/base/pdf.py:346
@@ -1280,7 +1280,7 @@ msgid ""
"Download a ZIP file including all files that have been uploaded by your "
"customers while creating an order."
msgstr ""
"ZIP-Datei mit allen Dateien, die von Kund*innen im Bestellprozess als "
"ZIP-Datei mit allen Dateien, die von Kunden im Bestellprozess als "
"Antwort auf eine Frage hochgeladen wurden."
#: pretix/base/exporters/answers.py:76 pretix/base/exporters/orderlist.py:629
@@ -2258,8 +2258,8 @@ msgid ""
"Download a text file with all email addresses collected either from buyers "
"or from ticket holders."
msgstr ""
"Textdatei mit allen E-Mail-Adressen, die von Käufer*innen und "
"Ticketinhaber*innen eingesammelt wurden."
"Textdatei mit allen E-Mail-Adressen, die von Käufer und "
"Ticketinhaber eingesammelt wurden."
#: pretix/base/exporters/mail.py:76 pretix/plugins/reports/exporters.py:502
#: pretix/plugins/reports/exporters.py:685
@@ -4240,7 +4240,7 @@ msgstr "Bitte wähle einen gültigen Staat aus."
#: pretix/base/modelimport_orders.py:359 pretix/control/forms/filter.py:688
msgid "Attendee email address"
msgstr "Teilnehmer*innen-E-Mail-Adresse"
msgstr "Teilnehmer-E-Mail-Adresse"
#: pretix/base/modelimport_orders.py:375 pretix/base/modelimport_orders.py:386
#: pretix/base/modelimport_orders.py:397 pretix/base/modelimport_orders.py:408
@@ -4252,7 +4252,7 @@ msgstr "Teilnehmer*innen-E-Mail-Adresse"
#: pretix/presale/templates/pretixpresale/event/fragment_cart.html:193
#: pretix/presale/templates/pretixpresale/event/fragment_cart.html:196
msgid "Attendee address"
msgstr "Teilnehmer*innen-Adresse"
msgstr "Teilnehmer-Adresse"
#: pretix/base/modelimport_orders.py:468
msgid "Calculate from product"
@@ -8102,7 +8102,7 @@ msgstr "Musterfirma"
#: pretix/base/pdf.py:193
msgid "Full attendee address"
msgstr "Volle Teilnehmer*innen-Adresse"
msgstr "Volle Teilnehmer-Adresse"
#: pretix/base/pdf.py:194
msgid ""
@@ -8120,23 +8120,23 @@ msgstr ""
#: pretix/base/pdf.py:198
msgid "Attendee street"
msgstr "Teilnehmer*innen-Straße"
msgstr "Teilnehmer-Straße"
#: pretix/base/pdf.py:203
msgid "Attendee ZIP code"
msgstr "Teilnehmer*innen-PLZ"
msgstr "Teilnehmer-PLZ"
#: pretix/base/pdf.py:208
msgid "Attendee city"
msgstr "Teilnehmer*innen-Stadt"
msgstr "Teilnehmer-Stadt"
#: pretix/base/pdf.py:213
msgid "Attendee state"
msgstr "Teilnehmer*innen-Bundesstaat"
msgstr "Teilnehmer-Bundesstaat"
#: pretix/base/pdf.py:218
msgid "Attendee country"
msgstr "Teilnehmer*innen-Land"
msgstr "Teilnehmer-Land"
#: pretix/base/pdf.py:230
msgid "Pseudonymization ID (lead scanning)"
@@ -8434,7 +8434,7 @@ msgstr "Herr Mustermann"
#: pretix/plugins/ticketoutputpdf/exporters.py:99
#, python-brace-format
msgid "Attendee name: {part}"
msgstr "Teilnehmer*innenname: {part}"
msgstr "Teilnehmer: {part}"
#: pretix/base/pdf.py:695
msgid "Invoice address name for salutation"
@@ -9345,7 +9345,7 @@ msgstr "Veranstaltungsort: {location}"
#, python-brace-format
msgctxt "invoice"
msgid "Attendee: {name}"
msgstr "Teilnehmer*in: {name}"
msgstr "Teilnehmer: {name}"
#: pretix/base/services/invoices.py:293 pretix/plugins/reports/exporters.py:308
#, python-brace-format
@@ -10214,7 +10214,7 @@ msgstr ""
#: pretix/base/settings.py:362
msgid "Hide prices on attendee ticket page"
msgstr "Preise auf Teilnehmer*innen-Ticket-Seite verstecken"
msgstr "Preise auf Teilnehmer-Ticket-Seite verstecken"
#: pretix/base/settings.py:363
msgid ""
@@ -10224,13 +10224,13 @@ msgid ""
"price."
msgstr ""
"Wenn eine Person mehrere Tickets erwirbt und E-Mails an alle "
"Teilnehmer*innen verschickt werden, wird mit dieser Option der Ticketpreis "
"auf der Ticket-Seite der einzelnen Teilnehmer*innen versteckt. Nur die "
"Teilnehmer verschickt werden, wird mit dieser Option der Ticketpreis "
"auf der Ticket-Seite der einzelnen Teilnehmer versteckt. Nur die "
"Person, welche die Tickets kauft, sieht den Preis."
#: pretix/base/settings.py:381
msgid "Ask for attendee names"
msgstr "Namen der Teilnehmer*innen erfragen"
msgstr "Namen der Teilnehmer erfragen"
#: pretix/base/settings.py:382
msgid "Ask for a name for all personalized tickets."
@@ -10239,11 +10239,11 @@ msgstr ""
#: pretix/base/settings.py:391
msgid "Require attendee names"
msgstr "Namen der Teilnehmer*innen erfordern"
msgstr "Namen der Teilnehmer erfordern"
#: pretix/base/settings.py:392
msgid "Require customers to fill in the names of all attendees."
msgstr "Erfordere die Eingabe aller Teilnehmer*innen-Namen."
msgstr "Erfordere die Eingabe aller Teilnehmer-Namen."
#: pretix/base/settings.py:402
msgid "Ask for email addresses per ticket"
@@ -10265,7 +10265,7 @@ msgstr ""
"aktivierst, fragt das System zusätzlich nach einzelnen E-Mail-Adressen für "
"jedes personalisierte Ticket in der Bestellung. Dies könnte z.B. nützlich "
"sein, wenn du auch im Falle von Gruppenbestellungen individuelle Adressen "
"von jeder Teilnehmer*in benötigst. pretix sendet die Bestellbestätigung "
"von jedem Teilnehmer benötigst. pretix sendet die Bestellbestätigung "
"standardmäßig nach wie vor nur an die primäre Adresse, dies kann jedoch in "
"den E-Mail-Einstellungen angepasst werden."
@@ -10773,8 +10773,8 @@ msgid ""
"but no indication of missing payment will be visible on the ticket pages of "
"attendees who did not buy the ticket themselves."
msgstr ""
"Die Box mit Zahlungsinstruktionen wird Ticketkäufer*innen weiter angezeigt, "
"aber Teilnehmer*innen, die ihr Ticket nicht selbst gekauft haben, werden "
"Die Box mit Zahlungsinstruktionen wird Ticketkäufern weiter angezeigt, "
"aber Teilnehmer, die ihr Ticket nicht selbst gekauft haben, werden "
"keine Anzeichen des fehlenden Zahlungseingangs sehen."
#: pretix/base/settings.py:1107
@@ -11272,7 +11272,7 @@ msgstr ""
#: pretix/base/settings.py:1750
msgid "Show number of check-ins to customer"
msgstr "Zeige Anzahl der Check-ins für Kund*innen an"
msgstr "Zeige Anzahl der Check-ins für Kunden an"
#: pretix/base/settings.py:1751
msgid ""
@@ -11283,12 +11283,12 @@ msgid ""
"failed scans will not be counted, and the user will not see the different "
"check-in lists."
msgstr ""
"Wenn diese Option aktiv ist, können Kund*innen selbst sehen, wie oft sie die "
"Wenn diese Option aktiv ist, können Kunden selbst sehen, wie oft sie die "
"Veranstaltung betreten haben. Das ist normalerweise nicht nötig, aber kann "
"nützlich sein, wenn es Tickets gibt, die eine bestimmte Anzahl an Eintritten "
"erlauben, sodass Kund*innen die bisherige Nutzung des Tickets einsehen "
"erlauben, sodass Kunden die bisherige Nutzung des Tickets einsehen "
"können. Ausgänge oder fehlgeschlagene Scans werden nicht angezeigt und die "
"Kund*innen sehen keine Aufschlüsselung verschiedener Check-in-Listen."
"Kunden sehen keine Aufschlüsselung verschiedener Check-in-Listen."
#: pretix/base/settings.py:1764
msgid "Allow users to download tickets"
@@ -11469,7 +11469,7 @@ msgstr ""
#: pretix/base/settings.py:1927 pretix/base/settings.py:1936
msgid "Both the attendee and the person who ordered can make changes"
msgstr ""
"Sowohl Besteller*in als auch Teilnehmer*innen können Änderungen vornehmen"
"Sowohl Besteller als auch Teilnehmer können Änderungen vornehmen"
#: pretix/base/settings.py:1931
msgid "Allow customers to modify their information"
@@ -11577,7 +11577,7 @@ msgstr ""
#: pretix/base/settings.py:2037
msgid "Allow individual attendees to change their ticket"
msgstr "Erlaubt einzelnen Teilnehmer*innen ihr Ticket zu ändern"
msgstr "Erlaubt einzelnen Teilnehmer ihr Ticket zu ändern"
#: pretix/base/settings.py:2038
msgid ""
@@ -11589,7 +11589,7 @@ msgid ""
msgstr ""
"Standardmäßig kann nur die Person, welche die Tickets gekauft hat, "
"Änderungen an der Bestellung vornehmen. Wenn diese Option aktiv ist, können "
"auch einzelne Teilnehmer*innen Änderungen vornehmen. Teilnehmer*innen können "
"auch einzelne Teilnehmer Änderungen vornehmen. Teilnehmer können "
"jedoch immer nur Änderungen vornehmen, welche die Gesamtkosten der "
"Bestellung nicht verändern. Solche Änderungen können nur vom Ticketkäufer "
"vorgenommen werden."
@@ -11773,7 +11773,7 @@ msgstr "Kontakt-E-Mail"
#: pretix/base/settings.py:2273 pretix/control/forms/event.py:1900
msgid "We'll show this publicly to allow attendees to contact you."
msgstr ""
"Wir werden diese Adresse veröffentlichen um Teilnehmer*innen zu ermöglichen, "
"Wir werden diese Adresse veröffentlichen um Teilnehmern zu ermöglichen, "
"dich zu kontaktieren."
#: pretix/base/settings.py:2281 pretix/control/forms/event.py:1892
@@ -11885,7 +11885,7 @@ msgid ""
"people."
msgstr ""
"Du kannst dieses Feld benutzen um zusätzliche Informationen mit deinen "
"Teilnehmer*innen zu teilen, wie z.B. Anreise-Informationen oder den Link zu "
"Teilnehmer zu teilen, wie z.B. Anreise-Informationen oder den Link zu "
"einer digitalen Veranstaltung. Wenn das Feld leer ist, fügen wir automatisch "
"einen Link zum Ticketshop, die Einlass-Uhrzeit und den Veranstalter hier "
"ein. Es sind keine Platzhalter mit sensiblen personenbezogenen Daten "
@@ -12125,8 +12125,8 @@ msgstr ""
"Diese Datei wird an die erste E-Mail angehängt, die wir beim Eingang einer "
"neuen Bestellung verschicken. Sie kann daher mit den Textvorlagen "
"\"Getätigte Bestellung\", \"Kostenlose Bestellung\" oder \"Erhaltene "
"Bestellung\" von oben auftreten. Sie wird ggf. sowohl an Besteller*innen als "
"auch Teilnehmer*innen verschickt. Nicht geeignet zum Versand nicht-"
"Bestellung\" von oben auftreten. Sie wird ggf. sowohl an Besteller als "
"auch Teilnehmer verschickt. Nicht geeignet zum Versand nicht-"
"öffentlicher Informationen, da die Datei unabhängig davon verschickt wird, "
"ob die Bestellung bezahlt oder freigegeben ist. Um zu vermeiden, dass diese "
"wichtige E-Mail nicht ankommt, können nur PDF-Dateien mit maximal {size} MB "
@@ -13389,7 +13389,7 @@ msgstr ""
msgid ""
"You cannot require specifying attendee names if you do not ask for them."
msgstr ""
"Du kannst die Angabe von Teilnehmer*innennamen nur erfordern, wenn auch nach "
"Du kannst die Angabe von Teilnehmernamen nur erfordern, wenn auch nach "
"Namen gefragt wird."
#: pretix/base/settings.py:4133
@@ -15063,11 +15063,11 @@ msgstr "Ticket-Downloads"
#: pretix/control/forms/event.py:1882
msgid "Your customers will be able to download their tickets in PDF format."
msgstr ""
"Die Teilnehmer*innen werden ihre Tickets im PDF-Format herunterladen können."
"Die Teilnehmer werden ihre Tickets im PDF-Format herunterladen können."
#: pretix/control/forms/event.py:1886
msgid "Require all attendees to fill in their names"
msgstr "Erfordere, dass alle Teilnehmer*innen ihre Namen ausfüllen"
msgstr "Erfordere, dass alle Teilnehmer ihre Namen ausfüllen"
#: pretix/control/forms/event.py:1887
msgid ""
@@ -15103,7 +15103,7 @@ msgid ""
"then import your bank statements to process the payments within pretix, or "
"mark them as paid manually."
msgstr ""
"Deine Teilnehmer*innen werden angewiesen, das Geld direkt auf dein Bankkonto "
"Deine Teilnehmer werden angewiesen, das Geld direkt auf dein Bankkonto "
"zu überweisen. Du kannst dann deinen Kontoauszug in pretix importieren, um "
"Zahlungen zuzuweisen, oder die Bestellungen manuell als bezahlt markieren."
@@ -15452,7 +15452,7 @@ msgstr "Check-in-Status"
#: pretix/control/forms/filter.py:2168
#: pretix/plugins/checkinlists/exporters.py:109
msgid "All attendees"
msgstr "Alle Teilnehmer*innen"
msgstr "Alle Teilnehmer"
#: pretix/control/forms/filter.py:2169
#: pretix/control/templates/pretixcontrol/checkin/index.html:183
@@ -16036,7 +16036,7 @@ msgid ""
"people over 65. This ticket includes access to all parts of the event, "
"except the VIP area."
msgstr ""
"z.B. Dieses reduzierte Ticket ist erhältlich für Vollzeitstudent*innen, "
"z.B. Dieses reduzierte Ticket ist erhältlich für Vollzeitstudenten, "
"Arbeitslose und Menschen über 65. Das Ticket enthält Zugang zu allen Teilen "
"der Veranstaltung außer des VIP-Bereiches."
@@ -17909,7 +17909,7 @@ msgstr "Eine individuelle E-Mail wurde verschickt."
#: pretix/control/logdisplay.py:554
msgid "A custom email has been sent to an attendee."
msgstr "Eine individuelle E-Mail wurde an eine Teilnehmer*in verschickt."
msgstr "Eine individuelle E-Mail wurde an eine Teilnehmer verschickt."
#: pretix/control/logdisplay.py:555
msgid ""
@@ -19625,7 +19625,7 @@ msgstr "Terminal-ID"
#: pretix/control/templates/pretixcontrol/boxoffice/payment.html:104
msgid "Card holder"
msgstr "Karteninhaber*in"
msgstr "Karteninhaber"
#: pretix/control/templates/pretixcontrol/boxoffice/payment.html:108
msgid "Card expiration"
@@ -19934,7 +19934,7 @@ msgstr "CSV"
#: pretix/control/templates/pretixcontrol/checkin/index.html:73
msgid "No attendee record was found."
msgstr "Keine passenden Teilnehmer*innen gefunden."
msgstr "Keine passenden Teilnehmer gefunden."
#: pretix/control/templates/pretixcontrol/checkin/index.html:91
#: pretix/control/templates/pretixcontrol/datasync/failed_jobs.html:19
@@ -21744,7 +21744,7 @@ msgid ""
"provide ways for your attendees to contact you:"
msgstr ""
"Wenn irgendetwas schiefgeht oder unklar ist, empfehlen wir, dass du deinen "
"Teilnehmer*innen die Möglichkeit gibst, dich zu benachrichtigen:"
"Teilnehmer die Möglichkeit gibst, dich zu benachrichtigen:"
#: pretix/control/templates/pretixcontrol/event/settings.html:21
msgid "Basics"
@@ -22720,7 +22720,7 @@ msgid ""
"Only purchases of such products will be considered \"attendees\" for most "
"statistical purposes or within some plugins."
msgstr ""
"Nur Käufe eines solchen Produkts werden als \"Teilnehmer*innen\" gewertet, "
"Nur Käufe eines solchen Produkts werden als \"Teilnehmer\" gewertet, "
"z.B. in Statistiken oder in Funktionen von Erweiterungen."
#: pretix/control/templates/pretixcontrol/item/create.html:39
@@ -22799,7 +22799,7 @@ msgid ""
"The system will not ask for a name or other attendee details. This only "
"affects system-provided fields, you can still add your own questions."
msgstr ""
"Das System wird nicht nach einem Namen oder anderen Teilnehmer*innen-Daten "
"Das System wird nicht nach einem Namen oder anderen Teilnehmer-Daten "
"fragen. Dies betrifft nur vom System bereitgestellte Felder, eigene Fragen "
"können trotzdem hinzugefügt werden."
@@ -23496,9 +23496,9 @@ msgid ""
"ticket. If you provide food, one example might be to ask your users about "
"dietary requirements."
msgstr ""
"Fragen erlauben deinen Besucher*innen, zusätzliche Informationen zu ihrem "
"Fragen erlauben deinen Besucher, zusätzliche Informationen zu ihrem "
"Ticket auszufüllen. Wenn deine Veranstaltung Verpflegung beinhaltet, "
"könntest du z.B. nach Allergien deiner Teilnehmer*innen fragen."
"könntest du z.B. nach Allergien deiner Teilnehmer fragen."
#: pretix/control/templates/pretixcontrol/items/questions.html:15
msgid "Create a new question"
@@ -23663,7 +23663,7 @@ msgstr ""
"Um deine Produkte verfügbar zu machen, musst du Kontingente anlegen. "
"Kontingente definieren, wie oft ein Produkt verkauft werden darf. Auf diese "
"Art kannst du konfigurieren, ob deine Veranstaltung unbegrenzt viele "
"Teilnehmer*innen aufnehmen kann oder ob die Anzahl begrenzt ist. Du kannst "
"Teilnehmer aufnehmen kann oder ob die Anzahl begrenzt ist. Du kannst "
"ein Produkt zu mehreren Kontingenten hinzufügen, um komplexere Anforderungen "
"abzubilden, z.B. wenn du die Gesamtzahl der Tickets begrenzen willst, aber "
"einen speziellen Ticket-Typ noch stärker begrenzen willst."
@@ -25133,7 +25133,7 @@ msgstr "Sonstige Datenexporte"
#: pretix/control/templates/pretixcontrol/orders/export.html:107
#: pretix/control/templates/pretixcontrol/organizers/export.html:107
msgid "Recommended for new users"
msgstr "Empfohlen für neue Benutzer*innen"
msgstr "Empfohlen für neue Benutzer"
#: pretix/control/templates/pretixcontrol/orders/export.html:120
#: pretix/control/templates/pretixcontrol/organizers/export.html:120
@@ -26090,7 +26090,7 @@ msgstr "Domains"
#: pretix/control/templates/pretixcontrol/organizers/edit.html:320
msgid "This dialog is intended for advanced users."
msgstr "Dieser Dialog ist für fortgeschrittene Anwender*innen gedacht."
msgstr "Dieser Dialog ist für fortgeschrittene Anwender gedacht."
#: pretix/control/templates/pretixcontrol/organizers/edit.html:321
msgid ""
@@ -28484,7 +28484,7 @@ msgid ""
"customers. This way, customers will not be able to discover the waiting list."
msgstr ""
"Entsprechend deiner Veranstaltungseinstellungen werden ausverkaufte Produkte "
"nicht angezeigt. Dies führt dazu, dass Kund*innen die Warteliste nicht "
"nicht angezeigt. Dies führt dazu, dass Kunden die Warteliste nicht "
"finden können."
#: pretix/control/templates/pretixcontrol/waitinglist/index.html:38
@@ -28794,11 +28794,11 @@ msgstr "Das ausgewählte List wurde gelöscht."
#: pretix/control/views/dashboards.py:115
msgid "Attendees (ordered)"
msgstr "Teilnehmende (bestellt)"
msgstr "Teilnehmer (bestellt)"
#: pretix/control/views/dashboards.py:125
msgid "Attendees (paid)"
msgstr "Teilnehmende (bezahlt)"
msgstr "Teilnehmer (bezahlt)"
#: pretix/control/views/dashboards.py:137
#, python-brace-format
@@ -30873,7 +30873,7 @@ msgstr "PDF-Sammlungen"
#: pretix/plugins/badges/exporters.py:431
msgid "Download all attendee badges as one large PDF for printing."
msgstr "Alle Teilnehmer*innen-Badges in einer großen PDF-Datei für den Druck."
msgstr "Alle Teilnehmer-Badges in einer großen PDF-Datei für den Druck."
#: pretix/plugins/badges/exporters.py:452
#: pretix/plugins/ticketoutputpdf/exporters.py:80
@@ -31152,7 +31152,7 @@ msgstr "Anderes Bankkonto"
#: pretix/plugins/banktransfer/payment.py:85
msgid "Name of account holder"
msgstr "Kontoinhaber*in"
msgstr "Kontoinhaber"
#: pretix/plugins/banktransfer/payment.py:87
msgid ""
@@ -31294,7 +31294,7 @@ msgstr "Bitte überweise den vollen Betrag auf das folgende Bankkonto:"
#: pretix/plugins/stripe/templates/pretixplugins/stripe/checkout_payment_confirm.html:32
#: pretix/plugins/stripe/templates/pretixplugins/stripe/checkout_payment_confirm.html:35
msgid "Account holder"
msgstr "Kontoinhaber*in"
msgstr "Kontoinhaber"
#: pretix/plugins/banktransfer/payment.py:304
#: pretix/plugins/banktransfer/templates/pretixplugins/banktransfer/checkout_payment_form.html:20
@@ -31890,7 +31890,7 @@ msgid ""
"Download a spreadsheet with all attendees that are included in a check-in "
"list."
msgstr ""
"Tabelle (Excel oder CSV) mit allen Teilnehmer*innen, die in einer Check-in-"
"Tabelle (Excel oder CSV) mit allen Teilnehmer, die in einer Check-in-"
"Liste zutrittsberechtigt sind."
#: pretix/plugins/checkinlists/exporters.py:500
@@ -33066,7 +33066,7 @@ msgstr "Geplante E-Mails"
#: pretix/plugins/sendmail/signals.py:122
msgid "Mass email was sent to customers or attendees."
msgstr "Rundmail wurde an Kunden oder Teilnehmer*innen verschickt."
msgstr "Rundmail wurde an Kunden oder Teilnehmer verschickt."
#: pretix/plugins/sendmail/signals.py:123
msgid "Mass email was sent to waiting list entries."
@@ -33098,7 +33098,7 @@ msgstr "Eine automatisierte E-Mail wurde an den Besteller verschickt"
#: pretix/plugins/sendmail/signals.py:142
msgid "A scheduled email was sent to a ticket holder"
msgstr "Eine automatisierte E-Mail wurde an eine Teilnehmer*in verschickt."
msgstr "Eine automatisierte E-Mail wurde an einen Teilnehmer verschickt."
#: pretix/plugins/sendmail/signals.py:143
msgid "An email rule was deleted"
@@ -33131,7 +33131,7 @@ msgstr "Alle nicht eingecheckten Kunden"
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/history_fragment_orders.html:23
msgid "Attendee contact addresses"
msgstr "Teilnehmer*innen-E-Mail-Adressen"
msgstr "Teilnehmer-E-Mail-Adressen"
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/history_fragment_orders.html:25
msgid "All contact addresses"
@@ -33292,14 +33292,14 @@ msgstr ""
#: pretix/plugins/sendmail/views.py:250
msgid "Orders or attendees"
msgstr "Bestellungen oder Teilnehmer*innen"
msgstr "Bestellungen oder Teilnehmer"
#: pretix/plugins/sendmail/views.py:251
msgid ""
"Send an email to every customer, or to every person a ticket has been "
"purchased for, or a combination of both."
msgstr ""
"Sende eine E-Mail an alle Ticketkäufer*innen, alle Ticketinhaber*innen oder "
"Sende eine E-Mail an alle Ticketkäufer, alle Ticketinhaber oder "
"eine Kombination aus beiden Gruppen."
#: pretix/plugins/sendmail/views.py:417
@@ -33871,23 +33871,23 @@ msgstr "SEPA-Lastschrift"
#: pretix/plugins/stripe/payment.py:1277
msgid "Account Holder Name"
msgstr "Kontoinhaber*in"
msgstr "Kontoinhaber"
#: pretix/plugins/stripe/payment.py:1282
msgid "Account Holder Street"
msgstr "Straße (Kontoinhaber*in)"
msgstr "Straße (Kontoinhaber)"
#: pretix/plugins/stripe/payment.py:1294
msgid "Account Holder Postal Code"
msgstr "PLZ (Kontoinhaber*in)"
msgstr "PLZ (Kontoinhaber)"
#: pretix/plugins/stripe/payment.py:1306
msgid "Account Holder City"
msgstr "Stadt (Kontoinhaber*in)"
msgstr "Stadt (Kontoinhaber)"
#: pretix/plugins/stripe/payment.py:1318
msgid "Account Holder Country"
msgstr "Land (Kontoinhaber*in)"
msgstr "Land (Kontoinhaber)"
#: pretix/plugins/stripe/payment.py:1362
msgid "Affirm via Stripe"
@@ -34336,7 +34336,7 @@ msgstr "MOTO"
#: pretix/plugins/stripe/templates/pretixplugins/stripe/control.html:64
#: pretix/plugins/stripe/templates/pretixplugins/stripe/control.html:67
msgid "Payer name"
msgstr "Name des Zahlenden"
msgstr "Name des Zahlers"
#: pretix/plugins/stripe/templates/pretixplugins/stripe/control.html:91
msgid "Payment receipt"
@@ -35726,7 +35726,7 @@ msgstr[1] "Das Ticket wurde %(count)s-mal eingelöst."
#: pretix/presale/templates/pretixpresale/event/fragment_cart.html:166
msgid "No attendee name provided"
msgstr "Name der teilnehmenden Person nicht angegeben"
msgstr "Name des Teilnehmenrs nicht angegeben"
#: pretix/presale/templates/pretixpresale/event/fragment_cart.html:219
msgid "The image you previously uploaded"
@@ -37486,12 +37486,12 @@ msgstr "Möchtest das folgende Profil wirklich aus deinem Kundenkonto löschen?"
#: pretix/presale/templates/pretixpresale/organizers/customer_profiles.html:11
#: pretix/presale/views/customer.py:386
msgid "Attendee profiles"
msgstr "Teilnehmer*innen-Adresse"
msgstr "Teilnehmer-Adresse"
#: pretix/presale/templates/pretixpresale/organizers/customer_profiles.html:37
msgid "You dont have any attendee profiles in your account yet."
msgstr ""
"In deinem Kundenkonto sind noch keine Teilnehmer*innen-Profile gespeichert."
"In deinem Kundenkonto sind noch keine Teilnehmer-Profile gespeichert."
#: pretix/presale/templates/pretixpresale/organizers/customer_registration.html:7
msgid "Registration"
@@ -39069,7 +39069,7 @@ msgstr "Kosovo"
#~ "This plugin allows you to generate badges or name tags for your attendees."
#~ msgstr ""
#~ "Diese Erweiterung erlaubt, Namensschilder oder Badges für die "
#~ "Teilnehmer*innen zu erstellen."
#~ "Teilnehmer zu erstellen."
#~ msgid "This plugin allows you to receive payments via PayPal"
#~ msgstr "Dieses Plugin erlaubt, Zahlungen über PayPal anzunehmen"
@@ -39647,7 +39647,7 @@ msgstr "Kosovo"
#~ msgstr "Biete Ticket-Download bereits vor Bezahlung einer Bestellung an"
#~ msgid "Attendee names"
#~ msgstr "Teilnehmer*innennamen"
#~ msgstr "Teilnehmername"
#~ msgid "Enable output"
#~ msgstr "Aktivieren"
@@ -39924,7 +39924,7 @@ msgstr "Kosovo"
#~ "If checked, users can cancel orders by themselves as long as they are not "
#~ "yet paid."
#~ msgstr ""
#~ "Wenn diese Option aktiviert ist, können Teilnehmer*innen selbstständig "
#~ "Wenn diese Option aktiviert ist, können Teilnehmer selbstständig "
#~ "Bestellungen stornieren solange sie nicht bezahlt wurden."
#~ msgid "Sales overview"
@@ -161,12 +161,12 @@ msgstr "Bezahlte Bestellungen"
#: pretix/plugins/statistics/static/pretixplugins/statistics/statistics.js:27
#: pretix/plugins/statistics/static/pretixplugins/statistics/statistics.js:39
msgid "Attendees (ordered)"
msgstr "Teilnehmende (bestellt)"
msgstr "Teilnehmer (bestellt)"
#: pretix/plugins/statistics/static/pretixplugins/statistics/statistics.js:27
#: pretix/plugins/statistics/static/pretixplugins/statistics/statistics.js:39
msgid "Attendees (paid)"
msgstr "Teilnehmende (bezahlt)"
msgstr "Teilnehmer (bezahlt)"
#: pretix/plugins/statistics/static/pretixplugins/statistics/statistics.js:51
msgid "Total revenue"
+9 -9
View File
@@ -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 += [
+78 -78
View File
@@ -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,
]
+13 -13
View File
@@ -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 = [
+2 -1
View File
@@ -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 = {}
+1 -1
View File
@@ -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 %}
+50
View File
@@ -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" />',
)