Compare commits

...

907 Commits

Author SHA1 Message Date
Richard Schreiber
d97648802e remove attestation 2022-03-25 12:05:24 +01:00
Raphael Michel
645a7489df Add missing log display message for waiting list removal 2022-03-24 21:51:29 +01:00
Raphael Michel
dff4bf2841 Docs: Pin jinja version 2022-03-24 17:40:42 +01:00
Raphael Michel
fc1e2ca3b0 Order search: Fix duplicate query part 2022-03-24 17:22:32 +01:00
Raphael Michel
2a0748a008 Questions step: Only prefill customer name for first ticket (#2555) 2022-03-24 17:04:23 +01:00
Raphael Michel
41d3f39fc7 Add pretixPOS endpoint to device security profile 2022-03-24 11:56:59 +01:00
Michał Rokita
8e481905e2 Translations: Update Polish
Currently translated at 12.1% (563 of 4636 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/pl/

powered by weblate
2022-03-24 11:55:53 +01:00
Michał Rokita
552e130fed Translations: Update Polish
Currently translated at 11.9% (556 of 4636 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/pl/

powered by weblate
2022-03-24 11:55:53 +01:00
Ismael Menéndez Fernández
f8b879c2e6 Translations: Update Galician
Currently translated at 11.3% (529 of 4648 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/gl/

powered by weblate
2022-03-24 11:55:53 +01:00
Raphael Michel
9e5403333c Add JSONField for device metadata (#2550) 2022-03-24 11:55:44 +01:00
Raphael Michel
2486c3d205 Show refund amount in refund choice screen 2022-03-23 09:15:10 +01:00
Raphael Michel
ee9bf25ae1 Bank transfer: Format IBAN in email 2022-03-23 09:08:10 +01:00
Martin Gross
c4d3c48837 Fix voucher redemption page markup #2 (Amend to d97b7b4fb6) 2022-03-22 14:17:27 +01:00
Raphael Michel
d97b7b4fb6 Fix voucher redemption page markup (#2548) 2022-03-22 10:41:36 +01:00
Raphael Michel
fd83e91174 Translations: Update German (informal) (de_Informal)
Currently translated at 100.0% (4636 of 4636 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/de_Informal/

powered by weblate
2022-03-21 18:19:46 +01:00
Raphael Michel
6e3b62696e Translations: Update German
Currently translated at 100.0% (4636 of 4636 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/de/

powered by weblate
2022-03-21 18:19:46 +01:00
Raphael Michel
a0b8fedaa7 Translations: Update German (informal) (de_Informal)
Currently translated at 100.0% (4636 of 4636 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/de_Informal/

powered by weblate
2022-03-21 18:19:46 +01:00
Raphael Michel
a4961d9dfb Translations: Update German
Currently translated at 100.0% (4636 of 4636 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/de/

powered by weblate
2022-03-21 18:19:46 +01:00
Raphael Michel
889f972768 Translations: Update German (informal) (de_Informal)
Currently translated at 100.0% (4636 of 4636 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/de_Informal/

powered by weblate
2022-03-21 18:19:46 +01:00
Raphael Michel
478ab633e0 Translations: Update German
Currently translated at 100.0% (4636 of 4636 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/de/

powered by weblate
2022-03-21 18:19:46 +01:00
Ismael Menéndez Fernández
0e4047018e Translations: Update Galician
Currently translated at 11.3% (528 of 4633 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/gl/

powered by weblate
2022-03-21 18:19:46 +01:00
Anna-itk
cbeaffa415 Translations: Update Danish
Currently translated at 35.6% (1653 of 4633 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/da/

powered by weblate
2022-03-21 18:19:46 +01:00
Raphael Michel
09972b62c9 Add more words to spell-check list 2022-03-21 18:14:47 +01:00
Raphael Michel
3b2eec659d Update potypo config 2022-03-21 18:09:28 +01:00
Raphael Michel
4e6c61316c Update German spellcheck word list 2022-03-21 18:00:19 +01:00
Raphael Michel
c308939f3c Add customizations to word list 2022-03-21 17:12:58 +01:00
Raphael Michel
41604ebdd9 Update po files
[CI skip]

Signed-off-by: Raphael Michel <michel@rami.io>
2022-03-21 16:59:35 +01:00
Raphael Michel
e2adf1fdb3 Layout refresh for plugin settings page (#2490)
Co-authored-by: Richard Schreiber <schreiber@rami.io>
2022-03-21 16:42:03 +01:00
Richard Schreiber
90d9ddebb5 Fix #2542 -- Validation of exporter forms (#2544) 2022-03-21 12:37:24 +01:00
Raphael Michel
684b8e4102 Accept arabic numbers from date picker (#2547) 2022-03-21 12:36:40 +01:00
Raphael Michel
a83e963ea5 Variation editor: Call form_handlers() when adding new form 2022-03-21 11:47:47 +01:00
Raphael Michel
c9392236f5 Datetime picker: Fix left/right arrows for rtl languages 2022-03-21 10:01:04 +01:00
Raphael Michel
57d68eaddb Update po files
[CI skip]

Signed-off-by: Raphael Michel <michel@rami.io>
2022-03-18 14:33:02 +01:00
Raphael Michel
ab1e063a93 Fix plural in button label (Z#2396370) 2022-03-18 14:32:20 +01:00
Ismael Menéndez Fernández
4b76dcedc4 Translations: Update Galician
Currently translated at 11.3% (523 of 4624 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/gl/

powered by weblate
2022-03-18 14:31:48 +01:00
Maico Timmerman
55752a4319 Event cancellation: Allow allow creating manual refunds for all orders (#2526) 2022-03-18 14:05:32 +01:00
Augustin Cavalier
5bde98e349 PayPal: Fail early on invalid webhook payload (#2528)
Co-authored-by: Raphael Michel <michel@rami.io>
2022-03-17 16:50:58 +01:00
Raphael Michel
1b2ca87f2d PDF editor: Check PDF file for readability on upload 2022-03-17 16:43:30 +01:00
Raphael Michel
8562c2f103 PDF editor: Limit file upload selector to PDF files 2022-03-17 16:43:30 +01:00
Raphael Michel
ea0283266f PDF editor: Fix invalid data type breaking preview 2022-03-17 16:43:30 +01:00
Raphael Michel
cc912b9ea7 PDF editor: Support for Ctrl+A 2022-03-17 16:43:30 +01:00
Raphael Michel
ebae275a2d PDF editor: Allow multi-lingual text 2022-03-17 16:43:30 +01:00
Raphael Michel
02f8bcbe23 PDF editor: Allow combinations of placeholders 2022-03-17 16:43:30 +01:00
Raphael Michel
4ae7cc9f50 PDF editor: Allow arbitrary fields in QR codes 2022-03-17 16:43:30 +01:00
Raphael Michel
c2f2e157d7 PDF editor: Add explanatory tooltip for dynamic image 2022-03-17 16:43:30 +01:00
Raphael Michel
ebf0320c2c PDF editor: Add multi-page support 2022-03-17 16:43:30 +01:00
Raphael Michel
b05fa89010 PDF editor: Allow button to download current background 2022-03-17 16:43:30 +01:00
Raphael Michel
ab0f76c7bb Revert "PDF editor improvements (#2529)"
This reverts commit e4c1f30b9d.
2022-03-17 16:43:18 +01:00
Raphael Michel
e4c1f30b9d PDF editor improvements (#2529) 2022-03-17 16:42:48 +01:00
Raphael Michel
9ffeafa6a5 Do not crash email layout preview with unknown placeholders 2022-03-17 09:41:15 +01:00
Richard Schreiber
cb7e014966 Fix #2438 - Parse dates in format YYYY-MM with isoparse (#2537)
Co-authored-by: Raphael Michel <michel@rami.io>
2022-03-16 14:15:56 +01:00
Yuriko Matsunami
7b7a8e655e Translated on translate.pretix.eu (Japanese)
Currently translated at 100.0% (172 of 172 strings)

Translation: pretix/pretix (JavaScript parts)
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/ja/

powered by weblate
2022-03-16 13:52:42 +01:00
Yuriko Matsunami
ac46172f6b Translations: Update Japanese
Currently translated at 3.1% (147 of 4624 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/ja/

powered by weblate
2022-03-16 13:52:42 +01:00
Raphael Michel
31eda01464 Fix error in SMTP settings when not changing password 2022-03-15 16:06:11 +01:00
Raphael Michel
0adddb3084 Widget: Set allow and referrerpolicy on iframe 2022-03-15 12:26:41 +01:00
Raphael Michel
0b91f9da31 Docs: Update example in lead scanning API 2022-03-15 11:02:37 +01:00
Raphael Michel
0a7ba606c6 Add docs on lead scanning API (#2533) 2022-03-15 10:55:52 +01:00
Raphael Michel
959b076b52 Unicode-aware sorting of phone prefixes (#2523) 2022-03-14 15:24:59 +01:00
Ismael Menéndez Fernández
06b0ef906a Translations: Update Galician
Currently translated at 11.2% (522 of 4624 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/gl/

powered by weblate
2022-03-14 15:07:10 +01:00
Emanuele Signoretta
4f1607040e Translations: Update Italian
Currently translated at 17.5% (812 of 4624 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/it/

powered by weblate
2022-03-14 15:07:10 +01:00
Ismael Menéndez Fernández
38a3fa23ed Translations: Update Galician
Currently translated at 11.2% (520 of 4624 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/gl/

powered by weblate
2022-03-14 15:07:10 +01:00
Yuriko Matsunami
baae16d9e7 Translated on translate.pretix.eu (Japanese)
Currently translated at 100.0% (172 of 172 strings)

Translation: pretix/pretix (JavaScript parts)
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/ja/

powered by weblate
2022-03-14 15:07:10 +01:00
Yuriko Matsunami
e2d9cc10e8 Translations: Update Japanese
Currently translated at 1.0% (49 of 4624 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/ja/

powered by weblate
2022-03-14 15:07:10 +01:00
DJG Bayern
f0f8bbb2b0 Translations: Update Japanese
Currently translated at 1.0% (49 of 4624 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/ja/

powered by weblate
2022-03-14 15:07:10 +01:00
DJG Bayern
8d57544ba3 Translations: Update Japanese
Currently translated at 0.7% (36 of 4624 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/ja/

powered by weblate
2022-03-14 15:07:10 +01:00
Yuriko Matsunami
065533ee4d Translations: Update Japanese
Currently translated at 0.7% (36 of 4624 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/ja/

powered by weblate
2022-03-14 15:07:10 +01:00
Raphael Michel
fb2ad2711f Translations: Add Ukrainian 2022-03-14 15:07:10 +01:00
Anna-itk
52004e5c46 Translations: Update Danish
Currently translated at 34.8% (1610 of 4624 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/da/

powered by weblate
2022-03-14 15:07:10 +01:00
Raphael Michel
9fcd099531 Fix phone number parsing of initial values 2022-03-14 10:30:03 +01:00
Raphael Michel
d8cf3552ba Do not allow self-service cancel of pending orders if they have any payments 2022-03-10 17:02:11 +01:00
Raphael Michel
cc04f66a48 Prevent broken email placeholders from stopping emails (#2525) 2022-03-10 14:06:57 +01:00
Martin Gross
86d28b3f21 Control Dashboard: Fix filter link to past events 2022-03-09 10:11:47 +01:00
Raphael Michel
07b401b7af Add docs on API of exhibitor plugin, remove deprecated docs (#2520) 2022-03-08 16:04:18 +01:00
Raphael Michel
8ff4f778bf Bump django-hierarkey to 1.1.* 2022-03-07 10:27:06 +01:00
Raphael Michel
5174d38017 Do not delete TicketLayout backgrounds long as they're referenced somewhere 2022-03-07 09:45:50 +01:00
Raphael Michel
5681ea121d API: Fix organizer exports with OAuth tokens (PRETIXEU-663) 2022-03-04 10:32:17 +01:00
Raphael Michel
4e2a3b45da Fix order split with add-ons involved (#2513) 2022-03-03 16:54:20 +01:00
Ismael Menéndez Fernández
d796c399a6 Translations: Update Galician
Currently translated at 11.0% (510 of 4624 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/gl/

powered by weblate
2022-03-03 16:43:45 +01:00
MaLund13
bee5b5f1af Translated on translate.pretix.eu (Swedish)
Currently translated at 100.0% (172 of 172 strings)

Translation: pretix/pretix (JavaScript parts)
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/sv/

powered by weblate
2022-03-03 16:43:45 +01:00
MaLund13
028be3a5c3 Translations: Update Swedish
Currently translated at 21.8% (1012 of 4624 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/sv/

powered by weblate
2022-03-03 16:43:45 +01:00
Ismael Menéndez Fernández
990fd9a40c Translations: Update Galician
Currently translated at 10.7% (498 of 4624 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/gl/

powered by weblate
2022-03-03 16:43:45 +01:00
dependabot[bot]
746c9af531 Bump @rollup/plugin-babel from 5.3.0 to 5.3.1 in /src/pretix/static/npm_dir (#2506)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-03-03 15:44:35 +01:00
dependabot[bot]
47208afcec Bump @babel/core from 7.17.2 to 7.17.5 in /src/pretix/static/npm_dir (#2505)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-03-03 15:44:30 +01:00
Henry Brink
b215a0bd04 Add the .mta file extension for Volksbank's MT940 (#2512)
Co-authored-by: Raphael Michel <mail@raphaelmichel.de>
2022-03-03 15:44:23 +01:00
dependabot[bot]
6c94b62bb1 Bump rollup from 2.66.1 to 2.68.0 in /src/pretix/static/npm_dir (#2504)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-03-03 15:44:13 +01:00
Richard Schreiber
d7a32d985b Fix -- Custom favicon not showing correctly in Safari (#Z181607) (#2511)
* remove mask-icon as it basically unused nowadays

* remove 194x194-size

favicon in 194x194 is a hack for firefox only useful when chrome-android-icon in 192x192 is specifically created for android with a drop-shadow. This is not the case with pretix-logo or even custom logos. So we can safely drop the 194x194 size.

Co-authored-by: Raphael Michel <michel@rami.io>
2022-03-03 15:36:09 +01:00
Raphael Michel
fac3e7f7bd Show comment on denied order in visible log 2022-03-03 14:44:59 +01:00
Raphael Michel
8e8dce7ccf Silence another deprecation warning 2022-03-03 12:53:47 +01:00
Raphael Michel
465e11c765 Silence some deprecation warnings in dependencies on Python 3.10 2022-03-03 12:50:12 +01:00
Raphael Michel
7e6c512a76 Fix isort issue 2022-03-03 11:05:51 +01:00
Martin Gross
5d0b8c5084 Restricted Plugins: Do not require a restricted plugin to be visible. 2022-03-01 17:27:53 +01:00
Raphael Michel
6008e3d11c API: Fix organizer-level export with OAuth token (Z#2395272) 2022-03-01 16:53:48 +01:00
Raphael Michel
aeeebc0b4b Add event list exporter 2022-03-01 16:11:00 +01:00
Raphael Michel
a568a37f4c Fix #2496 -- Do not call installed() when cloning events (#2497) 2022-03-01 15:49:26 +01:00
Raphael Michel
b74f5508b7 Group identical add-ons in cart (#2500)
Co-authored-by: Richard Schreiber <schreiber@rami.io>
2022-03-01 15:49:16 +01:00
Raphael Michel
a9b0651345 Add convenience redirect from /control/events/-/-/feature to real event (#2501) 2022-03-01 15:48:25 +01:00
Emanuele Signoretta
c126445fe0 Translated on translate.pretix.eu (Italian)
Currently translated at 100.0% (172 of 172 strings)

Translation: pretix/pretix (JavaScript parts)
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/it/

powered by weblate
2022-03-01 15:47:52 +01:00
Raphael Michel
f8b8d92f2c Add missing license header 2022-02-28 16:15:53 +01:00
Raphael Michel
a66fdc5084 [SECURITY] Fix stored XSS in help texts 2022-02-28 16:06:47 +01:00
Raphael Michel
6d6883b343 [SECURITY] Fix stored XSS in question errors 2022-02-28 16:06:47 +01:00
Raphael Michel
482968175b [SECURITY] Prevent untrusted values from creating Excel formulas 2022-02-28 16:06:47 +01:00
Raphael Michel
5d6302d5fd Pin django-countries to 7.2 2022-02-28 13:06:22 +01:00
Raphael Michel
92fbe76327 Fix anonymizing users by setting their auth_backend 2022-02-28 12:07:24 +01:00
Raphael Michel
2fd2462ef1 Voucher bulk creation: Fix validation for duplicate in input 2022-02-25 18:12:28 +01:00
Raphael Michel
19881dff44 Bump to 4.8.0.dev0 2022-02-25 12:32:00 +01:00
Raphael Michel
798fdbf25b Bump to 4.7.0 2022-02-25 12:31:44 +01:00
Raphael Michel
1718a537e6 Translations: Update German (informal) (de_Informal)
Currently translated at 100.0% (4624 of 4624 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/de_Informal/

powered by weblate
2022-02-25 12:30:40 +01:00
Raphael Michel
b4d8936b78 Translations: Update German
Currently translated at 100.0% (4624 of 4624 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/de/

powered by weblate
2022-02-25 12:30:40 +01:00
Raphael Michel
683bc3f6dc Translations: Update German (informal) (de_Informal)
Currently translated at 100.0% (4624 of 4624 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/de_Informal/

powered by weblate
2022-02-25 12:30:40 +01:00
Raphael Michel
b79c95f334 Translations: Update German
Currently translated at 100.0% (4624 of 4624 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/de/

powered by weblate
2022-02-25 12:30:40 +01:00
Raphael Michel
7821ba09ec Fix #2476 -- Document resilient setup with docker and redis 2022-02-25 11:54:57 +01:00
Raphael Michel
af2600fd52 Docs: Fix reference to UserManager 2022-02-25 11:53:05 +01:00
Raphael Michel
058282a583 Update po files
[CI skip]

Signed-off-by: Raphael Michel <michel@rami.io>
2022-02-25 11:05:55 +01:00
Raphael Michel
16fa01ac60 Translations: Update Spanish
Currently translated at 64.6% (2990 of 4622 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/es/

powered by weblate
2022-02-24 20:31:15 +01:00
Raphael Michel
2b5ce5364b Translations: Update Galician
Currently translated at 10.7% (495 of 4622 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/gl/

powered by weblate
2022-02-24 20:31:15 +01:00
Raphael Michel
4a93866cc3 Translations: Update Spanish
Currently translated at 64.7% (2991 of 4622 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/es/

powered by weblate
2022-02-24 20:31:15 +01:00
Ismael Menéndez Fernández
fbc1d862a1 Translations: Update Galician
Currently translated at 10.7% (496 of 4622 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/gl/

powered by weblate
2022-02-24 20:31:15 +01:00
Tonda Pavlík
638daa2c19 Translations: Update Czech
Currently translated at 11.0% (509 of 4622 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/cs/

powered by weblate
2022-02-24 20:31:15 +01:00
Ismael Menéndez Fernández
d400a3c7d3 Translations: Update Spanish
Currently translated at 64.6% (2988 of 4622 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/es/

powered by weblate
2022-02-24 20:31:15 +01:00
Raphael Michel
76f6947529 Voucher list: Filter case-sensitive for exact tag match 2022-02-24 18:56:45 +01:00
Raphael Michel
db7e299af1 Show voucher link even without subevent specified 2022-02-24 14:59:27 +01:00
Raphael Michel
7ed204ffc0 Workaround for PostgreSQL floating point quirk 2022-02-24 13:47:08 +01:00
Raphael Michel
14e0d9cbf4 Change restricted plugins from event-level action to org-level whitelist (#2489) 2022-02-23 15:04:16 +01:00
Raphael Michel
65fb492728 Fix crash in exporter API (PRETIXEU-650) 2022-02-23 15:03:49 +01:00
Raphael Michel
a4f64e94cc Settings form: Fix explicit unlocking, fix HTML button type 2022-02-23 13:24:47 +01:00
Raphael Michel
67ba1f81e4 OrderGo: Fix crash if invoice number matches different prefixes (PRETIXEU-64T) 2022-02-23 13:08:03 +01:00
Maico Timmerman
cc8282bef1 vouchers: allow deleting vouchers that are used on addon cartpositions (#2478) 2022-02-23 13:00:18 +01:00
Martin Gross
c7fc52cabe Doc: Improve SAML RegEx example to not match on partials. 2022-02-23 11:02:44 +01:00
Raphael Michel
4bc04de325 Update django-statici18n requirement from ==2.1.* to ==2.2.* 2022-02-23 10:35:54 +01:00
Martin Gross
9a2ecae021 Add pretix-presale-saml docs (#2468) 2022-02-23 10:32:16 +01:00
Ismael Menéndez Fernández
e55fb303c0 Translations: Update Galician
Currently translated at 9.9% (460 of 4622 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/gl/

powered by weblate
2022-02-23 10:29:35 +01:00
Ismael Menéndez Fernández
185761e9e6 Translations: Update Galician
Currently translated at 9.6% (448 of 4622 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/gl/

powered by weblate
2022-02-23 10:29:35 +01:00
Raphael Michel
a78cb039da Translations: Update Galician
Currently translated at 9.5% (441 of 4622 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/gl/

powered by weblate
2022-02-23 10:29:35 +01:00
Ismael Menéndez Fernández
5f07f0e80b Translations: Update Galician
Currently translated at 9.5% (442 of 4622 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/gl/

powered by weblate
2022-02-23 10:29:35 +01:00
Ismael Menéndez Fernández
4021b28d5f Translated on translate.pretix.eu (Galician)
Currently translated at 100.0% (172 of 172 strings)

Translation: pretix/pretix (JavaScript parts)
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/gl/

powered by weblate
2022-02-23 10:29:35 +01:00
Ismael Menéndez Fernández
8717b1f8db Translations: Update Galician
Currently translated at 8.7% (403 of 4622 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/gl/

powered by weblate
2022-02-23 10:29:35 +01:00
Ismael Menéndez Fernández
b789e64830 Translations: Update Galician
Currently translated at 8.0% (373 of 4622 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/gl/

powered by weblate
2022-02-23 10:29:35 +01:00
Raphael Michel
4d595e3fd4 Translations: Update Galician
Currently translated at 6.7% (310 of 4622 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/gl/

powered by weblate
2022-02-23 10:29:35 +01:00
Ismael Menéndez Fernández
482a9c6af7 Translations: Update Galician
Currently translated at 6.7% (310 of 4622 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/gl/

powered by weblate
2022-02-23 10:29:35 +01:00
MaLund13
99faa8b300 Translated on translate.pretix.eu (Swedish)
Currently translated at 83.1% (143 of 172 strings)

Translation: pretix/pretix (JavaScript parts)
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/sv/

powered by weblate
2022-02-23 10:29:35 +01:00
MaLund13
f0be03f93a Translations: Update Swedish
Currently translated at 21.2% (984 of 4622 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/sv/

powered by weblate
2022-02-23 10:29:35 +01:00
Emanuele Signoretta
5f12fca88a Translations: Update Italian
Currently translated at 17.2% (795 of 4622 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/it/

powered by weblate
2022-02-23 10:29:35 +01:00
Emanuele Signoretta
fa88686856 Translated on translate.pretix.eu (Italian)
Currently translated at 100.0% (172 of 172 strings)

Translation: pretix/pretix (JavaScript parts)
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/it/

powered by weblate
2022-02-23 10:29:35 +01:00
Marco Giacopuzzi
59a6e4130e Translations: Update Italian
Currently translated at 17.1% (792 of 4622 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/it/

powered by weblate
2022-02-23 10:29:35 +01:00
Emanuele Signoretta
35ccc3a9af Translations: Update Italian
Currently translated at 17.1% (792 of 4622 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/it/

powered by weblate
2022-02-23 10:29:35 +01:00
MaLund13
ef9e7fd92a Translations: Update Swedish
Currently translated at 21.2% (982 of 4622 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/sv/

powered by weblate
2022-02-23 10:29:35 +01:00
Raphael Michel
d7acd2b6bf Refs #2465 -- Introduce unique identifiers for authentication backends (#2466) 2022-02-23 10:16:35 +01:00
Raphael Michel
2bf5a0ce8a Update beautifulsoup4 requirement from ==4.8.* to ==4.10.* 2022-02-23 10:13:36 +01:00
Raphael Michel
7310fb3c6e Update requests requirement from ==2.26.* to ==2.27.* 2022-02-23 09:57:37 +01:00
Raphael Michel
069dd02ebc Update stripe requirement from ==2.42.* to ==2.66.* 2022-02-23 09:53:16 +01:00
Richard Schreiber
70e4b02370 Fix #2452 -- Disallow invalid values in min_per_order/max_per_order (#2453) 2022-02-23 09:48:45 +01:00
Richard Schreiber
b20797fe4b Fix details/summary marker on privacy-modal being shown twice (#2482) 2022-02-23 09:46:33 +01:00
Richard Schreiber
aee8de54ed Fix #2480 - move datetime-menu in front of map controls (#2481) 2022-02-22 13:58:54 +01:00
Raphael Michel
6d7e16c147 Fix single-event export if an exporter returns none 2022-02-21 21:07:00 +01:00
Raphael Michel
f511f5a646 Fix bug in error handling 2022-02-21 18:10:05 +01:00
Raphael Michel
6ba690932f Allow event-level exporters to return none 2022-02-21 17:58:33 +01:00
Raphael Michel
46b3e3c739 AsyncFormView: Allow to declare celery exception classes 2022-02-21 16:59:51 +01:00
Raphael Michel
3550197fc4 Fix bug in previous commit 2022-02-18 16:19:51 +01:00
Raphael Michel
db96211c7a Seating: Fix query in validate_plan_change 2022-02-18 15:21:20 +01:00
Richard Schreiber
758179f12f Add name_for_salutation to customer email placeholders (#2474) 2022-02-18 08:02:51 +01:00
Raphael Michel
98409b0a22 API: Minor robustness improvements in quota and checkinlist serializers 2022-02-17 17:37:24 +01:00
Raphael Michel
06ffa0bcd5 API: Fix creation of items with required membership types 2022-02-17 17:37:24 +01:00
Ismael Menéndez Fernández
18917769ef Translations: Update Galician
Currently translated at 4.7% (221 of 4622 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/gl/

powered by weblate
2022-02-17 14:50:47 +01:00
Miguel Magalhães
34e95bc7d2 Translations: Update Portuguese (Portugal)
Currently translated at 80.1% (3704 of 4622 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/pt_PT/

powered by weblate
2022-02-17 14:50:47 +01:00
Matthias Brück
5ba7ee3516 Translations: Update German
Currently translated at 99.9% (4618 of 4622 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/de/

powered by weblate
2022-02-17 14:50:47 +01:00
cpoisnel
3706eff795 Fix subevent not shown correctly in order change view (#2473) 2022-02-17 13:59:01 +01:00
Raphael Michel
28331e7538 Fix docs typo 2022-02-16 17:33:48 +01:00
Raphael Michel
62218ca0c6 Fix attendee mails not being sent if no order address set 2022-02-16 17:19:03 +01:00
Raphael Michel
14e2834a72 API: Allow send_email=none during order creation 2022-02-16 17:19:03 +01:00
Richard Schreiber
032653cec4 Localize customer name_parts in email-context (Z#179923) (#2470) 2022-02-16 13:27:54 +01:00
Raphael Michel
f3b355e9f3 Sendmail: Allow to attach tickets to emails 2022-02-16 10:42:48 +01:00
Raphael Michel
f7d2645e76 Fix isort issue 2022-02-14 17:33:20 +01:00
Raphael Michel
fb89e31c1c Bump djangorestframework to 3.13.* 2022-02-14 16:03:34 +01:00
Jonathan Weth
5e1cff53b4 Fix #2456 -- Allow shredding instantly after event end (#2462) 2022-02-14 15:57:38 +01:00
Raphael Michel
61cef87c9d Update .po files 2022-02-14 15:41:29 +01:00
Raphael Michel
2fcab70e3b Add very simple CAPTCHA to standalone customer registration form 2022-02-14 15:37:35 +01:00
Raphael Michel
1414db35b7 Perform some very basic validation on names 2022-02-14 14:56:31 +01:00
Raphael Michel
1d32d7a2d2 Fix copy-paste error in setup.py 2022-02-14 10:04:46 +01:00
Raphael Michel
9966912799 Fix test failing after last commit 2022-02-13 20:44:18 +01:00
Raphael Michel
a37ed6f001 Bump versions of pycodestyle and pep8-naming 2022-02-13 20:39:31 +01:00
dependabot[bot]
a307cf8934 Bump @babel/core from 7.16.7 to 7.17.2 in /src/pretix/static/npm_dir (#2458)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-02-13 20:25:25 +01:00
Richard Schreiber
0e900b74d7 Fix #2434 -- Disallow manually setting SecretKeySettingsField to ***** (#2436) 2022-02-13 20:24:53 +01:00
dependabot[bot]
7193da42c2 Bump @rollup/plugin-node-resolve from 13.1.2 to 13.1.3 in /src/pretix/static/npm_dir (#2444)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-02-13 20:23:20 +01:00
dependabot[bot]
48eb580ee8 Bump @babel/preset-env from 7.16.7 to 7.16.11 in /src/pretix/static/npm_dir (#2442)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-02-13 20:23:14 +01:00
dependabot[bot]
50a5622178 Bump rollup from 2.62.0 to 2.66.1 in /src/pretix/static/npm_dir (#2443)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-02-13 20:23:07 +01:00
kackey621
66027aed59 Translations: Update Japanese
Currently translated at 0.3% (18 of 4613 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/ja/

powered by weblate
2022-02-13 20:22:51 +01:00
Mauro Amico
8d62e3e2af Translations: Update Italian
Currently translated at 16.7% (774 of 4613 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/it/

powered by weblate
2022-02-13 20:22:51 +01:00
Ismael Menéndez Fernández
a8ce4845e2 Translations: Update Galician
Currently translated at 4.2% (197 of 4613 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/gl/

powered by weblate
2022-02-13 20:22:51 +01:00
Ismael Menéndez Fernández
efdb834a73 Translations: Update Galician
Currently translated at 1.7% (81 of 4613 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/gl/

powered by weblate
2022-02-13 20:22:51 +01:00
Ismael Menéndez Fernández
fd060b792c Translations: Update Spanish
Currently translated at 64.7% (2987 of 4613 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/es/

powered by weblate
2022-02-13 20:22:51 +01:00
Jozsef Ebenspanger
31df3e2129 Translations: Update Hungarian
Currently translated at 2.0% (93 of 4613 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/hu/

powered by weblate
2022-02-13 20:22:51 +01:00
Richard Schreiber
c71ba79e55 Fix #2449 -- Format variation-price with Intl.NumberFormat (#2451) 2022-02-13 20:15:51 +01:00
Maico Timmerman
6304b34600 Fix default reply-to header in emails (#2457) 2022-02-13 15:09:22 +01:00
Raphael Michel
81cc7540ec Add ticket secret to order list export 2022-02-11 14:39:00 +01:00
Raphael Michel
adced71706 Fix bugs from af3006a5b 2022-02-08 17:53:26 +01:00
Raphael Michel
8c7ed38441 Orders API: Support require_approval on order creation 2022-02-08 17:34:20 +01:00
Raphael Michel
b4d7d9bf76 Docs: Fix typo in digital content parameter table 2022-02-08 11:57:25 +01:00
Raphael Michel
af3006a5bd Fix mass-creation of vouchers on MySQL 2022-02-07 15:31:09 +01:00
Raphael Michel
d313e076a2 Widget: Fix another IE support bug introduced in Django 3.2 2022-02-07 13:13:04 +01:00
Raphael Michel
216bac2807 Fix getitem usage for non-dictionaries 2022-02-04 17:41:05 +01:00
Raphael Michel
8351e51cfe ORderChangeManager.set_addons: Fix check performed on parent item instead of actual item 2022-02-04 17:08:03 +01:00
Raphael Michel
b2d74dc652 Allow to use AsyncFormView outside of events 2022-02-04 17:08:03 +01:00
Raphael Michel
ea1322165b Add fallback value for getitem template filter 2022-02-04 17:08:03 +01:00
Martin Gross
c65883b328 Presale Order Change: Display public name of item instead of internal 2022-02-04 16:41:04 +01:00
Felix Schäfer
dfd37cc5e3 Fix guard in mail service (#2448) 2022-02-04 15:30:44 +01:00
Raphael Michel
4c71995560 Support for file upload in asynctask.js 2022-02-03 11:21:49 +01:00
Raphael Michel
02034cacbf Fix changing orders when only variants can be changed and no addons 2022-02-02 16:59:00 +01:00
Raphael Michel
d098cda8a8 Add new endpoints to pretixPOS device security profile 2022-02-01 18:10:11 +01:00
Raphael Michel
0b8432b2c5 Docs: Add node on MySQL's SQL mode 2022-01-31 15:44:26 +01:00
Raphael Michel
9be6ad4124 Add documentation on secrets import plugin 2022-01-31 10:20:23 +01:00
Raphael Michel
fdc77f6bd8 Quota deletion: Show internal names of products 2022-01-31 09:56:55 +01:00
Raphael Michel
e3d0a18bee Show internal category names in product list 2022-01-31 09:34:13 +01:00
Raphael Michel
81c271ee2a Fix ordering of add-on products in email info block 2022-01-28 17:05:46 +01:00
Raphael Michel
e981f00dc7 Fix typo 2022-01-27 17:52:37 +01:00
Raphael Michel
2daf35c39e Allow to customize description of calendar files (#2415)
Co-authored-by: Martin Gross <gross@rami.io>
2022-01-27 14:58:16 +01:00
Raphael Michel
c9530c56af Fix isort issue 2022-01-27 14:42:19 +01:00
Raphael Michel
f3e31287f4 Bump to 4.7.0.dev0 2022-01-27 13:44:53 +01:00
Raphael Michel
c9aaa343e6 Bump to 4.6.0 2022-01-27 13:44:39 +01:00
Raphael Michel
87a196c4df Translations: Update German
Currently translated at 100.0% (4613 of 4613 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/de/

powered by weblate
2022-01-27 12:27:58 +01:00
Raphael Michel
a220f1678b Translations: Update German (informal) (de_Informal)
Currently translated at 100.0% (4613 of 4613 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/de_Informal/

powered by weblate
2022-01-27 12:27:58 +01:00
Raphael Michel
c8fa0852b2 Add DNS to English word list 2022-01-27 12:15:31 +01:00
Raphael Michel
fe3433106c Extend spelling wordlists 2022-01-27 12:11:34 +01:00
Raphael Michel
f8086daf34 Update po files
[CI skip]

Signed-off-by: Raphael Michel <michel@rami.io>
2022-01-27 11:02:14 +01:00
Raphael Michel
66f75a5614 Revert dnspython to 1.x 2022-01-26 16:15:12 +01:00
Raphael Michel
6f30c347c0 [SECURITY] Make redirect view dependent on referer 2022-01-26 13:41:02 +01:00
Raphael Michel
3596fa9c5a [SECURITY] Fix (non-exploitable) XSS issue 2022-01-26 13:41:02 +01:00
Raphael Michel
e3c7cd7c6d Redesign of email settings (#2426)
Co-authored-by: Felix Rindt <felix@rindt.me>
2022-01-26 12:47:58 +01:00
Raphael Michel
194042dca5 Add-on selection: Fix incorrect pre-selection across multiple base positions 2022-01-26 09:45:44 +01:00
Raphael Michel
3be6e83f33 Add missing license header 2022-01-25 21:08:28 +01:00
Raphael Michel
4262bce2b5 Limit maximum length of passwords to 4096 characters 2022-01-25 17:24:48 +01:00
Raphael Michel
73ab962e16 Respect language headers on error 400/404/500 pages 2022-01-25 16:59:30 +01:00
Raphael Michel
13a86fc6f3 Event ical feed: Do not show events more than 31 days in the past 2022-01-24 15:47:04 +01:00
Raphael Michel
9d6f11718a Work around performance issue in vobject library 2022-01-24 15:46:48 +01:00
Raphael Michel
c9d3428996 Extend check_order_transactions by number of tickets 2022-01-22 22:00:35 +01:00
Felix Schäfer
d4ef16b31a Fix #2320 - Move file upload "required" attrs manipulation from init to rendering (#2399) 2022-01-21 15:49:24 +01:00
Yuriko Matsunami
6a35e7d3cd Translated on translate.pretix.eu (Japanese)
Currently translated at 97.0% (167 of 172 strings)

Translation: pretix/pretix (frontend)
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/ja/

powered by weblate
2022-01-21 15:49:03 +01:00
DJG Bayern
463443d606 Translations: Update Japanese
Currently translated at 0.1% (8 of 4582 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/ja/

powered by weblate
2022-01-21 15:49:03 +01:00
Raphael Michel
6f0da5c2ca TaxRules: Add internal_name and keep_gross_if_rate_changes (#2422)
Co-authored-by: ser8phin <eva.wolkwitz@gmx.de>
2022-01-21 15:39:27 +01:00
ser8phin
c1344422a5 Remove disabled attribute on checkbox (#2423)
Co-authored-by: Raphael Michel <michel@rami.io>
2022-01-19 21:38:14 +01:00
Raphael Michel
c2bd3dde44 GitHub actions: Do not run on 3.10 yet (too many warnings) 2022-01-19 17:12:15 +01:00
Raphael Michel
9e51736232 Fix GitHub actions scripts (No YAML, Python 3.10 is not 3.1) 2022-01-19 17:01:42 +01:00
Raphael Michel
5b27ce1265 Stop testing Python 3.6 on CI 2022-01-19 17:00:24 +01:00
Raphael Michel
0757542f4f Drop Python 3.6 compatibility 2022-01-19 16:49:19 +01:00
Raphael Michel
12be98c888 Update Pillow to 9.* 2022-01-19 16:46:43 +01:00
Raphael Michel
51e6b02aa9 Docs: Remove mention of local cache backend 2022-01-19 15:24:44 +01:00
Raphael Michel
acc4a167b1 Event series calendar: Fix incorrect show_names heuristic 2022-01-19 14:58:30 +01:00
Richard Schreiber
dd9429bbfa Fix: phone being "None" or format not recognized in checkout (#2420) 2022-01-18 12:27:57 +01:00
Richard Schreiber
768bb8c106 Add phone number to customer profile (Z#178346) (#2414) 2022-01-18 11:38:32 +01:00
Raphael Michel
cbdafac999 Web check-in: Fix search 2022-01-17 14:55:16 +01:00
Raphael Michel
96f694cf61 Translations: Update German (informal) (de_Informal)
Currently translated at 99.9% (4580 of 4582 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/de_Informal/

powered by weblate
2022-01-14 16:12:13 +01:00
Raphael Michel
5576829ebf Translations: Update German
Currently translated at 100.0% (4582 of 4582 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/de/

powered by weblate
2022-01-14 16:12:13 +01:00
Raphael Michel
b0d67e92ac Update po files
[CI skip]

Signed-off-by: Raphael Michel <michel@rami.io>
2022-01-14 15:33:22 +01:00
Yuriko Matsunami
63e28723d2 Translated on translate.pretix.eu (Japanese)
Currently translated at 73.2% (126 of 172 strings)

Translation: pretix/pretix (frontend)
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/ja/

powered by weblate
2022-01-14 14:51:58 +01:00
Mikkel Ricky
cc0656f169 Translations: Update Danish
Currently translated at 35.3% (1613 of 4565 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/da/

powered by weblate
2022-01-14 14:51:58 +01:00
ser8phin
849c8e719a Fix #555 -- Preselect a single required add-on (#2395) 2022-01-14 14:46:04 +01:00
Raphael Michel
a3ec2a4061 Clarify help text of invoice_address_custom_field 2022-01-14 14:42:42 +01:00
Raphael Michel
00a7187a7a Duplicate line break before invoice deadline 2022-01-13 16:45:15 +01:00
Richard Schreiber
701c4f768e Improve add-to-cart checkbox for items with max. 1 per order (Z#178704) (#2413) 2022-01-12 17:10:00 +01:00
Aya Yabuki
cf751d38d2 Translated on translate.pretix.eu (Japanese)
Currently translated at 16.8% (29 of 172 strings)

Translation: pretix/pretix (frontend)
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/ja/

powered by weblate
2022-01-12 16:21:14 +01:00
Aya Yabuki
888402a4bf Translated on translate.pretix.eu (Japanese)
Currently translated at 16.8% (29 of 172 strings)

Translation: pretix/pretix (frontend)
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/ja/

powered by weblate
2022-01-12 16:21:14 +01:00
Aya Yabuki
1134f610fd Translated on translate.pretix.eu (Japanese)
Currently translated at 8.7% (15 of 172 strings)

Translation: pretix/pretix (frontend)
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/ja/

powered by weblate
2022-01-12 16:21:14 +01:00
Raphael Michel
8ae4304c7d Add workaround for https://github.com/getsentry/responses/issues/464 2022-01-12 10:19:02 +01:00
Raphael Michel
357092ec44 API: Add InvoiceLine.subevent (#2411) 2022-01-10 14:11:06 +01:00
Raphael Michel
70a5c76d79 Allow tax rules to trigger approval requirement (#2409) 2022-01-10 14:10:51 +01:00
ser8phin
7a4db8ea23 Add approval requirement option to product variations (#2381) 2022-01-05 18:04:12 +01:00
Raphael Michel
223b160c0c Fix booked add-ons being hidden in order change due to hide_sold_out 2022-01-05 17:58:21 +01:00
Raphael Michel
30c1771d29 Thumbnail: Support for paletted PNG files 2022-01-04 16:26:13 +01:00
Raphael Michel
b3b7b9bbab Optimize rendering of very large calendars (#2406) 2022-01-04 10:48:48 +01:00
dependabot[bot]
be040cd6ea Bump @babel/core from 7.16.0 to 7.16.7 in /src/pretix/static/npm_dir (#2401)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-01-04 10:33:51 +01:00
dependabot[bot]
c6665ec2e6 Bump @rollup/plugin-node-resolve from 13.0.6 to 13.1.2 in /src/pretix/static/npm_dir (#2403)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-01-04 10:30:57 +01:00
dependabot[bot]
fd16ef1e4d Bump rollup from 2.60.2 to 2.62.0 in /src/pretix/static/npm_dir (#2402)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-01-04 10:30:51 +01:00
dependabot[bot]
39557fc452 Bump @babel/preset-env from 7.16.4 to 7.16.7 in /src/pretix/static/npm_dir (#2404)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-01-04 10:30:44 +01:00
cpoisnel
408397a639 Translations: Update French
Currently translated at 48.8% (2229 of 4565 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/fr/

powered by weblate
2022-01-04 10:30:34 +01:00
Raphael Michel
d4a2500204 Check-in list PDF: Escape HTML tags in names 2022-01-03 12:41:37 +01:00
Raphael Michel
e74d9e56cf Waiting list: Explain that you only get one ticket 2022-01-03 10:43:13 +01:00
Raphael Michel
f3767ab4ac Gift card: Log user who triggered reversal of transaction 2022-01-03 10:39:05 +01:00
Raphael Michel
5d13f5f885 Gift cards: Fix incorrect handling of return key 2022-01-03 10:38:54 +01:00
Raphael Michel
451d3fce05 Cookie consent: Fix crash without localStorage again 2021-12-22 10:29:27 +01:00
Raphael Michel
ccb61e0f56 Docs: Fix dead external link 2021-12-21 11:45:34 +01:00
Richard Schreiber
b6273adc57 Calendar-View: add short_month_day_format for week-views (#2392) 2021-12-21 11:19:58 +01:00
Richard Schreiber
0bf7bba6ba Fix: WEEK_FORMAT fallback in calender week-views (#2391)
* switch to context-week_format for fallback-handling

* set week_format fallback to en instead of de

* add french WEEK_FORMAT and WEEK_DAY_FORMAT
2021-12-21 10:10:13 +01:00
Raphael Michel
7090e0bae2 Event settings: Do not specify fields as optional that are actually required 2021-12-20 19:20:48 +01:00
Raphael Michel
c75cb0b8e3 Cookie consent: Fail softly if localStorage is unavailable 2021-12-20 16:11:33 +01:00
Raphael Michel
3dbf22f670 Remove django-compat from settings.py 2021-12-20 12:22:13 +01:00
Raphael Michel
f26cbdc257 Bump arabic-reshaper to 2.1.3 2021-12-20 09:52:38 +01:00
Raphael Michel
6b4adccee5 Bump django-hijack to 3.1.* 2021-12-20 09:51:52 +01:00
Raphael Michel
c2a8286022 Fix celery-specific issue in 9f4b834ab 2021-12-16 19:06:16 +01:00
Martin Gross
4145887a9b Web checkin: Redirect user to login if session expired (#2383)
Co-authored-by: Raphael Michel <michel@rami.io>
2021-12-16 18:35:09 +01:00
Raphael Michel
9f4b834abc Allow to attach files to order confirmation email (#2384)
Co-authored-by: Richard Schreiber <schreiber@rami.io>
2021-12-16 18:34:18 +01:00
Richard Schreiber
8fcc314f09 Add fixed scroll position when navigating calendar views (Z#177488) (#2385)
* add fixed scroll position when navigating calendar views

* change from local to sessionStorage

* add check for sessionStorage
2021-12-16 13:36:10 +01:00
Felix Rindt
94a7d02ab1 Fix event settings form considered changed even if unchanged (#1739)
Co-authored-by: Raphael Michel <michel@rami.io>
2021-12-16 11:27:18 +01:00
Raphael Michel
ad2943263c Fix unnecessary override of default settings 2021-12-16 10:31:46 +01:00
Raphael Michel
5210ac3a78 Reduce confusion about customer login with event level domains (#2380) 2021-12-15 16:47:08 +01:00
Raphael Michel
0e9600a7bf Fix test isolation issue 2021-12-15 16:46:50 +01:00
ser8phin
eccba09452 Add payment search page (#2335)
Co-authored-by: Raphael Michel <michel@rami.io>
Co-authored-by: Richard Schreiber <schreiber@rami.io>
2021-12-15 16:06:43 +01:00
Richard Schreiber
c8a830ecde Fix: change widget to use new date-based URLs in calendar-view (#2382) 2021-12-15 14:07:42 +01:00
Richard Schreiber
aed64d16f6 Improve calendar-navigation on organizer and events page (Z#177488) (#2373)
* hide icons for calendar-types and improve layout-breakpoints in calendar top-nav

* change month-selector to one dropdown "date"and redirect old URLs to new date-based URLs

* change week calendar to one dropdown "date“ and redirect old URLs to new date-based URLs
2021-12-14 16:38:32 +01:00
Raphael Michel
d16f6167f6 Fix rich_text crash on empty <a> element 2021-12-14 13:56:52 +01:00
Raphael Michel
77d59248e5 Translations: Update Galician
Currently translated at 1.3% (61 of 4565 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/gl/

powered by weblate
2021-12-14 13:30:11 +01:00
Raphael Michel
a0e05f8af6 Translations: Update Galician
Currently translated at 1.1% (52 of 4565 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/gl/

powered by weblate
2021-12-14 13:30:11 +01:00
Raphael Michel
9b8a47c8b8 Translations: Update Galician
Currently translated at 1.1% (52 of 4565 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/gl/

powered by weblate
2021-12-14 13:30:11 +01:00
Ismael Menéndez Fernández
b3d692276c Translations: Update Galician
Currently translated at 1.1% (52 of 4565 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/gl/

powered by weblate
2021-12-14 13:30:11 +01:00
DJG Bayern
55543e12f6 Translated on translate.pretix.eu (Japanese)
Currently translated at 7.5% (13 of 172 strings)

Translation: pretix/pretix (frontend)
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/ja/

powered by weblate
2021-12-14 13:30:11 +01:00
Yuriko Matsunami
1e16185c02 Translated on translate.pretix.eu (Japanese)
Currently translated at 7.5% (13 of 172 strings)

Translation: pretix/pretix (frontend)
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/ja/

powered by weblate
2021-12-14 13:30:11 +01:00
Raphael Michel
cd900e24bd Questions form: Do not persist values to questions hidden by dependencies 2021-12-13 15:46:58 +01:00
Raphael Michel
0dbedc07ce Fix CI dependency installation (#2376) 2021-12-13 15:24:27 +01:00
Raphael Michel
f71877b7fc Badges: Fix event copy data receiver not rewriting questions 2021-12-13 14:09:38 +01:00
Martin Gross
f69e270e4d Add filter for revoked devices (#2372)
Co-authored-by: Raphael Michel <michel@rami.io>
2021-12-13 12:47:43 +01:00
MrGamy
533939cae4 included missing adjective
fixes #2344
2021-12-10 19:29:45 +01:00
Ilona Zilgalve
91ec5fd78c Translated on translate.pretix.eu (Latvian)
Currently translated at 100.0% (172 of 172 strings)

Translation: pretix/pretix (frontend)
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/lv/

powered by weblate
2021-12-09 16:54:15 +01:00
Ilona Zilgalve
0056fb447b Translations: Update Latvian
Currently translated at 31.1% (1421 of 4565 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/lv/

powered by weblate
2021-12-09 16:54:15 +01:00
Ilona Zilgalve
20c4d12e98 Translations: Update Russian
Currently translated at 25.1% (1147 of 4565 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/ru/

powered by weblate
2021-12-09 16:54:15 +01:00
Ilona Zilgalve
e13c567e84 Translations: Update Latvian
Currently translated at 28.3% (1296 of 4565 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/lv/

powered by weblate
2021-12-09 16:54:15 +01:00
Ilona Zilgalve
9fef97a7c6 Translations: Update Russian
Currently translated at 24.9% (1139 of 4565 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/ru/

powered by weblate
2021-12-09 16:54:15 +01:00
Ilona Zilgalve
e68a995376 Translations: Update Latvian
Currently translated at 27.5% (1256 of 4565 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/lv/

powered by weblate
2021-12-09 16:54:15 +01:00
Ilona Zilgalve
6abdb40ef5 Translations: Update Latvian
Currently translated at 27.4% (1252 of 4565 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/lv/

powered by weblate
2021-12-09 16:54:15 +01:00
Ilona Zilgalve
43cc06b0a1 Translations: Update Russian
Currently translated at 24.4% (1115 of 4565 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/ru/

powered by weblate
2021-12-09 16:54:15 +01:00
Ondřej Sokol
d17476cd75 Translated on translate.pretix.eu (Czech)
Currently translated at 100.0% (172 of 172 strings)

Translation: pretix/pretix (frontend)
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/cs/

powered by weblate
2021-12-09 16:54:15 +01:00
Ondřej Sokol
5c3bfd2a71 Translations: Update Czech
Currently translated at 10.5% (482 of 4565 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/cs/

powered by weblate
2021-12-09 16:54:15 +01:00
Maico Timmerman
033b8d70e7 Email: Allow to override backend for custom SMTP connections (#2368) 2021-12-09 16:49:22 +01:00
Raphael Michel
bd22c2afc9 Set OrderRefund.execution_date on manual refund 2021-12-08 09:41:12 +01:00
Raphael Michel
b355733f53 Allow to link directly to voucher input form 2021-12-06 18:09:38 +01:00
Raphael Michel
e1f924c4ce Allow to reschedule a missed email 2021-12-06 17:36:49 +01:00
Raphael Michel
8038f4e173 Orders API: Allow to filter by subevent 2021-12-06 12:50:33 +01:00
Raphael Michel
5c55219d45 Allow to create new customers in backend (#2367) 2021-12-06 12:27:21 +01:00
Eva-Maria Obermann
bfd37af467 Translated on translate.pretix.eu (French)
Currently translated at 63.3% (109 of 172 strings)

Translation: pretix/pretix (frontend)
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/fr/

powered by weblate
2021-12-06 12:27:12 +01:00
Eva-Maria Obermann
b2509e120c Translations: Update German
Currently translated at 100.0% (4565 of 4565 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/de/

powered by weblate
2021-12-06 12:27:12 +01:00
ExtremeX-BB
e2339acd09 Translated on translate.pretix.eu (Chinese (Simplified))
Currently translated at 68.0% (117 of 172 strings)

Translation: pretix/pretix (frontend)
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/zh_Hans/

powered by weblate
2021-12-06 12:27:12 +01:00
ExtremeX-BB
c15b4fa03c Translations: Update Chinese (Simplified)
Currently translated at 66.2% (3025 of 4565 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/zh_Hans/

powered by weblate
2021-12-06 12:27:12 +01:00
Ilona Zilgalve
c4aa2e0484 Translations: Update Latvian
Currently translated at 27.0% (1235 of 4565 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/lv/

powered by weblate
2021-12-06 12:27:12 +01:00
Ilona Zilgalve
361eeb7159 Translations: Update Russian
Currently translated at 24.4% (1114 of 4565 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/ru/

powered by weblate
2021-12-06 12:27:12 +01:00
Raphael Michel
0109e1806f OrderChangeManager: Move invoice reissuing after payment cancellation (#2359) 2021-12-06 12:26:53 +01:00
Raphael Michel
30aadac099 Fix isort change 2021-12-03 15:02:46 +01:00
dependabot[bot]
0458f1b2dc Bump @babel/preset-env from 7.16.0 to 7.16.4 in /src/pretix/static/npm_dir (#2360)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-12-03 14:40:06 +01:00
dependabot[bot]
e006ca3feb Bump @rollup/plugin-node-resolve from 11.2.1 to 13.0.6 in /src/pretix/static/npm_dir (#2361)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-12-03 14:39:27 +01:00
dependabot[bot]
1f31ee2ea1 Bump rollup from 2.59.0 to 2.60.2 in /src/pretix/static/npm_dir (#2362)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-12-03 14:39:02 +01:00
Richard Schreiber
2d37b0df77 Fix: Day calendar - scroll current .tick into view without window being scrolled (#2365) 2021-12-03 14:36:28 +01:00
Raphael Michel
4133e5ac4d Fix incorrect order change tests 2021-12-03 14:08:19 +01:00
Richard Schreiber
0fd3d0fe71 Fix #2363 – Email: change text-alignment from center to left (right for rtl) (#2364) 2021-12-03 13:44:06 +01:00
Raphael Michel
d0685e99ad Return URL: Append error/success message to query 2021-12-03 10:30:33 +01:00
Raphael Michel
c6fd5bc864 Self-service order change: Fix price constraints not actually being enforced 2021-12-03 10:04:07 +01:00
Raphael Michel
9fa935099f Email rules: Show warning when date was missed 2021-12-03 09:36:54 +01:00
Raphael Michel
83b5a325e3 Fix bug in 832235411 2021-11-30 22:52:34 +01:00
pretix translation bot
97e12c5003 Translations update from Weblate (#2356)
Co-authored-by: Raphael Michel <michel@rami.io>
2021-11-30 17:55:32 +01:00
Raphael Michel
e6db8340f2 Extend German spellcheck wordlist 2021-11-30 17:52:02 +01:00
Raphael Michel
3cf9caa5d3 Add "analytics" to wordlist 2021-11-30 17:26:28 +01:00
Ilona Zilgalve
2ffd68ace7 Translated on translate.pretix.eu (Latvian)
Currently translated at 100.0% (172 of 172 strings)

Translation: pretix/pretix (frontend)
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/lv/

powered by weblate
2021-11-30 17:26:12 +01:00
Ilona Zilgalve
0231be63b4 Translations: Update Latvian
Currently translated at 27.1% (1232 of 4537 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/lv/

powered by weblate
2021-11-30 17:26:12 +01:00
Ilona Zilgalve
fae8bc254e Translations: Update Russian
Currently translated at 24.5% (1113 of 4537 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/ru/

powered by weblate
2021-11-30 17:26:12 +01:00
Tonda Pavlík
1d5c700fa2 Translations: Update Czech
Currently translated at 10.4% (474 of 4537 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/cs/

powered by weblate
2021-11-30 17:26:12 +01:00
Raphael Michel
e61775d5c1 Update po files
[CI skip]

Signed-off-by: Raphael Michel <michel@rami.io>
2021-11-30 17:13:43 +01:00
Raphael Michel
e767c6a68d Add central cookie consent mechanism (#2330)
Co-authored-by: Richard Schreiber <schreiber@rami.io>
2021-11-30 17:12:17 +01:00
Raphael Michel
832235411f Add subevent location to order info in emails (#2354) 2021-11-30 13:21:36 +01:00
Raphael Michel
1f0f7b752f Payment provider API: Add confirm_button_name 2021-11-29 20:54:24 +01:00
Raphael Michel
3117eceb72 Validate VAT ID when changing invoice addresses 2021-11-29 20:36:20 +01:00
Raphael Michel
c1b39782fd Bump to 4.6.0.dev0 2021-11-29 15:47:08 +01:00
Raphael Michel
860cfc3227 Bump version to 4.5.0 2021-11-29 15:46:42 +01:00
pretix translation bot
45859a07dd Translations update from Weblate (#2352)
Co-authored-by: Raphael Michel <michel@rami.io>
2021-11-29 10:35:24 +01:00
dependabot[bot]
04fb8efc0d Update flake8 requirement from ==3.7.* to >=3.7,<4.1 in /src
Updates the requirements on [flake8](https://github.com/pycqa/flake8) to permit the latest version.
- [Release notes](https://github.com/pycqa/flake8/releases)
- [Commits](https://github.com/pycqa/flake8/compare/3.7.0...4.0.1)

---
updated-dependencies:
- dependency-name: flake8
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-11-29 09:53:14 +01:00
Raphael Michel
fdb8a3720b Update po files
[CI skip]

Signed-off-by: Raphael Michel <michel@rami.io>
2021-11-29 09:28:15 +01:00
Raphael Michel
5638d68894 Raise some dependencies 2021-11-29 09:27:24 +01:00
Raphael Michel
f64042280a Tighten dependency ranges 2021-11-29 09:27:24 +01:00
Angel Saiz Velasco
50060cdc8d Translations: Update Spanish
Currently translated at 66.7% (2992 of 4483 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/es/

powered by weblate
2021-11-29 09:19:44 +01:00
Ismael Menéndez Fernández
4499f58e3d Translated on translate.pretix.eu (Galician)
Currently translated at 100.0% (172 of 172 strings)

Translation: pretix/pretix (frontend)
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/gl/

powered by weblate
2021-11-29 09:19:44 +01:00
Ismael Menéndez Fernández
918e4a5a89 Translations: Update Galician
Currently translated at 0.7% (33 of 4483 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/gl/

powered by weblate
2021-11-29 09:19:44 +01:00
Ismael Menéndez Fernández
15a86fd796 Translated on translate.pretix.eu (Spanish)
Currently translated at 100.0% (172 of 172 strings)

Translation: pretix/pretix (frontend)
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/es/

powered by weblate
2021-11-29 09:19:44 +01:00
Ismael Menéndez Fernández
4126d20f1c Translations: Update Spanish
Currently translated at 66.6% (2986 of 4483 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/es/

powered by weblate
2021-11-29 09:19:44 +01:00
Eva-Maria Obermann
ea3edf83f8 Translations: Update German (informal) (de_Informal)
Currently translated at 100.0% (4483 of 4483 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/de_Informal/

powered by weblate
2021-11-29 09:19:44 +01:00
Eva-Maria Obermann
9a42819b56 Translations: Update German
Currently translated at 100.0% (4483 of 4483 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/de/

powered by weblate
2021-11-29 09:19:44 +01:00
Marco Giacopuzzi
3e4ba28700 Translated on translate.pretix.eu (Italian)
Currently translated at 100.0% (172 of 172 strings)

Translation: pretix/pretix (frontend)
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/it/

powered by weblate
2021-11-29 09:19:44 +01:00
Marco Giacopuzzi
9014ffcc28 Translations: Update Italian
Currently translated at 17.1% (770 of 4483 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/it/

powered by weblate
2021-11-29 09:19:44 +01:00
Raphael Michel
48f4bcf88c Fix breaking multi-event exporters 2021-11-23 17:07:39 +01:00
Raphael Michel
b7dfb3697e Widget: Fix price box not shown for free-price events with one product 2021-11-23 11:13:09 +01:00
Richard Schreiber
475a5be351 Day calendar: Fix missing current-time-bar back for all browsers (#2342) 2021-11-22 15:12:51 +01:00
Richard Schreiber
8254d8f5cc Day-Calendar: improve width of row-names (#2341) 2021-11-22 15:09:40 +01:00
Raphael Michel
6f0f4755ef Restrict day calendar JS to day calendar page 2021-11-19 19:02:46 +01:00
Richard Schreiber
910a35dedc Fix: calculate day calendar grid in JS as chrome does not support calc-division in CSS-grid (#2340)
Co-authored-by: Raphael Michel <michel@rami.io>
2021-11-19 17:42:16 +01:00
Raphael Michel
e694bd8c21 Fix next crash in day calendar if there is no start time 2021-11-19 17:08:05 +01:00
Raphael Michel
29cf384c28 Fix crash in day calendar if there is no start time 2021-11-19 16:32:07 +01:00
Raphael Michel
492288f437 Allow customers to change add-ons on existing orders (#2283) 2021-11-19 14:59:54 +01:00
Raphael Michel
34e4f7e0fc Add day calendar to organizer page (#2100)
Co-authored-by: Richard Schreiber <wiffbi@gmail.com>
Co-authored-by: Richard Schreiber <schreiber@rami.io>
2021-11-19 14:59:35 +01:00
Rasmus Kock Grusgaard
f6f3bbcce6 Translations: Update Danish
Currently translated at 35.9% (1613 of 4483 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/da/

powered by weblate
2021-11-19 14:59:06 +01:00
Raphael Michel
16054893ed Avoid creation of manual payments with zero amount (#2325) 2021-11-19 12:02:36 +01:00
dependabot[bot]
f6038d2c39 Update django-statici18n requirement from ==1.9.* to >=1.9,<2.2 in /src (#2332)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-18 17:41:37 +01:00
dependabot[bot]
8d13b51271 Bump pycparser from 2.13 to 2.21 in /src (#2334)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-18 17:40:59 +01:00
Raphael Michel
83e1f365c2 Sendmail rules: Add warnings and scheduling view (#2328) 2021-11-18 12:48:27 +01:00
Raphael Michel
146e1aeb67 Upgrade mt-940 to 4.* (#2331) 2021-11-18 12:24:54 +01:00
dependabot[bot]
f9b2920984 Update libsass requirement from ==0.20.* to >=0.20,<0.22 in /src (#2315)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-18 12:14:24 +01:00
dependabot[bot]
2c01b214a7 Update pyflakes requirement from ==2.1.* to >=2.1,<2.5 in /src (#2313)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-18 12:14:01 +01:00
dependabot[bot]
fdab45e5ce Update bleach requirement from ==3.3.* to >=3.3,<4.2 in /src (#2317)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-18 12:13:17 +01:00
pretix translation bot
9d2cf18543 Translations update from Weblate (#2327)
Co-authored-by: +se <sebastiano@endsummercamp.org>
2021-11-18 12:12:42 +01:00
Martin Gross
2206ab1d35 Validate Swiss VAT ID against PROD and not TEST-env 2021-11-17 14:07:14 +01:00
Raphael Michel
ecd2c80dce Downgrade 'markdown' package (#2329) 2021-11-17 11:21:59 +01:00
Raphael Michel
3387df491a Fix error handling in Swiss VAT ID validation 2021-11-17 10:30:52 +01:00
pretix translation bot
b6974e0c77 Translations update from Weblate (#2319)
Co-authored-by: Maarten van den Berg <maartenberg1@gmail.com>
Co-authored-by: +se <sebastiano@endsummercamp.org>
2021-11-16 16:58:21 +01:00
Raphael Michel
31751cbd79 Stripe: Fix storage of failed refunds 2021-11-16 12:18:33 +01:00
Raphael Michel
993da5a392 VAT validation: Move cache to data directory 2021-11-16 10:21:08 +01:00
Richard Schreiber
72455209bb CSP: Strip keys with empty values from header (#2322) 2021-11-16 09:24:19 +01:00
Richard Schreiber
803aa0b70d Setup: Allow django-hijack v2.2 (#2321) 2021-11-16 09:24:06 +01:00
Bentrex95
954d86337c Docs: Fix typo in dev-setup-command (#2316) 2021-11-12 12:42:07 +01:00
Raphael Michel
38a58d62f3 Change default settings for background color, invoice attachmentes and name scheme (#2288) 2021-11-11 12:20:34 +01:00
Raphael Michel
e67b39a57b Increase padding if background color is set (#2301) 2021-11-11 12:20:20 +01:00
dependabot[bot]
148b67ac3f Update django-filter requirement from ==2.4.* to >=2.4,<21.2 in /src (#2311)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-11 11:14:35 +01:00
dependabot[bot]
d261cb3b6b Bump django-libsass from 0.8 to 0.9 in /src (#2312)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-11 11:14:07 +01:00
ser8phin
169a6c51b4 Add check to force users to change password (#2284) 2021-11-11 11:10:33 +01:00
Raphael Michel
245ad644ff Subevent calendar: Improve heuristic on when to show names (#2308) 2021-11-11 10:02:45 +01:00
Jaakko Rinta-Filppula
4fdce0d126 Translated on translate.pretix.eu (Finnish)
Currently translated at 50.0% (86 of 172 strings)

Translation: pretix/pretix (frontend)
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/fi/

powered by weblate
2021-11-11 10:02:32 +01:00
Jaakko Rinta-Filppula
a542bc7a5a Translated on translate.pretix.eu (Finnish)
Currently translated at 19.0% (856 of 4483 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/fi/

powered by weblate
2021-11-11 10:02:32 +01:00
dependabot[bot]
3164919923 Update pytest-rerunfailures requirement from ==9.* to >=9,<11 in /src (#2303)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Raphael Michel <michel@rami.io>
2021-11-09 19:22:43 +01:00
dependabot[bot]
8085311eb6 Update django-localflavor requirement from ==3.0.* to >=3.0,<3.2 in /src (#2305)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-09 19:21:53 +01:00
dependabot[bot]
3887a65961 Update pytest-mock requirement from ==2.0.* to >=2.0,<3.7 in /src (#2302)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-09 19:20:47 +01:00
dependabot[bot]
b229c6156a Update chardet requirement from <3.1.0,>=3.0.2 to >=3.0.2,<4.1.0 in /src (#2304)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-09 19:20:40 +01:00
Raphael Michel
c45298544e Fix incorrect settings propagagion 2021-11-09 18:45:45 +01:00
Maarten van den Berg
7bb9d3fc3d Translated on translate.pretix.eu (Dutch)
Currently translated at 99.9% (4482 of 4483 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/nl/

powered by weblate
2021-11-09 17:25:38 +01:00
Ismael Menéndez Fernández
8607df5a9c Translated on translate.pretix.eu (Galician)
Currently translated at 31.3% (54 of 172 strings)

Translation: pretix/pretix (frontend)
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/gl/

powered by weblate
2021-11-09 17:25:38 +01:00
Ismael Menéndez Fernández
c4150473fc Translated on translate.pretix.eu (Galician)
Currently translated at 0.4% (20 of 4483 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/gl/

powered by weblate
2021-11-09 17:25:38 +01:00
Martin Gross
172b2f74e0 Translated on translate.pretix.eu (German (informal) (de_Informal))
Currently translated at 100.0% (4483 of 4483 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/de_Informal/

powered by weblate
2021-11-09 17:25:38 +01:00
Svyatoslav
9586f71dc2 Translated on translate.pretix.eu (Latvian)
Currently translated at 24.0% (1077 of 4483 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/lv/

powered by weblate
2021-11-09 17:25:38 +01:00
Raphael Michel
25692d180f Make weblate script more robust 2021-11-09 16:34:57 +01:00
Raphael Michel
ae047037dc Docs: Add style guide for commit messages (#2281)
Co-authored-by: Richard Schreiber <schreiber@rami.io>
2021-11-09 16:30:32 +01:00
dependabot[bot]
265106034b Update django-otp requirement from ==0.7.*,>=0.7.5 to >=0.7,<1.2 in /src (#2290)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-09 12:00:15 +01:00
Raphael Michel
dd0a4df914 Fix error 500 on non-ASCII attachment file names 2021-11-09 11:55:03 +01:00
dependabot[bot]
b0ae40c264 Bump rollup from 1.32.1 to 2.59.0 in /src/pretix/static/npm_dir (#2298)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-09 11:54:28 +01:00
dependabot[bot]
ad95815043 Update redis requirement from ==3.4.* to >=3.4,<3.6 in /src (#2293)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-09 11:54:07 +01:00
dependabot[bot]
f68522ec0d Bump @babel/core from 7.13.14 to 7.16.0 in /src/pretix/static/npm_dir (#2297)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-09 09:51:38 +01:00
dependabot[bot]
b831e57351 Bump @rollup/plugin-node-resolve from 11.2.0 to 11.2.1 in /src/pretix/static/npm_dir (#2299)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-09 09:50:08 +01:00
dependabot[bot]
51166786ee Update phonenumberslite requirement from ==8.11.* to >=8.11,<8.13 in /src (#2291)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-09 09:49:52 +01:00
dependabot[bot]
909e7906ff Update sentry-sdk requirement from ==1.1.* to >=1.1,<1.5 in /src (#2292)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-09 09:47:55 +01:00
dependabot[bot]
e185d5f0e7 Bump @babel/preset-env from 7.13.12 to 7.16.0 in /src/pretix/static/npm_dir (#2295)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-09 09:46:21 +01:00
dependabot[bot]
ce8edf621b Bump vue and vue-template-compiler in /src/pretix/static/npm_dir (#2296)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-09 09:45:24 +01:00
Raphael Michel
e58b512876 Fix ordering of questions in backend if all system questions are 0 2021-11-09 09:44:44 +01:00
Raphael Michel
d1754f6d1b GitHub: Enable dependabot (#2289) 2021-11-09 09:43:52 +01:00
Raphael Michel
ff2f1b7424 Fix incorrect check for enabled fields in QuestionList 2021-11-09 09:32:52 +01:00
Raphael Michel
fb1838a2f0 Fix incorrect help text 2021-11-09 09:32:52 +01:00
Raphael Michel
d7b05063a4 Allow to print event location on invoices (#2278)
Co-authored-by: Richard Schreiber <schreiber@rami.io>
2021-11-05 09:47:41 +01:00
Raphael Michel
f64a42d61a Stripe: Fix handling of charges without source 2021-11-04 18:21:29 +01:00
Raphael Michel
c1994e89a5 Stripe: Fix MultipleObjectsReturned in webhook 2021-11-04 17:58:24 +01:00
Raphael Michel
f37de1ad2f Invoice renderer: Do not show end date if same as start date 2021-11-04 17:34:44 +01:00
Raphael Michel
e1ff6f8590 Stripe: Look up charges by their source ID as well 2021-11-04 17:20:45 +01:00
Raphael Michel
a5dd22eb4d Reduce number of global locks needed for confirming payments 2021-11-04 17:18:48 +01:00
Raphael Michel
19cde63505 Fix incorrect setting if Invoice.full_invoice_no 2021-11-04 13:48:39 +01:00
Raphael Michel
754d4f4f62 Sendmail: Fix subevent-less rules in event series 2021-11-04 10:21:03 +01:00
Bentrex95
e433230573 Docs: Update dependencies for dev setup (#2282)
Co-authored-by: Raphael Michel <michel@rami.io>
2021-11-03 12:35:26 +01:00
Julia Luna
f8927396d3 API: Add endpoints for automated email rules (#2178)
Co-authored-by: Raphael Michel <michel@rami.io>
2021-11-03 11:49:01 +01:00
Raphael Michel
60be99fbb2 Another attempt at correct sanitization of HTML in invoice content (#2279) 2021-11-03 11:13:43 +01:00
Raphael Michel
0c508c5ba4 Fix remaining DST error in auto check-out 2021-11-03 09:34:50 +01:00
Richard Schreiber
ea6067ab3f Fix Outlook >= 2010 trimming header image (#2277)
* fix image cutoff with mso-line-height: at-least
* align text to the left; fully centered text is hard to read
* remove mso cellpadding-tables as they double up the spacing
* additionally add background-color to a table with width=100% for broader support (e.g. Yahoo and AOL)
2021-11-02 12:59:09 +01:00
Raphael Michel
9d0fa84277 Add nodejs to update notes 2021-10-31 18:32:16 +01:00
Raphael Michel
a6835d3b14 Fix bug in 03de0d5d2 2021-10-31 18:26:45 +01:00
Raphael Michel
9ff565f772 Fix unreadable active tab 2021-10-31 17:28:35 +01:00
Raphael Michel
5d41b20bae Fix crash in waiting list 2021-10-31 17:28:29 +01:00
Raphael Michel
03de0d5d2e Do not ask authenticated customers to re-type their email address 2021-10-29 17:23:26 +02:00
Raphael Michel
2937acdc66 Bump to 4.5.0.dev0 2021-10-29 15:38:52 +02:00
Raphael Michel
6fd09e99e2 Bump version to 4.4.0 2021-10-29 15:38:52 +02:00
Raphael Michel
290e14689d Fix check_order_transactions on SQLite 2021-10-29 15:38:52 +02:00
Raphael Michel
89c937089b Translated on translate.pretix.eu (Galician)
Currently translated at 0.0% (0 of 4483 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/gl/

powered by weblate
2021-10-29 14:17:23 +02:00
Raphael Michel
0e02febe76 Translated on translate.pretix.eu (German)
Currently translated at 100.0% (4483 of 4483 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/de/

powered by weblate
2021-10-29 14:17:23 +02:00
Raphael Michel
771f822e5f Translated on translate.pretix.eu (German (informal) (de_Informal))
Currently translated at 100.0% (4483 of 4483 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/de_Informal/

powered by weblate
2021-10-29 14:17:23 +02:00
Raphael Michel
e8936551c0 Extend spellcheck word list 2021-10-29 13:58:29 +02:00
Raphael Michel
ea0f6dfc54 Update po files
[CI skip]

Signed-off-by: Raphael Michel <michel@rami.io>
2021-10-29 12:10:08 +02:00
Raphael Michel
abeddd360e Invoices: Change expected behaviour for switches in numbering scheme 2021-10-29 12:09:09 +02:00
Maarten van den Berg
c209d195bf Translated on translate.pretix.eu (Dutch)
Currently translated at 100.0% (172 of 172 strings)

Translation: pretix/pretix (frontend)
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/nl/

powered by weblate
2021-10-29 10:24:22 +02:00
Maarten van den Berg
35c46d320c Translated on translate.pretix.eu (Dutch)
Currently translated at 99.9% (4470 of 4474 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/nl/

powered by weblate
2021-10-29 10:24:22 +02:00
Raphael Michel
30621568ab Added translation on translate.pretix.eu (Galician) 2021-10-29 10:24:22 +02:00
Raphael Michel
403c4f4499 Add Galician as an incubating language 2021-10-29 10:23:57 +02:00
Raphael Michel
884bba0088 Fix transaction creation during split order creation 2021-10-29 10:21:37 +02:00
Raphael Michel
2b52edd5b7 Remove wrong optimization 2021-10-28 11:12:16 +02:00
Richard Schreiber
a4aed96784 Fix: add support for rtl-languages to checkout-step-bars 2021-10-27 16:16:04 +02:00
Raphael Michel
4bdfd56264 E-mail layout with logo: Make image display:block for outlook 2021-10-27 11:31:33 +02:00
Raphael Michel
31f0b07325 FIx typo causing test failure 2021-10-27 11:09:00 +02:00
pretix translation bot
3f08f3a7f4 Translations update from Weblate (#2266)
Co-authored-by: Tony Pavlik <kontakt@playton.cz>
Co-authored-by: Raphael Michel <michel@rami.io>
Co-authored-by: Maarten van den Berg <maartenberg1@gmail.com>
2021-10-27 09:22:02 +02:00
Raphael Michel
93263e7567 money template filter: coerce None to 0.00 2021-10-26 18:07:37 +02:00
Raphael Michel
69cf62d2ca Fix missing or wrong create_transactions calls 2021-10-26 18:07:23 +02:00
Raphael Michel
bb353e5fde Improve detection of missing transactions 2021-10-26 18:06:49 +02:00
Raphael Michel
2dceff1218 Fix transaction creation issues and improve debugging 2021-10-26 11:33:44 +02:00
Raphael Michel
5ea8a8ef82 Ask and validate VAT IDs for Switzerland (#2259)
Co-authored-by: Richard Schreiber <schreiber@rami.io>
2021-10-26 11:20:45 +02:00
pretix translation bot
03a7a3303c Translations update from Weblate (#2264)
Co-authored-by: Tony Pavlik <kontakt@playton.cz>
Co-authored-by: Raphael Michel <michel@rami.io>
Co-authored-by: Jacek Wielemborek <github@d33.pl>
Co-authored-by: Maarten van den Berg <maartenberg1@gmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
2021-10-26 11:19:45 +02:00
Raphael Michel
2beb0b20ca Check-in API: Work around libpretixsync issue with space encoding 2021-10-26 10:46:28 +02:00
Richard Schreiber
24eea02e0d API: sort ordered items’ answers by questions’ position (#2182) 2021-10-26 09:42:01 +02:00
Raphael Michel
15ab9c72d3 Invoice renderer: Reduce a few spacings 2021-10-22 13:10:48 +02:00
Raphael Michel
c957d77fe0 Fix linter issues 2021-10-22 12:58:45 +02:00
Raphael Michel
7697018ca4 Order JSON export: Add a lot more fields 2021-10-22 12:43:41 +02:00
Raphael Michel
3980a7b2a7 Docs: Fix missing files 2021-10-22 11:06:23 +02:00
pretix translation bot
035bb56386 Translations update from Weblate (#2254)
Co-authored-by: Tony Pavlik <kontakt@playton.cz>
Co-authored-by: Raphael Michel <michel@rami.io>
Co-authored-by: Jacek Wielemborek <github@d33.pl>
Co-authored-by: Maarten van den Berg <maartenberg1@gmail.com>
2021-10-22 11:04:42 +02:00
Raphael Michel
837b03fff3 Add ugprade note to docs 2021-10-22 11:01:35 +02:00
Raphael Michel
d3dec72831 Add missing import 2021-10-22 10:26:10 +02:00
Raphael Michel
3d78f68d94 Docs: Add page on errors 2021-10-22 10:25:58 +02:00
Raphael Michel
faa43d4df8 Remove duplicate form field 2021-10-21 13:25:52 +02:00
Raphael Michel
78917afa1a Event settings API: Expose mail_days_order_expire_warning 2021-10-19 17:12:13 +02:00
Raphael Michel
4b53d39e3e Add debug command check_order_transactions 2021-10-19 17:10:08 +02:00
Raphael Michel
02db07cd25 Work around potential caching issue 2021-10-19 17:04:28 +02:00
Raphael Michel
19fb6c8c34 create_order_transactions: Make suitable for large datasets 2021-10-19 15:25:34 +02:00
Raphael Michel
0c25b2df92 Docs: Fix typo in index name 2021-10-19 15:25:15 +02:00
Raphael Michel
6a543e4557 Fix missing log message 2021-10-18 18:50:53 +02:00
Raphael Michel
846527546a Improve visual transaction table 2021-10-18 18:35:02 +02:00
Raphael Michel
c8cdb2b311 Log silent DirtyTransactionsForOrderException to sentry 2021-10-18 17:57:36 +02:00
Raphael Michel
96ff3d532d Fix logic error 2021-10-18 17:55:32 +02:00
Raphael Michel
8ebba9de86 Data model for transactional history (#2147) 2021-10-18 17:28:58 +02:00
Raphael Michel
c4e71011ee Update English wordlist 2021-10-18 13:24:38 +02:00
Raphael Michel
e71ad4bfba CSS: Always clear floats before drawing footer 2021-10-18 10:37:23 +02:00
Raphael Michel
05a5a69128 Lightbox: Remove .min.js and make dependency on gettext optional 2021-10-18 09:23:12 +02:00
Raphael Michel
bb83cd2f39 Fix duplicate margin 2021-10-17 19:16:19 +02:00
Raphael Michel
df26171ff1 Fix/Improve responsiveness of calendar pages 2021-10-17 18:55:01 +02:00
Raphael Michel
da937dc4e3 [a11y] Small fixes and improvements 2021-10-17 18:35:55 +02:00
Raphael Michel
bb9508ad96 Fix typo 2021-10-17 17:38:34 +02:00
Raphael Michel
41fed7d6a2 Translated on translate.pretix.eu (German (informal) (de_Informal))
Currently translated at 98.8% (170 of 172 strings)

Translation: pretix/pretix (frontend)
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/de_Informal/

powered by weblate
2021-10-17 17:37:17 +02:00
Raphael Michel
f441e9984d Translated on translate.pretix.eu (German)
Currently translated at 98.8% (170 of 172 strings)

Translation: pretix/pretix (frontend)
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/de/

powered by weblate
2021-10-17 17:37:17 +02:00
Raphael Michel
05c6155f37 Translated on translate.pretix.eu (German (informal) (de_Informal))
Currently translated at 100.0% (4474 of 4474 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/de_Informal/

powered by weblate
2021-10-17 17:37:17 +02:00
Raphael Michel
3c096325bd Translated on translate.pretix.eu (German)
Currently translated at 100.0% (4474 of 4474 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/de/

powered by weblate
2021-10-17 17:37:17 +02:00
Raphael Michel
d06a352df5 Update wordlist 2021-10-17 17:36:58 +02:00
Raphael Michel
ba7b1bb89e Update po files
[CI skip]

Signed-off-by: Raphael Michel <mail@raphaelmichel.de>
2021-10-17 16:57:30 +02:00
Richard Schreiber
3dcfa57b70 A11y improvements (#2081)
Co-authored-by: Raphael Michel <michel@rami.io>
Co-authored-by: Raphael Michel <mail@raphaelmichel.de>
2021-10-17 16:56:16 +02:00
Raphael Michel
cc13ca1c3f Fix #2165 -- Idempotency key errors from Stripe 2021-10-15 12:01:58 +02:00
Raphael Michel
aac67ebf83 Refs #2165 -- Lock payment object while processing Stripe response 2021-10-15 11:57:40 +02:00
Raphael Michel
b51e1cfc6f Fix #2241 -- Display timezone for sale start 2021-10-15 11:46:45 +02:00
Raphael Michel
f0508cdcc3 Fix #2228 -- Date filter behavior in order data export 2021-10-15 11:46:45 +02:00
Raphael Michel
9ed2dc7b46 Add exporter for gift card transactions 2021-10-15 11:46:45 +02:00
Raphael Michel
0e568a3fca Translated on translate.pretix.eu (Spanish)
Currently translated at 67.8% (2995 of 4413 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/es/

powered by weblate
2021-10-15 11:18:45 +02:00
ityd
7f3606ee81 Translated on translate.pretix.eu (Spanish)
Currently translated at 67.8% (2996 of 4413 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/es/

powered by weblate
2021-10-15 11:18:45 +02:00
DJG Bayern
b22d43860a Translated on translate.pretix.eu (Japanese)
Currently translated at 2.9% (5 of 171 strings)

Translation: pretix/pretix (frontend)
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/ja/

powered by weblate
2021-10-15 11:18:45 +02:00
DJG Bayern
0f9b339f01 Translated on translate.pretix.eu (Japanese)
Currently translated at 0.1% (4 of 4413 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/ja/

powered by weblate
2021-10-15 11:18:45 +02:00
DJG Bayern
cd1e9c1740 Translated on translate.pretix.eu (Japanese)
Currently translated at 0.1% (2 of 4413 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/ja/

powered by weblate
2021-10-15 11:18:45 +02:00
Raphael Michel
aec1ce53fc Added translation on translate.pretix.eu (Japanese) 2021-10-15 11:18:45 +02:00
Raphael Michel
aae129be6a Added translation on translate.pretix.eu (Japanese) 2021-10-15 11:18:45 +02:00
Tony Pavlik
b906fe0fc3 Translated on translate.pretix.eu (Czech)
Currently translated at 8.8% (390 of 4413 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/cs/

powered by weblate
2021-10-15 11:18:45 +02:00
Adri
f0f1537e9c Translated on translate.pretix.eu (French)
Currently translated at 50.7% (2238 of 4413 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/fr/

powered by weblate
2021-10-15 11:18:45 +02:00
Raphael Michel
7b7e77d497 Subevent editor: Fix Quota.ignore_for_event_availability not being copied 2021-10-15 11:12:36 +02:00
Raphael Michel
9ac705cd88 Web check-in: Show subevent with check result 2021-10-14 18:48:19 +02:00
Richard Schreiber
01d9574ddf Fix #2244 -- Show products without category first on product-list (#2249) 2021-10-13 09:33:43 +02:00
Richard Schreiber
8121167d5e Control: Add drag and drop to sort categories and products (#2242)
* add drag and drop to categories

* add drag and drop to products

* add light grey background to dragged element

* add missing th, add sr-only desc of columns

* group up/down/move elements

* improve visualizing drag-area by dimming others

* change up/down-links to buttons in form-post

* limit sorting to POST requests

Co-authored-by: Raphael Michel <michel@rami.io>
2021-10-12 14:46:56 +02:00
Raphael Michel
dde4e12ce1 Fix bug in 6cd32400a 2021-10-11 17:36:57 +02:00
Raphael Michel
6cd32400ae Mails: Add elaborate retry logic for MS Exchange 2021-10-11 12:41:26 +02:00
Raphael Michel
8fa71ccad4 Show remaining quota on voucher redemption page 2021-10-08 18:08:28 +02:00
Raphael Michel
0f47bff5cd Allow to hide products that require membership (#2240)
Co-authored-by: Richard Schreiber <schreiber@rami.io>
2021-10-07 10:11:31 +02:00
Raphael Michel
f459f1f12d Fix logging error for automated emails 2021-10-07 10:08:30 +02:00
Richard Schreiber
65167cc290 Add new alert icons (#2226) 2021-10-06 12:31:08 +02:00
Raphael Michel
bc7300c393 Track if invoices have been sent via email (#2231) 2021-10-05 13:47:55 +02:00
Jaakko Rinta-Filppula
d8450202fe Translated on translate.pretix.eu (Finnish)
Currently translated at 50.2% (86 of 171 strings)

Translation: pretix/pretix (frontend)
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/fi/

powered by weblate
2021-10-05 12:47:08 +02:00
Jaakko Rinta-Filppula
41d2bcc34f Translated on translate.pretix.eu (Finnish)
Currently translated at 19.3% (852 of 4413 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/fi/

powered by weblate
2021-10-05 12:47:08 +02:00
Fabian Rodriguez
0e1589013a Translated on translate.pretix.eu (French)
Currently translated at 63.7% (109 of 171 strings)

Translation: pretix/pretix (frontend)
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/fr/

powered by weblate
2021-10-04 17:34:05 +02:00
cpoisnel
39f81617e1 Translated on translate.pretix.eu (French)
Currently translated at 63.7% (109 of 171 strings)

Translation: pretix/pretix (frontend)
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/fr/

powered by weblate
2021-10-04 17:34:05 +02:00
cpoisnel
b394ef6de1 Translated on translate.pretix.eu (French)
Currently translated at 50.5% (2231 of 4413 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/fr/

powered by weblate
2021-10-04 17:34:05 +02:00
Raphael Michel
177906e2ac Custom order emails: Allow to attach tickets and invoices 2021-09-30 12:15:55 +02:00
Raphael Michel
59f6b20129 Add email placeholder {voucher_url_list} 2021-09-30 11:54:41 +02:00
Raphael Michel
51998e820d Orders API: Add item and variation filters 2021-09-30 11:48:23 +02:00
Raphael Michel
e803b56716 Bump to 4.4.0.dev0 2021-09-29 11:17:50 +02:00
Raphael Michel
fa8b1c176b Bump to 4.3.0 2021-09-29 11:17:12 +02:00
Richard Schreiber
2598787602 Customer profiles: add minor improvements around disabled fields and margins (#2195) 2021-09-29 10:34:45 +02:00
Raphael Michel
003fa62996 Fix help text 2021-09-28 18:07:34 +02:00
Raphael Michel
798c21955e Translated on translate.pretix.eu (German)
Currently translated at 100.0% (4413 of 4413 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/de/

powered by weblate
2021-09-27 21:52:58 +02:00
Raphael Michel
fe6185af4b Translated on translate.pretix.eu (German (informal) (de_Informal))
Currently translated at 100.0% (4413 of 4413 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/de_Informal/

powered by weblate
2021-09-27 21:52:58 +02:00
Raphael Michel
7bacefa442 Update spelling wordlist 2021-09-27 21:47:05 +02:00
Raphael Michel
04e187c297 Update po files
[CI skip]

Signed-off-by: Raphael Michel <mail@raphaelmichel.de>
2021-09-27 20:49:18 +02:00
Raphael Michel
9f2ffc3276 Improvements around the waiting list (#2219)
* Waiting list: Support for seated events, pre-fill customer email address

* Allow people to remove themselves

* Update src/pretix/base/settings.py

Co-authored-by: Richard Schreiber <schreiber@rami.io>

* Update src/pretix/control/views/waitinglist.py

Co-authored-by: Richard Schreiber <schreiber@rami.io>

* Update src/pretix/control/views/waitinglist.py

Co-authored-by: Richard Schreiber <schreiber@rami.io>

* Update src/pretix/control/views/waitinglist.py

Co-authored-by: Richard Schreiber <schreiber@rami.io>

* Update src/pretix/presale/views/waiting.py

Co-authored-by: Richard Schreiber <schreiber@rami.io>

* Resolve a review note

* Review notes

* Fix linter issues

* Fix import

Co-authored-by: Richard Schreiber <schreiber@rami.io>
2021-09-27 20:48:02 +02:00
Diego Rodrigo
a9a4cf6fca Translated on translate.pretix.eu (Portuguese (Brazil))
Currently translated at 14.5% (641 of 4403 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/pt_BR/

powered by weblate
2021-09-27 20:26:20 +02:00
ofirtro
a563316e22 Translated on translate.pretix.eu (Hebrew)
Currently translated at 20.4% (35 of 171 strings)

Translation: pretix/pretix (frontend)
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/he/

powered by weblate
2021-09-27 20:26:20 +02:00
ofirtro
4b6f55c31d Added translation on translate.pretix.eu (Hebrew) 2021-09-27 20:26:20 +02:00
Klevagruva
21a8fad17a Translated on translate.pretix.eu (Swedish)
Currently translated at 17.0% (749 of 4403 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/sv/

powered by weblate
2021-09-27 20:26:20 +02:00
Raphael Michel
7586df9d3f Docs: Fix note on widget on mobile 2021-09-27 11:34:55 +02:00
Raphael Michel
1d4afa5d27 Sendmail: Fix invalid state if attachment is adde then removed 2021-09-23 17:36:33 +02:00
Richard Schreiber
720d9b924e [Fix] on rtl-languages float productpicture to the right (#2224) 2021-09-23 17:22:50 +02:00
Raphael Michel
9f56669f2a Truelink filter: Allow dots with spaces 2021-09-23 09:50:39 +02:00
Richard Schreiber
fc541016c6 fix typo in logo-image-settings 2021-09-23 08:28:39 +02:00
Raphael Michel
5eefe9ad1e Fix linter issues 2021-09-20 16:51:48 +02:00
Raphael Michel
1d065a7672 Add setting organizer_logo_image_inherit 2021-09-17 13:33:34 +02:00
Raphael Michel
101f5f7781 Rewrite default ticket PDF to make sure caches affected by previous bug are cleaned 2021-09-17 11:55:53 +02:00
Raphael Michel
af7c6d360f Partially revert migration command monkeypatching 2021-09-17 11:07:46 +02:00
Raphael Michel
8751e6e5ba Product list: Show "sold out" before expanding variations 2021-09-17 10:20:43 +02:00
Raphael Michel
93004a8125 Customer detailv iew: Do not show names as "None" 2021-09-17 10:20:43 +02:00
Raphael Michel
adf40e1d56 Refactor our migrate command monkeypatching 2021-09-17 10:20:43 +02:00
Raphael Michel
364cfe0131 Update nginx config for static files 2021-09-17 10:20:43 +02:00
Raphael Michel
1514527ef3 Consistent naming 2021-09-17 10:20:43 +02:00
Tim Neumann
680024234d Dockerfile: Move nginx client_max_body_size to seperate file (#2207) 2021-09-16 12:36:25 +02:00
Richard Schreiber
2a3660f2d1 Fix -- copy answers even when matching customer profiles exist (#2209) 2021-09-16 10:07:43 +02:00
Richard Schreiber
2041d1213a fix address expand button submit-bug 2021-09-16 09:10:20 +02:00
Raphael Michel
42a1fe9bd1 Event settings API: Fix setting confirm_texts 2021-09-15 16:28:57 +02:00
Raphael Michel
002469d523 Fix .po file 2021-09-15 14:47:58 +02:00
pretix translation bot
5be4af1305 Translations update from Weblate (#2205)
* Translated on translate.pretix.eu (German)

Currently translated at 100.0% (4403 of 4403 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/de/

powered by weblate

* Translated on translate.pretix.eu (German (informal) (de_Informal))

Currently translated at 100.0% (4403 of 4403 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/de_Informal/

powered by weblate

Co-authored-by: Raphael Michel <michel@rami.io>
2021-09-15 13:54:39 +02:00
Raphael Michel
0b241438e1 Update po files
[CI skip]

Signed-off-by: Raphael Michel <michel@rami.io>
2021-09-15 13:44:42 +02:00
Raphael Michel
61649ab2b8 Self-service cancellation: Allow to disable auto-refunds 2021-09-15 13:43:55 +02:00
Mohamed Tawfiq
848ea999c5 Translated on translate.pretix.eu (Arabic)
Currently translated at 96.4% (165 of 171 strings)

Translation: pretix/pretix (frontend)
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/ar/

powered by weblate
2021-09-15 13:43:52 +02:00
Mohamed Tawfiq
dfa82870fb Translated on translate.pretix.eu (Arabic)
Currently translated at 88.6% (3891 of 4391 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/ar/

powered by weblate
2021-09-15 13:43:52 +02:00
Mohamed Tawfiq
e05ac7ef34 Translated on translate.pretix.eu (Arabic)
Currently translated at 88.6% (3891 of 4391 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/ar/

powered by weblate
2021-09-15 13:43:52 +02:00
Raphael Michel
ad2334bffc WebHookCall: Increase max URL size 2021-09-15 13:06:25 +02:00
Raphael Michel
17adde99fa Allow to restrict availability of variations by date, sales channel, and voucher (#2202) 2021-09-15 12:04:17 +02:00
Raphael Michel
4789d82c4e Shredder: Fix crash in AttendeeInfoShredder 2021-09-14 15:28:39 +02:00
Raphael Michel
0567e2d22b API: Fix crash on missing require_membership_types property 2021-09-14 15:28:02 +02:00
Raphael Michel
2e0592b0a6 API: Fix crash on invalid input (PRETIXEU-5A9) 2021-09-14 15:02:06 +02:00
Mie Frydensbjerg
7f6d234b4c Translated on translate.pretix.eu (Danish)
Currently translated at 59.0% (101 of 171 strings)

Translation: pretix/pretix (frontend)
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/da/

powered by weblate
2021-09-13 12:00:03 +02:00
Mie Frydensbjerg
0436de316b Translated on translate.pretix.eu (Danish)
Currently translated at 36.4% (1600 of 4391 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/da/

powered by weblate
2021-09-13 12:00:03 +02:00
Klevagruva
e16d643d2a Translated on translate.pretix.eu (Swedish)
Currently translated at 16.6% (730 of 4391 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/sv/

powered by weblate
2021-09-13 12:00:03 +02:00
Raphael Michel
bdec22cf3b Translated on translate.pretix.eu (German (informal) (de_Informal))
Currently translated at 100.0% (4391 of 4391 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/de_Informal/

powered by weblate
2021-09-13 12:00:03 +02:00
Raphael Michel
b38df27dce Order import: Fix handling of seat IDs 2021-09-09 18:14:43 +02:00
Tim Neumann
b95f556d8f Add config options for max file upload sizes (#2199)
* feat(config): Add config options for max file upload sizes

Closes #2198

* Apply suggestions from code review

Fix docs and comment in settings.py

Co-authored-by: Richard Schreiber <wiffbi@gmail.com>

* Fix import order using isort

Co-authored-by: Richard Schreiber <wiffbi@gmail.com>
2021-09-09 15:55:06 +02:00
Raphael Michel
851a4c977c Fix inconsistent handling of all_optional 2021-09-08 20:43:56 +02:00
Raphael Michel
7bffd461d1 Allow sales channels to opt out of customer accounts 2021-09-08 20:33:18 +02:00
Richard Schreiber
9a3b4f7863 Subevent: fix overflow for long lines in location 2021-09-08 13:22:57 +02:00
Raphael Michel
673a38ddc8 Cart: Display subevent location and end time in cart (#2191) 2021-09-08 11:24:39 +02:00
Richard Schreiber
a27b8bf213 Subevent: add missing verbose_name for seating plan (#2194) 2021-09-07 09:09:16 +02:00
Raphael Michel
36e6f10b37 Check-in list rule visualization: Fix broken height calculation 2021-09-06 22:35:14 +02:00
Raphael Michel
fde10d7f55 Fix missing license header 2021-09-06 21:14:25 +02:00
Raphael Michel
6b44b2f429 Translated on translate.pretix.eu (German)
Currently translated at 100.0% (4391 of 4391 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/de/

powered by weblate
2021-09-06 21:14:11 +02:00
Raphael Michel
5e9018e0fd Translated on translate.pretix.eu (German (informal) (de_Informal))
Currently translated at 100.0% (4391 of 4391 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/de_Informal/

powered by weblate
2021-09-06 21:14:11 +02:00
Raphael Michel
185f8066ae Fix incorrect part of previous commit 2021-09-06 20:58:40 +02:00
Raphael Michel
6388f7b29c Fix #2192 -- Invoice address name-field always gets overwritten with customer profile 2021-09-06 20:57:45 +02:00
Raphael Michel
4aa2c9d51d Update po files
[CI skip]

Signed-off-by: Raphael Michel <mail@raphaelmichel.de>
2021-09-06 20:51:34 +02:00
Raphael Michel
ef9256f0b0 Fix typo in cache key 2021-09-06 20:50:47 +02:00
Raphael Michel
28d78e40f9 Allow to save invoice addresses and attendee profiles to customer account (#2084)
Co-authored-by: Raphael Michel <michel@rami.io>
Co-authored-by: Richard Schreiber <wiffbi@gmail.com>
Co-authored-by: Richard Schreiber <schreiber@rami.io>
2021-09-06 20:50:25 +02:00
Klevagruva
89554a82eb Translated on translate.pretix.eu (Swedish)
Currently translated at 16.2% (708 of 4364 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/sv/

powered by weblate
2021-09-06 20:18:55 +02:00
Klevagruva
ae99e82ad1 Translated on translate.pretix.eu (Swedish)
Currently translated at 15.1% (663 of 4364 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/sv/

powered by weblate
2021-09-06 20:18:55 +02:00
rauxenz
5ea3d01b8d Translated on translate.pretix.eu (Spanish)
Currently translated at 100.0% (171 of 171 strings)

Translation: pretix/pretix (frontend)
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/es/

powered by weblate
2021-09-06 20:18:55 +02:00
rauxenz
aa2bd79b99 Translated on translate.pretix.eu (Spanish)
Currently translated at 68.2% (2977 of 4364 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/es/

powered by weblate
2021-09-06 20:18:55 +02:00
Niklas Forsström
44ee35b885 Translated on translate.pretix.eu (Swedish)
Currently translated at 14.9% (654 of 4364 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/sv/

powered by weblate
2021-09-06 20:18:55 +02:00
Niklas Forsström
22b79a8c22 Translated on translate.pretix.eu (Swedish)
Currently translated at 14.7% (643 of 4364 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/sv/

powered by weblate
2021-09-06 20:18:55 +02:00
Klevagruva
65bbd537e6 Translated on translate.pretix.eu (Swedish)
Currently translated at 14.7% (643 of 4364 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/sv/

powered by weblate
2021-09-06 20:18:55 +02:00
Klevagruva
34387d7bc0 Translated on translate.pretix.eu (Swedish)
Currently translated at 11.1% (486 of 4364 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/sv/

powered by weblate
2021-09-06 20:18:55 +02:00
ityd
ca38204313 Translated on translate.pretix.eu (Spanish)
Currently translated at 68.0% (2969 of 4364 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/es/

powered by weblate
2021-09-06 20:18:55 +02:00
Niklas Forsström
b7083eca2e Translated on translate.pretix.eu (Swedish)
Currently translated at 10.3% (452 of 4364 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/sv/

powered by weblate
2021-09-06 20:18:55 +02:00
Raphael Michel
6bb8b428dc Stripe: API keys consistently are prefered over connect keys 2021-09-06 20:18:15 +02:00
Raphael Michel
677142d0c9 API: Fix storage of Item.picture 2021-09-06 19:56:57 +02:00
Raphael Michel
d1b66e365a API: Add test case for unsetting settings 2021-09-06 19:33:17 +02:00
Raphael Michel
50154c02ce Voucher: Add error message to form_invalid 2021-09-06 19:32:40 +02:00
Raphael Michel
04375d4fcf Fix voucher form validation (Z#2384192) 2021-09-06 19:32:15 +02:00
Raphael Michel
9c1ff296bb Add missing template 2021-09-06 16:33:41 +02:00
Raphael Michel
0b3acb06b5 Invoice: Fix incorrect reference to original invoice number 2021-09-06 16:33:27 +02:00
Raphael Michel
b2cdccedd6 Docker: Specify distribution of base image, upgrade to Python 3.9 2021-09-05 12:40:21 +02:00
Raphael Michel
7ebefa7b85 Allow to manually bump carts blocking a voucher 2021-08-30 15:57:28 +02:00
Raphael Michel
c7b5baa185 Widget: Only show new tab button on connection error 2021-08-30 15:49:22 +02:00
Raphael Michel
6d08e7a8b0 Docs: libmariadbclient-dev has been replaced by libmariadb-dev 2021-08-30 12:58:15 +02:00
Raphael Michel
0da2b12646 Check-in log exporter: Expose upload time 2021-08-30 12:44:25 +02:00
Raphael Michel
a0693483dc Bump to 4.3.0.dev0 2021-08-27 16:44:36 +02:00
Raphael Michel
29826a9f08 Bump to 4.2.0 2021-08-27 16:44:03 +02:00
Raphael Michel
36a045020f Fix typo check 2021-08-27 16:05:40 +02:00
Raphael Michel
40c2b774aa Translated on translate.pretix.eu (German (informal) (de_Informal))
Currently translated at 100.0% (4364 of 4364 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/de_Informal/

powered by weblate
2021-08-27 16:04:05 +02:00
Raphael Michel
8422b2b4aa Translated on translate.pretix.eu (German)
Currently translated at 100.0% (4364 of 4364 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/de/

powered by weblate
2021-08-27 16:04:05 +02:00
Raphael Michel
ae334c4860 Update po files
[CI skip]

Signed-off-by: Raphael Michel <michel@rami.io>
2021-08-27 15:05:38 +02:00
Raphael Michel
722f36121d Docs: Fix typo 2021-08-27 15:04:37 +02:00
Richard Schreiber
529092a4ed Order import: warn when char-replacement happens due to unknown encoding (#2184)
Co-authored-by: Raphael Michel <michel@rami.io>
2021-08-27 15:03:35 +02:00
Bruno
e7068020d5 Translated on translate.pretix.eu (Portuguese (Brazil))
Currently translated at 14.1% (616 of 4357 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/pt_BR/

powered by weblate
2021-08-27 15:03:19 +02:00
Raphael Michel
08acecf37b Docs: Add algorithm diagrams for checkin 2021-08-27 12:29:21 +02:00
Raphael Michel
b200ca5ad5 Set verbose name of Event.seating_plan 2021-08-24 12:25:42 +02:00
Raphael Michel
e564952148 Web check-in: Show ticket secret on result card 2021-08-24 12:05:45 +02:00
Maarten van den Berg
f4ad2a2293 Translated on translate.pretix.eu (Dutch (informal) (nl_Informal))
Currently translated at 93.6% (4079 of 4357 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/nl_Informal/

powered by weblate
2021-08-24 09:00:42 +02:00
Maarten van den Berg
854bbf26c2 Translated on translate.pretix.eu (Dutch)
Currently translated at 100.0% (4357 of 4357 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/nl/

powered by weblate
2021-08-24 09:00:42 +02:00
Raphael Michel
ec5a670ea6 PDF: Fix inheritance of answers to add-on products 2021-08-23 18:42:21 +02:00
Raphael Michel
276add9163 Refactor bulk-generation of voucher codes into utility function 2021-08-23 12:50:02 +02:00
Raphael Michel
de977f4818 event_copy_data Signal: Add quota_map argument 2021-08-23 12:03:03 +02:00
Raphael Michel
a4827fc992 Fix sorting of devices by descending name 2021-08-20 14:23:01 +02:00
Fabian
9a002bf172 Docs: no diff in mysql / pstgres in upgrade (#2180) 2021-08-19 12:37:48 +02:00
Raphael Michel
9a7f3e2d8a Stripe: Fix crash in shredder 2021-08-19 12:09:15 +02:00
Raphael Michel
e7546a7575 Manual refunds: Do not attempt to cancel if already canceled 2021-08-19 11:03:45 +02:00
Raphael Michel
434719285b Widget: Make "voucher required" clickable 2021-08-19 10:39:36 +02:00
Raphael Michel
5bc9ba4641 Deleting products: Catch ProtectedError 2021-08-19 10:20:01 +02:00
Raphael Michel
74dd13abd5 Fix unlimited quota issues 2021-08-18 18:40:33 +02:00
Raphael Michel
ead755aa86 Report canceled orders as underpaid if necessary 2021-08-18 17:36:36 +02:00
Raphael Michel
1f46a8b91b Remove print statement 2021-08-18 16:50:03 +02:00
Raphael Michel
eb77c2f6f6 API: Allow bulk-cration of cart positions 2021-08-18 15:28:41 +02:00
Raphael Michel
c5fe615be5 Sendmail: Force preview and show number of orders (#2099) 2021-08-18 15:28:33 +02:00
Raphael Michel
f5504e11ac Translated on translate.pretix.eu (German (informal) (de_Informal))
Currently translated at 100.0% (171 of 171 strings)

Translation: pretix/pretix (frontend)
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/de_Informal/

powered by weblate
2021-08-18 15:25:38 +02:00
Raphael Michel
e88a1a52f9 Translated on translate.pretix.eu (German)
Currently translated at 100.0% (171 of 171 strings)

Translation: pretix/pretix (frontend)
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/de/

powered by weblate
2021-08-18 15:25:38 +02:00
Raphael Michel
b86d54ea9f Translated on translate.pretix.eu (German (informal) (de_Informal))
Currently translated at 100.0% (4357 of 4357 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/de_Informal/

powered by weblate
2021-08-18 15:25:38 +02:00
Raphael Michel
b6e2ed14db Translated on translate.pretix.eu (German)
Currently translated at 100.0% (4357 of 4357 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/de/

powered by weblate
2021-08-18 15:25:38 +02:00
nd
c513868afa Fix TypeError in Order.ticket_download_available on "empty" orders (#2176)
Co-authored-by: alice <tickets-dev@kulturkosmos.de>
Co-authored-by: nd <git@notandy.de>

Co-authored-by: alice <tickets-dev@kulturkosmos.de>
2021-08-17 23:42:36 +02:00
Raphael Michel
3f7664f743 Subevent bulk-creation: Fix flaky test (#2177) 2021-08-17 08:11:11 +02:00
Raphael Michel
e654b951ed Fix typo in docs 2021-08-16 17:55:33 +02:00
Raphael Michel
b5c7556abe Add FEE_TYPE_INSURANCE 2021-08-16 17:03:57 +02:00
Raphael Michel
53e3619140 Self-service cancellation: Never suggest cancellation fee higher than order total 2021-08-16 14:43:08 +02:00
Raphael Michel
e191988b81 Self-service cancellation: Do not allow to adjust fee on free orders 2021-08-16 14:40:47 +02:00
Raphael Michel
bb7fd9423b Invoice: Auto-resize font of meta data line 2021-08-16 13:07:07 +02:00
Raphael Michel
c10c6ee28d Subevent bulk creation: Resolve AmbiguousTimeError by preferring non-dst time 2021-08-16 13:07:07 +02:00
Raphael Michel
3c64733e93 Fix crash trying to thumbnail invalid image 2021-08-16 13:07:07 +02:00
dependabot[bot]
08cb045f2e Bump path-parse from 1.0.6 to 1.0.7 in /src/pretix/static/npm_dir
Bumps [path-parse](https://github.com/jbgutierrez/path-parse) from 1.0.6 to 1.0.7.
- [Release notes](https://github.com/jbgutierrez/path-parse/releases)
- [Commits](https://github.com/jbgutierrez/path-parse/commits/v1.0.7)

---
updated-dependencies:
- dependency-name: path-parse
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-08-16 11:35:45 +02:00
Martin Gross
7bf854fe0b Add uninstall-handler for plugins 2021-08-16 11:35:27 +02:00
René Georgi
f2a1e11b85 Translated on translate.pretix.eu (German)
Currently translated at 99.8% (4349 of 4357 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/de/

powered by weblate
2021-08-15 19:11:03 +02:00
amandajurno
9b07912b7f Translated on translate.pretix.eu (Portuguese)
Currently translated at 4.2% (186 of 4357 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/pt/

powered by weblate
2021-08-15 19:11:03 +02:00
amandajurno
e1cec9882a Translated on translate.pretix.eu (Portuguese)
Currently translated at 4.2% (185 of 4357 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/pt/

powered by weblate
2021-08-15 19:11:03 +02:00
Alberto Leoncio
1ff9c1a84b Translated on translate.pretix.eu (Portuguese (Brazil))
Currently translated at 12.7% (556 of 4357 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/pt_BR/

powered by weblate
2021-08-15 19:11:03 +02:00
Michael
0035825f33 Translated on translate.pretix.eu (Czech)
Currently translated at 99.4% (170 of 171 strings)

Translation: pretix/pretix (frontend)
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/cs/

powered by weblate
2021-08-15 19:11:03 +02:00
Michael
1ec73b1b33 Translated on translate.pretix.eu (Czech)
Currently translated at 4.0% (177 of 4357 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/cs/

powered by weblate
2021-08-15 19:11:03 +02:00
Svyatoslav
ed83f4558e Translated on translate.pretix.eu (Russian)
Currently translated at 58.4% (100 of 171 strings)

Translation: pretix/pretix (frontend)
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/ru/

powered by weblate
2021-08-15 19:11:03 +02:00
Svyatoslav
b18ec7605a Translated on translate.pretix.eu (Russian)
Currently translated at 25.2% (1102 of 4357 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/ru/

powered by weblate
2021-08-15 19:11:03 +02:00
Maarten van den Berg
f96bc0776d Translated on translate.pretix.eu (Dutch (informal) (nl_Informal))
Currently translated at 100.0% (171 of 171 strings)

Translation: pretix/pretix (frontend)
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/nl_Informal/

powered by weblate
2021-08-15 19:11:03 +02:00
Maarten van den Berg
629bdcd55d Translated on translate.pretix.eu (Dutch (informal) (nl_Informal))
Currently translated at 91.5% (3989 of 4357 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/nl_Informal/

powered by weblate
2021-08-15 19:11:03 +02:00
Maarten van den Berg
829fd907a1 Translated on translate.pretix.eu (Dutch)
Currently translated at 100.0% (171 of 171 strings)

Translation: pretix/pretix (frontend)
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/nl/

powered by weblate
2021-08-15 19:11:03 +02:00
Abdullah
d7fe321f36 Translated on translate.pretix.eu (Arabic)
Currently translated at 89.3% (3891 of 4357 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/ar/

powered by weblate
2021-08-15 19:11:03 +02:00
Maarten van den Berg
517432319e Translated on translate.pretix.eu (Dutch)
Currently translated at 100.0% (4357 of 4357 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/nl/

powered by weblate
2021-08-15 19:11:03 +02:00
Richard Schreiber
edef9f1b23 Control: fix typo to correctly initialize basket’s reservation_time input 2021-08-12 12:39:41 +02:00
Martin Gross
617730ab76 Update Smallscale Docker Docs: bind exposed ports to 127.0.0.1 explicitly 2021-08-10 10:47:14 -04:00
Martin Gross
7c17d041f4 Fix wrong pretix.event.deleted webhook-label 2021-08-10 09:29:58 -04:00
Richard Schreiber
9295abb80e Fix item’s panel-title in checkout questions: move variation to name (#2172) 2021-08-09 15:13:19 +02:00
Richard Schreiber
4c3192f116 Payment availability-check: ignore country if no invoice address given (#2171) 2021-08-09 15:11:49 +02:00
Richard Schreiber
9c6a2eb85a Subevent bulk edit: check if list formset has changed only if not None (#2173) 2021-08-09 15:10:55 +02:00
Martin Gross
bcbc8a542f Update po files
[CI skip]

Signed-off-by: Martin Gross <gross@rami.io>
2021-08-04 13:18:45 -04:00
pretix translation bot
a915442efc Translations update from Weblate (#2167)
* Translated on translate.pretix.eu (Dutch)

Currently translated at 100.0% (4351 of 4351 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/nl/

powered by weblate

* Translated on translate.pretix.eu (Dutch)

Currently translated at 100.0% (170 of 170 strings)

Translation: pretix/pretix (frontend)
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/nl/

powered by weblate

* Translated on translate.pretix.eu (Dutch (informal) (nl_Informal))

Currently translated at 91.7% (3991 of 4351 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/nl_Informal/

powered by weblate

* Translated on translate.pretix.eu (Dutch (informal) (nl_Informal))

Currently translated at 78.8% (134 of 170 strings)

Translation: pretix/pretix (frontend)
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/nl_Informal/

powered by weblate

Co-authored-by: Maarten van den Berg <maartenberg1@gmail.com>
2021-08-04 13:17:07 -04:00
Richard Schreiber
103631a14b Bundled items: improve label for quantity (#2166)
The label was „Number“, which is rather ambiguous. Changed it to „Quantity“.
2021-08-03 11:24:38 +02:00
Maarten van den Berg
e9d7a24cbf Translated on translate.pretix.eu (Dutch)
Currently translated at 100.0% (4351 of 4351 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/nl/

powered by weblate
2021-08-02 19:00:31 +02:00
Maarten van den Berg
cc977e441a Translated on translate.pretix.eu (Dutch)
Currently translated at 99.9% (4347 of 4351 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/nl/

powered by weblate
2021-08-02 19:00:31 +02:00
Björn Out
add9bae018 Translated on translate.pretix.eu (Dutch)
Currently translated at 99.9% (4347 of 4351 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/nl/

powered by weblate
2021-08-02 19:00:31 +02:00
Hans Fraiponts
77d157ab8e Translated on translate.pretix.eu (Dutch)
Currently translated at 100.0% (170 of 170 strings)

Translation: pretix/pretix (frontend)
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/nl/

powered by weblate
2021-08-02 19:00:31 +02:00
Raphael Michel
e42bc94329 Remove print statement 2021-08-01 17:39:17 +02:00
Raphael Michel
b4bf5f998e Check-in API: Recover from IntegrityError when saving questions 2021-07-31 11:14:36 +02:00
Raphael Michel
dc785e9dac Fix failing tests 2021-07-30 17:34:07 +02:00
Raphael Michel
8f5f95b04e nginx: Do not attempt to parse and rewrite our URLs, please 2021-07-30 16:46:03 +02:00
Raphael Michel
c86839ed41 Translated on translate.pretix.eu (Russian)
Currently translated at 25.3% (1102 of 4351 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/ru/

powered by weblate
2021-07-26 17:23:38 +02:00
Björn Out
8b6e0f0de7 Translated on translate.pretix.eu (Dutch)
Currently translated at 98.2% (167 of 170 strings)

Translation: pretix/pretix (frontend)
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/nl/

powered by weblate
2021-07-26 17:23:38 +02:00
Björn Out
a65243e4bb Translated on translate.pretix.eu (Dutch)
Currently translated at 99.8% (4344 of 4351 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/nl/

powered by weblate
2021-07-26 17:23:38 +02:00
Svyatoslav
ac028be84e Translated on translate.pretix.eu (Russian)
Currently translated at 25.3% (1103 of 4351 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/ru/

powered by weblate
2021-07-26 17:23:38 +02:00
Raphael Michel
efd5b5b1da Thumbnailer: Do not screw with image modes 2021-07-26 17:08:26 +02:00
Raphael Michel
4be618bc93 Thumbnailing: Redefine min-size as exact-size 2021-07-26 15:53:21 +02:00
Raphael Michel
7b6d5a0cc9 Exporters: Allow extended XSLX formatting 2021-07-24 17:35:33 +02:00
Raphael Michel
f367d5e675 Multi-event exports: Pass organizer, fix in staff mode 2021-07-24 16:46:37 +02:00
Raphael Michel
f9b7894c4d Add tab-content stylesheet to frontend 2021-07-24 12:51:21 +02:00
Raphael Michel
354bbb485b Pagination: Add intcomma to number of results 2021-07-24 12:51:21 +02:00
Raphael Michel
8dc5dbd547 PDF: Add placehodler for order code + positionid 2021-07-23 19:29:27 +02:00
Raphael Michel
e04793d2eb pretixSCAN: Add new security profile PretixScanNoSyncSecurityProfile 2021-07-23 18:48:40 +02:00
Raphael Michel
db65c14733 Fix style check failures 2021-07-23 18:14:52 +02:00
Raphael Michel
f10c8b229f Check-in: Ticket should not count as redeemed if only exits have been scanned so far 2021-07-23 11:15:35 +02:00
Raphael Michel
4655d8237f Check-in API: Improve handling of unknown ticket codes 2021-07-23 10:49:33 +02:00
Raphael Michel
78f4f35ca3 Add documentation on certificates plugin 2021-07-21 18:00:33 +02:00
Raphael Michel
3a01a05a08 Check-in history: Fix incorrectly linked product 2021-07-21 10:17:18 +02:00
dedecosta
1738c710cb Translated on translate.pretix.eu (Italian)
Currently translated at 16.5% (720 of 4351 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/it/

powered by weblate
2021-07-21 09:47:27 +02:00
dedecosta
d07783a453 Translated on translate.pretix.eu (Italian)
Currently translated at 100.0% (170 of 170 strings)

Translation: pretix/pretix (frontend)
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/it/

powered by weblate
2021-07-21 09:47:27 +02:00
dedecosta
1ce331f163 Translated on translate.pretix.eu (Italian)
Currently translated at 16.5% (720 of 4351 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/it/

powered by weblate
2021-07-21 09:47:27 +02:00
Abdullah
586f95bc6d Translated on translate.pretix.eu (Arabic)
Currently translated at 89.4% (3892 of 4351 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/ar/

powered by weblate
2021-07-21 09:47:27 +02:00
Frank
5620aec5f2 Translated on translate.pretix.eu (Italian)
Currently translated at 14.4% (629 of 4351 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/it/

powered by weblate
2021-07-21 09:47:27 +02:00
Martin Gross
c1dfec20f6 Always register ApplePay domains in livemode (#2154) 2021-07-21 09:41:57 +02:00
Raphael Michel
7fef81bdef Add fix and text for 0f2e90567 2021-07-20 18:14:59 +02:00
Raphael Michel
0f2e905672 Allow to remove seats that have canceled tickets 2021-07-20 17:17:26 +02:00
Raphael Michel
a57a4e7350 Subevent filter: Filter by start time 2021-07-20 09:28:15 +02:00
Raphael Michel
b57a6e982a Order import: Ignore completely empty lines 2021-07-20 08:55:07 +02:00
Raphael Michel
39736ef0d4 Check-in list: Allow to filter by date 2021-07-19 14:27:23 +02:00
Raphael Michel
f7e5f0b567 Order overview: Adjust to new filter style 2021-07-19 13:58:45 +02:00
Raphael Michel
b6078d5272 Allow to filter and sort the list of devices 2021-07-19 13:57:17 +02:00
Raphael Michel
1ed1cd33e8 Consistent styling for filter forms 2021-07-19 13:40:44 +02:00
Raphael Michel
a4a2500725 Subevent list: Allow to filter by time of day or multiple week days 2021-07-19 13:10:21 +02:00
Raphael Michel
3fb44ec9dd Widget: Fix crash if availability is unknown 2021-07-16 13:49:53 +02:00
Raphael Michel
2a96575b4d Event-level domains: Fix customer logout 2021-07-16 13:30:21 +02:00
Raphael Michel
dcf29ec63e Add workaround for issue with "back" button in Safari 2021-07-15 18:38:16 +02:00
Raphael Michel
a743605bd3 Stats: Allow queryset of subevents 2021-07-15 13:21:36 +02:00
Raphael Michel
75dc80eb09 API: Allow to set a custom pagination size 2021-07-13 18:20:12 +02:00
Richard Schreiber
ac16d9d900 Event calendar: Fix duplicate hidden inputs in week select form (#2152) 2021-07-12 16:12:07 +02:00
Raphael Michel
736d26c232 Order import: Fix redirect after import 2021-07-12 16:03:12 +02:00
Richard Schreiber
8985dfc5eb Fix #2150 -- Do not expose internal product names in modify view (#2151) 2021-07-12 15:55:32 +02:00
Martin Gross
bb80ef067a Fix nodejs doku link 2021-07-12 15:09:16 +02:00
Richard Schreiber
bdd9751f0e Widget: remove limit of 50 events in list-view by adding a load-more-button (#2144) 2021-07-10 21:37:27 +02:00
Raphael Michel
965aac6ad5 Add postfix to spellcheck list 2021-07-09 12:01:18 +02:00
Raphael Michel
e3858373d1 Fix missing Event.save() call after setting default plugins 2021-07-09 12:01:18 +02:00
Raphael Michel
fcdfae88d7 Translated on translate.pretix.eu (German (informal) (de_Informal))
Currently translated at 100.0% (4351 of 4351 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/de_Informal/

powered by weblate
2021-07-09 11:54:40 +02:00
Raphael Michel
7d5a85e26f Translated on translate.pretix.eu (German)
Currently translated at 100.0% (4351 of 4351 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/de/

powered by weblate
2021-07-09 11:54:40 +02:00
Raphael Michel
b8b2c2eba3 Update po files
[CI skip]

Signed-off-by: Raphael Michel <michel@rami.io>
2021-07-09 11:37:19 +02:00
Raphael Michel
c6a3280d69 Improve order confirmation message for free orders requiring approval 2021-07-09 11:34:36 +02:00
Raphael Michel
7f9368c415 QuestionStep: Correctly apply order of "initial" values 2021-07-08 09:28:36 +02:00
Raphael Michel
add764e3f0 Update django-i18nfield to 1.9.3 2021-07-06 11:28:45 +02:00
Raphael Michel
a3431cd51e Update django-i18nfield to 1.9.2 2021-07-06 10:31:04 +02:00
Martin Gross
9772d43235 Move PayPal postfix to end of settings list 2021-07-05 15:33:33 +02:00
Richard Schreiber
2e29e369f5 Fix celery progress: assign state/info once to avoid race-conditions (#2142) 2021-07-05 15:09:47 +02:00
Raphael Michel
9f6ce81229 Order import: Support phone number 2021-07-05 10:12:06 +02:00
Martin Gross
d67954de3f Extend WebHook URL to 255 chars (was: 200) 2021-07-05 09:56:50 +02:00
Martin Gross
d04f93d45c Revert the revertion + Fix "PayPal: Add postfix-field and pass information to custom-field" 2021-07-02 13:58:03 +02:00
Raphael Michel
ef70209ba8 Revert "PayPal: Add postfix-field and pass information to custom-field (#2137)"
This reverts commit 32f690e9d0.
2021-07-02 13:50:01 +02:00
Raphael Michel
f127cfc46a Dockerfile: Fix pretix version not known to pip 2021-07-02 10:07:49 +02:00
Raphael Michel
ec444e5bf3 Invoice: Show preview in browser during rebugging 2021-07-02 10:04:30 +02:00
Martin Gross
32f690e9d0 PayPal: Add postfix-field and pass information to custom-field (#2137) 2021-07-02 09:44:38 +02:00
Raphael Michel
9089b630ed Add new settings invoice_regenerate_allowed (#2071) 2021-07-01 14:51:08 +02:00
Richard Schreiber
0c6971ff5f Email: make responsive and show header image in MS Outlook (#2138) 2021-07-01 11:49:30 +02:00
Raphael Michel
59e92245de Merge branch 'release/4.1.x' 2021-06-30 16:39:42 +02:00
Raphael Michel
9894954233 Add static files to check-manifest ignore list 2021-06-30 16:14:27 +02:00
Raphael Michel
6e7505abd5 Add tests/plugins/sendmail to check-manifest ignore 2021-06-30 15:38:31 +02:00
Raphael Michel
9df381ec4c Do not require requirements.txt in GitLab CI 2021-06-30 15:26:53 +02:00
Raphael Michel
be726183cb Bump to 4.2.0.dev0 2021-06-30 15:15:27 +02:00
Raphael Michel
be5e2d8c33 Bump to 4.1.0 2021-06-30 15:14:46 +02:00
Raphael Michel
9da7321a19 Translated on translate.pretix.eu (German (informal) (de_Informal))
Currently translated at 97.6% (166 of 170 strings)

Translation: pretix/pretix (frontend)
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/de_Informal/

powered by weblate
2021-06-30 15:14:03 +02:00
Raphael Michel
cc7d95b805 Translated on translate.pretix.eu (German)
Currently translated at 97.6% (166 of 170 strings)

Translation: pretix/pretix (frontend)
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/de/

powered by weblate
2021-06-30 15:14:03 +02:00
Raphael Michel
5376dbffc0 Translated on translate.pretix.eu (German (informal) (de_Informal))
Currently translated at 100.0% (4342 of 4342 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/de_Informal/

powered by weblate
2021-06-30 15:14:03 +02:00
Raphael Michel
cddefd98d3 Translated on translate.pretix.eu (German)
Currently translated at 100.0% (4342 of 4342 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/de/

powered by weblate
2021-06-30 15:14:03 +02:00
Frank
5ae0e55f7e Translated on translate.pretix.eu (Italian)
Currently translated at 14.5% (629 of 4328 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/it/

powered by weblate
2021-06-30 15:14:03 +02:00
Raphael Michel
856c36b85a Add "TODO" to German wordlist as well 2021-06-30 15:09:39 +02:00
Raphael Michel
a3bc717a5b Spellcheck: Add "TODO" to word list 2021-06-30 14:55:01 +02:00
Raphael Michel
6fa198a175 Update po files
[CI skip]

Signed-off-by: Raphael Michel <michel@rami.io>
2021-06-30 14:23:50 +02:00
Raphael Michel
9596f48fed Sendmail: Copy note from rule_update to rule_create 2021-06-30 12:41:34 +02:00
Raphael Michel
11b1c81633 Sendmail: Improve performance of creating rules 2021-06-30 12:41:03 +02:00
Maico Timmerman
212f33afee Widget: Use absolute URL for poweredby URL (#2131)
When the <pretix-widget> was hosted on a separate domain, the
source code URL was pointing to the wrong page. This updates the URL to
always point to the Pretix installation.
2021-06-30 12:40:10 +02:00
Raphael Michel
3fab15d086 Team list: Rewrite search query 2021-06-29 18:06:10 +02:00
Marc-Pascal Clement
2f1dd79162 Docs: Recommend upgrade strategy "eager" (#2135)
Pip's default strategy is to keep the version of all packages which do not explicitly require an upgrade. This caused issues for me for the second time now in the migration step, because some dependencies were not compatible with the new pretix version, but not explicitly listed as a such. Also I think the "eager" strategy better resembles what happens in the docker container build, as it always installs the newest versions.
2021-06-29 14:36:35 +02:00
Raphael Michel
e00ab01235 Teams list: Fix missing pagination 2021-06-29 11:23:39 +02:00
Raphael Michel
60f0e297e3 Order import: Improve subevent handling 2021-06-28 12:13:45 +02:00
Raphael Michel
93c791e16f Thumbnails: Do not convert JPG to PNG images 2021-06-28 09:18:28 +02:00
Raphael Michel
6da8caaa2b Calendar: Do not show next week if a same-day slot is still running 2021-06-27 18:19:03 +02:00
Raphael Michel
38ffd7d6ba OrderListExporter: Fix incorrect order of columns 2021-06-27 10:25:29 +02:00
Raphael Michel
ff4f56392d Fix isort issue 2021-06-25 11:34:44 +02:00
Raphael Michel
618b67ca2f Add meta_noindex setting on organizer level 2021-06-25 11:27:12 +02:00
Raphael Michel
a856a3ef6f Stripe: Fix a rare crash with malformed settings 2021-06-25 11:27:12 +02:00
Raphael Michel
573284c480 PayPal refunds: Actively fetch sale ID if not known (#2134)
Co-authored-by: Martin Gross <gross@rami.io>
2021-06-25 11:12:52 +02:00
Martin Gross
d4712266ff Properly slice the first 10 characters for QR-secret popover hint 2021-06-25 09:32:05 +02:00
Raphael Michel
e4f542b060 Check-in: Return "revoked" code even in online mode 2021-06-22 17:56:50 +02:00
Martin Gross
1b68e8bf0e Fix global invite-url (PRETIXEU-4N7) 2021-06-22 17:52:51 +02:00
Raphael Michel
c8d464ded7 Check-in: Change text represenation of "product" 2021-06-22 17:19:42 +02:00
Raphael Michel
12ab5ace9c Check-in errors: Change text of "result.product" 2021-06-22 17:19:05 +02:00
Raphael Michel
a2126c7b15 Check-in history: Allow to filter for error reasons 2021-06-22 17:18:42 +02:00
Raphael Michel
cba2ad5333 Add Invoice.fee_type, Invoice.fee_internal_type 2021-06-22 16:58:54 +02:00
Raphael Michel
8700c41f5e Event calendar: Forcefully break very long words 2021-06-22 16:48:14 +02:00
Raphael Michel
a88fed283a Customers: Additional filter form fields 2021-06-22 16:30:26 +02:00
Raphael Michel
130ffddf48 Customers: Allow admin to trigger password reset 2021-06-22 16:23:05 +02:00
Raphael Michel
f84b612d7b OrderChange: Fix invalid quota query 2021-06-22 15:51:26 +02:00
Richard Schreiber
e1ac22067a mail: strip image_src before converting to cid 2021-06-22 13:29:25 +02:00
Raphael Michel
60c3b76ee9 Order creation API: Fix possible crash (PRETIXEU-4MS) 2021-06-22 11:00:21 +02:00
Raphael Michel
fa8552e86f Event cancellation: Fix DivisionByZero when cancelling empty events 2021-06-22 10:58:56 +02:00
Raphael Michel
ecf1a40a5e Fix rare crash in async task handling (PRETIXEU-4MB) 2021-06-22 10:57:34 +02:00
Raphael Michel
ecfeae6ad9 OrderPosition.send_mail: Support for attach_ical argument 2021-06-22 10:56:43 +02:00
Raphael Michel
3544c3f5b8 Order change page: Fix pathological performance in event series 2021-06-22 10:50:09 +02:00
Raphael Michel
d8f3a3f5be Waiting list: Warn about functional limits 2021-06-22 10:49:27 +02:00
Raphael Michel
d6849c45fe Fix handling of cancellation requests with free and unpaid orders 2021-06-22 09:41:18 +02:00
Raphael Michel
eaf663794e Translated on translate.pretix.eu (Italian)
Currently translated at 14.5% (629 of 4328 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/it/

powered by weblate
2021-06-22 09:40:34 +02:00
Frank
dbbd4fe47f Translated on translate.pretix.eu (Italian)
Currently translated at 14.5% (629 of 4328 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/it/

powered by weblate
2021-06-22 09:40:34 +02:00
Raphael Michel
abab7dc874 Translated on translate.pretix.eu (Finnish)
Currently translated at 17.6% (766 of 4328 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/fi/

powered by weblate
2021-06-22 09:40:34 +02:00
Jaakko Rinta-Filppula
11ddfc511b Translated on translate.pretix.eu (Finnish)
Currently translated at 50.8% (85 of 167 strings)

Translation: pretix/pretix (frontend)
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/fi/

powered by weblate
2021-06-22 09:40:34 +02:00
Jaakko Rinta-Filppula
3e50f3dd33 Translated on translate.pretix.eu (Finnish)
Currently translated at 17.6% (766 of 4328 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/fi/

powered by weblate
2021-06-22 09:40:34 +02:00
Abdullah
bf5becad82 Translated on translate.pretix.eu (Arabic)
Currently translated at 89.9% (3892 of 4328 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/ar/

powered by weblate
2021-06-22 09:40:34 +02:00
Abdullah
f191ce823a Translated on translate.pretix.eu (Arabic)
Currently translated at 99.4% (166 of 167 strings)

Translation: pretix/pretix (frontend)
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/ar/

powered by weblate
2021-06-22 09:40:34 +02:00
Abdullah
b03fed979f Translated on translate.pretix.eu (Arabic)
Currently translated at 89.9% (3892 of 4328 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/ar/

powered by weblate
2021-06-22 09:40:34 +02:00
Abdullah
91de41b782 Translated on translate.pretix.eu (Arabic)
Currently translated at 89.8% (3888 of 4328 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/ar/

powered by weblate
2021-06-22 09:40:34 +02:00
digitalesIch
ff1cfe269f Translated on translate.pretix.eu (German (informal) (de_Informal))
Currently translated at 99.4% (166 of 167 strings)

Translation: pretix/pretix (frontend)
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/de_Informal/

powered by weblate
2021-06-22 09:40:34 +02:00
digitalesIch
2641a40142 Translated on translate.pretix.eu (German)
Currently translated at 99.4% (166 of 167 strings)

Translation: pretix/pretix (frontend)
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/de/

powered by weblate
2021-06-22 09:40:34 +02:00
Maico Timmerman
584d869729 Event creation: call plugin installed() after persisting event (#2133)
With #2089, PluginConfig.installed() is also called for newly created events.
However, the passed event argument is still not committed. The plugins
cannot use the event to insert or select objects, as it does not have an
ID yet.
2021-06-22 09:26:21 +02:00
Maico Timmerman
8b9b86a68d Event setup: do not create new team when staff session active (#2132)
With b260cca412, team provisioning has
been disabled for users that are staff. However, this leads to strange
UX, as a new event created by a staff member, not currently in a staff
session resulted in a 404 directly after creation.

The PR updates this requirement to not need to select a team, only when
a staff session is active.
2021-06-22 09:26:09 +02:00
Raphael Michel
b7f5631ad0 _merge_csp: Avoid duplicate values 2021-06-20 10:12:47 +02:00
Raphael Michel
038413be88 Remove pretix logo from error 500 pages
It triggers to many calls to our support if a self-hosted system is down
where we can do nothing at all...
2021-06-20 09:21:42 +02:00
Raphael Michel
4508745feb Widget: Support for rate-limited servers returning error 429 2021-06-19 18:03:14 +02:00
Raphael Michel
f9fa1733b0 OrganizerIndex cache: ignore cookies starting with __ 2021-06-18 19:28:43 +02:00
Raphael Michel
d50dff4a6e Add missing license header 2021-06-18 19:28:43 +02:00
Tim Neumann
2852722b50 Add redis sentinel support (#1909)
Co-authored-by: Raphael Michel <mail@raphaelmichel.de>
2021-06-18 19:25:08 +02:00
Raphael Michel
1ef076bb9b Deal with cancelling memberships (#2130) 2021-06-17 18:10:45 +02:00
Raphael Michel
8ad53256c2 Show frontpage_text even in iframes 2021-06-17 11:06:26 +02:00
Raphael Michel
f51155a5df Waiting list emails: Add subevent, subevent_date_from placeholders 2021-06-16 16:54:04 +02:00
Raphael Michel
75f9824095 Run rjsmin over widget.js 2021-06-16 11:51:41 +02:00
Raphael Michel
9678ef3dd4 Fix missing show_names variable 2021-06-15 17:04:30 +02:00
Raphael Michel
4d945cf1e3 Proper fix for 8f05de700 2021-06-15 13:55:40 +02:00
Raphael Michel
8f05de7004 Fix incorrect pre-selection of month select box 2021-06-15 12:25:53 +02:00
Raphael Michel
72388abd57 Performance: Cache complete organizer index page, cache subevent list template fragment (#2125) 2021-06-15 00:02:47 +02:00
Raphael Michel
5801c8602e Revert "Performance: Cache complete organizer index page, cache subevent list template fragment (#2125)"
This reverts commit ba895270fa.
2021-06-14 23:16:19 +02:00
Raphael Michel
eb77f67d28 Fix AttributeError usage when creating external refund matching pending sum 2021-06-14 20:45:15 +02:00
Raphael Michel
ba895270fa Performance: Cache complete organizer index page, cache subevent list template fragment (#2125) 2021-06-14 17:12:11 +02:00
Martin Gross
cd88659351 Show secret in backend ticket barcode popover 2021-06-14 09:40:36 +02:00
Raphael Michel
eabead4768 Email renderers: Improve line height 2021-06-13 21:47:25 +02:00
Raphael Michel
9cb0cf210a Email renderer preview: Do not wrap placeholders in title tags 2021-06-13 21:45:45 +02:00
Raphael Michel
d181241a63 Email renderers: Adjust CSS for compatibility with css-inline 2021-06-13 21:45:28 +02:00
Raphael Michel
b3edb82ffd Performance: Only load ArabicReshaper once 2021-06-13 21:20:35 +02:00
Raphael Michel
eb5ed2bdf9 Performance: Do not load the same TTF font multiple times 2021-06-13 21:20:35 +02:00
Raphael Michel
c132ccd141 Performance: Replace inlinestyler with css_inline; Add drf-ujson (#2123) 2021-06-13 21:20:18 +02:00
Raphael Michel
fb2827e9ab AsyncMixin: Fix occasional crash (PRETIXEU-49P) 2021-06-11 18:09:12 +02:00
Raphael Michel
bb89bf68ef Do not show waiting list if presale is over 2021-06-11 17:58:23 +02:00
Raphael Michel
97d67d58d5 Add Order.custom_followup_at (#2124) 2021-06-11 17:08:13 +02:00
Raphael Michel
3235f90876 Translated on translate.pretix.eu (German (informal) (de_Informal))
Currently translated at 100.0% (167 of 167 strings)

Translation: pretix/pretix (frontend)
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/de_Informal/

powered by weblate
2021-06-11 13:12:18 +02:00
Raphael Michel
227b2513b4 Translated on translate.pretix.eu (German (informal) (de_Informal))
Currently translated at 98.5% (4265 of 4328 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/de_Informal/

powered by weblate
2021-06-11 13:12:18 +02:00
Raphael Michel
5952fdccb8 Translated on translate.pretix.eu (German)
Currently translated at 100.0% (4328 of 4328 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/de/

powered by weblate
2021-06-11 13:12:18 +02:00
dependabot[bot]
4874748aa2 Bump glob-parent from 5.1.1 to 5.1.2 in /src/pretix/static/npm_dir (#2122)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-06-11 12:56:24 +02:00
Raphael Michel
030ea269b0 Typocheck: Add "OK" to german wordlist 2021-06-11 12:55:50 +02:00
Raphael Michel
d187a497f9 Restructure dependencies (#2121) 2021-06-11 12:51:20 +02:00
Richard Schreiber
efc2efac84 Exporters: Fix crash in error url determination (#2114) 2021-06-09 18:40:21 +02:00
Raphael Michel
3378744a5c Translated on translate.pretix.eu (German)
Currently translated at 100.0% (167 of 167 strings)

Translation: pretix/pretix (frontend)
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/de/

powered by weblate
2021-06-09 18:39:59 +02:00
Raphael Michel
71ac461929 Translated on translate.pretix.eu (German)
Currently translated at 98.7% (4273 of 4328 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/de/

powered by weblate
2021-06-09 18:39:59 +02:00
Raphael Michel
039da531c4 Translated on translate.pretix.eu (German (informal) (de_Informal))
Currently translated at 100.0% (167 of 167 strings)

Translation: pretix/pretix (frontend)
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/de_Informal/

powered by weblate
2021-06-09 18:39:59 +02:00
Raphael Michel
91e080d962 Translated on translate.pretix.eu (German)
Currently translated at 100.0% (167 of 167 strings)

Translation: pretix/pretix (frontend)
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/de/

powered by weblate
2021-06-09 18:39:59 +02:00
Raphael Michel
b78cf9f2c5 Translated on translate.pretix.eu (German (informal) (de_Informal))
Currently translated at 98.4% (4261 of 4328 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/de_Informal/

powered by weblate
2021-06-09 18:39:59 +02:00
Raphael Michel
af68053195 Translated on translate.pretix.eu (German)
Currently translated at 98.4% (4263 of 4328 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/de/

powered by weblate
2021-06-09 18:39:59 +02:00
Maico Timmerman
2dd1e567cf Event creation: set the organizer locales as default (#2109) 2021-06-09 18:39:44 +02:00
Raphael Michel
33400ed7cc Sendmail: Fix issue during scheduling (PRETIXEU-4FY) 2021-06-09 18:15:41 +02:00
Raphael Michel
fe2e01938a Orders API: Silently fail if an answer to a question is submitted twice 2021-06-09 17:42:39 +02:00
Richard Schreiber
fccd119a1f Waitinglist: fix batch selection, add event.slug to download-filename (#2116)
* fix missing id in inputs for batch delete

* add event.slug to filename
2021-06-09 13:21:02 +02:00
Raphael Michel
b1dee5ae7c AsyncFormView: Pass query string 2021-06-07 16:55:57 +02:00
Raphael Michel
3e178a7293 Calendar: Fix hide/show of selected day contents 2021-06-07 12:52:25 +02:00
Raphael Michel
193407d819 Update po files
[CI skip]

Signed-off-by: Raphael Michel <michel@rami.io>
2021-06-07 12:48:20 +02:00
lapor-kris
25419dc8e8 Translated on translate.pretix.eu (Slovenian)
Currently translated at 30.0% (1281 of 4261 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/sl/

powered by weblate
2021-06-07 12:47:33 +02:00
Raphael Michel
cec27d7a44 Docs: Fix wrong parameter documentation 2021-06-07 12:42:01 +02:00
Raphael Michel
881f0e04a0 Do not offer to create a new customer account if a membership is required 2021-06-07 12:41:03 +02:00
Raphael Michel
5ee51c8f9a Sendmail: Fix missing gettext() call 2021-06-07 12:40:53 +02:00
Julia Luna
050f3990c3 Sendmail: Allow to disable rules (#2111) 2021-06-07 12:40:01 +02:00
Raphael Michel
b11ae9e5dd Multiple choice fields: Make search optional 2021-06-05 13:01:31 +02:00
Raphael Michel
c7ef79be90 Store all check-in attempts, not only successful ones (#2074) 2021-06-05 13:00:58 +02:00
Raphael Michel
9c3fc69176 Quotas API: Allow to bulk-query availability 2021-06-04 17:16:42 +02:00
Maico Timmerman
18df9d66bb i18n: add config for enabling a set of languages
Adds the `enabled` setting to the `[languages]` table in the pretix.cfg.
If left empty, or  omitted all languages are enabled.
2021-06-04 15:35:09 +02:00
Richard Schreiber
a43625c5e8 fix ignore_for_event_availability for batch edit of subevents (#2107) 2021-06-04 11:02:35 +02:00
Raphael Michel
a4d9d7041c Customer login: Normalize email addresses 2021-06-03 16:46:56 +02:00
Raphael Michel
96eabebc15 Stripe: Improve refund error handling 2021-06-03 16:37:02 +02:00
Raphael Michel
3819df57d8 Fix incorrect session handling in checkout flow 2021-06-03 14:22:50 +02:00
Raphael Michel
0fee7b0613 Sendmail rules: Fix typo 2021-06-03 14:22:42 +02:00
Raphael Michel
1a17f54354 Sendmail rules: Add "missed" state 2021-06-03 13:57:18 +02:00
Raphael Michel
750231eb3c Sendmail rules: Use same labels as manual mail form 2021-06-03 13:55:05 +02:00
Raphael Michel
1bb84b7296 Sendmail rules: Allow 0 days 2021-06-03 13:54:52 +02:00
Raphael Michel
6d9ef397ee Fix bug introduced in PR refactoring 2021-06-03 13:28:45 +02:00
Julia Luna
64d07a2811 Auto-scheduled emails
Co-authored-by: Raphael Michel <michel@rami.io>
2021-06-03 12:47:07 +02:00
Mohamed Tawfiq
e4949b6491 Translated on translate.pretix.eu (Arabic)
Currently translated at 91.2% (3888 of 4261 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/ar/

powered by weblate
2021-06-03 09:30:45 +02:00
Maico Timmerman
71e7df3038 Event API: Add search parameter (#2088)
The events API is paginated with 50 events. When searching for a
specific event within a large Pretix installations UX becomes very bad.
Using this filter users are able to quickly find their event by slug or
their name/location in any language.
2021-06-03 09:25:45 +02:00
Raphael Michel
47df6fe2bc Rich text: Build regex lazily (slight import time improvement) 2021-06-03 09:09:40 +02:00
Raphael Michel
ec08faf205 Docs: Fix a missing colon 2021-06-03 09:08:03 +02:00
Raphael Michel
76e86cbdd1 Fix ignore_for_event_availability in SubEvent bulk editor 2021-06-02 18:24:01 +02:00
Raphael Michel
f1b072b9a4 Fix verification of used seats in is_still_available 2021-06-02 12:35:10 +02:00
Raphael Michel
e792e8fd1e Fix sender address for organizer-level emails 2021-06-02 12:30:50 +02:00
Raphael Michel
bc8b3f504c Event API: Allow to query availability 2021-06-01 19:14:41 +02:00
Raphael Michel
0bcbfda276 Do not propose "-2-2" as a slug when cloning events 2021-06-01 16:07:19 +02:00
Raphael Michel
db029882ec Fix case-sensitive email handling in password recovery 2021-06-01 15:30:36 +02:00
Fabian
a1cc17094d Docs: Note to use UTF8 in PostgreSQL #2096 (#2103)
* Docs: Note to use UTF8 in PostgreSQL #2096

* Docs:  Same like 288f85b but for Docker docs
2021-06-01 13:57:30 +02:00
Raphael Michel
1c763ccce3 Docs: Fix missing typo 2021-06-01 12:28:51 +02:00
Raphael Michel
36fd5e7d01 Require django-countries 7.2 2021-06-01 12:20:13 +02:00
Raphael Michel
66cf9c1ac7 Docs: We do no longer require the [postgres] item 2021-06-01 12:20:13 +02:00
Fabian
0b9b67d603 Docs: Update Python Version in Nginx Config (#2101)
Debian 10 is shipping with Python 3.7 so the Debian 10 based and tested guide should use Python 3.7 in the nginx config.
2021-06-01 08:16:42 +02:00
Raphael Michel
520fb62088 Bump to 4.1.0.dev0 2021-05-31 15:27:31 +02:00
591 changed files with 342543 additions and 142346 deletions

15
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,15 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
- package-ecosystem: "pip"
directory: "/src"
schedule:
interval: "daily"
- package-ecosystem: "npm"
directory: "/src/pretix/static/npm_dir"
schedule:
interval: "monthly"

View File

@@ -33,7 +33,8 @@ jobs:
- name: Install system packages
run: sudo apt update && sudo apt install enchant hunspell aspell-en
- name: Install Dependencies
run: pip3 install -Ur doc/requirements.txt
run: pip3 install -Ur requirements.txt
working-directory: ./doc
- name: Spellcheck docs
run: make spelling
working-directory: ./doc

View File

@@ -31,7 +31,8 @@ jobs:
- name: Install system packages
run: sudo apt update && sudo apt install gettext
- name: Install Dependencies
run: pip3 install -Ur src/requirements.txt
run: pip3 install -e ".[dev]"
working-directory: ./src
- name: Compile messages
run: python manage.py compilemessages
working-directory: ./src
@@ -56,7 +57,8 @@ jobs:
- name: Install system packages
run: sudo apt update && sudo apt install enchant hunspell hunspell-de-de aspell-en aspell-de
- name: Install Dependencies
run: pip3 install -Ur src/requirements/dev.txt
run: pip3 install -e ".[dev]"
working-directory: ./src
- name: Spellcheck translations
run: potypo
working-directory: ./src

View File

@@ -29,7 +29,8 @@ jobs:
restore-keys: |
${{ runner.os }}-pip-
- name: Install Dependencies
run: pip3 install -Ur src/requirements/dev.txt
run: pip3 install -e ".[dev]" mysqlclient psycopg2-binary
working-directory: ./src
- name: Run isort
run: isort -c .
working-directory: ./src
@@ -49,7 +50,8 @@ jobs:
restore-keys: |
${{ runner.os }}-pip-
- name: Install Dependencies
run: pip3 install -r src/requirements.txt -Ur src/requirements/dev.txt
run: pip3 install -e ".[dev]" mysqlclient psycopg2-binary
working-directory: ./src
- name: Run flake8
run: flake8 .
working-directory: ./src

View File

@@ -18,17 +18,17 @@ jobs:
name: Tests
strategy:
matrix:
python-version: [3.6, 3.7, 3.8]
python-version: ["3.7", "3.8", "3.9"]
database: [sqlite, postgres, mysql]
exclude:
- database: mysql
python-version: 3.7
- database: sqlite
python-version: 3.7
python-version: "3.8"
- database: mysql
python-version: 3.6
python-version: "3.9"
- database: sqlite
python-version: 3.6
python-version: "3.7"
- database: sqlite
python-version: "3.8"
steps:
- uses: actions/checkout@v2
- uses: getong/mariadb-action@v1.1
@@ -55,9 +55,10 @@ jobs:
restore-keys: |
${{ runner.os }}-pip-
- name: Install system dependencies
run: sudo apt update && sudo apt install gettext mysql-client
run: sudo apt update && sudo apt install gettext mariadb-client
- name: Install Python dependencies
run: pip3 install -r src/requirements.txt -Ur src/requirements/dev.txt mysqlclient psycopg2-binary
run: pip3 install -e ".[dev]" mysqlclient psycopg2-binary
working-directory: ./src
- name: Run checks
run: python manage.py check
working-directory: ./src

View File

@@ -5,8 +5,8 @@ tests:
- virtualenv env
- source env/bin/activate
- pip install -U pip wheel setuptools
- XDG_CACHE_HOME=/cache pip3 install -r src/requirements.txt --no-use-pep517 -Ur src/requirements/dev.txt
- cd src
- XDG_CACHE_HOME=/cache pip3 install -e ".[dev]"
- python manage.py check
- make all compress
- py.test --reruns 3 -n 3 tests
@@ -21,8 +21,8 @@ pypi:
- virtualenv env
- source env/bin/activate
- pip install -U pip wheel setuptools check-manifest twine
- XDG_CACHE_HOME=/cache pip3 install -Ur src/requirements.txt -r src/requirements/dev.txt
- cd src
- XDG_CACHE_HOME=/cache pip3 install -e ".[dev]"
- python setup.py sdist
- pip install dist/pretix-*.tar.gz
- python -m pretix migrate

View File

@@ -1,9 +1,9 @@
FROM python:3.8
FROM python:3.9-bullseye
RUN apt-get update && \
apt-get install -y --no-install-recommends \
build-essential \
default-libmysqlclient-dev \
libmariadb-dev \
gettext \
git \
libffi-dev \
@@ -15,8 +15,7 @@ RUN apt-get update && \
libxslt1-dev \
locales \
nginx \
python-dev \
python-virtualenv \
python3-virtualenv \
python3-dev \
sudo \
supervisor \
@@ -41,17 +40,14 @@ ENV LC_ALL=C.UTF-8 \
DJANGO_SETTINGS_MODULE=production_settings
# To copy only the requirements files needed to install from PIP
COPY src/requirements /pretix/src/requirements
COPY src/requirements.txt /pretix/src
COPY src/setup.py /pretix/src/setup.py
RUN pip3 install -U \
pip \
setuptools \
wheel && \
cd /pretix/src && \
pip3 install \
-r requirements.txt \
-r requirements/memcached.txt \
-r requirements/mysql.txt \
PRETIX_DOCKER_BUILD=TRUE pip3 install \
-e ".[memcached,mysql]" \
gunicorn django-extensions ipython && \
rm -rf ~/.cache/pip
@@ -60,10 +56,11 @@ COPY deployment/docker/supervisord /etc/supervisord
COPY deployment/docker/supervisord.all.conf /etc/supervisord.all.conf
COPY deployment/docker/supervisord.web.conf /etc/supervisord.web.conf
COPY deployment/docker/nginx.conf /etc/nginx/nginx.conf
COPY deployment/docker/nginx-max-body-size.conf /etc/nginx/conf.d/nginx-max-body-size.conf
COPY deployment/docker/production_settings.py /pretix/src/production_settings.py
COPY src /pretix/src
RUN cd /pretix/src && pip3 install .
RUN cd /pretix/src && python setup.py install
RUN chmod +x /usr/local/bin/pretix && \
rm /etc/nginx/sites-enabled/default && \

View File

@@ -0,0 +1 @@
client_max_body_size 100M;

View File

@@ -16,7 +16,6 @@ http {
charset utf-8;
tcp_nopush on;
tcp_nodelay on;
client_max_body_size 100M;
log_format private '[$time_local] $host "$request" $status $body_bytes_sent';
@@ -66,9 +65,18 @@ http {
access_log off;
expires 365d;
add_header Cache-Control "public";
add_header Access-Control-Allow-Origin "*";
gzip on;
}
location / {
proxy_pass http://unix:/tmp/pretix.sock:/;
# Very important:
# proxy_pass http://unix:/tmp/pretix.sock:;
# is not the same as
# proxy_pass http://unix:/tmp/pretix.sock:/;
# In the latter case, nginx will apply its URL parsing, in the former it doesn't.
# There are situations in which pretix' API will deal with "file names" containing %2F%2F, which
# nginx will normalize to %2F, which can break ticket validation.
proxy_pass http://unix:/tmp/pretix.sock:;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
}

View File

@@ -65,6 +65,9 @@ Example::
A comma-separated list of plugins that are not available even though they are installed.
Defaults to an empty string.
``plugins_show_meta``
Whether to show authors and versions of plugins, defaults to ``on``.
``auth_backends``
A comma-separated list of available auth backends. Defaults to ``pretix.base.auth.NativeAuthBackend``.
@@ -220,12 +223,30 @@ Example::
``user``, ``password``
The SMTP user data to use for the connection. Empty by default.
``tls``, ``ssl``
Use STARTTLS or SSL for the SMTP connection. Off by default.
``from``
The email address to set as ``From`` header in outgoing emails by the system.
Default: ``pretix@localhost``
``tls``, ``ssl``
Use STARTTLS or SSL for the SMTP connection. Off by default.
``from_notifications``
The email address to set as ``From`` header in admin notification emails by the system.
Defaults to the value of ``from``.
``from_organizers``
The email address to set as ``From`` header in outgoing emails by the system sent on behalf of organizers.
Defaults to the value of ``from``.
``custom_sender_verification_required``
If this is on (the default), organizers need to verify email addresses they want to use as senders in their event.
``custom_sender_spf_string``
If this is set to a valid SPF string, pretix will show a warning if organizers use a sender address from a domain
that does not include this value.
``custom_smtp_allow_private_networks``
If this is off (the default), custom SMTP servers cannot be private network addresses.
``admins``
Comma-separated list of email addresses that should receive a report about every error code 500 thrown by pretix.
@@ -282,7 +303,7 @@ You can use an existing memcached server as pretix's caching backend::
``location``
The location of memcached, either a host:port combination or a socket file.
If no memcached is configured, pretix will use Django's built-in local-memory caching method.
If no memcached is configured, pretix will use redis for caching. If neither is configured, pretix will not use any caching.
.. note:: If you use memcached and you deploy pretix across multiple servers, you should use *one*
shared memcached instance, not multiple ones, because cache invalidations would not be
@@ -297,6 +318,12 @@ to speed up various operations::
[redis]
location=redis://127.0.0.1:6379/1
sessions=false
sentinels=[
["sentinel_host_1", 26379],
["sentinel_host_2", 26379],
["sentinel_host_3", 26379]
]
password=password
``location``
The location of redis, as a URL of the form ``redis://[:password]@localhost:6379/0``
@@ -305,13 +332,34 @@ to speed up various operations::
``session``
When this is set to ``True``, redis will be used as the session storage.
``sentinels``
Configures redis sentinels to use.
If you don't want to use redis sentinels, you should omit this option.
If this is set, redis via sentinels will be used instead of plain redis.
In this case the location should be of the form ``redis://my_master/0``.
The ``sentinels`` variable should be a json serialized list of sentinels,
each being a list with the two elements hostname and port.
You cannot provide a password within the location when using sentinels.
Note that the configuration format requires you to either place the entire
value on one line or make sure all values are indented by at least one space.
``password``
If your redis setup doesn't require a password or you already specified it in the location you can omit this option.
If this is set it will be passed to redis as the connection option PASSWORD.
If redis is not configured, pretix will store sessions and locks in the database. If memcached
is configured, memcached will be used for caching instead of redis.
Translations
------------
pretix comes with a number of translations. Some of them are marked as "incubating", which means
pretix comes with a number of translations. All languages are enabled by default. If you want to limit
the languages available in your installation, you can enable a set of languages like this::
[languages]
enabled=en,de
Some of the languages them are marked as "incubating", which means
they can usually only be selected in development mode. If you want to use them nevertheless, you
can activate them like this::
@@ -337,11 +385,22 @@ an AMQP server (e.g. RabbitMQ) as a broker and redis or your database as a resul
[celery]
broker=amqp://guest:guest@localhost:5672//
backend=redis://localhost/0
broker_transport_options="{}"
backend_transport_options="{}"
RabbitMQ might be the better choice if you have a complex, multi-server, high-performance setup,
but as you already should have a redis instance ready for session and lock storage, we recommend
redis for convenience. See the `Celery documentation`_ for more details.
The two ``transport_options`` entries can be omitted in most cases.
If they are present they need to be a valid JSON dictionary.
For possible entries in that dictionary see the `Celery documentation`_.
To use redis with sentinels set the broker or backend to ``sentinel://sentinel_host_1:26379;sentinal_host_2:26379/0``
and the respective transport_options to ``{"master_name":"mymaster"}``.
If your redis instances behind the sentinel have a password use ``sentinel://:my_password@sentinel_host_1:26379;sentinal_host_2:26379/0``.
If your redis sentinels themselves have a password set the transport_options to ``{"master_name":"mymaster","sentinel_kwargs":{"password":"my_password"}}``.
Sentry
------
@@ -396,3 +455,21 @@ pretix can make use of some external tools if they are installed. Currently, the
.. _Python documentation: https://docs.python.org/3/library/configparser.html?highlight=configparser#supported-ini-file-structure
.. _Celery documentation: http://docs.celeryproject.org/en/latest/userguide/configuration.html
Maximum upload file sizes
-------------------------
You can configure the maximum file size for uploading various files::
[pretix_file_upload]
; Max upload size for images in MiB, defaults to 10 MiB
max_size_image = 12
; Max upload size for favicons in MiB, defaults to 1 MiB
max_size_favicon = 2
; Max upload size for email attachments of manually sent emails in MiB, defaults to 10 MiB
max_size_email_attachment = 15
; Max upload size for email attachments of automatically sent emails in MiB, defaults to 1 MiB
max_size_email_auto_attachment = 2
; Max upload size for other files in MiB, defaults to 10 MiB
; This includes all file upload type order questions
max_size_other = 100

40
doc/admin/errors.rst Normal file
View File

@@ -0,0 +1,40 @@
.. _`admin-errors`:
Dealing with errors
===================
If you encounter an error in pretix, please follow the following steps to debug it:
* If the error message is shown on a **white page** and the last line of the error includes "nginx", the error is not with pretix
directly but with your nginx webserver. This might mean that pretix is not running, but it could also be something else.
Please first check your nginx error log. The default location is ``/var/log/nginx/error.log``.
* If it turns out pretix is not running, check the output of ``docker logs pretix`` for a docker installation and
``journalctl -u pretix-web.service`` for a manual installation.
* If the error message is an "**Internal Server Error**" in purple pretix design, please check pretix' log file which by default is at
``/var/pretix-data/logs/pretix.log`` if you installed with docker and ``/var/pretix/data/logs/pretix.log`` otherwise. If you don't
know how to interpret it, open a discussion on GitHub with the relevant parts of the log file.
* If the error message includes ``/usr/bin/env: node: No such file or directory``, you forgot to install ``node.js``
* If the error message includes ``OfflineGenerationError``, you might have forgot to run the ``rebuild`` step after a pretix update
or plugin installation.
* If the error message mentions your database server or redis server, make sure these are running and accessible.
* If pretix loads fine but certain actions (creating carts, orders, or exports, downloading tickets, sending emails) **take forever**,
``pretix-worker`` is not running. Check the output of ``docker logs pretix`` for a docker installation and
``journalctl -u pretix-worker.service`` for a manual installation.
* If the page loads but all **styles are missing**, you probably forgot to update your nginx configuration file after an upgrade of your
operating system's python version.
If you are unable to debug the issue any further, please open a **discussion** on GitHub in our `Q&A Forum`_. Do **not** open an issue
right away, since most things turn out not to be a bug in pretix but a mistake in your server configuration. Make sure to include
relevant log excerpts in your question.
If you're a pretix Enterprise customer, you can also reach out to support@pretix.eu with your issue right away.
.. _Q&A Forum: https://github.com/pretix/pretix/discussions/categories/q-a

View File

@@ -9,7 +9,9 @@ This documentation is for everyone who wants to install pretix on a server.
:maxdepth: 2
installation/index
updates
config
maintainance
scaling
errors
indexes

View File

@@ -50,7 +50,7 @@ Here is the currently recommended set of commands::
CREATE INDEX CONCURRENTLY pretix_addidx_orderpos_name
ON pretixbase_orderposition
USING gin (upper("attendee_name_cached") gin_trgm_ops);
CREATE INDEX CONCURRENTLY pretix_addidx_orderpos_scret
CREATE INDEX CONCURRENTLY pretix_addidx_orderpos_secret
ON pretixbase_orderposition
USING gin (upper("secret") gin_trgm_ops);
CREATE INDEX CONCURRENTLY pretix_addidx_orderpos_email

View File

@@ -36,8 +36,9 @@ Linux and firewalls, we recommend that you start with `ufw`_.
SSL certificates can be obtained for free these days. We also *do not* provide support for HTTP-only
installations except for evaluation purposes.
.. warning:: We recommend **PostgreSQL**. If you go for MySQL, make sure you run **MySQL 5.7 or newer** or
**MariaDB 10.2.7 or newer**.
.. warning:: By default, using `ufw` in conjunction will not have any effect. Please make sure to either bind the exposed
ports of your docker container explicitly to 127.0.0.1 or configure docker to respect any set up firewall
rules.
On this guide
-------------
@@ -57,8 +58,16 @@ directory writable to the user that runs pretix inside the docker container::
Database
--------
.. warning:: **Please use PostgreSQL for all new installations**. If you need to go for MySQL, make sure you run
**MySQL 5.7 or newer** or **MariaDB 10.2.7 or newer**.
Next, we need a database and a database user. We can create these with any kind of database managing tool or directly on
our database's shell. For PostgreSQL, we would do::
our database's shell. Please make sure that UTF8 is used as encoding for the best compatibility. You can check this with
the following command::
# sudo -u postgres psql -c 'SHOW SERVER_ENCODING'
For PostgreSQL database creation, we would do::
# sudo -u postgres createuser -P pretix
# sudo -u postgres createdb -O pretix pretix
@@ -82,6 +91,8 @@ When using MySQL, make sure you set the character set of the database to ``utf8m
mysql > CREATE DATABASE pretix DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci;
You will also need to make sure that ``sql_mode`` in your ``my.cnf`` file does **not** include ``ONLY_FULL_GROUP_BY``.
Redis
-----
@@ -97,6 +108,18 @@ Now restart redis-server::
# systemctl restart redis-server
In this setup, systemd will delete ``/var/run/redis`` on every redis restart, which will cause issues with pretix. To
prevent this, you can execute::
# systemctl edit redis-server
And insert the following::
[Service]
# Keep the directory around so that pretix.service in docker does not need to be
# restarted when redis is restarted.
RuntimeDirectoryPreserve=yes
.. warning:: Setting the socket permissions to 777 is a possible security problem. If you have untrusted users on your
system or have high security requirements, please don't do this and let redis listen to a TCP socket
instead. We recommend the socket approach because the TCP socket in combination with docker's networking
@@ -178,7 +201,7 @@ named ``/etc/systemd/system/pretix.service`` with the following content::
TimeoutStartSec=0
ExecStartPre=-/usr/bin/docker kill %n
ExecStartPre=-/usr/bin/docker rm %n
ExecStart=/usr/bin/docker run --name %n -p 8345:80 \
ExecStart=/usr/bin/docker run --name %n -p 127.0.0.1:8345:80 \
-v /var/pretix-data:/data \
-v /etc/pretix:/etc/pretix \
-v /var/run/redis:/var/run/redis \
@@ -228,7 +251,7 @@ The following snippet is an example on how to configure a nginx proxy for pretix
ssl_certificate_key /path/to/key.pem;
location / {
proxy_pass http://localhost:8345/;
proxy_pass http://localhost:8345;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header Host $http_host;
@@ -247,6 +270,8 @@ create an event and start selling tickets!
You should probably read :ref:`maintainance` next.
.. _`docker_updates`:
Updates
-------
@@ -262,6 +287,8 @@ Restarting the service can take a few seconds, especially if the update requires
Replace ``stable`` above with a specific version number like ``1.0`` or with ``latest`` for the development
version, if you want to.
Make sure to also read :ref:`update_notes` and the release notes of the version you are updating to.
.. _`docker_plugininstall`:
Install a plugin

View File

@@ -25,7 +25,7 @@ installation guides):
* A HTTP reverse proxy, e.g. `nginx`_ or Apache to allow HTTPS connections
* A `PostgreSQL`_ 9.6+, `MySQL`_ 5.7+, or MariaDB 10.2.7+ database server
* A `redis`_ server
* A `nodejs_` installation
* A `nodejs`_ installation
We also recommend that you use a firewall, although this is not a pretix-specific recommendation. If you're new to
Linux and firewalls, we recommend that you start with `ufw`_.
@@ -34,9 +34,6 @@ Linux and firewalls, we recommend that you start with `ufw`_.
SSL certificates can be obtained for free these days. We also *do not* provide support for HTTP-only
installations except for evaluation purposes.
.. warning:: We recommend **PostgreSQL**. If you go for MySQL, make sure you run **MySQL 5.7 or newer** or
**MariaDB 10.2.7 or newer**.
Unix user
---------
@@ -50,8 +47,16 @@ In this guide, all code lines prepended with a ``#`` symbol are commands that yo
Database
--------
.. warning:: **Please use PostgreSQL for all new installations**. If you need to go for MySQL, make sure you run
**MySQL 5.7 or newer** or **MariaDB 10.2.7 or newer**.
Having the database server installed, we still need a database and a database user. We can create these with any kind
of database managing tool or directly on our database's shell. For PostgreSQL, we would do::
of database managing tool or directly on our database's shell. Please make sure that UTF8 is used as encoding for the
best compatibility. You can check this with the following command::
# sudo -u postgres psql -c 'SHOW SERVER_ENCODING'
For PostgreSQL database creation, we would do::
# sudo -u postgres createuser pretix
# sudo -u postgres createdb -O pretix pretix
@@ -60,6 +65,8 @@ When using MySQL, make sure you set the character set of the database to ``utf8m
mysql > CREATE DATABASE pretix DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci;
You will also need to make sure that ``sql_mode`` in your ``my.cnf`` file does **not** include ``ONLY_FULL_GROUP_BY``.
Package dependencies
--------------------
@@ -67,7 +74,7 @@ To build and run pretix, you will need the following debian packages::
# apt-get install git build-essential python-dev python3-venv python3 python3-pip \
python3-dev libxml2-dev libxslt1-dev libffi-dev zlib1g-dev libssl-dev \
gettext libpq-dev libmariadbclient-dev libjpeg-dev libopenjp2-7-dev
gettext libpq-dev libmariadb-dev libjpeg-dev libopenjp2-7-dev
Config file
-----------
@@ -129,12 +136,15 @@ python installation::
$ source /var/pretix/venv/bin/activate
(venv)$ pip3 install -U pip setuptools wheel
We now install pretix, its direct dependencies and gunicorn. Replace ``postgres`` with ``mysql`` in the following
command if you're running MySQL::
We now install pretix, its direct dependencies and gunicorn::
(venv)$ pip3 install "pretix[postgres]" gunicorn
(venv)$ pip3 install pretix gunicorn
Note that you need Python 3.6 or newer. You can find out your Python version using ``python -V``.
If you're running MySQL, also install the client library::
(venv)$ pip3 install mysqlclient
Note that you need Python 3.7 or newer. You can find out your Python version using ``python -V``.
We also need to create a data directory::
@@ -229,7 +239,7 @@ The following snippet is an example on how to configure a nginx proxy for pretix
add_header X-Content-Type-Options nosniff;
location / {
proxy_pass http://localhost:8345/;
proxy_pass http://localhost:8345;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header Host $http_host;
@@ -251,14 +261,14 @@ The following snippet is an example on how to configure a nginx proxy for pretix
}
location /static/ {
alias /var/pretix/venv/lib/python3.5/site-packages/pretix/static.dist/;
alias /var/pretix/venv/lib/python3.10/site-packages/pretix/static.dist/;
access_log off;
expires 365d;
add_header Cache-Control "public";
}
}
.. note:: Remember to replace the ``python3.5`` in the ``/static/`` path in the config
.. note:: Remember to replace the ``python3.10`` in the ``/static/`` path in the config
above with your python version.
We recommend reading about setting `strong encryption settings`_ for your web server.
@@ -272,21 +282,23 @@ create an event and start selling tickets!
You should probably read :ref:`maintainance` next.
.. _`manual_updates`:
Updates
-------
.. warning:: While we try hard not to break things, **please perform a backup before every upgrade**.
To upgrade to a new pretix release, pull the latest code changes and run the following commands (again, replace
``postgres`` with ``mysql`` if necessary)::
To upgrade to a new pretix release, pull the latest code changes and run the following commands::
$ source /var/pretix/venv/bin/activate
(venv)$ pip3 install -U pretix[postgres] gunicorn
(venv)$ pip3 install -U --upgrade-strategy eager pretix gunicorn
(venv)$ python -m pretix migrate
(venv)$ python -m pretix rebuild
(venv)$ python -m pretix updatestyles
# systemctl restart pretix-web pretix-worker
Make sure to also read :ref:`update_notes` and the release notes of the version you are updating to.
.. _`manual_plugininstall`:

View File

@@ -9,6 +9,8 @@ If you host your own pretix instance, you also need to care about the availabili
of your service and the safety of your data yourself. This page gives you some
information that you might need to do so properly.
.. _`backups`:
Backups
-------

51
doc/admin/updates.rst Normal file
View File

@@ -0,0 +1,51 @@
.. _`update_notes`:
Update notes
============
pretix receives regular feature and bugfix updates and we highly encourage you to always update to
the latest version for maximum quality and security. Updates are announces on our `blog`_. There are
usually 10 feature updates in a year, so you can expect a new release almost every month.
Pure bugfix releases are only issued in case of very critical bugs or security vulnerabilities. In these
case, we'll publish bugfix releases for the last three stable release branches.
Compatibility to plugins and in very rare cases API clients may break. For in-depth details on the
API changes of every version, please refer to the release notes published on our blog.
Upgrade steps
-------------
For the actual upgrade, you can usually just follow the steps from the installation guide for :ref:`manual installations <manual_updates>`
or :ref:`docker installations <docker_updates>` respectively.
Generally, it is always strongly recommended to perform a :ref:`backup <backups>` first.
It is possible to skip versions during updates, although we recommend not skipping over major version numbers
(i.e. if you want to go from 2.4 to 4.4, first upgrade to 3.0, then upgrade to 4.0, then to 4.4).
In addition to these standard update steps, the following list issues steps that should be taken when you upgrade
to specific versions for pretix. If you're skipping versions, please read the instructions for every version in
between as well.
Upgrade to 3.17.0 or newer
""""""""""""""""""""""""""
pretix 3.17 introduces a dependency on ``nodejs``, so you should install it on your system::
# apt install nodejs npm
Upgrade to 4.4.0 or newer
"""""""""""""""""""""""""
pretix 4.4 introduces a new data structure to store historical financial data. If you already have existing
data in your database, you will need to back-fill this data or you might get incorrect reports! This is not
done automatically as part of the usual update steps since it can take a while on large databases and you might
want to do it in parallel while the system is already running again. Please execute the following command::
(venv)$ python -m pretix create_order_transactions
Or, with a docker installation::
$ docker exec -it pretix.service pretix create_order_transactions
.. _blog: https://pretix.eu/about/en/blog/

View File

@@ -99,7 +99,8 @@ following endpoint:
"hardware_brand": "Samsung",
"hardware_model": "Galaxy S",
"software_brand": "pretixdroid",
"software_version": "4.1.0"
"software_version": "4.1.0",
"info": {"arbitrary": "data"}
}
You will receive a response equivalent to the response of your initialization request.

View File

@@ -43,6 +43,8 @@ Possible permissions are:
* Can view vouchers
* Can change vouchers
.. _`rest-compat`:
Compatibility
-------------
@@ -87,7 +89,8 @@ respectively, or ``null`` if there is no such page. You can use those URLs to re
respective page.
The field ``results`` contains a list of objects representing the first results. For most
objects, every page contains 50 results.
objects, every page contains 50 results. You can specify a lower pagination size using the
``page_size`` query parameter, but no more than 50.
Conditional fetching
--------------------

View File

@@ -97,7 +97,8 @@ For example, if you want users to be redirected to ``https://example.org/order/r
either enter ``https://example.org`` or ``https://example.org/order/``.
The user will be redirected back to your page instead of pretix' order confirmation page after the payment,
**regardless of whether it was successful or not**. Make sure you use our API to check if the payment actually
**regardless of whether it was successful or not**. We will append an ``error=…`` query parameter with an error
message, but you should not rely on that and instead make sure you use our API to check if the payment actually
worked! Your final URL could look like this::
https://test.pretix.eu/democon/3vjrh/order/NSLEZ/ujbrnsjzbq4dzhck/pay/123/?return_url=https%3A%2F%2Fexample.org%2Forder%2Freturn%3Ftx_id%3D1234

View File

@@ -243,6 +243,99 @@ Cart position endpoints
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to create this
order.
.. http:post:: /api/v1/organizers/(organizer)/events/(event)/cartpositions/bulk_create/
Creates multiple new cart position. This operation is deliberately not atomic, so each cart position can succeed
or fail individually, so the response code of the response is not the only thing to look at!
.. warning:: This endpoint is considered **experimental**. It might change at any time without prior notice.
.. warning:: The same limitations as with the regular creation endpoint apply.
**Example request**:
.. sourcecode:: http
POST /api/v1/organizers/bigevents/events/sampleconf/cartpositions/bulk_create/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content-Type: application/json
[
{
"item": 1,
"variation": null,
"price": "23.00",
"attendee_name_parts": {
"given_name": "Peter",
"family_name": "Miller"
},
"attendee_email": null,
"answers": [
{
"question": 1,
"answer": "23",
"options": []
}
],
"subevent": null
},
{
"item": 1,
"variation": null,
"price": "23.00",
"attendee_name_parts": {
"given_name": "Maria",
"family_name": "Miller"
},
"attendee_email": null,
"answers": [
{
"question": 1,
"answer": "23",
"options": []
}
],
"subevent": null
}
]
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
{
"results": [
{
"success": true,
"errors": null,
"data": {
"id": 1,
...
},
},
{
"success": "false",
"errors": {
"non_field_errors": ["There is not enough quota available on quota \"Tickets\" to perform the operation."]
},
"data": null
}
]
}
:param organizer: The ``slug`` field of the organizer of the event to create positions for
:param event: The ``slug`` field of the event to create positions for
:statuscode 200: See response for success
:statuscode 400: Your input could not be parsed
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to create this
order.
.. http:delete:: /api/v1/organizers/(organizer)/events/(event)/cartpositions/(id)/
Deletes a cart position, identified by its internal ID.

View File

@@ -362,6 +362,42 @@ Endpoints
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to delete this resource.
.. http:post:: /api/v1/organizers/(organizer)/events/(event)/checkinlists/(list)/failed_checkins/
Stores a failed check-in. Only necessary for statistical purposes if you perform scan validation offline.
:<json boolean error_reason: One of ``canceled``, ``invalid``, ``unpaid``, ``product``, ``rules``, ``revoked``,
``incomplete``, ``already_redeemed``, or ``error``. Required.
:<json raw_barcode: The raw barcode you scanned. Required.
:<json datetime: Date and time of the scan. Optional.
:<json type: Type of scan, defaults to ``"entry"``.
:<json position: Internal ID of an order position you matched. Optional.
:<json raw_item: Internal ID of an item you matched. Optional.
:<json raw_variation: Internal ID of an item variation you matched. Optional.
:<json raw_subevent: Internal ID of an event series date you matched. Optional.
**Example request**:
.. sourcecode:: http
POST /api/v1/organizers/bigevents/events/sampleconf/checkinlists/1/failed_checkins/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
{
"raw_barcode": "Pvrk50vUzQd0DhdpNRL4I4OcXsvg70uA",
"error_reason": "canceled"
}
:param organizer: The ``slug`` field of the organizer to fetch
:param event: The ``slug`` field of the event to fetch
:param list: The ID of the check-in list to save for
:statuscode 201: no error
:statuscode 400: Invalid request
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to view this resource.
:statuscode 404: The requested order position or check-in list does not exist.
Order position endpoints
------------------------
@@ -424,6 +460,9 @@ Order position endpoints
"checkins": [
{
"list": 1,
"type": "entry",
"gate": null,
"device": 2,
"datetime": "2017-12-25T12:45:23Z",
"auto_checked_in": true
}
@@ -535,6 +574,9 @@ Order position endpoints
{
"list": 1,
"datetime": "2017-12-25T12:45:23Z",
"type": "entry",
"gate": null,
"device": 2,
"auto_checked_in": true
}
],
@@ -562,6 +604,8 @@ Order position endpoints
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to view this resource.
:statuscode 404: The requested order position or check-in list does not exist.
.. _`rest-checkin-redeem`:
.. http:post:: /api/v1/organizers/(organizer)/events/(event)/checkinlists/(list)/positions/(id)/redeem/
Tries to redeem an order position, identified by its internal ID, i.e. checks the attendee in. This endpoint
@@ -576,8 +620,9 @@ Order position endpoints
:<json boolean canceled_supported: When this parameter is set to ``true``, the response code ``canceled`` may be
returned. Otherwise, canceled orders will return ``unpaid``.
:<json datetime datetime: Specifies the datetime of the check-in. If not supplied, the current time will be used.
:<json boolean force: Specifies that the check-in should succeed regardless of previous check-ins or required
questions that have not been filled. Defaults to ``false``.
:<json boolean force: Specifies that the check-in should succeed regardless of revoked barcode, previous check-ins or required
questions that have not been filled. This is usually used to upload offline scans that already happened,
because there's no point in validating them since they happened whether they are valid or not. Defaults to ``false``.
:<json string type: Send ``"exit"`` for an exit and ``"entry"`` (default) for an entry.
:<json boolean ignore_unpaid: Specifies that the check-in should succeed even if the order is in pending state.
Defaults to ``false`` and only works when ``include_pending`` is set on the check-in

View File

@@ -84,6 +84,12 @@ Endpoints
The ``clone_from`` parameter has been added to the event creation endpoint.
.. versionchanged:: 4.1
The ``with_availability_for`` parameter has been added.
The ``search`` query parameter has been added to filter events by their slug, name, or location in any language.
.. http:get:: /api/v1/organizers/(organizer)/events/
Returns a list of all events within a given organizer the authenticated user/token has access to.
@@ -162,6 +168,11 @@ Endpoints
events having set their ``Format`` meta data to ``Seminar``, ``?attr[Format]=`` only those, that have no value
set. Please note that this filter will respect default values set on organizer level.
:query sales_channel: If set to a sales channel identifier, only events allowed to be sold on the specified sales channel are returned.
:query with_availability_for: If set to a sales channel identifier, the response will contain a special ``best_availability_state``
attribute with values of 100 for "tickets available", values less than 100 for "tickets sold out or reserved",
and ``null`` for "status unknown". These values might be served from a cache. This parameter can make the response
slow.
:query search: Only return events matching a given search query.
:param organizer: The ``slug`` field of a valid organizer
:statuscode 200: no error
:statuscode 401: Authentication failure

View File

@@ -1,6 +1,11 @@
Resources and endpoints
=======================
With a few exceptions, this only lists resources bundled in the pretix core modules.
Additional endpoints are provided by pretix plugins. Some of them are documented
at :ref:`plugin-docs`.
.. toctree::
:maxdepth: 2
@@ -31,5 +36,6 @@ Resources and endpoints
webhooks
seatingplans
exporters
sendmail_rules
billing_invoices
billing_var
billing_var

View File

@@ -58,6 +58,21 @@ lines list of objects The actual invo
created before this field was introduced as well as for
all lines not created by a product (e.g. a shipping or
cancellation fee).
├ subevent integer Event series date ID used to create this line. Note that everything
about the subevent might have changed since the creation
of the invoice. Can be ``null`` for all invoice lines
created before this field was introduced as well as for
all lines not created by a product (e.g. a shipping or
cancellation fee) as well as for all events that are not a series.
├ fee_type string Fee type, e.g. ``shipping``, ``service``, ``payment``,
``cancellation``, ``giftcard``, or ``other. Can be ``null`` for
all invoice lines
created before this field was introduced as well as for
all lines not created by a fee (e.g. a product).
├ fee_internal_type string Additional fee type, e.g. type of payment provider. Can be ``null``
for all invoice lines
created before this field was introduced as well as for
all lines not created by a fee (e.g. a product).
├ event_date_from datetime Start date of the (sub)event this line was created for as it
was set during invoice creation. Can be ``null`` for all invoice
lines created before this was introduced as well as for lines in
@@ -69,6 +84,12 @@ lines list of objects The actual invo
an event series not created by a product (e.g. shipping or
cancellation fees) as well as whenever the respective (sub)event
has no end date set.
├ event_location string Location of the (sub)event this line was created for as it
was set during invoice creation. Can be ``null`` for all invoice
lines created before this was introduced as well as for lines in
an event series not created by a product (e.g. shipping or
cancellation fees) as well as whenever the respective (sub)event
has no location set.
├ attendee_name string Attendee name at time of invoice creation. Can be ``null`` if no
name was set or if names are configured to not be added to invoices.
├ gross_value money (string) Price including taxes
@@ -97,6 +118,18 @@ internal_reference string Customer's refe
``lines.event_date_to``, and ``lines.attendee_name`` have been added.
``refers`` now returns an invoice number including the prefix.
.. versionchanged:: 4.1
The attributes ``fee_type`` and ``fee_internal_type`` have been added.
.. versionchanged:: 4.1
The attribute ``lines.event_location`` has been added.
.. versionchanged:: 4.6
The attribute ``lines.subevent`` has been added.
Endpoints
---------
@@ -162,8 +195,12 @@ Endpoints
"description": "Budget Ticket",
"item": 1234,
"variation": 245,
"subevent": null,
"fee_type": null,
"fee_internal_type": null,
"event_date_from": "2017-12-27T10:00:00Z",
"event_date_to": null,
"event_location": "Heidelberg",
"attendee_name": null,
"gross_value": "23.00",
"tax_value": "0.00",
@@ -248,8 +285,12 @@ Endpoints
"description": "Budget Ticket",
"item": 1234,
"variation": 245,
"subevent": null,
"fee_type": null,
"fee_internal_type": null,
"event_date_from": "2017-12-27T10:00:00Z",
"event_date_to": null,
"event_location": "Heidelberg",
"attendee_name": null,
"gross_value": "23.00",
"tax_value": "0.00",

View File

@@ -24,8 +24,25 @@ active boolean If ``false``, t
description multi-lingual string A public description of the variation. May contain
Markdown syntax or can be ``null``.
position integer An integer, used for sorting
require_approval boolean If ``true``, orders with this variation will need to be
approved by the event organizer before they can be
paid.
require_membership boolean If ``true``, booking this variation requires an active membership.
require_membership_hidden boolean If ``true`` and ``require_membership`` is set, this variation will
be hidden from users without a valid membership.
require_membership_types list of integers Internal IDs of membership types valid if ``require_membership`` is ``true``
sales_channels list of strings Sales channels this variation is available on, such as
``"web"`` or ``"resellers"``. Defaults to all existing sales channels.
The item-level list takes precedence, i.e. a sales
channel needs to be on both lists for the item to be
available.
available_from datetime The first date time at which this variation can be bought
(or ``null``).
available_until datetime The last date time at which this variation can be bought
(or ``null``).
hide_without_voucher boolean If ``true``, this variation is only shown during the voucher
redemption process, but not in the normal shop
frontend.
===================================== ========================== =======================================================
Endpoints
@@ -62,8 +79,14 @@ Endpoints
"en": "S"
},
"active": true,
"require_approval": false,
"require_membership": false,
"require_membership_hidden": false,
"require_membership_types": [],
"sales_channels": ["web"],
"available_from": null,
"available_until": null,
"hide_without_voucher": false,
"description": {
"en": "Test2"
},
@@ -78,7 +101,9 @@ Endpoints
"en": "L"
},
"active": true,
"require_approval": false,
"require_membership": false,
"require_membership_hidden": false,
"require_membership_types": [],
"description": {},
"position": 1,
@@ -127,8 +152,14 @@ Endpoints
"price": "10.00",
"original_price": null,
"active": true,
"require_approval": false,
"require_membership": false,
"require_membership_hidden": false,
"require_membership_types": [],
"sales_channels": ["web"],
"available_from": null,
"available_until": null,
"hide_without_voucher": false,
"description": null,
"position": 0
}
@@ -158,8 +189,14 @@ Endpoints
"value": {"en": "Student"},
"default_price": "10.00",
"active": true,
"require_approval": false,
"require_membership": false,
"require_membership_hidden": false,
"require_membership_types": [],
"sales_channels": ["web"],
"available_from": null,
"available_until": null,
"hide_without_voucher": false,
"description": null,
"position": 0
}
@@ -179,8 +216,14 @@ Endpoints
"price": "10.00",
"original_price": null,
"active": true,
"require_approval": false,
"require_membership": false,
"require_membership_hidden": false,
"require_membership_types": [],
"sales_channels": ["web"],
"available_from": null,
"available_until": null,
"hide_without_voucher": false,
"description": null,
"position": 0
}
@@ -231,8 +274,14 @@ Endpoints
"price": "10.00",
"original_price": null,
"active": false,
"require_approval": false,
"require_membership": false,
"require_membership_hidden": false,
"require_membership_types": [],
"sales_channels": ["web"],
"available_from": null,
"available_until": null,
"hide_without_voucher": false,
"description": null,
"position": 1
}

View File

@@ -70,6 +70,8 @@ require_approval boolean If ``true``, or
paid.
require_bundling boolean If ``true``, this item is only available as part of bundles.
require_membership boolean If ``true``, booking this item requires an active membership.
require_membership_hidden boolean If ``true`` and ``require_membership`` is set, this product will
be hidden from users without a valid membership.
require_membership_types list of integers Internal IDs of membership types valid if ``require_membership`` is ``true``
grant_membership_type integer If set to the internal ID of a membership type, purchasing this item will
create a membership of the given type.
@@ -105,8 +107,22 @@ variations list of objects A list with one
├ active boolean If ``false``, this variation will not be sold or shown.
├ description multi-lingual string A public description of the variation. May contain
├ require_membership boolean If ``true``, booking this variation requires an active membership.
├ require_membership_hidden boolean If ``true`` and ``require_membership`` is set, this variation will
be hidden from users without a valid membership.
├ require_membership_types list of integers Internal IDs of membership types valid if ``require_membership`` is ``true``
Markdown syntax or can be ``null``.
├ sales_channels list of strings Sales channels this variation is available on, such as
``"web"`` or ``"resellers"``. Defaults to all existing sales channels.
The item-level list takes precedence, i.e. a sales
channel needs to be on both lists for the item to be
available.
├ available_from datetime The first date time at which this variation can be bought
(or ``null``).
├ available_until datetime The last date time at which this variation can be bought
(or ``null``).
├ hide_without_voucher boolean If ``true``, this variation is only shown during the voucher
redemption process, but not in the normal shop
frontend.
└ position integer An integer, used for sorting
addons list of objects Definition of add-ons that can be chosen for this item.
Only writable during creation,
@@ -143,6 +159,10 @@ meta_data object Values set for
The attributes ``require_membership``, ``require_membership_types``, ``grant_membership_type``, ``grant_membership_duration_like_event``,
``grant_membership_duration_days`` and ``grant_membership_duration_months`` have been added.
.. versionchanged:: 4.4
The attributes ``require_membership_hidden`` attribute has been added.
Notes
-----
@@ -230,6 +250,10 @@ Endpoints
"active": true,
"require_membership": false,
"require_membership_types": [],
"sales_channels": ["web"],
"available_from": null,
"available_until": null,
"hide_without_voucher": false,
"description": null,
"position": 0
},
@@ -241,6 +265,10 @@ Endpoints
"active": true,
"require_membership": false,
"require_membership_types": [],
"sales_channels": ["web"],
"available_from": null,
"available_until": null,
"hide_without_voucher": false,
"description": null,
"position": 1
}
@@ -337,6 +365,10 @@ Endpoints
"require_membership": false,
"require_membership_types": [],
"description": null,
"sales_channels": ["web"],
"available_from": null,
"available_until": null,
"hide_without_voucher": false,
"position": 0
},
{
@@ -347,6 +379,10 @@ Endpoints
"active": true,
"require_membership": false,
"require_membership_types": [],
"sales_channels": ["web"],
"available_from": null,
"available_until": null,
"hide_without_voucher": false,
"description": null,
"position": 1
}
@@ -422,6 +458,10 @@ Endpoints
"active": true,
"require_membership": false,
"require_membership_types": [],
"sales_channels": ["web"],
"available_from": null,
"available_until": null,
"hide_without_voucher": false,
"description": null,
"position": 0
},
@@ -433,6 +473,10 @@ Endpoints
"active": true,
"require_membership": false,
"require_membership_types": [],
"sales_channels": ["web"],
"available_from": null,
"available_until": null,
"hide_without_voucher": false,
"description": null,
"position": 1
}
@@ -497,6 +541,10 @@ Endpoints
"active": true,
"require_membership": false,
"require_membership_types": [],
"sales_channels": ["web"],
"available_from": null,
"available_until": null,
"hide_without_voucher": false,
"description": null,
"position": 0
},
@@ -508,6 +556,10 @@ Endpoints
"active": true,
"require_membership": false,
"require_membership_types": [],
"sales_channels": ["web"],
"available_from": null,
"available_until": null,
"hide_without_voucher": false,
"description": null,
"position": 1
}
@@ -603,6 +655,10 @@ Endpoints
"active": true,
"require_membership": false,
"require_membership_types": [],
"sales_channels": ["web"],
"available_from": null,
"available_until": null,
"hide_without_voucher": false,
"description": null,
"position": 0
},
@@ -614,6 +670,10 @@ Endpoints
"active": true,
"require_membership": false,
"require_membership_types": [],
"sales_channels": ["web"],
"available_from": null,
"available_until": null,
"hide_without_voucher": false,
"description": null,
"position": 1
}

View File

@@ -41,6 +41,7 @@ payment_date date **DEPRECATED AN
payment_provider string **DEPRECATED AND INACCURATE** Payment provider used for this order
total money (string) Total value of this order
comment string Internal comment on this order
custom_followup_at date Internal date for a custom follow-up action
checkin_attention boolean If ``true``, the check-in app should show a warning
that this ticket requires special attention if a ticket
of this order is scanned.
@@ -123,6 +124,18 @@ last_modified datetime Last modificati
The ``customer`` attribute has been added.
.. versionchanged:: 4.1
The ``custom_followup_at`` attribute has been added.
.. versionchanged:: 4.4
The ``item`` and ``variation`` query parameters have been added.
.. versionchanged:: 4.6
The ``subevent`` query parameters has been added.
.. _order-position-resource:
@@ -160,11 +173,13 @@ secret string Secret code pri
addon_to integer Internal ID of the position this position is an add-on for (or ``null``)
subevent integer ID of the date inside an event series this position belongs to (or ``null``).
pseudonymization_id string A random ID, e.g. for use in lead scanning apps
checkins list of objects List of check-ins with this ticket
checkins list of objects List of **successful** check-ins with this ticket
├ id integer Internal ID of the check-in event
├ list integer Internal ID of the check-in list
├ datetime datetime Time of check-in
├ type string Type of scan (defaults to ``entry``)
├ gate integer Internal ID of the gate. Can be ``null``.
├ device integer Internal ID of the device. Can be ``null``.
└ auto_checked_in boolean Indicates if this check-in been performed automatically by the system
downloads list of objects List of ticket download options
├ output string Ticket output provider (e.g. ``pdf``, ``passbook``)
@@ -305,6 +320,7 @@ List of all orders
"fees": [],
"total": "23.00",
"comment": "",
"custom_followup_at": null,
"checkin_attention": false,
"require_approval": false,
"invoice_address": {
@@ -355,6 +371,8 @@ List of all orders
{
"list": 44,
"type": "entry",
"gate": null,
"device": 2,
"datetime": "2017-12-25T12:45:23Z",
"auto_checked_in": false
}
@@ -405,6 +423,8 @@ List of all orders
:query string code: Only return orders that match the given order code
:query string status: Only return orders in the given order status (see above)
:query string search: Only return orders matching a given search query
:query integer item: Only return orders with a position that contains this item ID. *Warning:* Result will also include orders if they contain mixed items, and it will even return orders where the item is only contained in a canceled position.
:query integer variation: Only return orders with a position that contains this variation ID. *Warning:* Result will also include orders if they contain mixed items and variations, and it will even return orders where the variation is only contained in a canceled position.
:query boolean testmode: Only return orders with ``testmode`` set to ``true`` or ``false``
:query boolean require_approval: If set to ``true`` or ``false``, only categories with this value for the field
``require_approval`` will be returned.
@@ -417,6 +437,7 @@ List of all orders
recommend using this in combination with ``testmode=false``, since test mode orders can vanish at any time and
you will not notice it using this method.
:query datetime created_since: Only return orders that have been created since the given date.
:query integer subevent: Only return orders with a position that contains this subevent ID. *Warning:* Result will also include orders if they contain mixed subevents, and it will even return orders where the subevent is only contained in a canceled position.
:query datetime subevent_after: Only return orders that contain a ticket for a subevent taking place after the given date. This is an exclusive after, and it considers the **end** of the subevent (or its start, if the end is not set).
:query datetime subevent_before: Only return orders that contain a ticket for a subevent taking place after the given date. This is an exclusive before, and it considers the **start** of the subevent.
:query string exclude: Exclude a field from the output, e.g. ``fees`` or ``positions.downloads``. Can be used as a performance optimization. Can be passed multiple times.
@@ -474,6 +495,7 @@ Fetching individual orders
"fees": [],
"total": "23.00",
"comment": "",
"custom_followup_at": null,
"checkin_attention": false,
"require_approval": false,
"invoice_address": {
@@ -524,6 +546,8 @@ Fetching individual orders
{
"list": 44,
"type": "entry",
"gate": null,
"device": 2,
"datetime": "2017-12-25T12:45:23Z",
"auto_checked_in": false
}
@@ -638,6 +662,8 @@ Updating order fields
* ``comment``
* ``custom_followup_at``
* ``invoice_address`` (you always need to supply the full object, or ``null`` to delete the current address)
**Example request**:
@@ -811,7 +837,9 @@ Creating orders
charge will be created), this is just informative in case you *handled the payment already*.
* ``payment_date`` (optional) Date and time of the completion of the payment.
* ``comment`` (optional)
* ``custom_followup_at`` (optional)
* ``checkin_attention`` (optional)
* ``require_approval`` (optional)
* ``invoice_address`` (optional)
* ``company``
@@ -871,8 +899,9 @@ Creating orders
* ``force`` (optional). If set to ``true``, quotas will be ignored.
* ``send_email`` (optional). If set to ``true``, the same emails will be sent as for a regular order, regardless of
whether these emails are enabled for certain sales channels. Defaults to
``false``. Used to be ``send_mail`` before pretix 3.14.
whether these emails are enabled for certain sales channels. If set to ``null``, behavior will be controlled by pretix'
settings based on the sales channels (added in pretix 4.7). Defaults to ``false``.
Used to be ``send_mail`` before pretix 3.14.
If you want to use add-on products, you need to set the ``positionid`` fields of all positions manually
to incrementing integers starting with ``1``. Then, you can reference one of these
@@ -1421,6 +1450,8 @@ List of all order positions
{
"list": 44,
"type": "entry",
"gate": null,
"device": 2,
"datetime": "2017-12-25T12:45:23Z",
"auto_checked_in": false
}
@@ -1527,6 +1558,8 @@ Fetching individual positions
{
"list": 44,
"type": "entry",
"gate": null,
"device": 2,
"datetime": "2017-12-25T12:45:23Z",
"auto_checked_in": false
}

View File

@@ -28,12 +28,22 @@ closed boolean Whether the quo
field).
release_after_exit boolean Whether the quota regains capacity as soon as some tickets
have been scanned at an exit.
available boolean Whether this quota is available. Only returned if ``with_availability=true``
is set on the request. Do not rely on this value for critical operations, it may be
slightly out of date.
available_number integer Number of available tickets. Only returned if ``with_availability=true``
is set on the request. Do not rely on this value for critical operations, it may be
slightly out of date. ``null`` means unlimited.
===================================== ========================== =======================================================
.. versionchanged:: 3.10
The attribute ``release_after_exit`` has been added.
.. versionchanged:: 4.1
The ``with_availability`` query parameter has been added.
Endpoints
---------
@@ -80,6 +90,7 @@ Endpoints
:query string ordering: Manually set the ordering of results. Valid fields to be used are ``id`` and ``position``.
Default: ``position``
:query integer subevent: Only return quotas of the sub-event with the given ID
:query string with_availability: Set to ``true`` to get availability information. Can lead to increased answer times.
:param organizer: The ``slug`` field of the organizer to fetch
:param event: The ``slug`` field of the event to fetch
:statuscode 200: no error
@@ -120,6 +131,7 @@ Endpoints
:param organizer: The ``slug`` field of the organizer to fetch
:param event: The ``slug`` field of the event to fetch
:param id: The ``id`` field of the quota to fetch
:query string with_availability: Set to ``true`` to get availability information. Can lead to increased answer times.
:statuscode 200: no error
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to view this resource.

View File

@@ -0,0 +1,281 @@
Automated email rules
=====================
Resource description
--------------------
Automated email rules that specify emails that the system will send automatically at a specific point in time, e.g.
the day of the event.
.. rst-class:: rest-resource-table
===================================== ========================== =======================================================
Field Type Description
===================================== ========================== =======================================================
id integer Internal ID of the rule
enabled boolean If ``false``, the rule is ignored
subject multi-lingual string The subject of the email
template multi-lingual string The body of the email
all_products boolean If ``true``, the email is sent to buyers of all products
limit_products list of integers List of product IDs, if ``all_products`` is not set
include_pending boolean If ``true``, the email is sent to pending orders. If ``false``,
only paid orders are considered.
date_is_absolute boolean If ``true``, the email is set at a specific point in time.
send_date datetime If ``date_is_absolute`` is set: Date and time to send the email.
send_offset_days integer If ``date_is_absolute`` is not set, this is the number of days
before/after the email is sent.
send_offset_time time If ``date_is_absolute`` is not set, this is the time of day the
email is sent on the day specified by ``send_offset_days``.
offset_to_event_end boolean If ``true``, ``send_offset_days`` is relative to the event end
date. Otherwise it is relative to the event start date.
offset_is_after boolean If ``true``, ``send_offset_days`` is the number of days **after**
the event start or end date. Otherwise it is the number of days
**before**.
send_to string Can be ``"orders"`` if the email should be sent to customers
(one email per order),
``"attendees"`` if the email should be sent to every attendee,
or ``"both"``.
date. Otherwise it is relative to the event start date.
===================================== ========================== =======================================================
Endpoints
---------
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/sendmail_rules/
Returns a list of all rules configured for an event.
**Example request**:
.. sourcecode:: http
GET /api/v1/organizers/bigevents/events/sampleconf/sendmail_rules/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
{
"count": 1,
"next": null,
"previous": null,
"results": [
{
"id": 1,
"enabled": true,
"subject": {"en": "See you tomorrow!"},
"template": {"en": "Don't forget your tickets, download them at {url}"},
"all_products": true,
"limit_products": [],
"include_pending": false,
"send_date": null,
"send_offset_days": 1,
"send_offset_time": "18:00",
"date_is_absolute": false,
"offset_to_event_end": false,
"offset_is_after": false,
"send_to": "orders"
}
]
}
:query page: The page number in case of a multi-page result set, default is 1
:param organizer: The ``slug`` field of a valid organizer
:param event: The ``slug`` field of the event to fetch
:statuscode 200: no error
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer does not exist **or** you have no permission to view it.
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/sendmail_rules/(id)/
Returns information on one rule, identified by its ID.
**Example request**:
.. sourcecode:: http
GET /api/v1/organizers/bigevents/events/sampleconf/sendmail_rules/1/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
{
"id": 1,
"enabled": true,
"subject": {"en": "See you tomorrow!"},
"template": {"en": "Don't forget your tickets, download them at {url}"},
"all_products": true,
"limit_products": [],
"include_pending": false,
"send_date": null,
"send_offset_days": 1,
"send_offset_time": "18:00",
"date_is_absolute": false,
"offset_to_event_end": false,
"offset_is_after": false,
"send_to": "orders"
}
:param organizer: The ``slug`` field of the organizer to fetch
:param event: The ``slug`` field of the event to fetch
:param id: The ``id`` field of the rule to fetch
:statuscode 200: no error
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event/rule does not exist **or** you have no permission to view it.
.. http:post:: /api/v1/organizers/(organizer)/events/(event)/sendmail_rules/
Create a new rule.
**Example request**:
.. sourcecode:: http
POST /api/v1/organizers/bigevents/events/sampleconf/sendmail_rules/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content-Type: application/json
Content-Length: 166
{
"enabled": true,
"subject": {"en": "See you tomorrow!"},
"template": {"en": "Don't forget your tickets, download them at {url}"},
"all_products": true,
"limit_products": [],
"include_pending": false,
"send_date": null,
"send_offset_days": 1,
"send_offset_time": "18:00",
"date_is_absolute": false,
"offset_to_event_end": false,
"offset_is_after": false,
"send_to": "orders"
}
**Example response**:
.. sourcecode:: http
HTTP/1.1 201 Created
Vary: Accept
Content-Type: application/json
{
"id": 1,
"enabled": true,
"subject": {"en": "See you tomorrow!"},
"template": {"en": "Don't forget your tickets, download them at {url}"},
"all_products": true,
"limit_products": [],
"include_pending": false,
"send_date": null,
"send_offset_days": 1,
"send_offset_time": "18:00",
"date_is_absolute": false,
"offset_to_event_end": false,
"offset_is_after": false,
"send_to": "orders"
}
:param organizer: The ``slug`` field of the organizer to create a rule for
:param event: The ``slug`` field of the event to create a rule for
:statuscode 201: no error
:statuscode 400: The rule could not be created due to invalid submitted data.
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to create rules.
.. http:patch:: /api/v1/organizers/(organizer)/events/(event)/sendmail_rules/(id)/
Update a rule. You can also use ``PUT`` instead of ``PATCH``. With ``PUT``, you have to provide all fields of
the resource, other fields will be reset to default. With ``PATCH``, you only need to provide the fields that you
want to change.
**Example request**:
.. sourcecode:: http
PATCH /api/v1/organizers/bigevents/events/sampleconf/sendmail_rules/1/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content-Type: application/json
Content-Length: 34
{
"enabled": false,
}
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: text/javascript
{
"id": 1,
"enabled": false,
"subject": {"en": "See you tomorrow!"},
"template": {"en": "Don't forget your tickets, download them at {url}"},
"all_products": true,
"limit_products": [],
"include_pending": false,
"send_date": null,
"send_offset_days": 1,
"send_offset_time": "18:00",
"date_is_absolute": false,
"offset_to_event_end": false,
"offset_is_after": false,
"send_to": "orders"
}
:param organizer: The ``slug`` field of the organizer to modify
:param event: The ``slug`` field of the event to modify
:param id: The ``id`` field of the rule to modify
:statuscode 200: no error
:statuscode 400: The rule could not be modified due to invalid submitted data.
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event/rule does not exist **or** you have no permission to change it.
.. http:delete:: /api/v1/organizers/(organizer)/events/(event)/sendmail_rules/(id)/
Delete a rule.
**Example request**:
.. sourcecode:: http
DELETE /api/v1/organizers/bigevents/events/sampleconf/sendmail_rules/1/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 204 No Content
Vary: Accept
:param organizer: The ``slug`` field of the organizer to modify
:param event: The ``slug`` field of the event to modify
:param id: The ``id`` field of the rule to delete
:statuscode 204: no error
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event/rule does not exist **or** you have no permission to change it **or** this rule cannot be deleted since it is currently in use.

View File

@@ -82,6 +82,10 @@ Endpoints
The sub-events resource can now be filtered by meta data attributes.
.. versionchanged:: 4.1
The ``with_availability_for`` parameter has been added.
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/subevents/
Returns a list of all sub-events of an event.
@@ -152,6 +156,10 @@ Endpoints
only those sub-events having set their ``Format`` meta data to ``Seminar``, ``?attr[Format]=`` only those, that
have no value set. Please note that this filter will respect default values set on
organizer or event level.
:query with_availability_for: If set to a sales channel identifier, the response will contain a special ``best_availability_state``
attribute with values of 100 for "tickets available", values less than 100 for "tickets sold out or reserved",
and ``null`` for "status unknown". These values might be served from a cache. This parameter can make the response
slow.
:statuscode 200: no error
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer does not exist **or** you have no permission to view it.

View File

@@ -16,15 +16,22 @@ Field Type Description
===================================== ========================== =======================================================
id integer Internal ID of the tax rule
name multi-lingual string The tax rules' name
internal_name string An optional name that is only used in the backend
rate decimal (string) Tax rate in percent
price_includes_tax boolean If ``true`` (default), tax is assumed to be included in
the specified product price
eu_reverse_charge boolean If ``true``, EU reverse charge rules are applied
home_country string Merchant country (required for reverse charge), can be
``null`` or empty string
keep_gross_if_rate_changes boolean If ``true``, changes of the tax rate based on custom
rules keep the gross price constant (default is ``false``)
===================================== ========================== =======================================================
.. versionchanged:: 4.6
The ``internal_name`` and ``keep_gross_if_rate_changes`` attributes have been added.
Endpoints
---------
@@ -56,9 +63,11 @@ Endpoints
{
"id": 1,
"name": {"en": "VAT"},
"internal_name": "VAT",
"rate": "19.00",
"price_includes_tax": true,
"eu_reverse_charge": false,
"keep_gross_if_rate_changes": false,
"home_country": "DE"
}
]
@@ -94,9 +103,11 @@ Endpoints
{
"id": 1,
"name": {"en": "VAT"},
"internal_name": "VAT",
"rate": "19.00",
"price_includes_tax": true,
"eu_reverse_charge": false,
"keep_gross_if_rate_changes": false,
"home_country": "DE"
}
@@ -140,9 +151,11 @@ Endpoints
{
"id": 1,
"name": {"en": "VAT"},
"internal_name": "VAT",
"rate": "19.00",
"price_includes_tax": true,
"eu_reverse_charge": false,
"keep_gross_if_rate_changes": false,
"home_country": "DE"
}
@@ -185,9 +198,11 @@ Endpoints
{
"id": 1,
"name": {"en": "VAT"},
"internal_name": "VAT",
"rate": "20.00",
"price_includes_tax": true,
"eu_reverse_charge": false,
"keep_gross_if_rate_changes": false,
"home_country": "DE"
}

View File

@@ -0,0 +1,28 @@
.. spelling: libpretixsync
Check-in algorithms
===================
When a ticket is scanned at the entrance or exit of an event, we follow a series of steps to determine whether
the check-in is allowed or not. To understand some of the terms in the following diagrams, you should also check
out the documentation of the :ref:`ticket redemption API endpoint <rest-checkin-redeem>`.
Server-side
-----------
The following diagram shows the series of checks executed on the server when a ticket is redeemed through the API.
Some simplifications have been made, for example the deduplication mechanism based on the ``nonce`` parameter
to prevent re-uploads of the same scan is not shown.
.. image:: /images/checkin_online.png
Client-side
-----------
The process of verifying tickets offline is a little different. There are two different approaches,
depending on whether we have information about all tickets in the local database. The following diagram shows
the algorithm as currently implemented in recent versions of `libpretixsync`_.
.. image:: /images/checkin_offline.png
.. _libpretixsync: https://github.com/pretix/libpretixsync

View File

@@ -0,0 +1,13 @@
Algorithms
==========
The business logic inside pretix is full of complex algorithms making decisions based on all the hundreds of settings
and input parameters available. Some of them are documented here as graphs, either because fully understanding them is very important
when working on features close to them, or because they also need to be re-implemented by client-side components like our
ticket scanning apps and we want to ensure the implementations are as similar as possible to avoid confusion.
.. toctree::
:maxdepth: 2
checkin
layouts

View File

@@ -0,0 +1,15 @@
.. spelling: pretixPOS
Ticket layout
=============
When a ticket is exported to PDF, the system needs to decide which of multiple PDF layouts to use. The
following diagram shows the steps of the decision, showing both the implementation in pretix itself as
well as the implementation in `pretixPOS`_.
The process can be influenced by plugins, which is demonstrated with the example of the shipping plugin.
.. image:: /images/ticket_layouts.png
.. _pretixPOS: https://pretix.eu/about/en/pos

View File

@@ -20,20 +20,31 @@ Basically, three pre-defined flows are supported:
* Authentication mechanisms that rely on **redirection**, e.g. to an OAuth provider. These can be implemented by
supplying a ``authentication_url`` method and implementing a custom return view.
Authentication backends are *not* collected through a signal. Instead, they must explicitly be set through the
``auth_backends`` directive in the ``pretix.cfg`` :ref:`configuration file <config>`.
For security reasons, authentication backends are *not* automatically discovered through a signal. Instead, they must
explicitly be set through the ``auth_backends`` directive in the ``pretix.cfg`` :ref:`configuration file <config>`.
In each of these methods (``form_authenticate``, ``request_authenticate`` or your custom view) you are supposed to
either get an existing :py:class:`pretix.base.models.User` object from the database or create a new one. There are a
few rules you need to follow:
In each of these methods (``form_authenticate``, ``request_authenticate``, or your custom view) you are supposed to
use ``User.objects.get_or_create_for_backend`` to get a :py:class:`pretix.base.models.User` object from the database
or create a new one.
* You **MUST** only return users with the ``auth_backend`` attribute set to the ``identifier`` value of your backend.
There are a few rules you need to follow:
* You **MUST** create new users with the ``auth_backend`` attribute set to the ``identifier`` value of your backend.
* You **MUST** have some kind of identifier for a user that is globally unique and **SHOULD** never change, even if the
user's name or email address changes. This could e.g. be the ID of the user in an external database. The identifier
must not be longer than 190 characters. If you worry your backend might generated longer identifiers, consider
using a hash function to trim them to a constant length.
* You **SHOULD** not allow users created by other authentication backends to log in through your code, and you **MUST**
only create, modify or return users with ``auth_backend`` set to your backend.
* Every user object **MUST** have an email address. Email addresses are globally unique. If the email address is
already registered to a user who signs in through a different backend, you **SHOULD** refuse the login.
``User.objects.get_or_create_for_backend`` will follow these rules for you automatically. It works like this:
.. autoclass:: pretix.base.models.auth.UserManager
:members: get_or_create_for_backend
The backend interface
---------------------
@@ -59,6 +70,7 @@ The backend interface
.. automethod:: authentication_url
Logging users in
----------------
@@ -68,3 +80,45 @@ recommend that you use the following utility method to correctly set session val
authentication (if activated):
.. autofunction:: pretix.control.views.auth.process_login
A custom view that is called after a redirect from an external identity provider could look like this::
from django.contrib import messages
from django.shortcuts import redirect
from django.urls import reverse
from pretix.base.models import User
from pretix.base.models.auth import EmailAddressTakenError
from pretix.control.views.auth import process_login
def return_view(request):
# Verify validity of login with the external provider's API
api_response = my_verify_login_function(
code=request.GET.get('code')
)
try:
u = User.objects.get_or_create_for_backend(
'my_backend_name',
api_response['userid'],
api_response['email'],
set_always={
'fullname': '{} {}'.format(
api_response.get('given_name', ''),
api_response.get('family_name', ''),
),
},
set_on_creation={
'locale': api_response.get('locale').lower()[:2],
'timezone': api_response.get('zoneinfo', 'UTC'),
}
)
except EmailAddressTakenError:
messages.error(
request, _('We cannot create your user account as a user account in this system '
'already exists with the same email address.')
)
return redirect(reverse('control:auth.login'))
else:
return process_login(request, u, keep_logged_in=False)

View File

@@ -0,0 +1,119 @@
.. highlight:: python
:linenothreshold: 5
.. _`cookieconsent`:
Handling cookie consent
=======================
pretix includes an optional feature to handle cookie consent explicitly to comply with EU regulations.
If your plugin sets non-essential cookies or includes a third-party service that does so, you should
integrate with this feature.
Server-side integration
-----------------------
First, you need to declare that you are using non-essential cookies by responding to the following
signal:
.. automodule:: pretix.presale.signals
:members: register_cookie_providers
You are expected to return a list of ``CookieProvider`` objects instantiated from the following class:
.. class:: pretix.presale.cookies.CookieProvider
.. py:attribute:: CookieProvider.identifier
A short and unique identifier used to distinguish this cookie provider form others (required).
.. py:attribute:: CookieProvider.provider_name
A human-readable name of the entity of feature responsible for setting the cookie (required).
.. py:attribute:: CookieProvider.usage_classes
A list of enum values from the ``pretix.presale.cookies.UsageClass`` enumeration class, such as
``UsageClass.ANALYTICS``, ``UsageClass.MARKETING``, or ``UsageClass.SOCIAL`` (required).
.. py:attribute:: CookieProvider.privacy_url
A link to a privacy policy (optional).
Here is an example of such a receiver:
.. code-block:: python
@receiver(register_cookie_providers)
def recv_cookie_providers(sender, request, **kwargs):
return [
CookieProvider(
identifier='google_analytics',
provider_name='Google Analytics',
usage_classes=[UsageClass.ANALYTICS],
)
]
JavaScript-side integration
---------------------------
The server-side integration only causes the cookie provider to show up in the cookie dialog. You still
need to care about actually enforcing the consent state.
You can access the consent state through the ``window.pretix.cookie_consent`` variable. Whenever the
value changes, a ``pretix:cookie-consent:change`` event is fired on the ``document`` object.
The variable will generally have one of the following states:
.. rst-class:: rest-resource-table
================================================================ =====================================================
State Interpretation
================================================================ =====================================================
``pretix === undefined || pretix.cookie_consent === undefined`` Your JavaScript has loaded before the cookie consent
script. Wait for the event to be fired, then try again,
do not yet set a cookie.
``pretix.cookie_consent === null`` The cookie consent mechanism has not been enabled. This
usually means that you can set cookies however you like.
``pretix.cookie_consent[identifier] === undefined`` The cookie consent mechanism is loaded, but has no data
on your cookie yet, wait for the event to be fired, do not
yet set a cookie.
``pretix.cookie_consent[identifier] === true`` The user has consented to your cookie.
``pretix.cookie_consent[identifier] === false`` The user has actively rejected your cookie.
================================================================ =====================================================
If you are integrating e.g. a tracking provider with native cookie consent support such
as Facebook's Pixel, you can integrate it like this:
.. code-block:: javascript
var consent = (window.pretix || {}).cookie_consent;
if (consent !== null && !(consent || {}).facebook) {
fbq('consent', 'revoke');
}
fbq('init', ...);
document.addEventListener('pretix:cookie-consent:change', function (e) {
fbq('consent', (e.detail || {}).facebook ? 'grant' : 'revoke');
})
If you have a JavaScript function that you only want to load if consent for a specific ``identifier``
is given, you can wrap it like this:
.. code-block:: javascript
var consent_identifier = "youridentifier";
var consent = (window.pretix || {}).cookie_consent;
if (consent === null || (consent || {})[consent_identifier] === true) {
// Cookie consent tool is either disabled or consent is given
addScriptElement(src);
return;
}
// Either cookie consent tool has not loaded yet or consent is not given
document.addEventListener('pretix:cookie-consent:change', function onChange(e) {
var consent = e.detail || {};
if (consent === null || consent[consent_identifier] === true) {
addScriptElement(src);
document.removeEventListener('pretix:cookie-consent:change', onChange);
}
})

View File

@@ -17,6 +17,7 @@ Contents:
shredder
import
customview
cookieconsent
auth
general
quality

View File

@@ -62,6 +62,8 @@ The provider class
.. autoattribute:: public_name
.. autoattribute:: confirm_button_name
.. autoattribute:: is_enabled
.. autoattribute:: priority

View File

@@ -45,13 +45,17 @@ Attribute Type Description
name string The human-readable name of your plugin
author string Your name
version string A human-readable version code of your plugin
description string A more verbose description of what your plugin does.
description string A more verbose description of what your plugin does. May contain HTML.
category string Category of a plugin. Either one of ``"FEATURE"``, ``"PAYMENT"``,
``"INTEGRATION"``, ``"CUSTOMIZATION"``, ``"FORMAT"``, or ``"API"``,
or any other string.
picture string (optional) Path to a picture resolvable through the static file system.
featured boolean (optional) ``False`` by default, can promote a plugin if it's something many users will want, use carefully.
visible boolean (optional) ``True`` by default, can hide a plugin so it cannot be normally activated.
restricted boolean (optional) ``False`` by default, restricts a plugin such that it can only be enabled
for an event by system administrators / superusers.
experimental boolean (optional) ``False`` by default, marks a plugin as an experimental feature in the plugins list.
picture string (optional) Path to a picture resolvable through the static file system.
compatibility string Specifier for compatible pretix versions.
================== ==================== ===========================================================
@@ -74,8 +78,10 @@ A working example would be:
name = _("PayPal")
author = _("the pretix team")
version = '1.0.0'
category = 'PAYMENT
category = 'PAYMENT'
picture = 'pretix_paypal/paypal_logo.svg'
visible = True
featured = False
restricted = False
description = _("This plugin allows you to receive payments via PayPal")
compatibility = "pretix>=2.7.0"
@@ -92,6 +98,7 @@ those will be displayed but not block the plugin execution.
The ``AppConfig`` class may implement a method ``is_available(event)`` that checks if a plugin
is available for a specific event. If not, it will not be shown in the plugin list of that event.
You should not define ``is_available`` and ``restricted`` on the same plugin.
Plugin registration
-------------------

View File

@@ -1,6 +1,11 @@
.. spelling:: Rebase rebasing
Coding style and quality
========================
Code
----
* Basically, we want all python code to follow the `PEP 8`_ standard. There are a few exceptions where
we see things differently or just aren't that strict. The ``setup.cfg`` file in the project's source
folder contains definitions that allow `flake8`_ to check for violations automatically. See :ref:`checksandtests`
@@ -20,8 +25,62 @@ Coding style and quality
test suite are in the style of Python's unit test module. If you extend those files, you might continue in this style,
but please use ``pytest`` style for any new test files.
* Please keep the first line of your commit messages short. When referencing an issue, please phrase it like
``Fix #123 -- Problems with order creation`` or ``Refs #123 -- Fix this part of that bug``.
Commits and Pull Requests
-------------------------
Most commits should start as pull requests, therefore this applies to the titles of pull requests as well since
the pull request title will become the commit message on merge. We prefer merging with GitHub's "Squash and merge"
feature if the PR contains multiple commits that do not carry value to keep. If there is value in keeping the
individual commits, we use "Rebase and merge" instead. Merge commits should be avoided.
* The commit message should start with a single subject line and can optionally be followed by a commit message body.
* The subject line should be the shortest possible representation of what the commit changes. Someone who reviewed
the commit should able to immediately remember the commit in a couple of weeks based on the subject line and tell
it apart from other commits.
* If there's additional useful information that we should keep, such as reasoning behind the commit, you can
add a longer body, separated from the first line by a blank line.
* The body should explain **what** you changed and more importantly **why** you changed it. There's no need to iterate
**how** you changed something.
* The subject line should be capitalized ("Add new feature" instead of "add new feature") and should not end with a period
("Add new feature" instead of "Add new feature.")
* The subject line should be written in imperative mood, as if you were giving a command what the computer should do if the
commit is applied. This is how generated commit messages by git itself are already written ("Merge branch …", "Revert …")
and makes for short and consistent messages.
* Good: "Fix typo in template"
* Good: "Add Chinese translation"
* Good: "Remove deprecated method"
* Good: "Bump version to 4.4.0"
* Bad: "Fixed bug with …"
* Bad: "Fixes bug with …"
* Bad: "Fixing bug …"
* If all changes in your commit are in context of a single feature or e.g. a bundled plugin, it makes sense to prefix the
subject line with the name of that feature. Examples:
* "API: Add support for PATCH on customers"
* "Docs: Add chapter on alpaca feeding"
* "Stripe: Fix duplicate payments"
* "Order change form: Fix incorrect validation"
* If your commit references a GitHub issue that is fully resolved by your commit, start your subject line with the issue
ID in the form of "Fix #1234 -- Crash in order list". In this case, you can omit the verb "Fix" at the beginning of the
second part of the message to avoid repetition of the word "fix". If your commit only partially resolves the issue, use
"Refs #1234 -- Crash in order list" instead.
* Applies to pretix employees only: If your commit references a sentry issue, please put it in parentheses at the end
of the subject line or inside the body ("Fix crash in order list (PRETIXEU-ABC)"). If your commit references a support
ticket, please put it in parentheses at the end of the subject line with a "Z#" prefix ("Fix crash in order list (Z#12345)").
* If your PR was open for a while and might cause conflicts on merge, please prefer rebasing it (``git rebase -i master``)
over merging ``master`` into your branch unless it is prohibitively complicated.
.. _PEP 8: https://legacy.python.org/dev/peps/pep-0008/

View File

@@ -92,6 +92,9 @@ Carts and Orders
.. autoclass:: pretix.base.models.OrderRefund
:members:
.. autoclass:: pretix.base.models.Transaction
:members:
.. autoclass:: pretix.base.models.CartPosition
:members:

View File

@@ -8,6 +8,7 @@ Developer documentation
setup
contribution/index
implementation/index
translation/index
algorithms/index
api/index
structure
translation/index

View File

@@ -26,7 +26,7 @@ Your should install the following on your system:
* ``libssl`` (Debian package: ``libssl-dev``)
* ``libxml2`` (Debian package ``libxml2-dev``)
* ``libxslt`` (Debian package ``libxslt1-dev``)
* ``libenchant1c2a`` (Debian package ``libenchant1c2a``)
* ``libenchant-2-2`` (Debian package ``libenchant-2-2``)
* ``msgfmt`` (Debian package ``gettext``)
* ``git``
@@ -51,10 +51,15 @@ the dependencies might fail::
Working with the code
---------------------
The first thing you need are all the main application's dependencies::
If you do not have a recent installation of ``nodejs``, install it now::
curl -sL https://deb.nodesource.com/setup_17.x | sudo -E bash -
sudo apt install nodejs
To make sure it is on your path variable, close and reopen your terminal. Now, install the Python-level dependencies of pretix::
cd src/
pip3 install -r requirements.txt -r requirements/dev.txt
pip3 install -e ".[dev]"
Next, you need to copy the SCSS files from the source folder to the STATIC_ROOT directory::

Binary file not shown.

After

Width:  |  Height:  |  Size: 236 KiB

View File

@@ -0,0 +1,146 @@
@startuml
partition "data-based check" {
"Check based on local database" --> "Is the order in status PAID or PENDING\nand is the position not canceled?"
--> if "" then
-right->[no] "Return error CANCELED"
else
-down->[yes] "Is the product part of the check-in list?"
--> if "" then
-right->[no] "Return error PRODUCT"
else
-down->[yes] "Is the subevent part of the check-in list?"
--> if "" then
-right->[no] "Return error INVALID"
note bottom: TODO\ninconsistent\nwith online\ncheck
else
-down->[yes] "Is the order in status PAID?"
--> if "" then
-right->[no] "Does the check-in list include pending orders?"
--> if "" then
-right->[no] "Return error UNPAID "
else
-down->[yes] "Is ignore_unpaid set?\n(Has the operator confirmed\nthe checkin?)"
--> if "" then
-right->[no] "Return error UNPAID "
else
-down->[yes] "Is this an entry or exit?"
endif
endif
else
-down->[yes] "Is this an entry or exit?"
endif
endif
endif
endif
"Is this an entry or exit?" --> if "" then
-right->[entry] Evaluate custom logic (rules)
--> if "" then
-right->[error] "Return error RULES"
else
-down->[ok] "Are all required questions answered?"
--> if "" then
-right->[no] "Return error INCOMPLETE"
else
-down->[yes] "Does the check-in list allow multi-entry?"
endif
endif
else
-->[exit] "Return OK "
endif
"Does the check-in list allow multi-entry?" --> if "" then
-right->[yes] "Return OK"
else
-down->[no] "Is this the first checkin\nfor this ticket on this list?"
--> if "" then
-right->[yes] "Return OK"
else
-down->[no] "Are all previous checkins\nfor this ticket on this list exits?"
--> if "" then
-right->[yes] "Return OK"
else
-down->[no] "Does the check-in list\n allow entry after exit\nand is the last checkin\nan exit?"
--> if "" then
-right->[yes] "Return OK"
else
-down->[no] "Return error ALREADY_REDEEMED"
endif
endif
endif
endif
}
partition "dataless check" {
"Check based on secret content" --> "Does the secret decode with\nany supported scheme\nand has a valid signature?"
--> if "" then
-down->[yes] "Is the ticket secret on the revocation list?"
--> if "" then
-right->[yes] "Return error REVOKED"
else
-down->[no] "Is the product part of the check-in list? "
--> if "" then
-right->[no] "Return error PRODUCT "
else
-down->[yes] "Is the subevent part of the check-in list? "
--> if "" then
-right->[no] "Return error INVALID "
note bottom: TODO\ninconsistent\nwith online\ncheck
else
--> "Is this an entry or exit? "
endif
endif
endif
else
-right>[no] "Return error INVALID "
endif
"Is this an entry or exit? " --> if "" then
-right->[entry] "Evaluate custom logic (rules) "
--> if "" then
-right->[error] "Return error RULES "
else
-down->[ok] "Are all required questions answered? "
--> if "" then
-right->[no] "Return error INCOMPLETE "
else
-down->[yes] "Does the check-in list allow multi-entry? "
endif
endif
else
-->[exit] " Return OK "
endif
"Does the check-in list allow multi-entry? " --> if "" then
-right->[yes] " Return OK "
else
-down->[no] "Are any locally queued checkins for\nthis ticket of this list known?"
--> if "" then
-right->[no] " Return OK "
else
-down->[yes] "Are all locally queued checkins\nfor this ticket on this list exits? "
--> if "" then
-right->[yes] " Return OK "
else
-down->[no] "Does the check-in list\n allow entry after exit\nand is the last locally\nqueued checkin\nan exit? "
--> if "" then
-right->[yes] " Return OK "
else
-down->[no] "Return error ALREADY_REDEEMED "
endif
endif
endif
endif
}
(*) --> "Check if order position with\nscanned ticket secret exists"
--> if "" then
-down->[yes] "Check based on local database"
else
-->[no] "Check based on secret content"
endif
@enduml

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 KiB

View File

@@ -0,0 +1,92 @@
@startuml
(*) --> "Check if order position with\nscanned ticket secret exists"
--> if "" then
-down->[yes] ===CHECK===
else
-->[no] "Check if secret exists\nin revocation list"
--> if "" then
--> "Is this a forced upload?"
--> if "" then
-->[yes] ===CHECK===
else
-right->[no] "Return error REVOKED"
endif
else
-right->[no] "Return error INVALID"
endif
endif
===CHECK=== -down-> "Is the order in status PAID or PENDING\nand is the position not canceled?"
--> if "" then
-right->[no] "Return error CANCELED"
else
-down->[yes] "Is the product part of the check-in list?"
--> if "" then
-right->[no] "Return error PRODUCT"
else
-down->[yes] "Is the subevent part of the check-in list?"
--> if "" then
-right->[no] "Return error PRODUCT "
else
-down->[yes] "Is the order in status PAID\nor is this a forced upload?"
--> if "" then
-right->[no] "Does the check-in list include pending orders?"
--> if "" then
-right->[no] "Return error UNPAID "
else
-down->[yes] "Is ignore_unpaid set?\n(Has the operator confirmed\nthe checkin?)"
--> if "" then
-right->[no] "Return error UNPAID "
else
-down->[yes] "Is this an entry or exit?\nIs the upload forced?"
endif
endif
else
-down->[yes] "Is this an entry or exit?\nIs the upload forced?"
endif
endif
endif
endif
"Is this an entry or exit?\nIs the upload forced?" --> if "" then
-right->[entry && not force] Evaluate custom logic (rules)
--> if "" then
-right->[error] "Return error RULES"
else
-down->[ok] "Are all required questions answered?"
--> if "" then
-right->[no && questions_supported] "Return error INCOMPLETE"
else
-down->[yes || not questions_supported] "Does the check-in list allow multi-entry?"
endif
endif
else
-->[exit || force=true] "Return OK "
endif
"Does the check-in list allow multi-entry?" --> if "" then
-right->[yes] "Return OK"
else
-down->[no] "Is this the first checkin\nfor this ticket on this list?"
--> if "" then
-right->[yes] "Return OK"
else
-down->[no] "Are all previous checkins\nfor this ticket on this list exits?"
--> if "" then
-right->[yes] "Return OK"
else
-down->[no] "Does the check-in list\n allow entry after exit\nand is the last checkin\nan exit?"
--> if "" then
-right->[yes] "Return OK"
else
-down->[no] "Return error ALREADY_REDEEMED"
endif
endif
endif
endif
@enduml

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

View File

@@ -0,0 +1,52 @@
@startuml
(*) --> "Which implementation?"
--> if "" then
-down->[pretixPOS] "Check for TicketLayoutItem with\nsales_channel=pretixpos [libpretixsync]"
--> if "" then
--> (*)
else
-->[not found] "Check for TicketLayoutItem with\nsales_channel=web [libpretixsync]"
--> if "" then
--> (*)
else
-->[not found] "Use event default [libpretixsync]"
--> (*)
endif
endif
else
-right->[pretix] "Check for TicketLayoutItem with\nsales_channel=order.sales_channel"
--> if "" then
-right-> "Run override_layout plugin signal on result"
else
-down->[not found] "Check for TicketLayoutItem with\nsales_channel=web"
--> if "" then
--> "Run override_layout plugin signal on result"
else
-->[not found] "Use event default"
--> "Run override_layout plugin signal on result"
endif
endif
endif
"Run override_layout plugin signal on result" -> (*)
partition pretix_shipping {
"Run override_layout plugin signal on result" --> "Check for ShippingLayoutItem with\nmethod=order.shipping_method"
--> if "" then
--> (*)
else
-down->[not found] "Check for ShippingMethod.layout"
--> if "" then
--> (*)
else
-down->[not found] "Keep original layout"
--> (*)
endif
endif
}
@enduml

View File

@@ -0,0 +1,64 @@
Certificates of attendance
==========================
The certificates plugin provides a HTTP API that allows you to download the certificate for a specific attendee.
Certificate download
--------------------
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/orderpositions/(id)/certificate/
Downloads the certificate for one order position, identified by its internal ID. Download is a two-step
process. You will always get a :http:statuscode:`303` response with a ``Location`` header to a different
URL. In the background, our server starts preparing the PDF file.
If you then do a ``GET`` to the URL you were given, you will either receive a :http:statuscode:`409` response
indicating to retry after a few seconds, or a :http:statuscode:`200` response with the PDF file.
**Example request**:
.. sourcecode:: http
GET /api/v1/organizers/bigevents/events/sampleconf/orderpositions/23442/download/certificate/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 303 See Other
Location: /api/v1/organizers/democon/events/3vjrh/orderpositions/426/certificate/?result=1f550651-ae7b-4911-a76c-2be8f348aaa5
**Example request**:
.. sourcecode:: http
GET /api/v1/organizers/bigevents/events/sampleconf/orderpositions/23442/download/certificate/?result=1f550651-ae7b-4911-a76c-2be8f348aaa5 HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/pdf
...
:param organizer: The ``slug`` field of the organizer to fetch
:param event: The ``slug`` field of the event to fetch
:param id: The ``id`` field of the order position to fetch
:statuscode 200: File ready for download
:statuscode 303: Processing started
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to view this resource
**or** downloads are not available for this order position at this time. The response content will
contain more details.
:statuscode 404: The requested order position or download provider does not exist.
:statuscode 409: The file is not yet ready and will now be prepared. Retry the request after waiting for a few
seconds.

View File

@@ -61,7 +61,7 @@ Variable Description
``attendee_city`` City of the ticket holder's address (or empty)
``attendee_country`` Country code of the ticket holder's address (or empty)
``attendee_state`` State of the ticket holder's address (or empty)
``answer[XYZ]`` Answer to the custom question with identifier ``XYZ``
``answers[XYZ]`` Answer to the custom question with identifier ``XYZ``
``invoice_name`` Full name of the invoice address (or empty)
``invoice_name_*`` Name parts of the invoice address, depending on configuration, e.g. ``invoice_name_given_name`` or ``invoice_name_family_name``
``invoice_company`` Company of the invoice address (or empty)

562
doc/plugins/exhibitors.rst Normal file
View File

@@ -0,0 +1,562 @@
Exhibitors
==========
The exhibitors plugin allows to manage exhibitors at your trade show or conference. After signing up your exhibitors
in the system, you can assign vouchers to exhibitors and give them access to the data of these vouchers. The exhibitors
module is also the basis of the pretixLEAD lead scanning application.
.. note:: On pretix Hosted, using the lead scanning feature of the exhibitors plugin can add additional costs
depending on your contract.
The plugin exposes two APIs. One (REST API) is intended for bulk-data operations from the admin side, and one
(App API) that is used by the pretixLEAD app.
REST API
---------
The REST API for exhibitors requires the usual :ref:`rest-auth`.
Resources
"""""""""
The exhibitors plugin provides a HTTP API that allows you to create new exhibitors.
The exhibitors resource contains the following public fields:
.. rst-class:: rest-resource-table
===================================== ========================== =======================================================
Field Type Description
===================================== ========================== =======================================================
id integer Internal exhibitor ID in pretix
name string Exhibitor name
internal_id string Can be used for the ID in your exhibition system, your customer ID, etc. Can be ``null``. Maximum 255 characters.
contact_name string Contact person (or ``null``)
contact_name_parts object of strings Decomposition of contact name (i.e. given name, family name)
contact_email string Contact person email address (or ``null``)
booth string Booth number (or ``null``). Maximum 100 characters.
locale string Locale for communication with the exhibitor (or ``null``).
access_code string Access code for the exhibitor to access their data or use the lead scanning app (read-only).
allow_lead_scanning boolean Enables lead scanning app
allow_lead_access boolean Enables access to data gathered by the lead scanning app
allow_voucher_access boolean Enables access to data gathered by exhibitor vouchers
comment string Internal comment, not shown to exhibitor
===================================== ========================== =======================================================
You can also access the scanned leads through the API which contains the following public fields:
.. rst-class:: rest-resource-table
===================================== ========================== =======================================================
Field Type Description
===================================== ========================== =======================================================
attendee_order string Order code of the order the scanned attendee belongs to
attendee_positionid integer ``positionid`` if the attendee within the order specified by ``attendee_order``
rating integer A rating of 0 to 5 stars (or ``null``)
notes string A note taken by the exhibitor after scanning
tags list of strings Additional tags selected by the exhibitor
first_upload datetime Date and time of the first upload of this lead
data list of objects Attendee data set that may be shown to the exhibitor based o
the event's configuration. Each entry contains the fields ``id``, ``label``, and ``value``.
device_name string User-defined name for the device used for scanning (or ``null``).
===================================== ========================== =======================================================
Endpoints
"""""""""
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/exhibitors/
Returns a list of all exhibitors configured for an event.
**Example request**:
.. sourcecode:: http
GET /api/v1/organizers/bigevents/events/sampleconf/exhibitors/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
{
"count": 1,
"next": null,
"previous": null,
"results": [
{
"id": 1,
"name": "Aperture Science",
"internal_id": null,
"contact_name": "Dr Cave Johnson",
"contact_name_parts": {
"_scheme": "salutation_title_given_family",
"family_name": "Johnson",
"given_name": "Cave",
"salutation": "",
"title": "Dr"
},
"contact_email": "johnson@as.example.org",
"booth": "A2",
"locale": "de",
"access_code": "VKHZ2FU8",
"allow_lead_scanning": true,
"allow_lead_access": true,
"allow_voucher_access": true,
"comment": ""
}
]
}
:query page: The page number in case of a multi-page result set, default is 1
:param organizer: The ``slug`` field of a valid organizer
:param event: The ``slug`` field of the event to fetch
:statuscode 200: no error
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer or event does not exist **or** you have no permission to view it.
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/exhibitors/(id)/
Returns information on one exhibitor, identified by its ID.
**Example request**:
.. sourcecode:: http
GET /api/v1/organizers/bigevents/events/sampleconf/exhibitors/1/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
{
"id": 1,
"name": "Aperture Science",
"internal_id": null,
"contact_name": "Dr Cave Johnson",
"contact_name_parts": {
"_scheme": "salutation_title_given_family",
"family_name": "Johnson",
"given_name": "Cave",
"salutation": "",
"title": "Dr"
},
"contact_email": "johnson@as.example.org",
"booth": "A2",
"locale": "de",
"access_code": "VKHZ2FU8",
"allow_lead_scanning": true,
"allow_lead_access": true,
"allow_voucher_access": true,
"comment": ""
}
:param organizer: The ``slug`` field of the organizer to fetch
:param event: The ``slug`` field of the event to fetch
:param id: The ``id`` field of the exhibitor to fetch
:statuscode 200: no error
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event/exhibitor does not exist **or** you have no permission to view it.
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/exhibitors/(id)/leads/
Returns a list of all scanned leads of an exhibitor.
**Example request**:
.. sourcecode:: http
GET /api/v1/organizers/bigevents/events/sampleconf/exhibitors/1/leads/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
{
"count": 1,
"next": null,
"previous": null,
"results": [
{
"attendee_order": "T0E7E",
"attendee_positionid": 1,
"rating": 1,
"notes": "",
"tags": [],
"first_upload": "2021-07-06T11:03:31.414491+01:00",
"data": [
{
"id": "attendee_name",
"label": "Attendee name",
"value": "Peter",
}
]
}
]
}
:query page: The page number in case of a multi-page result set, default is 1
:param organizer: The ``slug`` field of a valid organizer
:param event: The ``slug`` field of the event to fetch
:param id: The ``id`` field of the exhibitor to fetch
:statuscode 200: no error
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer or event or exhibitor does not exist **or** you have no permission to view it.
.. http:post:: /api/v1/organizers/(organizer)/events/(event)/exhibitors/
Create a new exhibitor.
**Example request**:
.. sourcecode:: http
POST /api/v1/organizers/bigevents/events/sampleconf/exhibitors/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content-Type: application/json
Content-Length: 166
{
"name": "Aperture Science",
"internal_id": null,
"contact_name_parts": {
"_scheme": "salutation_title_given_family",
"family_name": "Johnson",
"given_name": "Cave",
"salutation": "",
"title": "Dr"
},
"contact_email": "johnson@as.example.org",
"booth": "A2",
"locale": "de",
"access_code": "VKHZ2FU8",
"allow_lead_scanning": true,
"allow_lead_access": true,
"allow_voucher_access": true,
"comment": ""
}
**Example response**:
.. sourcecode:: http
HTTP/1.1 201 Created
Vary: Accept
Content-Type: application/json
{
"id": 1,
"name": "Aperture Science",
"internal_id": null,
"contact_name": "Dr Cave Johnson",
"contact_name_parts": {
"_scheme": "salutation_title_given_family",
"family_name": "Johnson",
"given_name": "Cave",
"salutation": "",
"title": "Dr"
},
"contact_email": "johnson@as.example.org",
"booth": "A2",
"locale": "de",
"access_code": "VKHZ2FU8",
"allow_lead_scanning": true,
"allow_lead_access": true,
"allow_voucher_access": true,
"comment": ""
}
:param organizer: The ``slug`` field of the organizer to create new exhibitor for
:param event: The ``slug`` field of the event to create new exhibitor for
:statuscode 201: no error
:statuscode 400: The exhibitor could not be created due to invalid submitted data.
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to create exhibitors.
.. http:patch:: /api/v1/organizers/(organizer)/events/(event)/exhibitors/(id)/
Update an exhibitor. You can also use ``PUT`` instead of ``PATCH``. With ``PUT``, you have to provide all fields of
the resource, other fields will be reset to default. With ``PATCH``, you only need to provide the fields that you
want to change.
**Example request**:
.. sourcecode:: http
PATCH /api/v1/organizers/bigevents/events/sampleconf/digitalcontents/1/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content-Type: application/json
Content-Length: 34
{
"internal_id": "ABC"
}
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: text/javascript
{
"id": 1,
"name": "Aperture Science",
"internal_id": "ABC",
"contact_name": "Dr Cave Johnson",
"contact_name_parts": {
"_scheme": "salutation_title_given_family",
"family_name": "Johnson",
"given_name": "Cave",
"salutation": "",
"title": "Dr"
},
"contact_email": "johnson@as.example.org",
"booth": "A2",
"locale": "de",
"access_code": "VKHZ2FU8",
"allow_lead_scanning": true,
"allow_lead_access": true,
"allow_voucher_access": true,
"comment": ""
}
:param organizer: The ``slug`` field of the organizer to modify
:param event: The ``slug`` field of the event to modify
:param id: The ``id`` field of the exhibitor to modify
:statuscode 200: no error
:statuscode 400: The exhibitor could not be modified due to invalid submitted data.
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event/exhibitor does not exist **or** you have no permission to change it.
.. http:delete:: /api/v1/organizers/(organizer)/events/(event)/exhibitors/(id)/
Delete an exhibitor.
.. warning:: This deletes all lead scan data and removes all connections to vouchers (the vouchers are not deleted).
**Example request**:
.. sourcecode:: http
DELETE /api/v1/organizers/bigevents/events/sampleconf/exhibitors/1/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 204 No Content
Vary: Accept
:param organizer: The ``slug`` field of the organizer to modify
:param event: The ``slug`` field of the event to modify
:param id: The ``id`` field of the exhibitor to delete
:statuscode 204: no error
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event/exhibitor does not exist **or** you have no permission to change it
App API
-------
The App API is used for communication between the pretixLEAD app and the pretix server.
.. warning:: We consider this an internal API, it is not intended for external use. You may still use it, but
our :ref:`compatibility commitment <rest-compat>` does not apply.
Authentication
""""""""""""""
Every exhibitor has an "access code", usually consisting of 8 alphanumeric uppercase characters.
This access code is communicated to event exhibitors by the event organizers, so this is also what
exhibitors should enter into a login screen.
All API requests need to contain this access code as a header like this::
Authorization: Exhibitor ABCDE123
Exhibitor profile
"""""""""""""""""
Upon login and in regular intervals after that, the API should fetch the exhibitors profile.
This serves two purposes:
* Checking if the authorization code is actually valid
* Obtaining information that can be shown in the app
The resource consists of the following fields:
.. rst-class:: rest-resource-table
===================================== ========================== =======================================================
Field Type Description
===================================== ========================== =======================================================
name string Exhibitor name
booth string Booth number (or ``null``)
event object Object describing the event
├ name multi-lingual string Event name
├ imprint_url string URL to legal notice page. If not ``null``, a button in the app should link to this page.
├ privacy_url string URL to privacy notice page. If not ``null``, a button in the app should link to this page.
├ help_url string URL to help page. If not ``null``, a button in the app should link to this page.
├ logo_url string URL to event logo. If not ``null``, this logo may be shown in the app.
├ slug string Event short form
└ organizer string Organizer short form
notes boolean Specifies whether the exhibitor is allowed to take notes on leads
tags list of strings List of tags the exhibitor can assign to their leads
scan_types list of objects Only used for a special case, fixed value that external API consumers should ignore
===================================== ========================== =======================================================
.. http:get:: /exhibitors/api/v1/profile
**Example request:**
.. sourcecode:: http
GET /exhibitors/api/v1/profile HTTP/1.1
Authorization: Exhibitor ABCDE123
Accept: application/json, text/javascript
**Example response:**
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
{
"name": "Aperture Science",
"booth": "A2",
"event": {
"name": {"en": "Sample conference", "de": "Beispielkonferenz"},
"slug": "bigevents",
"imprint_url": null,
"privacy_url": null,
"help_url": null,
"logo_url": null,
"organizer": "sampleconf"
},
"notes": true,
"tags": ["foo", "bar"],
"scan_types": [
{
"key": "lead",
"label": "Lead Scanning"
}
]
}
:statuscode 200: no error
:statuscode 401: Invalid authentication code
Submitting a lead
"""""""""""""""""
After a ticket/badge is scanned, it should immediately be submitted to the server
so the scan is stored and information about the person can be shown in the app. The same
code can be submitted multiple times, so it's no problem to just submit it again after the
exhibitor set a note or a rating (0-5) inside the app.
On the request, you should set the following properties:
* ``code`` with the scanned barcode
* ``notes`` with the exhibitor's notes
* ``scanned`` with the date and time of the actual scan (not the time of the upload)
* ``scan_type`` set to ``lead`` statically
* ``tags`` with the list of selected tags
* ``rating`` with the rating assigned by the exhibitor
* ``device_name`` with a user-specified name of the device used for scanning (max. 190 characters), or ``null``
If you submit ``tags`` and ``rating`` to be ``null`` and ``notes`` to be ``""``, the server
responds with the previously saved information and will not delete that information. If you
supply other values, the information saved on the server will be overridden.
The response will also contain ``tags``, ``rating``, and ``notes``. Additionally,
it will include ``attendee`` with a list of ``fields`` that can be shown to the
user. Each field has an internal ``id``, a human-readable ``label``, and a ``value`` (all strings).
Note that the ``fields`` array can contain any number of dynamic keys!
Depending on the exhibitors permission and event configuration this might be empty,
or contain lots of details. The app should dynamically show these values (read-only)
with the labels sent by the server.
The request for this looks like this:
.. http:post:: /exhibitors/api/v1/leads/
**Example request:**
.. sourcecode:: http
POST /exhibitors/api/v1/leads/ HTTP/1.1
Authorization: Exhibitor ABCDE123
Accept: application/json, text/javascript
Content-Type: application/json
{
"code": "qrcodecontent",
"notes": "Great customer, wants our newsletter",
"scanned": "2020-10-18T12:24:23.000+00:00",
"scan_type": "lead",
"tags": ["foo"],
"rating": 4,
"device_name": "DEV1"
}
**Example response:**
.. sourcecode:: http
HTTP/1.1 201 Created
Vary: Accept
Content-Type: application/json
{
"attendee": {
"fields": [
{
"id": "attendee_name",
"label": "Name",
"value": "Jon Doe"
},
{
"id": "attendee_email",
"label": "Email",
"value": "test@example.com"
}
]
},
"rating": 4,
"tags": ["foo"],
"notes": "Great customer, wants our newsletter"
}
:statuscode 200: No error, leads was not scanned for the first time
:statuscode 201: No error, leads was scanned for the first time
:statuscode 400: Invalid data submitted
:statuscode 401: Invalid authentication code

View File

@@ -0,0 +1,301 @@
Secrets Import
==============
Usually, pretix generates ticket secrets (i.e. the QR code used for scanning) itself. You can read more about this
process at :ref:`secret_generators`.
With the "Secrets Import" plugin, you can upload your own list of secrets to be used instead. This is useful for
integrating with third-party check-in systems.
API Resource description
-------------------------
The secrets import plugin provides a HTTP API that allows you to create new secrets.
The imported secret resource contains the following public fields:
.. rst-class:: rest-resource-table
===================================== ========================== =======================================================
Field Type Description
===================================== ========================== =======================================================
id integer Internal ID of the secret
secret string Actual string content of the secret (QR code content)
used boolean Whether the secret was already used for a ticket. If ``true``,
the secret can no longer be deleted. Secrets are never used
twice, even if an order is canceled or deleted.
item integer Internal ID of a product, or ``null``. If set, the secret
will only be used for tickets of this product.
variation integer Internal ID of a product variation, or ``null``. If set, the secret
will only be used for tickets of this product variation.
subevent integer Internal ID of an event series date, or ``null``. If set, the secret
will only be used for tickets of this event series date.
===================================== ========================== =======================================================
API Endpoints
-------------
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/imported_secrets/
Returns a list of all secrets imported for an event.
**Example request**:
.. sourcecode:: http
GET /api/v1/organizers/bigevents/events/sampleconf/imported_secrets/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
{
"count": 1,
"next": null,
"previous": null,
"results": [
{
"id": 1,
"secret": "foobar",
"used": false,
"item": null,
"variation": null,
"subevent": null
}
]
}
:query page: The page number in case of a multi-page result set, default is 1
:param organizer: The ``slug`` field of a valid organizer
:param event: The ``slug`` field of the event to fetch
:statuscode 200: no error
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer or event does not exist **or** you have no permission to view it.
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/imported_secrets/(id)/
Returns information on one secret, identified by its ID.
**Example request**:
.. sourcecode:: http
GET /api/v1/organizers/bigevents/events/sampleconf/imported_secrets/1/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
{
"id": 1,
"secret": "foobar",
"used": false,
"item": null,
"variation": null,
"subevent": null
}
:param organizer: The ``slug`` field of the organizer to fetch
:param event: The ``slug`` field of the event to fetch
:param id: The ``id`` field of the secret to fetch
:statuscode 200: no error
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event/secret does not exist **or** you have no permission to view it.
.. http:post:: /api/v1/organizers/(organizer)/events/(event)/imported_secrets/
Create a new secret.
**Example request**:
.. sourcecode:: http
POST /api/v1/organizers/bigevents/events/sampleconf/imported_secrets/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content-Type: application/json
Content-Length: 166
{
"secret": "foobar",
"used": false,
"item": null,
"variation": null,
"subevent": null
}
**Example response**:
.. sourcecode:: http
HTTP/1.1 201 Created
Vary: Accept
Content-Type: application/json
{
"id": 1,
"secret": "foobar",
"used": false,
"item": null,
"variation": null,
"subevent": null
}
:param organizer: The ``slug`` field of the organizer to a create new secret for
:param event: The ``slug`` field of the event to create a new secret for
:statuscode 201: no error
:statuscode 400: The secret could not be created due to invalid submitted data.
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to create secrets.
.. http:post:: /api/v1/organizers/(organizer)/events/(event)/imported_secrets/bulk_create/
Create new secrets in bulk (up to 500 per request). The request either succeeds or fails entirely.
**Example request**:
.. sourcecode:: http
POST /api/v1/organizers/bigevents/events/sampleconf/imported_secrets/bulk_create/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content-Type: application/json
Content-Length: 166
[
{
"secret": "foobar",
"used": false,
"item": null,
"variation": null,
"subevent": null
},
{
"secret": "baz",
"used": false,
"item": null,
"variation": null,
"subevent": null
}
]
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
[
{
"id": 1,
"secret": "foobar",
"used": false,
"item": null,
"variation": null,
"subevent": null
},
{
"id": 2,
"secret": "baz",
"used": false,
"item": null,
"variation": null,
"subevent": null
}
]
:param organizer: The ``slug`` field of the organizer to create new secrets for
:param event: The ``slug`` field of the event to create new secrets for
:statuscode 201: no error
:statuscode 400: The secrets could not be created due to invalid submitted data.
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to create secrets.
.. http:patch:: /api/v1/organizers/(organizer)/events/(event)/imported_secrets/(id)/
Update a secret. You can also use ``PUT`` instead of ``PATCH``. With ``PUT``, you have to provide all fields of
the resource, other fields will be reset to default. With ``PATCH``, you only need to provide the fields that you
want to change.
**Example request**:
.. sourcecode:: http
PATCH /api/v1/organizers/bigevents/events/sampleconf/imported_secrets/1/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content-Type: application/json
Content-Length: 34
{
"item": 2
}
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: text/javascript
{
"id": 1,
"secret": "foobar",
"used": false,
"item": 2,
"variation": null,
"subevent": null
}
:param organizer: The ``slug`` field of the organizer to modify
:param event: The ``slug`` field of the event to modify
:param id: The ``id`` field of the secret to modify
:statuscode 200: no error
:statuscode 400: The secret could not be modified due to invalid submitted data.
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event/secret does not exist **or** you have no permission to change it.
.. http:delete:: /api/v1/organizers/(organizer)/events/(event)/imported_secrets/(id)/
Delete a secret. You can only delete secrets that have not yet been used.
**Example request**:
.. sourcecode:: http
DELETE /api/v1/organizers/bigevents/events/sampleconf/imported_secrets/1/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 204 No Content
Vary: Accept
:param organizer: The ``slug`` field of the organizer to modify
:param event: The ``slug`` field of the event to modify
:param id: The ``id`` field of the secret to delete
:statuscode 204: no error
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event/secret does not exist **or** you have no permission to change it **or** the secret has already been used

View File

@@ -1,3 +1,5 @@
.. _`plugin-docs`:
Plugin documentation
====================
@@ -10,10 +12,13 @@ If you want to **create** a plugin, please go to the
:maxdepth: 2
list
pretixdroid
banktransfer
ticketoutputpdf
badges
campaigns
certificates
digital
exhibitors
imported_secrets
webinar
presale-saml

View File

@@ -5,6 +5,6 @@ List of plugins
===============
A detailed list of plugins that are available for pretix can be found on the
`project website`_.
`pretix Marketplace`_.
.. _project website: https://pretix.eu/about/en/plugins
.. _pretix Marketplace: https://marketplace.pretix.eu

View File

@@ -0,0 +1,405 @@
.. highlight:: ini
.. spelling::
IdP
skIDentity
ePA
NPA
Presale SAML Authentication
===========================
The Presale SAML Authentication plugin is an advanced plugin, which most event
organizers will not need to use. However, for the select few who do require
strong customer authentication that cannot be covered by the built-in customer
account functionality, this plugin allows pretix to connect to a SAML IdP and
perform authentication and retrieval of user information.
Usage of the plugin is governed by two separate sets of settings: The plugin
installation, the Service Provider (SP) configuration and the event
configuration.
Plugin installation and initial configuration
---------------------------------------------
.. note:: If you are a customer of our hosted `pretix.eu`_ offering, you can
skip this section.
The plugin is installed as any other plugin in the pretix ecosystem. As a
pretix system administrator, please follow the instructions in the the
:ref:`Administrator documentation <admindocs>`.
Once installed, you will need to assess, if you want (or need) your pretix
instance to be a single SP for all organizers and events or if every event
organizer has to provide their own SP.
Take the example of a university which runs pretix under an pretix Enterprise
agreement. Since they only provide ticketing services to themselves (every
organizer is still just a different department of the same university), a
single SP should be enough.
On the other hand, a reseller such as `pretix.eu`_ who services a multitude
of clients would not work that way. Here, every organizer is a separate
legal entity and as such will also need to provide their own SP configuration:
Company A will expect their SP to reflect their company - and not a generalized
"pretix SP".
Once you have decided on the mode of operation, the :ref:`Configuration file
<config>` needs to be extended to reflect your choice.
Example::
[presale-saml]
level=global
``level``
``global`` to use only a single, system-wide SP, ``organizer`` for multiple
SPs, configured on the organizer-level. Defaults to ``organizer``.
Service Provider configuration
------------------------------
Global Level
^^^^^^^^^^^^
.. note:: If you are a customer of our hosted `pretix.eu`_ offering, you can
skip this section and follow the instructions on the upcoming
Organizer Level settings.
As a user with administrative privileges, please activate them by clicking the
`Admin Mode` button in the top right hand corner.
You should now see a new menu-item titled `SAML` appear.
Organizer Level
^^^^^^^^^^^^^^^
Navigate to the organizer settings in the pretix backend. In the navigation
bar, you will find a menu-item titled `SAML` if your user has the `Can
change organizer settings` permission.
.. note:: If you are a customer of our hosted `pretix.eu`_ offering, the menu
will only appear once one of our friendly customer service agents
has enabled the Presale SAML Authentication plugin for at least one
of your events. Feel free to get in touch with us!
Setting up the SP
^^^^^^^^^^^^^^^^^
No matter where your SP configuration lives, you will be greeted by a very
long list of fields of which almost all of them will need to be filled. Please
don't be discouraged - most of the settings don't need to be decided by yourself
and/or are already preset with a sensible default setting.
If you are not sure what setting you should choose for any of the fields, you
should reach out to your IdP operator as they can tell you exactly what the IdP
expects and - more importantly - supports.
``IdP Metadata URL``
Please provide the URL where your IdP outputs its metadata. For most IdPs,
this URL is static and the same for all SPs. If you are a member of the
DFN-AAI, you can find the meta-data for the `Test-, Basic- and
Advanced-Federation`_ on their website. Please do talk with your local
IdP operator though, as you might not even need to go through the DFN-AAI
and might just use your institutions local IdP which will also host their
metadata on a different URL.
The URL needs to be publicly accessible, as saving the settings form will
fail if the IdP metadata cannot be retrieved. pretix will also automatically
refresh the IdP metadata on a regular basis.
``SP Entity Id``
By default, we recommend that you use the system-proposed metadata-URL as
the Entity Id of your SP. However, if so desired or required by your IdP,
you can also set any other, arbitrary URL as the SP Entity Id.
``SP Name / SP Decription``
Most IdP will display the name and description of your SP to the users
during authentication. The description field can be used to explain to the
users how their data is being used.
``SP X.509 Certificate / SP X.509 Private Key``
Your SP needs a certificate and a private key for said certificate. Please
coordinate with your IdP, if you are supposed to generate these yourself or
if they are provided to you.
``SP X.509 New Certificate``
As certificates have an expiry date, they need to be renewed on a regular
basis. In order to facilitate the rollover from the expiring to the new
certificate, you can provide the new certificate already before the expiration
of the existing one. That way, the system will automatically use the correct
one. Once the old certificate has expired and is not used anymore at all,
you can move the new certificate into the slot of the normal certificate and
keep the new slot empty for your next renewal process.
``Requested Attributes``
An IdP can hold a variety of attributes of an authenticating user. While
your IdP will dictate which of the available attributes your SP can consume
in theory, you will still need to define exactly which attributes the SP
should request.
The notation is a JSON list of objects with 5 attributes each:
* ``attributeValue``: Can be defaulted to ``[]``.
* ``friendlyName``: String used in the upcoming event-level settings to
retrieve the attributes data.
* ``isRequired``: Boolean indicating whether the IdP must enforce the
transmission of this attribute. In most cases, ``true`` is the best
choice.
* ``name``: String of the internal, technical name of the requested
attribute. Often starting with ``urn:mace:dir:attribute-def:``,
``urn:oid:`` or ``http://``/``https://``.
* ``nameFormat``: String describing the type of ``name`` that has been
set in the previous section. Often starting with
``urn:mace:shibboleth:1.0:`` or ``urn:oasis:names:tc:SAML:2.0:``.
Your IdP can provide you with a list of available attributes. See below
for a sample configuration in an academic context.
Note, that you can have multiple attributes with the same ``friendlyName``
but different ``name``s. This is often used in systems, where the same
information (for example a persons name) is saved in different fields -
for example because one institution is returning SAML 1.0 and other
institutions are returning SAML 2.0 style attributes. Typically, this only
occurs in mix environments like the DFN-AAI with a large number of
participants. If you are only using your own institutions IdP and not
authenticating anyone outside of your realm, this should not be a common
sight.
``Encrypt/Sign/Require ...``
Does what is says on the box - please inquire with your IdP for the
necessary settings. Most settings can be turned on as they increase security,
however some IdPs might stumble over some of them.
``Signature / Digest Algorithm``
Please chose appropriate algorithms, that both pretix/your SP and the IdP
can communicate with. A common source of issues when connecting to a
Shibboleth-based IdP is the Digest Algorithm: pretix does not support
``http://www.w3.org/2009/xmlenc11#rsa-oaep`` and authentication will fail
if the IdP enforces this.
``Technical/Support Contacts``
Those contacts are encoded into the SPs public meta data and might be
displayed to users having trouble authenticating. It is recommended to
provide a dedicated point of contact for technical issues, as those will
be the ones to change the configuration for the SP.
Event / Authentication configuration
------------------------------------
Basic settings
^^^^^^^^^^^^^^
Once the plugin has been enabled for a pretix event using the Plugins-menu from
the event's settings, a new *SAML* menu item will show up.
On this page, the actual authentication can be configured.
``Checkout Explanation``
Since most users probably won't be familiar with why they have to authenticate
to buy a ticket, you can provide them a small blurb here. Markdown is supported.
``Attribute RegEx``
By default, any successful authentication with the IdP will allow the user to
proceed with their purchase. Should the allowed audience needed to be restricted
further, a set of regular Expressions can be used to do this.
An Attribute RegEx of ``{}`` will allow any authenticated user to pass.
A RegEx of ``{ "affiliation": "^(employee@pretix.eu|staff@pretix.eu)$" }`` will
only allow user to pass which have the ``affiliation`` attribute and whose
attribute either matches ``employee@pretix.eu`` or ``staff@pretix.eu``.
Please make sure that the attribute you are querying is also requested from the
IdP in the first place - for a quick check you can have a look at the top of
the page where all currently configured attributes are listed.
``RegEx Fail Explanation``
Only used in conjunction with the above Attribute RegEx. Should the user not
pass the restrictions imposed by the regular expression, the user is shown
this error-message.
If you are - for example in an university context - restricting access to
students only, you might want to explain here that Employees are not allowed
to book tickets.
``Ticket Secret SAML Attribute``
In very specific instances, it might be desirable that the ticket-secret is
not the randomly one generated by pretix but rather based on one of the
users attributes - for example their unique ID or access card number.
To achieve this, the name of a SAML-attribute can be specified here.
It is however necessary to note, that even with this setting in use,
ticket-secrets need to be unique. This is why when this setting is enabled,
the default, pretix-generated ticket-secret is prefixed with the attributes
value.
Example: A users ``cardid`` attribute has the value of ``01189998819991197253``.
The default random ticket secret would have been
``yczygpw9877akz2xwdhtdyvdqwkv7npj``. The resulting new secret will now be
``01189998819991197253_yczygpw9877akz2xwdhtdyvdqwkv7npj``.
That way, the ticket secret is still unique, but when checking into an event,
the user can easily be searched and found using their identifier.
IdP-provided E-Mail addresses, names
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
By default, pretix will only authenticate the user and not process the received
data any further.
However, there are a few exceptions to this rule.
There are a few `magic` attributes that pretix will use to automatically populate
the corresponding fields within the checkout process **and lock them out from
user editing**.
* ``givenName`` and ``sn``: If both of those attributes are present and pretix
is configured to collect the users name, these attributes' values are used
for the given and family name respectively.
* ``email``: If this attribute is present, the E-Mail-address of the users will
be set to the one transmitted through the attributes.
The latter might pose a problem, if the IdP is transmitting an ``email`` attribute
which does contain a system-level mail address which is only used as an internal
identifier but not as a real mailbox. In this case, please consider setting the
``friendlyName`` of the attribute to a different value than ``email`` or removing
this field from the list of requested attributes altogether.
Saving attributes to questions
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
By setting the ``internal identifier`` of a user-defined question to the same name
as a SAML attribute, pretix will save the value of said attribute into the question.
All the same as in the above section on E-Mail addresses, those fields become
non-editable by the user.
Please be aware that some specialty question types might not be compatible with
the SAML attributes due to specific format requirements. If in doubt (or if the
checkout fails/the information is not properly saved), try setting the question
type to a simple type like "Text (one line)".
Notes and configuration examples
--------------------------------
Requesting SAML 1.0 and 2.0 attributes from an academic IdP
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This requests the ``eduPersonPrincipalName`` (also sometimes called EPPN),
``email``, ``givenName`` and ``sn`` both in SAML 1.0 and SAML 2.0 attributes.
.. sourcecode:: json
[
{
"attributeValue": [],
"friendlyName": "eduPersonPrincipalName",
"isRequired": true,
"name": "urn:mace:dir:attribute-def:eduPersonPrincipalName",
"nameFormat": "urn:mace:shibboleth:1.0:attributeNamespace:uri"
},
{
"attributeValue": [],
"friendlyName": "eduPersonPrincipalName",
"isRequired": true,
"name": "urn:oid:1.3.6.1.4.1.5923.1.1.1.6",
"nameFormat": "urn:oasis:names:tc:SAML:2.0:attrname-format:uri"
},
{
"attributeValue": [],
"friendlyName": "email",
"isRequired": true,
"name": "urn:mace:dir:attribute-def:mail",
"nameFormat": "urn:mace:shibboleth:1.0:attributeNamespace:uri"
},
{
"attributeValue": [],
"friendlyName": "email",
"isRequired": true,
"name": "urn:oid:0.9.2342.19200300.100.1.3",
"nameFormat": "urn:oasis:names:tc:SAML:2.0:attrname-format:uri"
},
{
"attributeValue": [],
"friendlyName": "givenName",
"isRequired": true,
"name": "urn:mace:dir:attribute-def:givenName",
"nameFormat": "urn:mace:shibboleth:1.0:attributeNamespace:uri"
},
{
"attributeValue": [],
"friendlyName": "givenName",
"isRequired": true,
"name": "urn:oid:2.5.4.42",
"nameFormat": "urn:oasis:names:tc:SAML:2.0:attrname-format:uri"
},
{
"attributeValue": [],
"friendlyName": "sn",
"isRequired": true,
"name": "urn:mace:dir:attribute-def:sn",
"nameFormat": "urn:mace:shibboleth:1.0:attributeNamespace:uri"
},
{
"attributeValue": [],
"friendlyName": "sn",
"isRequired": true,
"name": "urn:oid:2.5.4.4",
"nameFormat": "urn:oasis:names:tc:SAML:2.0:attrname-format:uri"
}
]
skIDentity IdP Metadata URL
^^^^^^^^^^^^^^^^^^^^^^^^^^^
Since the IdP Metadata URL for `skIDentity`_ is not readily documented/visible
in their backend, we document it here:
``https://service.skidentity.de/fs/saml/metadata``
Requesting skIDentity attributes for electronic identity cards
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This requests the basic ``eIdentifier``, ``IDType``, ``IDIssuer``, and
``NameID`` from the `skIDentity`_ SAML service, which are available for
electronic ID cards such as the German ePA/NPA. (Other attributes such as
the name and address are available at additional cost from the IdP).
.. sourcecode:: json
[
{
"attributeValue": [],
"friendlyName": "eIdentifier",
"isRequired": true,
"name": "http://www.skidentity.de/att/eIdentifier",
"nameFormat": "urn:oasis:names:tc:SAML:2.0:attrname-format:uri"
},
{
"attributeValue": [],
"friendlyName": "IDType",
"isRequired": true,
"name": "http://www.skidentity.de/att/IDType",
"nameFormat": "urn:oasis:names:tc:SAML:2.0:attrname-format:uri"
},
{
"attributeValue": [],
"friendlyName": "IDIssuer",
"isRequired": true,
"name": "http://www.skidentity.de/att/IDIssuer",
"nameFormat": "urn:oasis:names:tc:SAML:2.0:attrname-format:uri"
},
{
"attributeValue": [],
"friendlyName": "NameID",
"isRequired": true,
"name": "http://www.skidentity.de/att/NameID",
"nameFormat": "urn:oasis:names:tc:SAML:2.0:attrname-format:uri"
}
]
.. _pretix.eu: https://pretix.eu
.. _Test-, Basic- and Advanced-Federation: https://doku.tid.dfn.de/en:metadata
.. _skIDentity: https://www.skidentity.de/

View File

@@ -1,368 +0,0 @@
pretixdroid HTTP API
====================
The pretixdroid plugin provides a HTTP API that the `pretixdroid Android app`_
uses to communicate with the pretix server.
.. warning:: This API is **DEPRECATED** and will probably go away soon. It is used **only** to serve the pretixdroid
Android app. There are no backwards compatibility guarantees on this API. We will not add features that
are not required for the Android App. There is a general-purpose :ref:`rest-api` that provides all
features that you need to check in.
.. versionchanged:: 1.12
Support for check-in-time questions has been added. The new API features are fully backwards-compatible and
negotiated live, so clients which do not need this feature can ignore the change. For this reason, the API version
has not been increased and is still set to 3.
.. versionchanged:: 1.13
Support for checking in unpaid tickets has been added.
.. http:post:: /pretixdroid/api/(organizer)/(event)/redeem/
Redeems a ticket, i.e. checks the user in.
**Example request**:
.. sourcecode:: http
POST /pretixdroid/api/demoorga/democon/redeem/?key=ABCDEF HTTP/1.1
Host: demo.pretix.eu
Accept: application/json, text/javascript
Content-Type: application/x-www-form-urlencoded
secret=az9u4mymhqktrbupmwkvv6xmgds5dk3&questions_supported=true
You **must** set the parameter secret.
You **must** set the parameter ``questions_supported`` to ``true`` **if** you support asking questions
back to the app operator. You **must not** set it if you do not support this feature. In that case, questions
will just be ignored.
You **may** set the additional parameter ``datetime`` in the body containing an ISO8601-encoded
datetime of the entry attempt. If you don"t, the current date and time will be used.
You **may** set the additional parameter ``force`` to indicate that the request should be logged
regardless of previous check-ins for the same ticket. This might be useful if you made the entry decision offline.
Questions will also always be ignored in this case (i.e. supplied answers will be saved, but no error will be
thrown if they are missing or invalid).
You **may** set the additional parameter ``nonce`` with a globally unique random value to identify this
check-in. This is meant to be used to prevent duplicate check-ins when you are just retrying after a connection
failure.
You **may** set the additional parameter ``ignore_unpaid`` to indicate that the check-in should be performed even
if the order is in pending state.
If questions are supported and required, you will receive a dictionary ``questions`` containing details on the
particular questions to ask. To answer them, just re-send your redemption request with additional parameters of
the form ``answer_<question>=<answer>``, e.g. ``answer_12=24``.
**Example successful response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Content-Type: text/json
{
"status": "ok"
"version": 3,
"data": {
"secret": "az9u4mymhqktrbupmwkvv6xmgds5dk3",
"order": "ABCDE",
"item": "Standard ticket",
"item_id": 1,
"variation": null,
"variation_id": null,
"attendee_name": "Peter Higgs",
"attention": false,
"redeemed": true,
"checkin_allowed": true,
"addons_text": "Parking spot",
"paid": true
}
}
**Example response with required questions**:
.. sourcecode:: http
HTTP/1.1 200 OK
Content-Type: text/json
{
"status": "incomplete"
"version": 3
"data": {
"secret": "az9u4mymhqktrbupmwkvv6xmgds5dk3",
"order": "ABCDE",
"item": "Standard ticket",
"item_id": 1,
"variation": null,
"variation_id": null,
"attendee_name": "Peter Higgs",
"attention": false,
"redeemed": true,
"checkin_allowed": true,
"addons_text": "Parking spot",
"paid": true
},
"questions": [
{
"id": 12,
"type": "C",
"question": "Choose a shirt size",
"required": true,
"position": 2,
"items": [1],
"options": [
{
"id": 24,
"answer": "M"
},
{
"id": 25,
"answer": "L"
}
]
}
]
}
**Example error response with data**:
.. sourcecode:: http
HTTP/1.1 200 OK
Content-Type: text/json
{
"status": "error",
"reason": "already_redeemed",
"version": 3,
"data": {
"secret": "az9u4mymhqktrbupmwkvv6xmgds5dk3",
"order": "ABCDE",
"item": "Standard ticket",
"item_id": 1,
"variation": null,
"variation_id": null,
"attendee_name": "Peter Higgs",
"attention": false,
"redeemed": true,
"checkin_allowed": true,
"addons_text": "Parking spot",
"paid": true
}
}
**Example error response without data**:
.. sourcecode:: http
HTTP/1.1 200 OK
Content-Type: text/json
{
"status": "error",
"reason": "unkown_ticket",
"version": 3
}
Possible error reasons:
* ``unpaid`` - Ticket is not paid for or has been refunded
* ``already_redeemed`` - Ticket already has been redeemed
* ``product`` - Tickets with this product may not be scanned at this device
* ``unknown_ticket`` - Secret does not match a ticket in the database
:query key: Secret API key
:statuscode 200: Valid request
:statuscode 404: Unknown organizer or event
:statuscode 403: Invalid authorization key
.. http:get:: /pretixdroid/api/(organizer)/(event)/search/
Searches for a ticket.
At most 25 results will be returned. **Queries with less than 4 characters will always return an empty result set.**
**Example request**:
.. sourcecode:: http
GET /pretixdroid/api/demoorga/democon/search/?key=ABCDEF&query=Peter HTTP/1.1
Host: demo.pretix.eu
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Content-Type: text/json
{
"results": [
{
"secret": "az9u4mymhqktrbupmwkvv6xmgds5dk3",
"order": "ABCE6",
"item": "Standard ticket",
"variation": null,
"attendee_name": "Peter Higgs",
"redeemed": false,
"attention": false,
"checkin_allowed": true,
"addons_text": "Parking spot",
"paid": true
},
...
],
"version": 3
}
:query query: Search query
:query key: Secret API key
:statuscode 200: Valid request
:statuscode 404: Unknown organizer or event
:statuscode 403: Invalid authorization key
.. http:get:: /pretixdroid/api/(organizer)/(event)/download/
Download data for all tickets.
**Example request**:
.. sourcecode:: http
GET /pretixdroid/api/demoorga/democon/download/?key=ABCDEF HTTP/1.1
Host: demo.pretix.eu
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Content-Type: text/json
{
"version": 3,
"results": [
{
"secret": "az9u4mymhqktrbupmwkvv6xmgds5dk3",
"order": "ABCE6",
"item": "Standard ticket",
"variation": null,
"attendee_name": "Peter Higgs",
"redeemed": false,
"attention": false,
"checkin_allowed": true,
"paid": true
},
...
],
"questions": [
{
"id": 12,
"type": "C",
"question": "Choose a shirt size",
"required": true,
"position": 2,
"items": [1],
"options": [
{
"id": 24,
"answer": "M"
},
{
"id": 25,
"answer": "L"
}
]
}
]
}
:query key: Secret API key
:statuscode 200: Valid request
:statuscode 404: Unknown organizer or event
:statuscode 403: Invalid authorization key
.. http:get:: /pretixdroid/api/(organizer)/(event)/status/
Returns status information, such as the total number of tickets and the
number of performed check-ins.
**Example request**:
.. sourcecode:: http
GET /pretixdroid/api/demoorga/democon/status/?key=ABCDEF HTTP/1.1
Host: demo.pretix.eu
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Content-Type: text/json
{
"checkins": 17,
"total": 42,
"version": 3,
"event": {
"name": "Demo Conference",
"slug": "democon",
"date_from": "2016-12-27T17:00:00Z",
"date_to": "2016-12-30T18:00:00Z",
"timezone": "UTC",
"url": "https://demo.pretix.eu/demoorga/democon/",
"organizer": {
"name": "Demo Organizer",
"slug": "demoorga"
},
},
"items": [
{
"name": "T-Shirt",
"id": 1,
"checkins": 1,
"admission": False,
"total": 1,
"variations": [
{
"name": "Red",
"id": 1,
"checkins": 1,
"total": 12
},
{
"name": "Blue",
"id": 2,
"checkins": 4,
"total": 8
}
]
},
{
"name": "Ticket",
"id": 2,
"checkins": 15,
"admission": True,
"total": 22,
"variations": []
}
]
}
:query key: Secret API key
:statuscode 200: Valid request
:statuscode 404: Unknown organizer or event
:statuscode 403: Invalid authorization key
.. _pretixdroid Android app: https://github.com/pretix/pretixdroid

View File

@@ -1,5 +1,6 @@
-r ../src/requirements.txt
-e ../src/
sphinx==2.3.*
jinja2==3.0.*
sphinx-rtd-theme
sphinxcontrib-httpdomain
sphinxcontrib-images

View File

@@ -17,10 +17,12 @@ bic
BIC
boolean
booleans
bugfix
cancelled
casted
Ceph
checkbox
checkins
checksum
config
contenttypes
@@ -76,6 +78,7 @@ mixin
mixins
multi
multidomain
multiplicator
namespace
namespaced
namespaces
@@ -100,6 +103,7 @@ prepending
preprocessor
presale
pretix
pretixLEAD
pretixSCAN
pretixdroid
pretixPOS

View File

@@ -203,4 +203,4 @@ Then, please contact support@pretix.eu and we will enable DKIM for your domain o
.. _Sender Policy Framework: https://en.wikipedia.org/wiki/Sender_Policy_Framework
.. _SPF specification: http://www.openspf.org/SPF_Record_Syntax
.. _SPF specification: http://www.open-spf.org/SPF_Record_Syntax

View File

@@ -1,3 +1,5 @@
.. _secret_generators:
Ticket secret generators
========================

View File

@@ -4,8 +4,7 @@ Embeddable Widget
=================
If you want to show your ticket shop on your event website or blog, you can use our JavaScript widget. This way,
users will not need to leave your site to buy their ticket in most cases. The widget will still open a new tab
for the checkout if the user is on a mobile device.
users will not need to leave your site to buy their ticket in most cases.
To obtain the correct HTML code for embedding your event into your website, we recommend that you go to the "Widget"
tab of your event's settings. You can specify some optional settings there (for example the language of the widget)
@@ -310,6 +309,10 @@ Currently, the following attributes are understood by pretix itself:
always be modified. Note that this is not a security feature and can easily be overridden by users, so do not rely
on this for authentication.
* If ``data-consent="…"`` is given, the cookie consent mechanism will be initialized with consent for the given cookie
providers. All other providers will be disabled, no consent dialog will be shown. This is useful if you already
asked the user for consent and don't want them to be asked again. Example: ``data-consent="facebook,google_analytics"``
Any configured pretix plugins might understand more data fields. For example, if the appropriate plugins on pretix
Hosted or pretix Enterprise are active, you can pass the following fields:

2
src/.gitignore vendored
View File

@@ -9,4 +9,4 @@ dist/
*.bak
pretix/static/jsi18n/
node_modules/
.eggs/

View File

@@ -34,5 +34,7 @@ git push
# Unlock Weblate
for c in $COMPONENTS; do
wlc unlock $c;
done
for c in $COMPONENTS; do
wlc pull $c;
done

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__ = "4.0.0"
__version__ = "4.8.0.dev0"

View File

@@ -59,6 +59,7 @@ class PretixScanSecurityProfile(AllowListSecurityProfile):
('GET', 'api-v1:badgeitem-list'),
('GET', 'api-v1:checkinlist-list'),
('GET', 'api-v1:checkinlist-status'),
('POST', 'api-v1:checkinlist-failed_checkins'),
('GET', 'api-v1:checkinlistpos-list'),
('POST', 'api-v1:checkinlistpos-redeem'),
('GET', 'api-v1:revokedsecrets-list'),
@@ -69,9 +70,9 @@ class PretixScanSecurityProfile(AllowListSecurityProfile):
)
class PretixScanNoSyncSecurityProfile(AllowListSecurityProfile):
class PretixScanNoSyncNoSearchSecurityProfile(AllowListSecurityProfile):
identifier = 'pretixscan_online_kiosk'
verbose_name = _('pretixSCAN (kiosk mode, online only)')
verbose_name = _('pretixSCAN (kiosk mode, no order sync, no search)')
allowlist = (
('GET', 'api-v1:version'),
('GET', 'api-v1:device.eventselection'),
@@ -89,6 +90,37 @@ class PretixScanNoSyncSecurityProfile(AllowListSecurityProfile):
('GET', 'api-v1:badgeitem-list'),
('GET', 'api-v1:checkinlist-list'),
('GET', 'api-v1:checkinlist-status'),
('POST', 'api-v1:checkinlist-failed_checkins'),
('POST', 'api-v1:checkinlistpos-redeem'),
('GET', 'api-v1:revokedsecrets-list'),
('GET', 'api-v1:orderposition-pdf_image'),
('GET', 'api-v1:event.settings'),
('POST', 'api-v1:upload'),
)
class PretixScanNoSyncSecurityProfile(AllowListSecurityProfile):
identifier = 'pretixscan_online_noorders'
verbose_name = _('pretixSCAN (online only, no order sync)')
allowlist = (
('GET', 'api-v1:version'),
('GET', 'api-v1:device.eventselection'),
('POST', 'api-v1:device.update'),
('POST', 'api-v1:device.revoke'),
('POST', 'api-v1:device.roll'),
('GET', 'api-v1:event-list'),
('GET', 'api-v1:event-detail'),
('GET', 'api-v1:subevent-list'),
('GET', 'api-v1:subevent-detail'),
('GET', 'api-v1:itemcategory-list'),
('GET', 'api-v1:item-list'),
('GET', 'api-v1:question-list'),
('GET', 'api-v1:badgelayout-list'),
('GET', 'api-v1:badgeitem-list'),
('GET', 'api-v1:checkinlist-list'),
('GET', 'api-v1:checkinlist-status'),
('POST', 'api-v1:checkinlist-failed_checkins'),
('GET', 'api-v1:checkinlistpos-list'),
('POST', 'api-v1:checkinlistpos-redeem'),
('GET', 'api-v1:revokedsecrets-list'),
('GET', 'api-v1:orderposition-pdf_image'),
@@ -131,9 +163,12 @@ class PretixPosSecurityProfile(AllowListSecurityProfile):
('POST', 'api-v1:orderrefund-list'),
('POST', 'api-v1:orderrefund-done'),
('POST', 'api-v1:cartposition-list'),
('POST', 'api-v1:cartposition-bulk-create'),
('GET', 'api-v1:checkinlist-list'),
('POST', 'api-v1:checkinlistpos-redeem'),
('POST', 'plugins:pretix_posbackend:order.posprintlog'),
('POST', 'plugins:pretix_posbackend:order.poslock'),
('DELETE', 'plugins:pretix_posbackend:order.poslock'),
('DELETE', 'api-v1:cartposition-detail'),
('GET', 'api-v1:giftcard-list'),
('POST', 'api-v1:giftcard-transact'),
@@ -141,8 +176,11 @@ class PretixPosSecurityProfile(AllowListSecurityProfile):
('POST', 'plugins:pretix_posbackend:posreceipt-list'),
('POST', 'plugins:pretix_posbackend:posclosing-list'),
('POST', 'plugins:pretix_posbackend:posdebugdump-list'),
('POST', 'plugins:pretix_posbackend:posdebuglogentry-list'),
('POST', 'plugins:pretix_posbackend:posdebuglogentry-bulk-create'),
('GET', 'plugins:pretix_posbackend:poscashier-list'),
('POST', 'plugins:pretix_posbackend:stripeterminal.token'),
('PUT', 'plugins:pretix_posbackend:file.upload'),
('GET', 'api-v1:revokedsecrets-list'),
('GET', 'api-v1:event.settings'),
('GET', 'plugins:pretix_seating:event.event'),
@@ -158,6 +196,7 @@ DEVICE_SECURITY_PROFILES = {
FullAccessSecurityProfile,
PretixScanSecurityProfile,
PretixScanNoSyncSecurityProfile,
PretixScanNoSyncNoSearchSecurityProfile,
PretixPosSecurityProfile,
)
}

View File

@@ -49,7 +49,9 @@ class EventPermission(BasePermission):
if not request.user.is_authenticated and not isinstance(request.auth, (Device, TeamAPIToken)):
return False
if request.method not in SAFE_METHODS and hasattr(view, 'write_permission'):
if hasattr(view, '_get_permission_name'):
required_permission = getattr(view, '_get_permission_name')(request)
elif request.method not in SAFE_METHODS and hasattr(view, 'write_permission'):
required_permission = getattr(view, 'write_permission')
elif hasattr(view, 'permission'):
required_permission = getattr(view, 'permission')

View File

@@ -0,0 +1,18 @@
# Generated by Django 3.2.2 on 2021-07-05 07:56
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('pretixapi', '0005_auto_20191028_1541'),
]
operations = [
migrations.AlterField(
model_name='webhook',
name='target_url',
field=models.URLField(max_length=255),
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 3.2.4 on 2021-09-15 11:06
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('pretixapi', '0006_alter_webhook_target_url'),
]
operations = [
migrations.AlterField(
model_name='webhookcall',
name='target_url',
field=models.URLField(max_length=255),
),
]

View File

@@ -95,7 +95,7 @@ class OAuthRefreshToken(AbstractRefreshToken):
class WebHook(models.Model):
organizer = models.ForeignKey('pretixbase.Organizer', on_delete=models.CASCADE, related_name='webhooks')
enabled = models.BooleanField(default=True, verbose_name=_("Enable webhook"))
target_url = models.URLField(verbose_name=_("Target URL"))
target_url = models.URLField(verbose_name=_("Target URL"), max_length=255)
all_events = models.BooleanField(default=True, verbose_name=_("All events (including newly created ones)"))
limit_events = models.ManyToManyField('pretixbase.Event', verbose_name=_("Limit to events"), blank=True)
@@ -120,7 +120,7 @@ class WebHookEventListener(models.Model):
class WebHookCall(models.Model):
webhook = models.ForeignKey('WebHook', on_delete=models.CASCADE, related_name='calls')
datetime = models.DateTimeField(auto_now_add=True)
target_url = models.URLField()
target_url = models.URLField(max_length=255)
action_type = models.CharField(max_length=255)
is_retry = models.BooleanField(default=False)
execution_time = models.FloatField(null=True)

View File

@@ -0,0 +1,27 @@
#
# This file is part of pretix (Community Edition).
#
# Copyright (C) 2014-2020 Raphael Michel and contributors
# Copyright (C) 2020-2021 rami.io 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/>.
#
from rest_framework.pagination import PageNumberPagination
class Pagination(PageNumberPagination):
page_size_query_param = 'page_size'
max_page_size = 50

View File

@@ -73,53 +73,61 @@ class CartPositionCreateSerializer(I18nAwareModelSerializer):
minutes=self.context['event'].settings.get('reservation_time', as_type=int)
)
with self.context['event'].lock():
new_quotas = (validated_data.get('variation').quotas.filter(subevent=validated_data.get('subevent'))
if validated_data.get('variation')
else validated_data.get('item').quotas.filter(subevent=validated_data.get('subevent')))
if len(new_quotas) == 0:
new_quotas = (validated_data.get('variation').quotas.filter(subevent=validated_data.get('subevent'))
if validated_data.get('variation')
else validated_data.get('item').quotas.filter(subevent=validated_data.get('subevent')))
if len(new_quotas) == 0:
raise ValidationError(
gettext_lazy('The product "{}" is not assigned to a quota.').format(
str(validated_data.get('item'))
)
)
for quota in new_quotas:
avail = quota.availability(_cache=self.context['quota_cache'])
if avail[0] != Quota.AVAILABILITY_OK or (avail[1] is not None and avail[1] < 1):
raise ValidationError(
gettext_lazy('The product "{}" is not assigned to a quota.').format(
str(validated_data.get('item'))
gettext_lazy('There is not enough quota available on quota "{}" to perform '
'the operation.').format(
quota.name
)
)
for quota in new_quotas:
avail = quota.availability()
if avail[0] != Quota.AVAILABILITY_OK or (avail[1] is not None and avail[1] < 1):
raise ValidationError(
gettext_lazy('There is not enough quota available on quota "{}" to perform '
'the operation.').format(
quota.name
)
)
attendee_name = validated_data.pop('attendee_name', '')
if attendee_name and not validated_data.get('attendee_name_parts'):
validated_data['attendee_name_parts'] = {
'_legacy': attendee_name
}
seated = validated_data.get('item').seat_category_mappings.filter(subevent=validated_data.get('subevent')).exists()
if validated_data.get('seat'):
if not seated:
raise ValidationError('The specified product does not allow to choose a seat.')
try:
seat = self.context['event'].seats.get(seat_guid=validated_data['seat'], subevent=validated_data.get('subevent'))
except Seat.DoesNotExist:
raise ValidationError('The specified seat does not exist.')
except Seat.MultipleObjectsReturned:
raise ValidationError('The specified seat ID is not unique.')
else:
validated_data['seat'] = seat
if not seat.is_available(
sales_channel=validated_data.get('sales_channel', 'web'),
distance_ignore_cart_id=validated_data['cart_id'],
):
raise ValidationError(gettext_lazy('The selected seat "{seat}" is not available.').format(seat=seat.name))
elif seated:
raise ValidationError('The specified product requires to choose a seat.')
for quota in new_quotas:
oldsize = self.context['quota_cache'][quota.pk][1]
newsize = oldsize - 1 if oldsize is not None else None
self.context['quota_cache'][quota.pk] = (
Quota.AVAILABILITY_OK if newsize is None or newsize > 0 else Quota.AVAILABILITY_GONE,
newsize
)
validated_data.pop('sales_channel')
cp = CartPosition.objects.create(event=self.context['event'], **validated_data)
attendee_name = validated_data.pop('attendee_name', '')
if attendee_name and not validated_data.get('attendee_name_parts'):
validated_data['attendee_name_parts'] = {
'_legacy': attendee_name
}
seated = validated_data.get('item').seat_category_mappings.filter(subevent=validated_data.get('subevent')).exists()
if validated_data.get('seat'):
if not seated:
raise ValidationError('The specified product does not allow to choose a seat.')
try:
seat = self.context['event'].seats.get(seat_guid=validated_data['seat'], subevent=validated_data.get('subevent'))
except Seat.DoesNotExist:
raise ValidationError('The specified seat does not exist.')
except Seat.MultipleObjectsReturned:
raise ValidationError('The specified seat ID is not unique.')
else:
validated_data['seat'] = seat
if not seat.is_available(
sales_channel=validated_data.get('sales_channel', 'web'),
distance_ignore_cart_id=validated_data['cart_id'],
):
raise ValidationError(gettext_lazy('The selected seat "{seat}" is not available.').format(seat=seat.name))
elif seated:
raise ValidationError('The specified product requires to choose a seat.')
validated_data.pop('sales_channel')
cp = CartPosition.objects.create(event=self.context['event'], **validated_data)
for answ_data in answers_data:
options = answ_data.pop('options')

View File

@@ -60,7 +60,7 @@ class CheckinListSerializer(I18nAwareModelSerializer):
full_data = self.to_internal_value(self.to_representation(self.instance)) if self.instance else {}
full_data.update(data)
for item in full_data.get('limit_products'):
for item in full_data.get('limit_products', []):
if event != item.event:
raise ValidationError(_('One or more items do not belong to this event.'))

View File

@@ -42,6 +42,7 @@ from django.utils.functional import cached_property
from django.utils.translation import gettext as _
from django_countries.serializers import CountryFieldMixin
from pytz import common_timezones
from rest_framework import serializers
from rest_framework.fields import ChoiceField, Field
from rest_framework.relations import SlugRelatedField
@@ -53,7 +54,7 @@ from pretix.base.models.items import SubEventItem, SubEventItemVariation
from pretix.base.services.seating import (
SeatProtected, generate_seats, validate_plan_change,
)
from pretix.base.settings import validate_event_settings
from pretix.base.settings import LazyI18nStringList, validate_event_settings
from pretix.base.signals import api_event_settings_fields
logger = logging.getLogger(__name__)
@@ -93,9 +94,12 @@ class MetaPropertyField(Field):
class SeatCategoryMappingField(Field):
def to_representation(self, value):
qs = value.seat_category_mappings.all()
if isinstance(value, Event):
qs = qs.filter(subevent=None)
if hasattr(value, '_seat_category_mappings'):
qs = value._seat_category_mappings
else:
qs = value.seat_category_mappings.all()
if isinstance(value, Event):
qs = qs.filter(subevent=None)
return {
v.layout_category: v.product_id for v in qs
}
@@ -156,6 +160,7 @@ class EventSerializer(I18nAwareModelSerializer):
seat_category_mapping = SeatCategoryMappingField(source='*', required=False)
timezone = TimeZoneField(required=False, choices=[(a, a) for a in common_timezones])
valid_keys = ValidKeysField(source='*', read_only=True)
best_availability_state = serializers.IntegerField(allow_null=True, read_only=True)
class Meta:
model = Event
@@ -163,12 +168,14 @@ class EventSerializer(I18nAwareModelSerializer):
'date_to', 'date_admission', 'is_public', 'presale_start',
'presale_end', 'location', 'geo_lat', 'geo_lon', 'has_subevents', 'meta_data', 'seating_plan',
'plugins', 'seat_category_mapping', 'timezone', 'item_meta_properties', 'valid_keys',
'sales_channels')
'sales_channels', 'best_availability_state')
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if not hasattr(self.context['request'], 'event'):
self.fields.pop('valid_keys')
if not self.context.get('request') or 'with_availability_for' not in self.context['request'].GET:
self.fields.pop('best_availability_state')
def validate(self, data):
data = super().validate(data)
@@ -441,13 +448,19 @@ class SubEventSerializer(I18nAwareModelSerializer):
seat_category_mapping = SeatCategoryMappingField(source='*', required=False)
event = SlugRelatedField(slug_field='slug', read_only=True)
meta_data = MetaDataField(source='*')
best_availability_state = serializers.IntegerField(allow_null=True, read_only=True)
class Meta:
model = SubEvent
fields = ('id', 'name', 'date_from', 'date_to', 'active', 'date_admission',
'presale_start', 'presale_end', 'location', 'geo_lat', 'geo_lon', 'event', 'is_public',
'frontpage_text', 'seating_plan', 'item_price_overrides', 'variation_price_overrides',
'meta_data', 'seat_category_mapping', 'last_modified')
'meta_data', 'seat_category_mapping', 'last_modified', 'best_availability_state')
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if not self.context.get('request') or 'with_availability_for' not in self.context['request'].GET:
self.fields.pop('best_availability_state')
def validate(self, data):
data = super().validate(data)
@@ -624,7 +637,7 @@ class SubEventSerializer(I18nAwareModelSerializer):
class TaxRuleSerializer(CountryFieldMixin, I18nAwareModelSerializer):
class Meta:
model = TaxRule
fields = ('id', 'name', 'rate', 'price_includes_tax', 'eu_reverse_charge', 'home_country')
fields = ('id', 'name', 'rate', 'price_includes_tax', 'eu_reverse_charge', 'home_country', 'internal_name', 'keep_gross_if_rate_changes')
class EventSettingsSerializer(SettingsSerializer):
@@ -691,6 +704,7 @@ class EventSettingsSerializer(SettingsSerializer):
'payment_term_accept_late',
'payment_explanation',
'payment_pending_hidden',
'mail_days_order_expire_warning',
'ticket_download',
'ticket_download_date',
'ticket_download_addons',
@@ -699,7 +713,6 @@ class EventSettingsSerializer(SettingsSerializer):
'ticket_download_require_validated_email',
'ticket_secret_length',
'mail_prefix',
'mail_from',
'mail_from_name',
'mail_attach_ical',
'mail_attach_tickets',
@@ -720,6 +733,7 @@ class EventSettingsSerializer(SettingsSerializer):
'invoice_numbers_prefix_cancellations',
'invoice_numbers_counter_length',
'invoice_attendee_name',
'invoice_event_location',
'invoice_include_expire_date',
'invoice_address_explanation_text',
'invoice_email_attachment',
@@ -749,6 +763,7 @@ class EventSettingsSerializer(SettingsSerializer):
'cancel_allow_user_paid_refund_as_giftcard',
'cancel_allow_user_paid_require_approval',
'change_allow_user_variation',
'change_allow_user_addons',
'change_allow_user_until',
'change_allow_user_price',
'primary_color',
@@ -776,6 +791,10 @@ class EventSettingsSerializer(SettingsSerializer):
data = super().validate(data)
settings_dict = self.instance.freeze()
settings_dict.update(data)
if data.get('confirm_texts') is not None:
data['confirm_texts'] = LazyI18nStringList(data['confirm_texts'])
validate_event_settings(self.event, settings_dict)
return data

View File

@@ -31,9 +31,10 @@
# Unless required by applicable law or agreed to in writing, software distributed under the Apache License 2.0 is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under the License.
import os.path
from decimal import Decimal
from django.conf import settings
from django.core.exceptions import ValidationError
from django.db import transaction
from django.db.models import QuerySet
@@ -57,8 +58,10 @@ class InlineItemVariationSerializer(I18nAwareModelSerializer):
class Meta:
model = ItemVariation
fields = ('id', 'value', 'active', 'description',
'position', 'default_price', 'price', 'original_price',
'require_membership', 'require_membership_types',)
'position', 'default_price', 'price', 'original_price', 'require_approval',
'require_membership', 'require_membership_types',
'require_membership_hidden', 'available_from', 'available_until',
'sales_channels', 'hide_without_voucher',)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@@ -72,8 +75,10 @@ class ItemVariationSerializer(I18nAwareModelSerializer):
class Meta:
model = ItemVariation
fields = ('id', 'value', 'active', 'description',
'position', 'default_price', 'price', 'original_price',
'require_membership', 'require_membership_types',)
'position', 'default_price', 'price', 'original_price', 'require_approval',
'require_membership', 'require_membership_types',
'require_membership_hidden', 'available_from', 'available_until',
'sales_channels', 'hide_without_voucher',)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@@ -161,7 +166,7 @@ class ItemSerializer(I18nAwareModelSerializer):
meta_data = MetaDataField(required=False, source='*')
picture = UploadedFileField(required=False, allow_null=True, allowed_types=(
'image/png', 'image/jpeg', 'image/gif'
), max_size=10 * 1024 * 1024)
), max_size=settings.FILE_UPLOAD_MAX_SIZE_IMAGE)
class Meta:
model = Item
@@ -172,7 +177,7 @@ class ItemSerializer(I18nAwareModelSerializer):
'min_per_order', 'max_per_order', 'checkin_attention', 'has_variations', 'variations',
'addons', 'bundles', 'original_price', 'require_approval', 'generate_tickets',
'show_quota_left', 'hidden_if_available', 'allow_waitinglist', 'issue_giftcard', 'meta_data',
'require_membership', 'require_membership_types', 'grant_membership_type',
'require_membership', 'require_membership_types', 'require_membership_hidden', 'grant_membership_type',
'grant_membership_duration_like_event', 'grant_membership_duration_days',
'grant_membership_duration_months')
read_only_fields = ('has_variations',)
@@ -245,10 +250,16 @@ class ItemSerializer(I18nAwareModelSerializer):
addons_data = validated_data.pop('addons') if 'addons' in validated_data else {}
bundles_data = validated_data.pop('bundles') if 'bundles' in validated_data else {}
meta_data = validated_data.pop('meta_data', None)
picture = validated_data.pop('picture', None)
require_membership_types = validated_data.pop('require_membership_types', [])
item = Item.objects.create(**validated_data)
if picture:
item.picture.save(os.path.basename(picture.name), picture)
if require_membership_types:
item.require_membership_types.add(*require_membership_types)
for variation_data in variations_data:
require_membership_types = variation_data.pop('require_membership_types')
require_membership_types = variation_data.pop('require_membership_types', [])
v = ItemVariation.objects.create(item=item, **variation_data)
if require_membership_types:
v.require_membership_types.add(*require_membership_types)
@@ -269,7 +280,10 @@ class ItemSerializer(I18nAwareModelSerializer):
def update(self, instance, validated_data):
meta_data = validated_data.pop('meta_data', None)
picture = validated_data.pop('picture', None)
item = super().update(instance, validated_data)
if picture:
item.picture.save(os.path.basename(picture.name), picture)
# Meta data
if meta_data is not None:
@@ -408,10 +422,19 @@ class QuestionSerializer(I18nAwareModelSerializer):
class QuotaSerializer(I18nAwareModelSerializer):
available = serializers.BooleanField(read_only=True)
available_number = serializers.IntegerField(read_only=True)
class Meta:
model = Quota
fields = ('id', 'name', 'size', 'items', 'variations', 'subevent', 'closed', 'close_when_sold_out', 'release_after_exit')
fields = ('id', 'name', 'size', 'items', 'variations', 'subevent', 'closed', 'close_when_sold_out',
'release_after_exit', 'available', 'available_number')
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if 'request' not in self.context or self.context['request'].GET.get('with_availability') != 'true':
del self.fields['available']
del self.fields['available_number']
def validate(self, data):
data = super().validate(data)

View File

@@ -26,6 +26,7 @@ from collections import Counter, defaultdict
from decimal import Decimal
import pycountry
from django.conf import settings
from django.core.files import File
from django.db.models import F, Q
from django.utils.timezone import now
@@ -191,7 +192,7 @@ class AnswerSerializer(I18nAwareModelSerializer):
)
if cf.type not in allowed_types:
raise ValidationError('The submitted file "{fid}" has a file type that is not allowed in this field.'.format(fid=data))
if cf.file.size > 10 * 1024 * 1024:
if cf.file.size > settings.FILE_UPLOAD_MAX_SIZE_OTHER:
raise ValidationError('The submitted file "{fid}" is too large to be used in this field.'.format(fid=data))
data['options'] = []
@@ -199,7 +200,9 @@ class AnswerSerializer(I18nAwareModelSerializer):
return data
def validate(self, data):
if data.get('question').type == Question.TYPE_FILE:
if not data.get('question'):
raise ValidationError('Question not specified.')
elif data.get('question').type == Question.TYPE_FILE:
return self._handle_file_upload(data)
elif data.get('question').type in (Question.TYPE_CHOICE, Question.TYPE_CHOICE_MULTIPLE):
if not data.get('options'):
@@ -250,7 +253,30 @@ class AnswerSerializer(I18nAwareModelSerializer):
class CheckinSerializer(I18nAwareModelSerializer):
class Meta:
model = Checkin
fields = ('id', 'datetime', 'list', 'auto_checked_in', 'type')
fields = ('id', 'datetime', 'list', 'auto_checked_in', 'gate', 'device', 'type')
class FailedCheckinSerializer(I18nAwareModelSerializer):
error_reason = serializers.ChoiceField(choices=Checkin.REASONS, required=True, allow_null=False)
raw_barcode = serializers.CharField(required=True, allow_null=False)
position = serializers.PrimaryKeyRelatedField(queryset=OrderPosition.all.none(), required=False, allow_null=True)
raw_item = serializers.PrimaryKeyRelatedField(queryset=Item.objects.none(), required=False, allow_null=True)
raw_variation = serializers.PrimaryKeyRelatedField(queryset=ItemVariation.objects.none(), required=False, allow_null=True)
raw_subevent = serializers.PrimaryKeyRelatedField(queryset=SubEvent.objects.none(), required=False, allow_null=True)
class Meta:
model = Checkin
fields = ('error_reason', 'error_explanation', 'raw_barcode', 'raw_item', 'raw_variation',
'raw_subevent', 'datetime', 'type', 'position')
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
event = self.context['event']
self.fields['raw_item'].queryset = event.items.all()
self.fields['raw_variation'].queryset = ItemVariation.objects.filter(item__event=event)
self.fields['position'].queryset = OrderPosition.all.filter(order__event=event)
if event.has_subevents:
self.fields['raw_subevent'].queryset = event.subevents.all()
class OrderDownloadsField(serializers.Field):
@@ -631,7 +657,7 @@ class OrderSerializer(I18nAwareModelSerializer):
model = Order
fields = (
'code', 'status', 'testmode', 'secret', 'email', 'phone', 'locale', 'datetime', 'expires', 'payment_date',
'payment_provider', 'fees', 'total', 'comment', 'invoice_address', 'positions', 'downloads',
'payment_provider', 'fees', 'total', 'comment', 'custom_followup_at', 'invoice_address', 'positions', 'downloads',
'checkin_attention', 'last_modified', 'payments', 'refunds', 'require_approval', 'sales_channel',
'url', 'customer'
)
@@ -662,7 +688,7 @@ class OrderSerializer(I18nAwareModelSerializer):
def update(self, instance, validated_data):
# Even though all fields that shouldn't be edited are marked as read_only in the serializer
# (hopefully), we'll be extra careful here and be explicit about the model fields we update.
update_fields = ['comment', 'checkin_attention', 'email', 'locale', 'phone']
update_fields = ['comment', 'custom_followup_at', 'checkin_attention', 'email', 'locale', 'phone']
if 'invoice_address' in validated_data:
iadata = validated_data.pop('invoice_address')
@@ -902,12 +928,14 @@ class OrderCreateSerializer(I18nAwareModelSerializer):
min_length=5
)
comment = serializers.CharField(required=False, allow_blank=True)
custom_followup_at = serializers.DateField(required=False, allow_null=True)
payment_provider = serializers.CharField(required=False, allow_null=True)
payment_info = CompatibleJSONField(required=False)
consume_carts = serializers.ListField(child=serializers.CharField(), required=False)
force = serializers.BooleanField(default=False, required=False)
payment_date = serializers.DateTimeField(required=False, allow_null=True)
send_email = serializers.BooleanField(default=False, required=False)
send_email = serializers.BooleanField(default=False, required=False, allow_null=True)
require_approval = serializers.BooleanField(default=False, required=False)
simulate = serializers.BooleanField(default=False, required=False)
customer = serializers.SlugRelatedField(slug_field='identifier', queryset=Customer.objects.none(), required=False)
@@ -920,7 +948,7 @@ class OrderCreateSerializer(I18nAwareModelSerializer):
model = Order
fields = ('code', 'status', 'testmode', 'email', 'phone', 'locale', 'payment_provider', 'fees', 'comment', 'sales_channel',
'invoice_address', 'positions', 'checkin_attention', 'payment_info', 'payment_date', 'consume_carts',
'force', 'send_email', 'simulate', 'customer')
'force', 'send_email', 'simulate', 'customer', 'custom_followup_at', 'require_approval')
def validate_payment_provider(self, pp):
if pp is None:
@@ -1014,6 +1042,8 @@ class OrderCreateSerializer(I18nAwareModelSerializer):
force = validated_data.pop('force', False)
simulate = validated_data.pop('simulate', False)
self._send_mail = validated_data.pop('send_email', False)
if self._send_mail is None:
self._send_mail = validated_data.get('sales_channel') in self.context['event'].settings.mail_sales_channel_placed_paid
if 'invoice_address' in validated_data:
iadata = validated_data.pop('invoice_address')
@@ -1192,6 +1222,8 @@ class OrderCreateSerializer(I18nAwareModelSerializer):
order.set_expires(subevents=[p.get('subevent') for p in positions_data])
order.meta_info = "{}"
order.total = Decimal('0.00')
if validated_data.get('require_approval') is not None:
order.require_approval = validated_data['require_approval']
if simulate:
order = WrappedModel(order)
order.last_modified = now()
@@ -1269,7 +1301,13 @@ class OrderCreateSerializer(I18nAwareModelSerializer):
if pos.voucher:
Voucher.objects.filter(pk=pos.voucher.pk).update(redeemed=F('redeemed') + 1)
pos.save()
seen_answers = set()
for answ_data in answers_data:
# Workaround for a pretixPOS bug :-(
if answ_data.get('question') in seen_answers:
continue
seen_answers.add(answ_data.get('question'))
options = answ_data.pop('options', [])
if isinstance(answ_data['answer'], File):
@@ -1371,6 +1409,7 @@ class OrderCreateSerializer(I18nAwareModelSerializer):
state=OrderPayment.PAYMENT_STATE_CREATED
)
order.create_transactions(is_new=True, fees=fees, positions=pos_map.values())
return order
@@ -1392,8 +1431,9 @@ class InlineInvoiceLineSerializer(I18nAwareModelSerializer):
class Meta:
model = InvoiceLine
fields = ('position', 'description', 'item', 'variation', 'attendee_name', 'event_date_from',
'event_date_to', 'gross_value', 'tax_value', 'tax_rate', 'tax_name')
fields = ('position', 'description', 'item', 'variation', 'subevent', 'attendee_name', 'event_date_from',
'event_date_to', 'gross_value', 'tax_value', 'tax_rate', 'tax_name', 'fee_type',
'fee_internal_type', 'event_location')
class InvoiceSerializer(I18nAwareModelSerializer):

View File

@@ -275,6 +275,7 @@ class OrganizerSettingsSerializer(SettingsSerializer):
default_fields = [
'customer_accounts',
'customer_accounts_link_by_email',
'invoice_regenerate_allowed',
'contact_mail',
'imprint_url',
'organizer_info_text',
@@ -294,7 +295,15 @@ class OrganizerSettingsSerializer(SettingsSerializer):
'theme_color_background',
'theme_round_borders',
'primary_font',
'organizer_logo_image'
'organizer_logo_image_inherit',
'organizer_logo_image',
'privacy_url',
'cookie_consent',
'cookie_consent_dialog_title',
'cookie_consent_dialog_text',
'cookie_consent_dialog_text_secondary',
'cookie_consent_dialog_button_yes',
'cookie_consent_dialog_button_no',
]
def __init__(self, *args, **kwargs):

View File

@@ -54,8 +54,8 @@ class SettingsSerializer(serializers.Serializer):
f = DEFAULTS[fname]['serializer_class'](
**kwargs
)
f._label = form_kwargs.get('label', fname)
f._help_text = form_kwargs.get('help_text')
f._label = str(form_kwargs.get('label', fname))
f._help_text = str(form_kwargs.get('help_text'))
f.parent = self
self.fields[fname] = f

View File

@@ -21,14 +21,18 @@
#
from django.db import transaction
from rest_framework import status, viewsets
from rest_framework.decorators import action
from rest_framework.exceptions import ValidationError
from rest_framework.filters import OrderingFilter
from rest_framework.mixins import CreateModelMixin, DestroyModelMixin
from rest_framework.response import Response
from rest_framework.settings import api_settings
from pretix.api.serializers.cart import (
CartPositionCreateSerializer, CartPositionSerializer,
)
from pretix.base.models import CartPosition
from pretix.base.services.locking import NoLockManager
class CartPositionViewSet(CreateModelMixin, DestroyModelMixin, viewsets.ReadOnlyModelViewSet):
@@ -50,18 +54,61 @@ class CartPositionViewSet(CreateModelMixin, DestroyModelMixin, viewsets.ReadOnly
def get_serializer_context(self):
ctx = super().get_serializer_context()
ctx['event'] = self.request.event
ctx['quota_cache'] = {}
return ctx
def create(self, request, *args, **kwargs):
serializer = CartPositionCreateSerializer(data=request.data, context=self.get_serializer_context())
serializer.is_valid(raise_exception=True)
with transaction.atomic():
with transaction.atomic(), self.request.event.lock():
self.perform_create(serializer)
cp = serializer.instance
serializer = CartPositionSerializer(cp, context=serializer.context)
cp = serializer.instance
serializer = CartPositionSerializer(cp, context=serializer.context)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
@action(detail=False, methods=['POST'])
def bulk_create(self, request, *args, **kwargs):
if not isinstance(request.data, list): # noqa
return Response({"error": "Please supply a list"}, status=status.HTTP_400_BAD_REQUEST)
ctx = self.get_serializer_context()
with transaction.atomic():
serializers = [
CartPositionCreateSerializer(data=d, context=ctx)
for d in request.data
]
lockfn = self.request.event.lock
if not any(s.is_valid(raise_exception=False) for s in serializers):
lockfn = NoLockManager
results = []
with lockfn():
for s in serializers:
if s.is_valid(raise_exception=False):
try:
cp = s.save()
except ValidationError as e:
results.append({
'success': False,
'data': None,
'errors': {api_settings.NON_FIELD_ERRORS_KEY: e.detail},
})
else:
results.append({
'success': True,
'data': CartPositionSerializer(cp, context=ctx).data,
'errors': None,
})
else:
results.append({
'success': False,
'data': None,
'errors': s.errors,
})
return Response({'results': results}, status=status.HTTP_200_OK)
def perform_create(self, serializer):
serializer.save()

View File

@@ -20,7 +20,9 @@
# <https://www.gnu.org/licenses/>.
#
import django_filters
from django.conf import settings
from django.core.exceptions import ValidationError
from django.db import transaction
from django.db.models import (
Count, Exists, F, Max, OrderBy, OuterRef, Prefetch, Q, Subquery,
)
@@ -31,19 +33,24 @@ from django.utils.functional import cached_property
from django.utils.timezone import now
from django_filters.rest_framework import DjangoFilterBackend, FilterSet
from django_scopes import scopes_disabled
from packaging.version import parse
from rest_framework import viewsets
from rest_framework.decorators import action
from rest_framework.fields import DateTimeField
from rest_framework.permissions import SAFE_METHODS
from rest_framework.response import Response
from pretix.api.serializers.checkin import CheckinListSerializer
from pretix.api.serializers.item import QuestionSerializer
from pretix.api.serializers.order import CheckinListOrderPositionSerializer
from pretix.api.serializers.order import (
CheckinListOrderPositionSerializer, FailedCheckinSerializer,
)
from pretix.api.views import RichOrderingFilter
from pretix.api.views.order import OrderPositionFilter
from pretix.base.i18n import language
from pretix.base.models import (
CachedFile, Checkin, CheckinList, Event, Order, OrderPosition, Question,
CachedFile, Checkin, CheckinList, Device, Event, Order, OrderPosition,
Question,
)
from pretix.base.services.checkin import (
CheckInError, RequiredQuestionsError, SQLLogic, perform_checkin,
@@ -79,8 +86,14 @@ class CheckinListViewSet(viewsets.ModelViewSet):
queryset = CheckinList.objects.none()
filter_backends = (DjangoFilterBackend,)
filterset_class = CheckinListFilter
permission = ('can_view_orders', 'can_checkin_orders',)
write_permission = 'can_change_event_settings'
def _get_permission_name(self, request):
if request.path.endswith('/failed_checkins/'):
return 'can_checkin_orders', 'can_change_orders'
elif request.method in SAFE_METHODS:
return 'can_view_orders', 'can_checkin_orders',
else:
return 'can_change_event_settings'
def get_queryset(self):
qs = self.request.event.checkin_lists.prefetch_related(
@@ -125,6 +138,49 @@ class CheckinListViewSet(viewsets.ModelViewSet):
)
super().perform_destroy(instance)
@action(detail=True, methods=['POST'], url_name='failed_checkins')
@transaction.atomic()
def failed_checkins(self, *args, **kwargs):
serializer = FailedCheckinSerializer(
data=self.request.data,
context={'event': self.request.event}
)
serializer.is_valid(raise_exception=True)
kwargs = {}
if not serializer.validated_data.get('position'):
kwargs['position'] = OrderPosition.all.filter(
secret=serializer.validated_data['raw_barcode']
).first()
c = serializer.save(
list=self.get_object(),
successful=False,
forced=True,
device=self.request.auth if isinstance(self.request.auth, Device) else None,
gate=self.request.auth.gate if isinstance(self.request.auth, Device) else None,
**kwargs,
)
if c.position:
c.position.order.log_action('pretix.event.checkin.denied', data={
'position': c.position.id,
'positionid': c.position.positionid,
'errorcode': c.error_reason,
'reason_explanation': c.error_explanation,
'datetime': c.datetime,
'type': c.type,
'list': c.list.pk
}, user=self.request.user, auth=self.request.auth)
else:
self.request.event.log_action('pretix.event.checkin.unknown', data={
'datetime': c.datetime,
'type': c.type,
'list': c.list.pk,
'barcode': c.raw_barcode
}, user=self.request.user, auth=self.request.auth)
return Response(serializer.data, status=201)
@action(detail=True, methods=['GET'])
def status(self, *args, **kwargs):
with language(self.request.event.settings.locale):
@@ -294,7 +350,7 @@ class CheckinListPositionViewSet(viewsets.ReadOnlyModelViewSet):
lookup='checkins',
queryset=Checkin.objects.filter(list_id=self.checkinlist.pk)
),
'checkins', 'answers', 'answers__options', 'answers__question',
'answers', 'answers__options', 'answers__question',
Prefetch('addons', OrderPosition.objects.select_related('item', 'variation')),
Prefetch('order', Order.objects.select_related('invoice_address').prefetch_related(
Prefetch(
@@ -304,7 +360,8 @@ class CheckinListPositionViewSet(viewsets.ReadOnlyModelViewSet):
Prefetch(
'positions',
OrderPosition.objects.prefetch_related(
'checkins', 'item', 'variation', 'answers', 'answers__options', 'answers__question',
Prefetch('checkins', queryset=Checkin.objects.all()),
'item', 'variation', 'answers', 'answers__options', 'answers__question',
)
)
))
@@ -356,30 +413,113 @@ class CheckinListPositionViewSet(viewsets.ReadOnlyModelViewSet):
else:
dt = now()
common_checkin_args = dict(
raw_barcode=self.kwargs['pk'],
type=type,
list=self.checkinlist,
datetime=dt,
device=self.request.auth if isinstance(self.request.auth, Device) else None,
gate=self.request.auth.gate if isinstance(self.request.auth, Device) else None,
nonce=nonce,
forced=force,
)
raw_barcode_for_checkin = None
try:
queryset = self.get_queryset(ignore_status=True, ignore_products=True)
if self.kwargs['pk'].isnumeric():
op = queryset.get(Q(pk=self.kwargs['pk']) | Q(secret=self.kwargs['pk']))
else:
op = queryset.get(secret=self.kwargs['pk'])
# In application/x-www-form-urlencoded, you can encodes space ' ' with '+' instead of '%20'.
# `id`, however, is part of a path where this technically is not allowed. Old versions of our
# scan apps still do it, so we try work around it!
try:
op = queryset.get(secret=self.kwargs['pk'])
except OrderPosition.DoesNotExist:
op = queryset.get(secret=self.kwargs['pk'].replace('+', ' '))
except OrderPosition.DoesNotExist:
revoked_matches = list(self.request.event.revoked_secrets.filter(secret=self.kwargs['pk']))
if len(revoked_matches) == 0 or not force:
if len(revoked_matches) == 0:
self.request.event.log_action('pretix.event.checkin.unknown', data={
'datetime': dt,
'type': type,
'list': self.checkinlist.pk,
'barcode': self.kwargs['pk']
}, user=self.request.user, auth=self.request.auth)
raise Http404()
op = revoked_matches[0].position
op.order.log_action('pretix.event.checkin.revoked', data={
'datetime': dt,
'type': type,
'list': self.checkinlist.pk,
'barcode': self.kwargs['pk']
}, user=self.request.user, auth=self.request.auth)
for k, s in self.request.event.ticket_secret_generators.items():
try:
parsed = s.parse_secret(self.kwargs['pk'])
common_checkin_args.update({
'raw_item': parsed.item,
'raw_variation': parsed.variation,
'raw_subevent': parsed.subevent,
})
except:
pass
Checkin.objects.create(
position=None,
successful=False,
error_reason=Checkin.REASON_INVALID,
**common_checkin_args,
)
if force and isinstance(self.request.auth, Device):
# There was a bug in libpretixsync: If you scanned a ticket in offline mode that was
# valid at the time but no longer exists at time of upload, the device would retry to
# upload the same scan over and over again. Since we can't update all devices quickly,
# here's a dirty workaround to make it stop.
try:
brand = self.request.auth.software_brand
ver = parse(self.request.auth.software_version)
legacy_mode = (
(brand == 'pretixSCANPROXY' and ver < parse('0.0.3')) or
(brand == 'pretixSCAN Android' and ver < parse('1.11.2')) or
(brand == 'pretixSCAN' and ver < parse('1.11.2'))
)
if legacy_mode:
return Response({
'status': 'error',
'reason': Checkin.REASON_ALREADY_REDEEMED,
'reason_explanation': None,
'require_attention': False,
'__warning': 'Compatibility hack active due to detected old pretixSCAN version',
}, status=400)
except: # we don't care e.g. about invalid version numbers
pass
return Response({
'detail': 'Not found.', # for backwards compatibility
'status': 'error',
'reason': Checkin.REASON_INVALID,
'reason_explanation': None,
'require_attention': False,
}, status=404)
elif revoked_matches and force:
op = revoked_matches[0].position
raw_barcode_for_checkin = self.kwargs['pk']
else:
op = revoked_matches[0].position
op.order.log_action('pretix.event.checkin.revoked', data={
'datetime': dt,
'type': type,
'list': self.checkinlist.pk,
'barcode': self.kwargs['pk']
}, user=self.request.user, auth=self.request.auth)
Checkin.objects.create(
position=op,
successful=False,
error_reason=Checkin.REASON_REVOKED,
**common_checkin_args
)
return Response({
'status': 'error',
'reason': Checkin.REASON_REVOKED,
'reason_explanation': None,
'require_attention': False,
'position': CheckinListOrderPositionSerializer(op, context=self.get_serializer_context()).data
}, status=400)
given_answers = {}
if 'answers' in self.request.data:
@@ -409,6 +549,8 @@ class CheckinListPositionViewSet(viewsets.ReadOnlyModelViewSet):
user=self.request.user,
auth=self.request.auth,
type=type,
raw_barcode=raw_barcode_for_checkin,
from_revoked_secret=True,
)
except RequiredQuestionsError as e:
return Response({
@@ -424,11 +566,19 @@ class CheckinListPositionViewSet(viewsets.ReadOnlyModelViewSet):
'position': op.id,
'positionid': op.positionid,
'errorcode': e.code,
'reason_explanation': e.reason,
'force': force,
'datetime': dt,
'type': type,
'list': self.checkinlist.pk
}, user=self.request.user, auth=self.request.auth)
Checkin.objects.create(
position=op,
successful=False,
error_reason=e.code,
error_explanation=e.reason,
**common_checkin_args,
)
return Response({
'status': 'error',
'reason': e.code,
@@ -460,7 +610,7 @@ class CheckinListPositionViewSet(viewsets.ReadOnlyModelViewSet):
)
if cf.type not in allowed_types:
raise ValidationError('The submitted file "{fid}" has a file type that is not allowed in this field.'.format(fid=data))
if cf.file.size > 10 * 1024 * 1024:
if cf.file.size > settings.FILE_UPLOAD_MAX_SIZE_OTHER:
raise ValidationError('The submitted file "{fid}" is too large to be used in this field.'.format(fid=data))
return cf.file

View File

@@ -42,6 +42,7 @@ class InitializationRequestSerializer(serializers.Serializer):
hardware_model = serializers.CharField(max_length=190)
software_brand = serializers.CharField(max_length=190)
software_version = serializers.CharField(max_length=190)
info = serializers.JSONField(required=False, allow_null=True)
class UpdateRequestSerializer(serializers.Serializer):
@@ -49,6 +50,7 @@ class UpdateRequestSerializer(serializers.Serializer):
hardware_model = serializers.CharField(max_length=190)
software_brand = serializers.CharField(max_length=190)
software_version = serializers.CharField(max_length=190)
info = serializers.JSONField(required=False, allow_null=True)
class GateSerializer(serializers.ModelSerializer):
@@ -94,6 +96,7 @@ class InitializeView(APIView):
device.hardware_model = serializer.validated_data.get('hardware_model')
device.software_brand = serializer.validated_data.get('software_brand')
device.software_version = serializer.validated_data.get('software_version')
device.info = serializer.validated_data.get('info')
device.api_token = generate_api_token()
device.save()
@@ -114,6 +117,7 @@ class UpdateView(APIView):
device.hardware_model = serializer.validated_data.get('hardware_model')
device.software_brand = serializer.validated_data.get('software_brand')
device.software_version = serializer.validated_data.get('software_version')
device.info = serializer.validated_data.get('info')
device.save()
device.log_action('pretix.device.updated', data=serializer.validated_data, auth=device)

View File

@@ -34,7 +34,7 @@
import django_filters
from django.db import transaction
from django.db.models import ProtectedError, Q
from django.db.models import Prefetch, ProtectedError, Q
from django.utils.timezone import now
from django_filters.rest_framework import DjangoFilterBackend, FilterSet
from django_scopes import scopes_disabled
@@ -49,20 +49,24 @@ from pretix.api.serializers.event import (
)
from pretix.api.views import ConditionalListView
from pretix.base.models import (
CartPosition, Device, Event, TaxRule, TeamAPIToken,
CartPosition, Device, Event, SeatCategoryMapping, TaxRule, TeamAPIToken,
)
from pretix.base.models.event import SubEvent
from pretix.base.services.quotas import QuotaAvailability
from pretix.base.settings import SETTINGS_AFFECTING_CSS
from pretix.helpers.dicts import merge_dicts
from pretix.helpers.i18n import i18ncomp
from pretix.presale.style import regenerate_css
from pretix.presale.views.organizer import filter_qs_by_attr
with scopes_disabled():
class EventFilter(FilterSet):
is_past = django_filters.rest_framework.BooleanFilter(method='is_past_qs')
is_future = django_filters.rest_framework.BooleanFilter(method='is_future_qs')
ends_after = django_filters.rest_framework.IsoDateTimeFilter(method='ends_after_qs')
sales_channel = django_filters.rest_framework.CharFilter(method='sales_channel_qs')
search = django_filters.rest_framework.CharFilter(method='search_qs')
class Meta:
model = Event
@@ -107,6 +111,13 @@ with scopes_disabled():
def sales_channel_qs(self, queryset, name, value):
return queryset.filter(sales_channels__contains=value)
def search_qs(self, queryset, name, value):
return queryset.filter(
Q(name__icontains=i18ncomp(value))
| Q(slug__icontains=value)
| Q(location__icontains=i18ncomp(value))
)
class EventViewSet(viewsets.ModelViewSet):
serializer_class = EventSerializer
@@ -136,10 +147,43 @@ class EventViewSet(viewsets.ModelViewSet):
)
qs = filter_qs_by_attr(qs, self.request)
if 'with_availability_for' in self.request.GET:
qs = Event.annotated(qs, channel=self.request.GET.get('with_availability_for'))
return qs.prefetch_related(
'meta_values', 'meta_values__property', 'seat_category_mappings'
'organizer',
'meta_values',
'meta_values__property',
'item_meta_properties',
Prefetch(
'seat_category_mappings',
to_attr='_seat_category_mappings',
queryset=SeatCategoryMapping.objects.filter(subevent=None)
),
)
def list(self, request, *args, **kwargs):
queryset = self.filter_queryset(self.get_queryset())
page = self.paginate_queryset(queryset)
if 'with_availability_for' in self.request.GET:
quotas_to_compute = []
qcache = {}
for se in page:
se._quota_cache = qcache
quotas_to_compute += se.active_quotas
if quotas_to_compute:
qa = QuotaAvailability()
qa.queue(*quotas_to_compute)
qa.compute(allow_cache=True)
qcache.update(qa.results)
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
def perform_update(self, serializer):
current_live_value = serializer.instance.live
updated_live_value = serializer.validated_data.get('live', None)
@@ -197,7 +241,6 @@ class EventViewSet(viewsets.ModelViewSet):
except Event.DoesNotExist:
raise ValidationError('Event to copy from was not found')
print(copy_from, self.request.GET)
new_event = serializer.save(organizer=self.request.organizer)
if copy_from:
@@ -336,8 +379,18 @@ class SubEventViewSet(ConditionalListView, viewsets.ModelViewSet):
qs = filter_qs_by_attr(qs, self.request)
if 'with_availability_for' in self.request.GET:
qs = SubEvent.annotated(qs, channel=self.request.GET.get('with_availability_for'))
return qs.prefetch_related(
'subeventitem_set', 'subeventitemvariation_set', 'seat_category_mappings', 'meta_values'
'event',
'subeventitem_set',
'subeventitemvariation_set',
'meta_values',
Prefetch(
'seat_category_mappings',
to_attr='_seat_category_mappings',
),
)
def list(self, request, **kwargs):
@@ -345,14 +398,24 @@ class SubEventViewSet(ConditionalListView, viewsets.ModelViewSet):
queryset = self.filter_queryset(self.get_queryset())
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(page, many=True)
resp = self.get_paginated_response(serializer.data)
resp['X-Page-Generated'] = date
return resp
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data, headers={'X-Page-Generated': date})
if 'with_availability_for' in self.request.GET:
quotas_to_compute = []
qcache = {}
for se in page:
se._quota_cache = qcache
quotas_to_compute += se.active_quotas
if quotas_to_compute:
qa = QuotaAvailability()
qa.queue(*quotas_to_compute)
qa.compute(allow_cache=True)
qcache.update(qa.results)
serializer = self.get_serializer(page, many=True)
resp = self.get_paginated_response(serializer.data)
resp['X-Page-Generated'] = date
return resp
def perform_update(self, serializer):
original_data = self.get_serializer(instance=serializer.instance).data

View File

@@ -69,7 +69,7 @@ class ExportersMixin:
cf = get_object_or_404(CachedFile, id=kwargs['cfid'])
if cf.file:
resp = ChunkBasedFileResponse(cf.file.file, content_type=cf.type)
resp['Content-Disposition'] = 'attachment; filename="{}"'.format(cf.filename)
resp['Content-Disposition'] = 'attachment; filename="{}"'.format(cf.filename).encode("ascii", "ignore")
return resp
elif not settings.HAS_CELERY:
return Response(
@@ -132,7 +132,7 @@ class EventExportersViewSet(ExportersMixin, viewsets.ViewSet):
def exporters(self):
exporters = []
responses = register_data_exporters.send(self.request.event)
for ex in sorted([response(self.request.event) for r, response in responses], key=lambda ex: str(ex.verbose_name)):
for ex in sorted([response(self.request.event, self.request.organizer) for r, response in responses if response], key=lambda ex: str(ex.verbose_name)):
ex._serializer = JobRunSerializer(exporter=ex)
exporters.append(ex)
return exporters
@@ -147,18 +147,26 @@ class OrganizerExportersViewSet(ExportersMixin, viewsets.ViewSet):
@cached_property
def exporters(self):
exporters = []
events = (self.request.auth or self.request.user).get_events_with_permission('can_view_orders', request=self.request).filter(
if isinstance(self.request.auth, (Device, TeamAPIToken)):
perm_holder = self.request.auth
else:
perm_holder = self.request.user
events = perm_holder.get_events_with_permission('can_view_orders', request=self.request).filter(
organizer=self.request.organizer
)
responses = register_multievent_data_exporters.send(self.request.organizer)
for ex in sorted([response(events) for r, response in responses if response], key=lambda ex: str(ex.verbose_name)):
for ex in sorted([response(events, self.request.organizer) for r, response in responses if response], key=lambda ex: str(ex.verbose_name)):
ex._serializer = JobRunSerializer(exporter=ex, events=events)
exporters.append(ex)
return exporters
def get_serializer_kwargs(self):
if isinstance(self.request.auth, (Device, TeamAPIToken)):
perm_holder = self.request.auth
else:
perm_holder = self.request.user
return {
'events': self.request.auth.get_events_with_permission('can_view_orders', request=self.request).filter(
'events': perm_holder.get_events_with_permission('can_view_orders', request=self.request).filter(
organizer=self.request.organizer
)
}

View File

@@ -477,6 +477,23 @@ class QuotaViewSet(ConditionalListView, viewsets.ModelViewSet):
def get_queryset(self):
return self.request.event.quotas.all()
def list(self, request, *args, **kwargs):
queryset = self.filter_queryset(self.get_queryset())
page = self.paginate_queryset(queryset)
if self.request.GET.get('with_availability') == 'true':
if page:
qa = QuotaAvailability()
qa.queue(*page)
qa.compute(allow_cache=False)
for q in page:
q.available = qa.results[q][0] == Quota.AVAILABILITY_OK
q.available_number = qa.results[q][1]
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
def perform_create(self, serializer):
serializer.save(event=self.request.event)
serializer.instance.log_action(
@@ -496,6 +513,7 @@ class QuotaViewSet(ConditionalListView, viewsets.ModelViewSet):
def get_serializer_context(self):
ctx = super().get_serializer_context()
ctx['event'] = self.request.event
ctx['request'] = self.request
return ctx
def perform_update(self, serializer):

View File

@@ -55,9 +55,9 @@ from pretix.api.serializers.order import (
)
from pretix.base.i18n import language
from pretix.base.models import (
CachedCombinedTicket, CachedTicket, Device, Event, Invoice, InvoiceAddress,
Order, OrderFee, OrderPayment, OrderPosition, OrderRefund, Quota, SubEvent,
TaxRule, TeamAPIToken, generate_secret,
CachedCombinedTicket, CachedTicket, Checkin, Device, Event, Invoice,
InvoiceAddress, Order, OrderFee, OrderPayment, OrderPosition, OrderRefund,
Quota, SubEvent, TaxRule, TeamAPIToken, generate_secret,
)
from pretix.base.models.orders import QuestionAnswer, RevokedTicketSecret
from pretix.base.payment import PaymentException
@@ -92,6 +92,9 @@ with scopes_disabled():
subevent_after = django_filters.IsoDateTimeFilter(method='subevent_after_qs')
subevent_before = django_filters.IsoDateTimeFilter(method='subevent_before_qs')
search = django_filters.CharFilter(method='search_qs')
item = django_filters.CharFilter(field_name='all_positions', lookup_expr='item_id')
variation = django_filters.CharFilter(field_name='all_positions', lookup_expr='variation_id')
subevent = django_filters.CharFilter(field_name='all_positions', lookup_expr='subevent_id')
class Meta:
model = Order
@@ -201,7 +204,8 @@ class OrderViewSet(viewsets.ModelViewSet):
Prefetch(
'positions',
opq.all().prefetch_related(
'checkins', 'item', 'variation', 'answers', 'answers__options', 'answers__question',
Prefetch('checkins', queryset=Checkin.objects.all()),
'item', 'variation', 'answers', 'answers__options', 'answers__question',
'item__category', 'addon_to', 'seat',
Prefetch('addons', opq.select_related('item', 'variation', 'seat'))
)
@@ -212,7 +216,10 @@ class OrderViewSet(viewsets.ModelViewSet):
Prefetch(
'positions',
opq.all().prefetch_related(
'checkins', 'item', 'variation', 'answers', 'answers__options', 'answers__question', 'seat',
Prefetch('checkins', queryset=Checkin.objects.all()),
'item', 'variation',
Prefetch('answers', queryset=QuestionAnswer.objects.prefetch_related('options', 'question').order_by('question__position')),
'seat',
)
)
)
@@ -639,7 +646,11 @@ class OrderViewSet(viewsets.ModelViewSet):
payment and order.total == Decimal('0.00') and order.status == Order.STATUS_PAID and
not order.require_approval and payment.provider == "free"
)
if free_flow:
if order.require_approval:
email_template = request.event.settings.mail_text_order_placed_require_approval
log_entry = 'pretix.event.order.email.order_placed_require_approval'
email_attendees = False
elif free_flow:
email_template = request.event.settings.mail_text_order_free
log_entry = 'pretix.event.order.email.order_free'
email_attendees = request.event.settings.mail_send_order_free_attendee
@@ -652,12 +663,13 @@ class OrderViewSet(viewsets.ModelViewSet):
_order_placed_email(
request.event, order, payment.payment_provider if payment else None, email_template,
log_entry, invoice, payment
log_entry, invoice, payment, is_free=free_flow
)
if email_attendees:
for p in order.positions.all():
if p.addon_to_id is None and p.attendee_email and p.attendee_email != order.email:
_order_placed_email_attendee(request.event, order, p, email_attendees_template, log_entry)
_order_placed_email_attendee(request.event, order, p, email_attendees_template, log_entry,
is_free=free_flow)
if not free_flow and order.status == Order.STATUS_PAID and payment:
payment._send_paid_mail(invoice, None, '')
@@ -690,6 +702,16 @@ class OrderViewSet(viewsets.ModelViewSet):
}
)
if 'custom_followup_at' in self.request.data and serializer.instance.custom_followup_at != self.request.data.get('custom_followup_at'):
serializer.instance.log_action(
'pretix.event.order.custom_followup_at',
user=self.request.user,
auth=self.request.auth,
data={
'new_custom_followup_at': self.request.data.get('custom_followup_at')
}
)
if 'checkin_attention' in self.request.data and serializer.instance.checkin_attention != self.request.data.get('checkin_attention'):
serializer.instance.log_action(
'pretix.event.order.checkin_attention',
@@ -781,7 +803,7 @@ with scopes_disabled():
)
def has_checkin_qs(self, queryset, name, value):
return queryset.filter(checkins__isnull=not value)
return queryset.alias(ce=Exists(Checkin.objects.filter(position=OuterRef('pk')))).filter(ce=value)
def attendee_name_qs(self, queryset, name, value):
return queryset.filter(Q(attendee_name_cached__iexact=value) | Q(addon_to__attendee_name_cached__iexact=value))
@@ -835,7 +857,8 @@ class OrderPositionViewSet(mixins.DestroyModelMixin, mixins.UpdateModelMixin, vi
qs = qs.filter(order__event=self.request.event)
if self.request.query_params.get('pdf_data', 'false') == 'true':
qs = qs.prefetch_related(
'checkins', 'answers', 'answers__options', 'answers__question',
Prefetch('checkins', queryset=Checkin.objects.all()),
'answers', 'answers__options', 'answers__question',
Prefetch('addons', qs.select_related('item', 'variation')),
Prefetch('order', Order.objects.select_related('invoice_address').prefetch_related(
Prefetch(
@@ -845,7 +868,8 @@ class OrderPositionViewSet(mixins.DestroyModelMixin, mixins.UpdateModelMixin, vi
Prefetch(
'positions',
qs.prefetch_related(
'checkins', 'item', 'variation', 'answers', 'answers__options', 'answers__question',
'item', 'variation', 'answers', 'answers__options', 'answers__question',
Prefetch('checkins', queryset=Checkin.objects.all()),
)
)
))
@@ -854,7 +878,8 @@ class OrderPositionViewSet(mixins.DestroyModelMixin, mixins.UpdateModelMixin, vi
)
else:
qs = qs.prefetch_related(
'checkins', 'answers', 'answers__options', 'answers__question',
Prefetch('checkins', queryset=Checkin.objects.all()),
'answers', 'answers__options', 'answers__question',
).select_related(
'item', 'order', 'order__event', 'order__event__organizer', 'seat'
)
@@ -1436,8 +1461,14 @@ class InvoiceViewSet(viewsets.ReadOnlyModelViewSet):
inv = self.get_object()
if inv.canceled:
raise ValidationError('The invoice has already been canceled.')
if not inv.event.settings.invoice_regenerate_allowed:
raise PermissionDenied('Invoices may not be changed after they are created.')
elif inv.shredded:
raise PermissionDenied('The invoice file is no longer stored on the server.')
elif inv.sent_to_organizer:
raise PermissionDenied('The invoice file has already been exported.')
elif now().astimezone(self.request.event.timezone).date() - inv.date > datetime.timedelta(days=1):
raise PermissionDenied('The invoice file is too old to be regenerated.')
else:
inv = regenerate_invoice(inv)
inv.order.log_action(

View File

@@ -261,7 +261,7 @@ def register_default_webhook_events(sender, **kwargs):
),
ParametrizedEventWebhookEvent(
'pretix.event.deleted',
_('Event details changed'),
_('Event deleted'),
),
ParametrizedSubEventWebhookEvent(
'pretix.subevent.added',

View File

@@ -47,6 +47,7 @@ class PretixBaseConfig(AppConfig):
from . import notifications # NOQA
from . import email # NOQA
from .services import auth, checkin, export, mail, tickets, cart, orderimport, orders, invoices, cleanup, update_check, quotas, notifications, vouchers # NOQA
from .models import _transactions # NOQA
from django.conf import settings
try:

View File

@@ -94,6 +94,9 @@ class BaseAuthBackend:
This method will be called after the user filled in the login form. ``request`` will contain
the current request and ``form_data`` the input for the form fields defined in ``login_form_fields``.
You are expected to either return a ``User`` object (if login was successful) or ``None``.
You are expected to either return a ``User`` object (if login was successful) or ``None``. You should
obtain this user object using ``User.objects.get_or_create_for_backend``.
"""
return
@@ -104,7 +107,9 @@ class BaseAuthBackend:
reverse proxy, you can directly return a ``User`` object that will be logged in.
``request`` will contain the current request.
You are expected to either return a ``User`` object (if login was successful) or ``None``.
You are expected to either return a ``User`` object (if login was successful) or ``None``. You should
obtain this user object using ``User.objects.get_or_create_for_backend``.
"""
return
@@ -146,7 +151,8 @@ class NativeAuthBackend(BaseAuthBackend):
d = OrderedDict([
('email', forms.EmailField(label=_("E-mail"), max_length=254,
widget=forms.EmailInput(attrs={'autofocus': 'autofocus'}))),
('password', forms.CharField(label=_("Password"), widget=forms.PasswordInput)),
('password', forms.CharField(label=_("Password"), widget=forms.PasswordInput,
max_length=4096)),
])
return d

View File

@@ -82,6 +82,13 @@ class SalesChannel:
"""
return False
@property
def customer_accounts_supported(self) -> bool:
"""
If this property is ``True``, checkout will show the customer login step.
"""
return True
def get_all_sales_channels():
global _ALL_CHANNELS

View File

@@ -30,7 +30,7 @@ from pretix.base.settings import GlobalSettingsObject
from pretix.base.templatetags.safelink import safelink as sl
def get_powered_by(safelink=True):
def get_powered_by(request, safelink=True):
gs = GlobalSettingsObject()
d = gs.settings.license_check_input
if d.get('poweredby_name'):
@@ -57,7 +57,7 @@ def get_powered_by(safelink=True):
if d.get('base_license') == 'agpl':
msg += ' (<a href="{}" target="_blank" rel="noopener">{}</a>)'.format(
reverse('source'),
request.build_absolute_uri(reverse('source')),
gettext('source code')
)
@@ -69,7 +69,7 @@ def contextprocessor(request):
'rtl': getattr(request, 'LANGUAGE_CODE', 'en') in settings.LANGUAGES_RTL,
}
try:
ctx['poweredby'] = get_powered_by(safelink=True)
ctx['poweredby'] = get_powered_by(request, safelink=True)
except Exception:
ctx['poweredby'] = 'powered by <a href="https://pretix.eu/" target="_blank" rel="noopener">pretix</a>'
if settings.DEBUG and 'runserver' not in sys.argv:

View File

@@ -25,17 +25,19 @@ from datetime import timedelta
from decimal import Decimal
from itertools import groupby
from smtplib import SMTPResponseException
from typing import TypeVar
import css_inline
from django.conf import settings
from django.core.mail.backends.smtp import EmailBackend
from django.db.models import Count
from django.dispatch import receiver
from django.template.loader import get_template
from django.utils.formats import date_format
from django.utils.timezone import now
from django.utils.translation import (
get_language, gettext_lazy as _, pgettext_lazy,
)
from inlinestyler.utils import inline_css
from pretix.base.i18n import (
LazyCurrencyNumber, LazyDate, LazyExpiresDate, LazyNumber,
@@ -49,23 +51,23 @@ from pretix.base.templatetags.rich_text import markdown_compile_email
logger = logging.getLogger('pretix.base.email')
T = TypeVar("T", bound=EmailBackend)
class CustomSMTPBackend(EmailBackend):
def test(self, from_addr):
try:
self.open()
self.connection.ehlo_or_helo_if_needed()
(code, resp) = self.connection.mail(from_addr, [])
if code != 250:
logger.warn('Error testing mail settings, code %d, resp: %s' % (code, resp))
raise SMTPResponseException(code, resp)
(code, resp) = self.connection.rcpt('testdummy@pretix.eu')
if (code != 250) and (code != 251):
logger.warn('Error testing mail settings, code %d, resp: %s' % (code, resp))
raise SMTPResponseException(code, resp)
finally:
self.close()
def test_custom_smtp_backend(backend: T, from_addr: str) -> None:
try:
backend.open()
backend.connection.ehlo_or_helo_if_needed()
(code, resp) = backend.connection.mail(from_addr, [])
if code != 250:
logger.warning('Error testing mail settings, code %d, resp: %s' % (code, resp))
raise SMTPResponseException(code, resp)
(code, resp) = backend.connection.rcpt('testdummy@pretix.eu')
if (code != 250) and (code != 251):
logger.warning('Error testing mail settings, code %d, resp: %s' % (code, resp))
raise SMTPResponseException(code, resp)
finally:
backend.close()
class BaseHTMLMailRenderer:
@@ -163,9 +165,20 @@ class TemplateBasedMailRenderer(BaseHTMLMailRenderer):
has_addons=Count('addons')
))
htmlctx['cart'] = [(k, list(v)) for k, v in groupby(
positions, key=lambda op: (
op.item, op.variation, op.subevent, op.attendee_name,
(op.pk if op.addon_to_id else None), (op.pk if op.has_addons else None)
sorted(
positions,
key=lambda op: (
(op.addon_to.positionid if op.addon_to_id else op.positionid),
op.positionid
)
),
key=lambda op: (
op.item,
op.variation,
op.subevent,
op.attendee_name,
op.addon_to_id,
(op.pk if op.has_addons else None)
)
)]
@@ -174,7 +187,11 @@ class TemplateBasedMailRenderer(BaseHTMLMailRenderer):
htmlctx['ev'] = position.subevent or self.event
tpl = get_template(self.template_name)
body_html = inline_css(tpl.render(htmlctx))
body_html = tpl.render(htmlctx)
inliner = css_inline.CSSInliner(remove_style_tags=True)
body_html = inliner.inline(body_html)
return body_html
@@ -293,7 +310,11 @@ def get_email_context(**kwargs):
val = [val]
for v in val:
if all(rp in kwargs for rp in v.required_context):
ctx[v.identifier] = v.render(kwargs)
try:
ctx[v.identifier] = v.render(kwargs)
except:
ctx[v.identifier] = '(error)'
logger.exception(f'Failed to process email placeholder {v.identifier}.')
return ctx
@@ -448,6 +469,35 @@ def base_placeholders(sender, **kwargs):
}
),
),
SimpleFunctionalMailTextPlaceholder(
'event_location', ['event_or_subevent'], lambda event_or_subevent: str(event_or_subevent.location or ''),
lambda event: str(event.location or ''),
),
SimpleFunctionalMailTextPlaceholder(
'event_admission_time', ['event_or_subevent'],
lambda event_or_subevent: date_format(event_or_subevent.date_admission, 'TIME_FORMAT') if event_or_subevent.date_admission else '',
lambda event: date_format(event.date_admission, 'TIME_FORMAT') if event.date_admission else '',
),
SimpleFunctionalMailTextPlaceholder(
'subevent', ['waiting_list_entry', 'event'],
lambda waiting_list_entry, event: str(waiting_list_entry.subevent or event),
lambda event: str(event if not event.has_subevents or not event.subevents.exists() else event.subevents.first())
),
SimpleFunctionalMailTextPlaceholder(
'subevent_date_from', ['waiting_list_entry', 'event'],
lambda waiting_list_entry, event: (waiting_list_entry.subevent or event).get_date_from_display(),
lambda event: (event if not event.has_subevents or not event.subevents.exists() else event.subevents.first()).get_date_from_display()
),
SimpleFunctionalMailTextPlaceholder(
'url_remove', ['waiting_list_entry', 'event'],
lambda waiting_list_entry, event: build_absolute_uri(
event, 'presale:event.waitinglist.remove'
) + '?voucher=' + waiting_list_entry.voucher.code,
lambda event: build_absolute_uri(
event,
'presale:event.waitinglist.remove',
) + '?voucher=68CYU2H6ZTP3WLK5',
),
SimpleFunctionalMailTextPlaceholder(
'url', ['waiting_list_entry', 'event'],
lambda waiting_list_entry, event: build_absolute_uri(
@@ -515,6 +565,22 @@ def base_placeholders(sender, **kwargs):
'voucher_list', ['voucher_list'], lambda voucher_list: ' \n'.join(voucher_list),
' 68CYU2H6ZTP3WLK5\n 7MB94KKPVEPSMVF2'
),
SimpleFunctionalMailTextPlaceholder(
# join vouchers with two spaces at end of line so markdown-parser inserts a <br>
'voucher_url_list', ['event', 'voucher_list'],
lambda event, voucher_list: ' \n'.join([
build_absolute_uri(
event, 'presale:event.redeem'
) + '?voucher=' + c
for c in voucher_list
]),
lambda event: ' \n'.join([
build_absolute_uri(
event, 'presale:event.redeem'
) + '?voucher=' + c
for c in ['68CYU2H6ZTP3WLK5', '7MB94KKPVEPSMVF2']
]),
),
SimpleFunctionalMailTextPlaceholder(
'url', ['event', 'voucher_list'], lambda event, voucher_list: build_absolute_uri(event, 'presale:event.index', kwargs={
'event': event.slug,
@@ -581,6 +647,10 @@ def base_placeholders(sender, **kwargs):
'meta_%s' % k, ['event'], lambda event, k=k: event.meta_data[k],
v
))
ph.append(SimpleFunctionalMailTextPlaceholder(
'meta_%s' % k, ['event_or_subevent'], lambda event_or_subevent, k=k: event_or_subevent.meta_data[k],
v
))
return ph

View File

@@ -33,7 +33,6 @@
# License for the specific language governing permissions and limitations under the License.
import io
import re
import tempfile
from collections import OrderedDict, namedtuple
from decimal import Decimal
@@ -46,23 +45,13 @@ from django.conf import settings
from django.db.models import QuerySet
from django.utils.formats import localize
from django.utils.translation import gettext, gettext_lazy as _
from openpyxl import Workbook
from openpyxl.cell.cell import ILLEGAL_CHARACTERS_RE, KNOWN_TYPES
from pretix.base.models import Event
from pretix.helpers.safe_openpyxl import ( # NOQA: backwards compatibility for plugins using excel_safe
SafeWorkbook, remove_invalid_excel_chars as excel_safe,
)
def excel_safe(val):
if not isinstance(val, KNOWN_TYPES):
val = str(val)
if isinstance(val, bytes):
val = val.decode("utf-8", errors="ignore")
if isinstance(val, str):
val = re.sub(ILLEGAL_CHARACTERS_RE, '', val)
return val
__ = excel_safe # just so the compatbility import above is "used" and doesn't get removed by linter
class BaseExporter:
@@ -70,8 +59,9 @@ class BaseExporter:
This is the base class for all data exporters
"""
def __init__(self, event, progress_callback=lambda v: None):
def __init__(self, event, organizer, progress_callback=lambda v: None):
self.event = event
self.organizer = organizer
self.progress_callback = progress_callback
self.is_multievent = isinstance(event, QuerySet)
if isinstance(event, QuerySet):
@@ -220,9 +210,13 @@ class ListExporter(BaseExporter):
writer.writerow(line)
return self.get_filename() + '.csv', 'text/csv', output.getvalue().encode("utf-8")
def prepare_xlsx_sheet(self, ws):
pass
def _render_xlsx(self, form_data, output_file=None):
wb = Workbook(write_only=True)
wb = SafeWorkbook(write_only=True)
ws = wb.create_sheet()
self.prepare_xlsx_sheet(ws)
try:
ws.title = str(self.verbose_name)
except:
@@ -234,7 +228,7 @@ class ListExporter(BaseExporter):
total = line.total
continue
ws.append([
excel_safe(val) for val in line
val for val in line
])
if total:
counter += 1
@@ -339,7 +333,7 @@ class MultiSheetListExporter(ListExporter):
return self.get_filename() + '.csv', 'text/csv', output.getvalue().encode("utf-8")
def _render_xlsx(self, form_data, output_file=None):
wb = Workbook(write_only=True)
wb = SafeWorkbook(write_only=True)
n_sheets = len(self.sheets)
for i_sheet, (s, l) in enumerate(self.sheets):
ws = wb.create_sheet(str(l))
@@ -353,8 +347,7 @@ class MultiSheetListExporter(ListExporter):
total = line.total
continue
ws.append([
excel_safe(val)
for val in line
val for val in line
])
if total:
counter += 1

View File

@@ -21,6 +21,7 @@
#
from .answers import * # noqa
from .dekodi import * # noqa
from .events import * # noqa
from .invoices import * # noqa
from .json import * # noqa
from .mail import * # noqa

View File

@@ -0,0 +1,100 @@
#
# This file is part of pretix (Community Edition).
#
# Copyright (C) 2014-2020 Raphael Michel and contributors
# Copyright (C) 2020-2021 rami.io 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/>.
#
# This file is based on an earlier version of pretix which was released under the Apache License 2.0. The full text of
# the Apache License 2.0 can be obtained at <http://www.apache.org/licenses/LICENSE-2.0>.
#
# This file may have since been changed and any changes are released under the terms of AGPLv3 as described above. A
# full history of changes and contributors is available at <https://github.com/pretix/pretix>.
#
# This file contains Apache-licensed contributions copyrighted by: Tobias Kunze
#
# Unless required by applicable law or agreed to in writing, software distributed under the Apache License 2.0 is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under the License.
from django.dispatch import receiver
from django.utils.formats import date_format
from django.utils.functional import cached_property
from django.utils.translation import gettext_lazy as _
from ...control.forms.filter import get_all_payment_providers
from ..exporter import ListExporter
from ..signals import register_multievent_data_exporters
class EventDataExporter(ListExporter):
identifier = 'eventdata'
verbose_name = _('Event data')
@cached_property
def providers(self):
return dict(get_all_payment_providers())
def iterate_list(self, form_data):
header = [
_("Event name"),
_("Short form"),
_("Shop is live"),
_("Event currency"),
_("Event start time"),
_("Event end time"),
_("Admission time"),
_("Start of presale"),
_("End of presale"),
_("Location"),
_("Latitude"),
_("Longitude"),
_("Internal comment"),
]
props = list(self.organizer.meta_properties.all())
for p in props:
header.append(p.name)
yield header
for e in self.events.all():
m = e.meta_data
yield [
str(e.name),
e.slug,
_('Yes') if e.live else _('No'),
e.currency,
date_format(e.date_from, 'SHORT_DATETIME_FORMAT'),
date_format(e.date_to, 'SHORT_DATETIME_FORMAT') if e.date_to else '',
date_format(e.date_admission, 'SHORT_DATETIME_FORMAT') if e.date_admission else '',
date_format(e.presale_start, 'SHORT_DATETIME_FORMAT') if e.presale_start else '',
date_format(e.presale_end, 'SHORT_DATETIME_FORMAT') if e.presale_end else '',
str(e.location),
e.geo_lat or '',
e.geo_lon or '',
e.comment,
] + [
m.get(p.name, '') for p in props
]
def get_filename(self):
return '{}_events'.format(self.events.first().organizer.slug)
@receiver(register_multievent_data_exporters, dispatch_uid="multiexporter_eventdata")
def register_multievent_eventdata_exporter(sender, **kwargs):
return EventDataExporter

View File

@@ -324,7 +324,6 @@ class InvoiceDataExporter(InvoiceExporterMixin, MultiSheetListExporter):
_('Tax rate'),
_('Tax name'),
_('Event start date'),
_('Date'),
_('Order code'),
_('E-mail address'),
@@ -348,6 +347,8 @@ class InvoiceDataExporter(InvoiceExporterMixin, MultiSheetListExporter):
_('Invoice recipient:') + ' ' + _('Beneficiary'),
_('Invoice recipient:') + ' ' + _('Internal reference'),
_('Payment providers'),
_('Event end date'),
_('Location'),
]
p_providers = OrderPayment.objects.filter(
@@ -406,7 +407,9 @@ class InvoiceDataExporter(InvoiceExporterMixin, MultiSheetListExporter):
', '.join([
str(self.providers.get(p, p)) for p in sorted(set((l.payment_providers or '').split(',')))
if p and p != 'free'
])
]),
date_format(l.event_date_to, "SHORT_DATE_FORMAT") if l.event_date_to else "",
l.event_location or "",
]
@cached_property

View File

@@ -55,16 +55,20 @@ class JSONExporter(BaseExporter):
'name': str(self.event.organizer.name),
'slug': self.event.organizer.slug
},
'meta_data': self.event.meta_data,
'categories': [
{
'id': category.id,
'name': str(category.name),
'description': str(category.description),
'position': category.position,
'internal_name': category.internal_name
} for category in self.event.categories.all()
],
'items': [
{
'id': item.id,
'position': item.position,
'name': str(item.name),
'internal_name': str(item.internal_name),
'category': item.category_id,
@@ -73,13 +77,35 @@ class JSONExporter(BaseExporter):
'tax_name': str(item.tax_rule.name) if item.tax_rule else None,
'admission': item.admission,
'active': item.active,
'sales_channels': item.sales_channels,
'description': str(item.description),
'available_from': item.available_from,
'available_until': item.available_until,
'require_voucher': item.require_voucher,
'hide_without_voucher': item.hide_without_voucher,
'allow_cancel': item.allow_cancel,
'require_bundling': item.require_bundling,
'min_per_order': item.min_per_order,
'max_per_order': item.max_per_order,
'checkin_attention': item.checkin_attention,
'original_price': item.original_price,
'issue_giftcard': item.issue_giftcard,
'meta_data': item.meta_data,
'require_membership': item.require_membership,
'variations': [
{
'id': variation.id,
'active': variation.active,
'price': variation.default_price if variation.default_price is not None else
item.default_price,
'name': str(variation)
'name': str(variation),
'description': str(variation.description),
'position': variation.position,
'require_membership': variation.require_membership,
'sales_channels': variation.sales_channels,
'available_from': variation.available_from,
'available_until': variation.available_until,
'hide_without_voucher': variation.hide_without_voucher,
} for variation in item.variations.all()
]
} for item in self.event.items.select_related('tax_rule').prefetch_related('variations')
@@ -87,7 +113,13 @@ class JSONExporter(BaseExporter):
'questions': [
{
'id': question.id,
'identifier': question.identifier,
'required': question.required,
'question': str(question.question),
'position': question.position,
'hidden': question.hidden,
'ask_during_checkin': question.ask_during_checkin,
'help_text': str(question.help_text),
'type': question.type
} for question in self.event.questions.all()
],
@@ -95,7 +127,18 @@ class JSONExporter(BaseExporter):
{
'code': order.code,
'status': order.status,
'customer': order.customer.identifier if order.customer else None,
'testmode': order.testmode,
'user': order.email,
'email': order.email,
'phone': str(order.phone),
'locale': order.locale,
'comment': order.comment,
'custom_followup_at': order.custom_followup_at,
'require_approval': order.require_approval,
'checkin_attention': order.checkin_attention,
'sales_channel': order.sales_channel,
'expires': order.expires,
'datetime': order.datetime,
'fees': [
{
@@ -108,11 +151,21 @@ class JSONExporter(BaseExporter):
'positions': [
{
'id': position.id,
'positionid': position.positionid,
'item': position.item_id,
'variation': position.variation_id,
'subevent': position.subevent_id,
'seat': position.seat.seat_guid if position.seat else None,
'price': position.price,
'tax_rate': position.tax_rate,
'tax_value': position.tax_value,
'attendee_name': position.attendee_name,
'attendee_email': position.attendee_email,
'company': position.company,
'street': position.street,
'zipcode': position.zipcode,
'country': str(position.country) if position.country else None,
'state': position.state,
'secret': position.secret,
'addon_to': position.addon_to_id,
'answers': [
@@ -124,15 +177,30 @@ class JSONExporter(BaseExporter):
} for position in order.positions.all()
]
} for order in
self.event.orders.all().prefetch_related('positions', 'positions__answers', 'fees')
self.event.orders.all().prefetch_related('positions', 'positions__answers', 'positions__seat', 'customer', 'fees')
],
'quotas': [
{
'id': quota.id,
'size': quota.size,
'subevent': quota.subevent_id,
'items': [item.id for item in quota.items.all()],
'variations': [variation.id for variation in quota.variations.all()],
} for quota in self.event.quotas.all().prefetch_related('items', 'variations')
],
'subevents': [
{
'id': se.id,
'name': str(se.name),
'location': str(se.location),
'date_from': se.date_from,
'date_to': se.date_to,
'date_admission': se.date_admission,
'geo_lat': se.geo_lat,
'geo_lon': se.geo_lon,
'is_public': se.is_public,
'meta_data': se.meta_data,
} for se in self.event.subevents.all()
]
}
}

View File

@@ -33,6 +33,7 @@
# License for the specific language governing permissions and limitations under the License.
from collections import OrderedDict
from datetime import date, datetime, time
from decimal import Decimal
import dateutil
@@ -42,10 +43,10 @@ from django.db.models import (
Case, CharField, Count, DateTimeField, F, IntegerField, Max, Min, OuterRef,
Q, Subquery, Sum, When,
)
from django.db.models.functions import Coalesce, TruncDate
from django.db.models.functions import Coalesce
from django.dispatch import receiver
from django.utils.functional import cached_property
from django.utils.timezone import get_current_timezone, now
from django.utils.timezone import get_current_timezone, make_aware, now
from django.utils.translation import gettext as _, gettext_lazy, pgettext
from pretix.base.models import (
@@ -129,7 +130,7 @@ class OrderListExporter(MultiSheetListExporter):
label=_('End event date'),
widget=forms.DateInput(attrs={'class': 'datepickerfield'}),
required=False,
help_text=_('Only include orders including at least one ticket for a date on or after this date. '
help_text=_('Only include orders including at least one ticket for a date on or before this date. '
'Will also include other dates in case of mixed orders!')
)),
]
@@ -181,41 +182,43 @@ class OrderListExporter(MultiSheetListExporter):
if form_data.get('date_from'):
date_value = form_data.get('date_from')
if isinstance(date_value, str):
if not isinstance(date_value, date):
date_value = dateutil.parser.parse(date_value).date()
datetime_value = make_aware(datetime.combine(date_value, time(0, 0, 0)), self.timezone)
annotations['date'] = TruncDate(f'{rel}datetime')
filters['date__gte'] = date_value
filters[f'{rel}datetime__gte'] = datetime_value
if form_data.get('date_to'):
date_value = form_data.get('date_to')
if isinstance(date_value, str):
if not isinstance(date_value, date):
date_value = dateutil.parser.parse(date_value).date()
datetime_value = make_aware(datetime.combine(date_value, time(23, 59, 59, 999999)), self.timezone)
annotations['date'] = TruncDate(f'{rel}datetime')
filters['date__lte'] = date_value
filters[f'{rel}datetime__lte'] = datetime_value
if form_data.get('event_date_from'):
date_value = form_data.get('event_date_from')
if isinstance(date_value, str):
if not isinstance(date_value, date):
date_value = dateutil.parser.parse(date_value).date()
datetime_value = make_aware(datetime.combine(date_value, time(0, 0, 0)), self.timezone)
annotations['event_date_max'] = Case(
When(**{f'{rel}event__has_subevents': True}, then=Max(f'{rel}all_positions__subevent__date_from')),
default=F(f'{rel}event__date_from'),
)
filters['event_date_max__gte'] = date_value
filters['event_date_max__gte'] = datetime_value
if form_data.get('event_date_to'):
date_value = form_data.get('event_date_to')
if isinstance(date_value, str):
if not isinstance(date_value, date):
date_value = dateutil.parser.parse(date_value).date()
datetime_value = make_aware(datetime.combine(date_value, time(23, 59, 59, 999999)), self.timezone)
annotations['event_date_min'] = Case(
When(**{f'{rel}event__has_subevents': True}, then=Min(f'{rel}all_positions__subevent__date_from')),
default=F(f'{rel}event__date_from'),
)
filters['event_date_min__lte'] = date_value
filters['event_date_min__lte'] = datetime_value
if filters:
return qs.annotate(**annotations).filter(**filters)
@@ -288,6 +291,7 @@ class OrderListExporter(MultiSheetListExporter):
headers.append(_('Sales channel'))
headers.append(_('Requires special attention'))
headers.append(_('Comment'))
headers.append(_('Follow-up date'))
headers.append(_('Positions'))
headers.append(_('E-mail address verified'))
headers.append(_('Payment providers'))
@@ -393,6 +397,7 @@ class OrderListExporter(MultiSheetListExporter):
row.append(order.sales_channel)
row.append(_('Yes') if order.checkin_attention else _('No'))
row.append(order.comment or "")
row.append(order.custom_followup_at.strftime("%Y-%m-%d") if order.custom_followup_at else "")
row.append(order.pcnt)
row.append(_('Yes') if order.email_known_to_work else _('No'))
row.append(', '.join([
@@ -568,12 +573,14 @@ class OrderListExporter(MultiSheetListExporter):
pgettext('address', 'State'),
_('Voucher'),
_('Pseudonymization ID'),
_('Ticket secret'),
_('Seat ID'),
_('Seat name'),
_('Seat zone'),
_('Seat row'),
_('Seat number'),
_('Order comment'),
_('Follow-up date'),
]
questions = list(Question.objects.filter(event__in=self.events))
@@ -663,6 +670,7 @@ class OrderListExporter(MultiSheetListExporter):
op.state or '',
op.voucher.code if op.voucher else '',
op.pseudonymization_id,
op.secret,
]
if op.seat:
@@ -677,6 +685,7 @@ class OrderListExporter(MultiSheetListExporter):
row += ['', '', '', '', '']
row.append(order.comment)
row.append(order.custom_followup_at.strftime("%Y-%m-%d") if order.custom_followup_at else "")
acache = {}
for a in op.answers.all():
# We do not want to localize Date, Time and Datetime question answers, as those can lead
@@ -721,7 +730,7 @@ class OrderListExporter(MultiSheetListExporter):
row += [
order.sales_channel,
order.locale,
row.append(_('Yes') if order.email_known_to_work else _('No'))
_('Yes') if order.email_known_to_work else _('No')
]
row.append(', '.join([
str(self.providers.get(p, p)) for p in sorted(set((op.payment_providers or '').split(',')))
@@ -780,7 +789,7 @@ class PaymentListExporter(ListExporter):
headers = [
_('Event slug'), _('Order'), _('Payment ID'), _('Creation date'), _('Completion date'), _('Status'),
_('Status code'), _('Amount'), _('Payment method'), _('Comment')
_('Status code'), _('Amount'), _('Payment method'), _('Comment'),
]
yield headers
@@ -866,6 +875,78 @@ class QuotaListExporter(ListExporter):
return '{}_quotas'.format(self.event.slug)
def generate_GiftCardTransactionListExporter(organizer): # hackhack
class GiftcardTransactionListExporter(ListExporter):
identifier = 'giftcardtransactionlist'
verbose_name = gettext_lazy('Gift card transactions')
@property
def additional_form_fields(self):
d = [
('date_from',
forms.DateField(
label=_('Start date'),
widget=forms.DateInput(attrs={'class': 'datepickerfield'}),
required=False,
)),
('date_to',
forms.DateField(
label=_('End date'),
widget=forms.DateInput(attrs={'class': 'datepickerfield'}),
required=False,
)),
]
d = OrderedDict(d)
return d
def iterate_list(self, form_data):
qs = GiftCardTransaction.objects.filter(
card__issuer=organizer,
).order_by('datetime').select_related('card', 'order', 'order__event')
if form_data.get('date_from'):
date_value = form_data.get('date_from')
if isinstance(date_value, str):
date_value = dateutil.parser.parse(date_value).date()
qs = qs.filter(
datetime__gte=make_aware(datetime.combine(date_value, time(0, 0, 0)), self.timezone)
)
if form_data.get('date_to'):
date_value = form_data.get('date_to')
if isinstance(date_value, str):
date_value = dateutil.parser.parse(date_value).date()
qs = qs.filter(
datetime__lte=make_aware(datetime.combine(date_value, time(23, 59, 59, 999999)), self.timezone)
)
headers = [
_('Gift card code'),
_('Test mode'),
_('Date'),
_('Amount'),
_('Currency'),
_('Order'),
]
yield headers
for obj in qs:
row = [
obj.card.secret,
_('TEST MODE') if obj.card.testmode else '',
obj.datetime.astimezone(self.timezone).strftime('%Y-%m-%d %H:%M:%S'),
obj.value,
obj.card.currency,
obj.order.full_code if obj.order else None,
]
yield row
def get_filename(self):
return '{}_giftcardtransactions'.format(organizer.slug)
return GiftcardTransactionListExporter
class GiftcardRedemptionListExporter(ListExporter):
identifier = 'giftcardredemptionlist'
verbose_name = gettext_lazy('Gift card redemptions')
@@ -1058,3 +1139,8 @@ def register_multievent_i_giftcardredemptionlist_exporter(sender, **kwargs):
@receiver(register_multievent_data_exporters, dispatch_uid="multiexporter_giftcardlist")
def register_multievent_i_giftcardlist_exporter(sender, **kwargs):
return generate_GiftCardListExporter(sender)
@receiver(register_multievent_data_exporters, dispatch_uid="multiexporter_giftcardtransactionlist")
def register_multievent_i_giftcardtransactionlist_exporter(sender, **kwargs):
return generate_GiftCardTransactionListExporter(sender)

View File

@@ -38,6 +38,7 @@ import i18nfield.forms
from django import forms
from django.forms.models import ModelFormMetaclass
from django.utils.crypto import get_random_string
from django.utils.translation import gettext_lazy as _
from formtools.wizard.views import SessionWizardView
from hierarkey.forms import HierarkeyForm
@@ -112,12 +113,42 @@ class SettingsForm(i18nfield.forms.I18nFormMixin, HierarkeyForm):
if isinstance(f, (RelativeDateTimeField, RelativeDateField)):
f.set_event(self.obj)
def save(self):
def _unmask_secret_fields(self):
for k, v in self.cleaned_data.items():
if isinstance(self.fields.get(k), SecretKeySettingsField) and self.cleaned_data.get(k) == SECRET_REDACTED:
self.cleaned_data[k] = self.initial[k]
def save(self):
self._unmask_secret_fields()
return super().save()
def clean(self):
d = super().clean()
# There is logic in HierarkeyForm.save() to only persist fields that changed. HierarkeyForm determines if
# something changed by comparing `self._s.get(name)` to `value`. This leaves an edge case open for multi-lingual
# text fields. On the very first load, the initial value in `self._s.get(name)` will be a LazyGettextProxy-based
# string. However, only some of the languages are usually visible, so even if the user does not change anything
# at all, it will be considered a changed value and stored. We do not want that, as it makes it very hard to add
# languages to an organizer/event later on. So we trick it and make sure nothing gets changed in that situation.
for name, field in self.fields.items():
if isinstance(field, SecretKeySettingsField) and d.get(name) == SECRET_REDACTED and not self.initial.get(name):
self.add_error(
name,
_('Due to technical reasons you cannot set inputs, that need to be masked (e.g. passwords), to %(value)s.') % {'value': SECRET_REDACTED}
)
if isinstance(field, i18nfield.forms.I18nFormField):
value = d.get(name)
if not value:
continue
current = self._s.get(name, as_type=type(value))
if name not in self.changed_data:
d[name] = current
return d
def get_new_filename(self, name: str) -> str:
from pretix.base.models import Event

View File

@@ -154,6 +154,7 @@ class RegistrationForm(forms.Form):
widget=forms.PasswordInput(attrs={
'autocomplete': 'new-password' # see https://bugs.chromium.org/p/chromium/issues/detail?id=370363#c7
}),
max_length=4096,
required=True
)
password_repeat = forms.CharField(
@@ -161,6 +162,7 @@ class RegistrationForm(forms.Form):
widget=forms.PasswordInput(attrs={
'autocomplete': 'new-password' # see https://bugs.chromium.org/p/chromium/issues/detail?id=370363#c7
}),
max_length=4096,
required=True
)
keep_logged_in = forms.BooleanField(label=_("Keep me logged in"), required=False)
@@ -204,11 +206,13 @@ class PasswordRecoverForm(forms.Form):
password = forms.CharField(
label=_('Password'),
widget=forms.PasswordInput,
max_length=4096,
required=True
)
password_repeat = forms.CharField(
label=_('Repeat password'),
widget=forms.PasswordInput
widget=forms.PasswordInput,
max_length=4096,
)
def __init__(self, user_id=None, *args, **kwargs):

Some files were not shown because too many files have changed in this diff Show More