Knowing what file a string comes from is useful, but the line number is less
useful and changes a lot, causing very unreadable diffs of translation
files. I propose we drop them and only include the file names
We initially didn't do this for two reasons:
- Performance implications of calling the settings store for every event
that shows up in the calendar. As of d43e85da, we need that anyways.
- Performance implications of filtering in Python except SQL but... it
can't really be worse than not filtering at all.
- We don't easily know if it's valid for all events so we can't stop
rendering the unused calendar rows. That's an acceptable issue for
now, still better than nothing. We can always optimize later.
So we might as well implement it.
* use cookieretry only on presale event pages
* use csrfcookieretry only on event index page
* include static tag
* include csrfcookieretry in order.html as well
* Update src/pretix/static/pretixpresale/js/csrfcookieretry.js
Co-authored-by: Richard Schreiber <schreiber@pretix.eu>
---------
Co-authored-by: Richard Schreiber <schreiber@pretix.eu>
* Fix EPC QR beneficiary escaping
* Fix EPC QR script encoding
Keep EPC QR helper output as a plain string and serialize payment QR payloads as JSON script data before the QR replacement JavaScript parses them. This preserves apostrophes without relying on mark_safe in the helper.
Assisted-by: OpenAI GPT-5 <noreply@openai.com>
* "type safety"
---------
Co-authored-by: Puneet Dixit <236133619+puneetdixit200@users.noreply.github.com>
Co-authored-by: Raphael Michel <michel@rami.io>
* Add Docker secrets support in config
* ruff format
* Remove gracefully fallback exception handling
* Add support for loading secret fallbacks from environment file
* Improve SSO one-time token error message
Replace the generic 'invalid one-time token' message shown after failed
SSO login attempts with a clearer, user-facing explanation of what went
wrong and how to recover.
* Remove unneeded classes from headings
Co-authored-by: Raphael Michel <mail@raphaelmichel.de>
* Apply suggestions from code review
Added `trimmed` to translation blocks
Co-authored-by: Raphael Michel <mail@raphaelmichel.de>
* Reword SSO error template for improved translation clarity
---------
Co-authored-by: Raphael Michel <mail@raphaelmichel.de>
* Add event placeholder support to more fields
I found it useful to be able to use the `{event}` placeholder in
some fields such as the "End of presale text" because I don't need
to fix the texts creating a new event by cloning another event.
I made placeholders available to the other fields as well (where not too
difficult). Specifically `presale_has_ended_text`, `voucher_explanation_text`
`banner_text`, `banner_text_bottom` and `event_info_text`.
In addition, I grouped them under a new `texts` variable in the `context`
(including `frontpage_text` which was part of the root `context` previously).
* change compute location
---------
Co-authored-by: Raphael Michel <michel@rami.io>
* Improve subject lines for admin-facing emails
A few of the current subjects are ambiguous about the expected
action, and some omit context that would help in an inbox preview
(which event, which address). The rewrites bring them closer to
common conventions in modern transactional email (verb-led,
recipient-addressed, with recipient-meaningful variables). Two
themes:
- Action-required emails lead with the action verb. "Reset your
password", "Confirm event cancellation and bulk refund", and
"Confirm <address> as a sender address" tell the recipient up
front what's expected, where "Password recovery", "Bulk-refund
confirmation" and "Sender address verification" did not.
- Surface the relevant variable when the email is about something
specific. "Data shredding completed for <event>" is more useful
than the generic version when an admin manages several events.
"You've been invited to join <organizer>" names the inviting
organizer. "Confirm <address> as a sender address" names the
address.
The remaining rewrites are lighter rewordings. "New sign-in to
your account" replaces "Login from new source detected" because
"source" is jargon a non-technical recipient wouldn't recognise.
"Changes to your account" replaces "Account information changed"
because the possessive frames the email as being about the
recipient's own account.
Also fixes a hardcoded "pretix" in the confirmation-code subject.
* Standardise admin email sign-offs as "Thanks, The <instance> Team"
The current sign-offs ("Best regards, Your <instance> team") have
a formal tone. A review of the last ~20 transactional emails in
my inbox showed most senders use something friendlier:
- Thanks: Deliveroo, Starling Bank, GitHub, Cloudflare
- Thank you: AWS
- Sincerely: Google Workspace
A small minority (e.g., Sentry) had no sign-off at all. "Thanks"
was the most common, and among that group "The <instance> Team"
was the consistent phrasing rather than "Your <instance> team".
Two templates (cancel_confirm, export_failed) didn't have a
sign-off; they now get one for consistency. Notification emails
are deliberately excluded: they're system alerts rather than
direct correspondence.
* Add anti-phishing notice to admin emails containing confirmation codes
Three admin emails send the recipient a confirmation code to
enter back into a form: confirmation_code, email_setup, and
cancel_confirm. Only confirmation_code had an anti-phishing
warning, and its wording was awkward ("Please do never give this
code to another person. Our support team will never ask for this
code.").
This commit standardises the warning across all three:
> Don't share this code with anyone. The <instance> team will
> never ask you for it.
* Add structured details to login-notice email
The single-sentence body ("The login was performed using <agent>
on <os> from <country>.") is replaced with a labelled bullet list:
Time, Browser, Operating system, Device, Country.
Time and Device are new fields. Device is omitted when ua-parser
can't identify the device, Country when GeoIP isn't available,
so the user only sees fields with real values.
* Restructure notification.txt for clearer layout
- Attributes: bullet list instead of paragraph-per-attribute.
- Actions: label gets a colon, URL on its own paragraph (was
4-space-indented code block).
- Footer: separated by --- and bulleted (manage / disable
links). "Click here X" phrasing dropped (incidentally moots
a missing-"to" typo).
- Minor whitespace fix: detail-block endif now matches the
placement of the rest of the template.
notification.html's footer text is also updated, only to match
the new .txt wording (link labels and intro line). No
structural changes to the HTML template.
* Improve confirmation-code email reason strings
- Drop the redundant "to confirm" opener.
- Replace hardcoded "your pretix account" in email_verify
with "{instance}".
* Polish admin email body copy
A small wording and formatting pass on the admin email bodies,
in three loosely-grouped themes:
1. Sentence case for body text (previously lowercase after
"Hello,"), matching standard English convention.
2. Light restructuring where helpful: bullet lists for sets
of labelled facts; 4-space-indented code blocks for codes
the recipient is meant to type back.
3. Phrasing polish. Some sentences tightened or shortened.
Largely matters of taste, but generally read smoother.
---------
Co-authored-by: Raphael Michel <michel@pretix.eu>
During our [2026-06-27 incident](https://pretix.eu/about/en/blog/20260630-pretix-hosted-outage/),
we noticed that pretix is using redis at import time. This means that
gunicorn and celery process were unable to start on servers who could
currently not reach redis. This is kinda mitigated through auto-restart
on systemd or docker level, but that's not really how it is supposed to
work. Celery even has smart retry/reconnect logic that becomes pointless
this way.
This is a follow-up for #6318, basically the same problem but in a
neighboring query.
Performance comparison for real-world event with 13k subevents:
In [15]: %time get_grouped_items(e, subevent=se, channel=e.organizer.sales_channels.get(identifier="web"))
CPU times: user 49.1 ms, sys: 3.79 ms, total: 52.9 ms
Wall time: 1.12 s
Out[15]: ([<Item: xxx>], True
In [16]: %time get_grouped_items_patched(e, subevent=se, channel=e.organizer.sales_channels.get(identifier="web"))
CPU times: user 30.2 ms, sys: 445 μs, total: 30.6 ms
Wall time: 45.3 ms
Out[16]: ([<Item: xxx>], True)
* Add trace IDs from request to celery task
Celery tasks will log the request ID they were triggered from:
[2026-07-02 10:33:17,614: INFO/MainProcess] Task pretix.base.services.orders.cancel_order[5f3104b3-0a54-4e49-921e-c866c4dc4c6d] received
[2026-07-02 10:33:17,614: INFO/MainProcess] Task 5f3104b3-0a54-4e49-921e-c866c4dc4c6d has trace 4d389638-00ab-4c4d-bdec-d73ac322bf44
Nested celery tasks will then contain both the request ID as well as the previous tasks:
[2026-07-02 10:33:18,354: INFO/MainProcess] Task pretix.base.services.notifications.notify[d52a3a49-9c89-4f67-bdde-9f773586fc07] received
[2026-07-02 10:33:18,354: INFO/MainProcess] Task d52a3a49-9c89-4f67-bdde-9f773586fc07 has trace 4d389638-00ab-4c4d-bdec-d73ac322bf44 5f3104b3-0a54-4e49-921e-c866c4dc4c6d
* Apply suggestion from @luelista
Co-authored-by: luelista <weller@rami.io>
---------
Co-authored-by: luelista <weller@rami.io>
* Drop nullability on Order.organizer and OrderPosition.organizer
* Rebase migration and add autoclean
* Utilize new relationship for scopes
* Declare reverse noop
* Update src/pretix/base/migrations/0302_resolve_duplicate_codes_and_secrets.py
Co-authored-by: Martin Gross <gross@rami.io>
* Update src/pretix/base/migrations/0302_resolve_duplicate_codes_and_secrets.py
Co-authored-by: Martin Gross <gross@rami.io>
---------
Co-authored-by: Martin Gross <gross@rami.io>
As a security precaution, we change the contract of some signals such that a
SafeString needs to be returned if HTML should be rendered without further
escaping.
Before, the `{% signal ... %}` and `{% eventsignal ... %}` template tags
called mark_safe themselves on all strings returned from signals. That could
lead to unsafe coding practices, where untrusted values are interpolated into
HTML format strings. However, such interpolations should usually be performed
using helpers such Django's format_html, which automatically escapes inputs
and returns a SafeString.
Now, we call conditional_escape on signal results, so that any HTML not explicitly
marked as safe gets escaped.
Most plugins are not affected by this change as they return a SafeString as a
result of Template.render already.
* Fix customer views using wrong csrf middleware
This lead to persistent csrf validation errors if the token from the cookie expired, which could only be solved by clearing cookies.
* Remove unneccesary csrf_protect decorators
* Fix typo
Co-authored-by: Raphael Michel <michel@pretix.eu>
---------
Co-authored-by: Raphael Michel <michel@pretix.eu>
* Add proper CSP policy for HTML email preview
* Handle safe types centrally
* Precompile schema validation function to get rid of ajv2020
* Fix tests
* adjust more tests
* Fix crash in rebooking if addon has hidden_if_item_available
* Move get_grouped_items into its own file and rename to prepare_item_list_for_shop
(but keep a reference under original name for plugin compatibility)
* Make Item[Variation].unavailability_reason helper functions next to get_grouped_items
* Use current_unavailability_reason attribute instead of unavailability_reason()
* Add test case for:
Order has product with addon 1; Addon 1 is hidden if Addon 2 is available; When order is changed, Addon 1 should not be removable
* Fix special case if addon product is now set to require a voucher (vouchers for addons are not supported)
---------
Co-authored-by: Kara Engelhardt <engelhardt@pretix.eu>
This changes the API of pretix.control.signals.event_dashboard_widgets and
pretix.control.signals.user_dashboard_widgets, such that the "content" key of
returned dictionaries must contain a SafeString if HTML therein is to be
rendered.
After enforcing this on application level in commit 4c37351, we can now also add
a unique constraint on DB level and a migration to fix any left over non-unique entries.
---------
Co-authored-by: Raphael Michel <mail@raphaelmichel.de>
* Event-dashboard: Lazy-load warnings
I don't like this, it's not pretty, but we have no quick way of fixing
the overpaid orders check otherwise and a slow query on the event
dashboard feels really bad, because even if you don't wanna see the
dashboard ou are always going through it in navigation and that can get
very annoying very quickly.
* Update src/pretix/control/templates/pretixcontrol/event/dashboard_partial_warnings.html
Co-authored-by: pajowu <engelhardt@pretix.eu>
---------
Co-authored-by: pajowu <engelhardt@pretix.eu>
* rders: Fix N+1 query issues on order detail and change page
There is one I couldn't fix: Loading the list of tax rules for every
select box on the OrderChange page. Unfortunately, Django has a
cache-breaking .all() in ModelChoiceField and that would need nasty
patching that didn't feel worth it
* Fix isort
* Fix N+1 query in gate call
* Fix leftovers
* Add local cache for objects referenced in log entries
* Update src/pretix/control/views/orders.py
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
* Add event placeholder support to more fields I found it useful to be able to use the `{event}` placeholder in some fields such as the "End of presale text" because I don't need to fix the texts creating a new event by cloning another event. I made placeholders available to the other fields as well (where not too difficult). Specifically `presale_has_ended_text`, `voucher_explanation_text` `banner_text`, `banner_text_bottom` and `event_info_text`. In addition, I grouped them under a new `texts` variable in the `context` (including `frontpage_text` which was part of the root `context` previously). * change compute location --------- Co-authored-by: Raphael Michel <michel@rami.io>* Improve subject lines for admin-facing emails A few of the current subjects are ambiguous about the expected action, and some omit context that would help in an inbox preview (which event, which address). The rewrites bring them closer to common conventions in modern transactional email (verb-led, recipient-addressed, with recipient-meaningful variables). Two themes: - Action-required emails lead with the action verb. "Reset your password", "Confirm event cancellation and bulk refund", and "Confirm <address> as a sender address" tell the recipient up front what's expected, where "Password recovery", "Bulk-refund confirmation" and "Sender address verification" did not. - Surface the relevant variable when the email is about something specific. "Data shredding completed for <event>" is more useful than the generic version when an admin manages several events. "You've been invited to join <organizer>" names the inviting organizer. "Confirm <address> as a sender address" names the address. The remaining rewrites are lighter rewordings. "New sign-in to your account" replaces "Login from new source detected" because "source" is jargon a non-technical recipient wouldn't recognise. "Changes to your account" replaces "Account information changed" because the possessive frames the email as being about the recipient's own account. Also fixes a hardcoded "pretix" in the confirmation-code subject. * Standardise admin email sign-offs as "Thanks, The <instance> Team" The current sign-offs ("Best regards, Your <instance> team") have a formal tone. A review of the last ~20 transactional emails in my inbox showed most senders use something friendlier: - Thanks: Deliveroo, Starling Bank, GitHub, Cloudflare - Thank you: AWS - Sincerely: Google Workspace A small minority (e.g., Sentry) had no sign-off at all. "Thanks" was the most common, and among that group "The <instance> Team" was the consistent phrasing rather than "Your <instance> team". Two templates (cancel_confirm, export_failed) didn't have a sign-off; they now get one for consistency. Notification emails are deliberately excluded: they're system alerts rather than direct correspondence. * Add anti-phishing notice to admin emails containing confirmation codes Three admin emails send the recipient a confirmation code to enter back into a form: confirmation_code, email_setup, and cancel_confirm. Only confirmation_code had an anti-phishing warning, and its wording was awkward ("Please do never give this code to another person. Our support team will never ask for this code."). This commit standardises the warning across all three: > Don't share this code with anyone. The <instance> team will > never ask you for it. * Add structured details to login-notice email The single-sentence body ("The login was performed using <agent> on <os> from <country>.") is replaced with a labelled bullet list: Time, Browser, Operating system, Device, Country. Time and Device are new fields. Device is omitted when ua-parser can't identify the device, Country when GeoIP isn't available, so the user only sees fields with real values. * Restructure notification.txt for clearer layout - Attributes: bullet list instead of paragraph-per-attribute. - Actions: label gets a colon, URL on its own paragraph (was 4-space-indented code block). - Footer: separated by --- and bulleted (manage / disable links). "Click here X" phrasing dropped (incidentally moots a missing-"to" typo). - Minor whitespace fix: detail-block endif now matches the placement of the rest of the template. notification.html's footer text is also updated, only to match the new .txt wording (link labels and intro line). No structural changes to the HTML template. * Improve confirmation-code email reason strings - Drop the redundant "to confirm" opener. - Replace hardcoded "your pretix account" in email_verify with "{instance}". * Polish admin email body copy A small wording and formatting pass on the admin email bodies, in three loosely-grouped themes: 1. Sentence case for body text (previously lowercase after "Hello,"), matching standard English convention. 2. Light restructuring where helpful: bullet lists for sets of labelled facts; 4-space-indented code blocks for codes the recipient is meant to type back. 3. Phrasing polish. Some sentences tightened or shortened. Largely matters of taste, but generally read smoother. --------- Co-authored-by: Raphael Michel <michel@pretix.eu>* Add trace IDs from request to celery task Celery tasks will log the request ID they were triggered from: [2026-07-02 10:33:17,614: INFO/MainProcess] Task pretix.base.services.orders.cancel_order[5f3104b3-0a54-4e49-921e-c866c4dc4c6d] received [2026-07-02 10:33:17,614: INFO/MainProcess] Task 5f3104b3-0a54-4e49-921e-c866c4dc4c6d has trace 4d389638-00ab-4c4d-bdec-d73ac322bf44 Nested celery tasks will then contain both the request ID as well as the previous tasks: [2026-07-02 10:33:18,354: INFO/MainProcess] Task pretix.base.services.notifications.notify[d52a3a49-9c89-4f67-bdde-9f773586fc07] received [2026-07-02 10:33:18,354: INFO/MainProcess] Task d52a3a49-9c89-4f67-bdde-9f773586fc07 has trace 4d389638-00ab-4c4d-bdec-d73ac322bf44 5f3104b3-0a54-4e49-921e-c866c4dc4c6d * Apply suggestion from @luelista Co-authored-by: luelista <weller@rami.io> --------- Co-authored-by: luelista <weller@rami.io>As a security precaution, we change the contract of some signals such that a SafeString needs to be returned if HTML should be rendered without further escaping. Before, the `{% signal ... %}` and `{% eventsignal ... %}` template tags called mark_safe themselves on all strings returned from signals. That could lead to unsafe coding practices, where untrusted values are interpolated into HTML format strings. However, such interpolations should usually be performed using helpers such Django's format_html, which automatically escapes inputs and returns a SafeString. Now, we call conditional_escape on signal results, so that any HTML not explicitly marked as safe gets escaped. Most plugins are not affected by this change as they return a SafeString as a result of Template.render already.