From 631cded0d67a013987787d4cf7616bcfc06e0b35 Mon Sep 17 00:00:00 2001 From: Raphael Michel Date: Tue, 29 Aug 2017 23:10:59 +0200 Subject: [PATCH] New pretixdroid configuration system --- src/pretix/locale/de/LC_MESSAGES/django.po | 155 +++++++++++++---- src/pretix/locale/de/LC_MESSAGES/djangojs.po | 2 +- .../locale/de_Informal/LC_MESSAGES/django.po | 157 ++++++++++++++---- .../de_Informal/LC_MESSAGES/djangojs.po | 2 +- src/pretix/plugins/pretixdroid/forms.py | 23 +++ .../migrations/0003_appconfiguration.py | 48 ++++++ src/pretix/plugins/pretixdroid/models.py | 27 +++ .../pretixdroid/configuration.html | 112 ++++++++----- .../pretixdroid/configuration_code.html | 36 ++++ src/pretix/plugins/pretixdroid/urls.py | 4 +- src/pretix/plugins/pretixdroid/views.py | 153 ++++++++++++----- src/tests/plugins/pretixdroid/test_simple.py | 81 ++++++--- .../plugins/pretixdroid/test_subevents.py | 48 +++--- 13 files changed, 658 insertions(+), 190 deletions(-) create mode 100644 src/pretix/plugins/pretixdroid/forms.py create mode 100644 src/pretix/plugins/pretixdroid/migrations/0003_appconfiguration.py create mode 100644 src/pretix/plugins/pretixdroid/models.py create mode 100644 src/pretix/plugins/pretixdroid/templates/pretixplugins/pretixdroid/configuration_code.html diff --git a/src/pretix/locale/de/LC_MESSAGES/django.po b/src/pretix/locale/de/LC_MESSAGES/django.po index e26ca2372f..3350e7857c 100644 --- a/src/pretix/locale/de/LC_MESSAGES/django.po +++ b/src/pretix/locale/de/LC_MESSAGES/django.po @@ -4,8 +4,8 @@ msgid "" msgstr "" "Project-Id-Version: 1\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-08-27 07:30+0000\n" -"PO-Revision-Date: 2017-08-27 09:33+0200\n" +"POT-Creation-Date: 2017-08-29 21:12+0000\n" +"PO-Revision-Date: 2017-08-29 23:16+0200\n" "Last-Translator: Raphael Michel \n" "Language-Team: Raphael Michel \n" "Language: de\n" @@ -13,7 +13,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -"X-Generator: Poedit 2.0.3\n" +"X-Generator: Poedit 2.0.2\n" "X-Poedit-Bookmarks: -1,-1,904,-1,-1,-1,-1,-1,-1,-1\n" #: pretix/base/exporters/answers.py:18 @@ -1034,6 +1034,8 @@ msgstr "Antwort" #: pretix/control/templates/pretixcontrol/waitinglist/index.html:119 #: pretix/plugins/checkinlists/exporters.py:69 #: pretix/plugins/checkinlists/exporters.py:109 +#: pretix/plugins/pretixdroid/models.py:15 +#: pretix/plugins/pretixdroid/templates/pretixplugins/pretixdroid/configuration.html:46 #: pretix/plugins/reports/exporters.py:248 msgctxt "subevent" msgid "Date" @@ -1227,6 +1229,8 @@ msgstr "Das Produkt \"{item}\" ist nicht mehr verfügbar." #: pretix/base/models/orders.py:549 pretix/control/views/item.py:436 #: pretix/control/views/vouchers.py:81 pretix/control/views/vouchers.py:82 #: pretix/plugins/checkinlists/exporters.py:124 +#: pretix/plugins/pretixdroid/templates/pretixplugins/pretixdroid/configuration.html:71 +#: pretix/plugins/pretixdroid/templates/pretixplugins/pretixdroid/configuration.html:72 #: pretix/presale/checkoutflow.py:477 msgid "Yes" msgstr "Ja" @@ -1236,6 +1240,8 @@ msgstr "Ja" #: pretix/control/views/vouchers.py:82 #: pretix/plugins/checkinlists/exporters.py:124 #: pretix/plugins/paypal/templates/pretixplugins/paypal/action_refund.html:14 +#: pretix/plugins/pretixdroid/templates/pretixplugins/pretixdroid/configuration.html:71 +#: pretix/plugins/pretixdroid/templates/pretixplugins/pretixdroid/configuration.html:72 #: pretix/plugins/stripe/templates/pretixplugins/stripe/action_refund.html:14 #: pretix/presale/checkoutflow.py:479 msgid "No" @@ -3769,6 +3775,8 @@ msgstr "Alle Veranstalter" #: pretix/control/forms/filter.py:195 #: pretix/control/templates/pretixcontrol/organizers/teams.html:39 +#: pretix/plugins/pretixdroid/templates/pretixplugins/pretixdroid/configuration.html:59 +#: pretix/plugins/pretixdroid/templates/pretixplugins/pretixdroid/configuration.html:63 msgid "All" msgstr "Alle" @@ -6063,6 +6071,7 @@ msgid "The following products might be no longer available for sale:" msgstr "Die folgenden Produkte stehen möglicherweise nicht mehr zum Verkauf:" #: pretix/control/templates/pretixcontrol/items/quota_edit.html:27 +#: pretix/plugins/pretixdroid/templates/pretixplugins/pretixdroid/configuration.html:48 msgid "Items" msgstr "Produkte" @@ -8786,6 +8795,43 @@ msgid "" "This plugin allows you to use the pretixdroid Android app for your event." msgstr "Dieses Plugin erlaubt, die pretixdroid-Android-App zu verwenden." +#: pretix/plugins/pretixdroid/models.py:11 +msgid "Can scan all products" +msgstr "Kann alle Produkte scannen" + +#: pretix/plugins/pretixdroid/models.py:12 +msgid "Can scan these products" +msgstr "Nur diese Produkte" + +#: pretix/plugins/pretixdroid/models.py:13 +msgid "pretixdroid 1.6 or newer only" +msgstr "pretixdroid 1.6 oder neuer" + +#: pretix/plugins/pretixdroid/models.py:16 +msgid "Show information" +msgstr "Informationen anzeigen" + +#: pretix/plugins/pretixdroid/models.py:17 +msgid "" +"If disabled, the device can not see how many tickets exist and how many are " +"already scanned. pretixdroid 1.6 or newer only." +msgstr "" +"Wenn diese Option deaktiviert ist, kann das Gerät nicht sehen wie viele " +"Tickets existieren und wie viele bereits gescannt wurden (ab pretixdroid " +"1.6)." + +#: pretix/plugins/pretixdroid/models.py:19 +msgid "Search allowed" +msgstr "Suche erlaubt" + +#: pretix/plugins/pretixdroid/models.py:20 +msgid "" +"If disabled, the device can not search for attendees by name. pretixdroid " +"1.6 or newer only." +msgstr "" +"Wenn diese Option deaktiviert ist, kann mit dem Gerät nicht nach Namen " +"gesucht werden (ab pretixdroid 1.6)." + #: pretix/plugins/pretixdroid/signals.py:21 msgid "pretixdroid" msgstr "pretixdroid" @@ -8820,6 +8866,8 @@ msgstr "" #: pretix/plugins/pretixdroid/templates/pretixplugins/pretixdroid/configuration.html:5 #: pretix/plugins/pretixdroid/templates/pretixplugins/pretixdroid/configuration.html:7 +#: pretix/plugins/pretixdroid/templates/pretixplugins/pretixdroid/configuration_code.html:5 +#: pretix/plugins/pretixdroid/templates/pretixplugins/pretixdroid/configuration_code.html:8 msgid "pretixdroid configuration" msgstr "pretixdroid-Konfiguration" @@ -8832,45 +8880,76 @@ msgstr "" "die Tickets scannen und überprüfen können." #: pretix/plugins/pretixdroid/templates/pretixplugins/pretixdroid/configuration.html:11 -msgid "App download" -msgstr "App-Download" +msgid "Create app configuration" +msgstr "App-Konfiguration erstellen" -#: pretix/plugins/pretixdroid/templates/pretixplugins/pretixdroid/configuration.html:15 +#: pretix/plugins/pretixdroid/templates/pretixplugins/pretixdroid/configuration.html:13 +msgid "" +"To start scanning tickets with our Android app, first create a configuration " +"code here:" +msgstr "" +"Um mit unserer Android-App Tickets zu scannen, erstellen Sie hier erst einen " +"Konfigurations-Code:" + +#: pretix/plugins/pretixdroid/templates/pretixplugins/pretixdroid/configuration.html:30 +msgid "Create" +msgstr "Erstellen" + +#: pretix/plugins/pretixdroid/templates/pretixplugins/pretixdroid/configuration.html:38 +msgid "Existing app configurations" +msgstr "Bestehende App-Konfigurationen" + +#: pretix/plugins/pretixdroid/templates/pretixplugins/pretixdroid/configuration.html:44 +msgid "ID" +msgstr "ID" + +#: pretix/plugins/pretixdroid/templates/pretixplugins/pretixdroid/configuration.html:49 +msgid "Show info" +msgstr "Infos anzeigen" + +#: pretix/plugins/pretixdroid/templates/pretixplugins/pretixdroid/configuration.html:50 +msgid "Allow search" +msgstr "Sucher erlauben" + +#: pretix/plugins/pretixdroid/templates/pretixplugins/pretixdroid/configuration.html:75 +msgid "Show QR code" +msgstr "QR-Code anzeigen" + +#: pretix/plugins/pretixdroid/templates/pretixplugins/pretixdroid/configuration_code.html:10 +msgid "Back to overview" +msgstr "Zurück zur Übersicht" + +#: pretix/plugins/pretixdroid/templates/pretixplugins/pretixdroid/configuration_code.html:13 +msgid "1. Download app" +msgstr "1. App herunterladen" + +#: pretix/plugins/pretixdroid/templates/pretixplugins/pretixdroid/configuration_code.html:17 msgid "Download the app from the Google Play Store" msgstr "Laden Sie die App aus dem Play Store herunter" -#: pretix/plugins/pretixdroid/templates/pretixplugins/pretixdroid/configuration.html:20 +#: pretix/plugins/pretixdroid/templates/pretixplugins/pretixdroid/configuration_code.html:22 msgid "" "Android, Google Play and the Google Play logo are trademarks of Google Inc." msgstr "" "Android, Google Play und das Google Play-Logo sind eingetragene Marken von " "Google Inc." -#: pretix/plugins/pretixdroid/templates/pretixplugins/pretixdroid/configuration.html:25 -msgid "App configuration" -msgstr "App-Konfiguration" +#: pretix/plugins/pretixdroid/templates/pretixplugins/pretixdroid/configuration_code.html:27 +msgid "2. Scan code" +msgstr "2. Code scannen" -#: pretix/plugins/pretixdroid/templates/pretixplugins/pretixdroid/configuration.html:27 -msgid "" -"If you start the app for the first time, it will request that you scan the " -"following code. The code tells the app all it needs about your event." -msgstr "" -"Wenn Sie die App das erste Mal starten wird sie darum bitten, den folgenden " -"Code einzuscannen. Der Code enthält alle nötigen Informationen über die " -"Veranstaltung." +#: pretix/plugins/pretixdroid/templates/pretixplugins/pretixdroid/configuration_code.html:33 +msgid "3. Start scanning tickets" +msgstr "3. Los geht's – Tickets scannen" -#: pretix/plugins/pretixdroid/templates/pretixplugins/pretixdroid/configuration.html:37 -msgctxt "subevent" -msgid "Choose date" -msgstr "Termin wählen" +#: pretix/plugins/pretixdroid/views.py:42 +#: pretix/plugins/pretixdroid/views.py:102 +msgid "The selected configuration does not exist." +msgstr "Die ausgewählte Konfiguration existiert nicht." -#: pretix/plugins/pretixdroid/templates/pretixplugins/pretixdroid/configuration.html:46 -msgid "Show configuration" -msgstr "Konfiguration anzeigen" - -#: pretix/plugins/pretixdroid/templates/pretixplugins/pretixdroid/configuration.html:52 -msgid "Reset authentication token" -msgstr "Authentifizierungstoken zurücksetzen" +#: pretix/plugins/pretixdroid/views.py:100 +msgid "The selected configuration has been deleted." +msgstr "Die ausgewählte Konfiguration wurde gelöscht." #: pretix/plugins/reports/__init__.py:10 pretix/plugins/reports/__init__.py:13 msgid "Report exporter" @@ -10670,6 +10749,24 @@ msgstr "Deutsch" msgid "German (informal)" msgstr "Deutsch (Du)" +#~ msgid "App download" +#~ msgstr "App-Download" + +#~ msgid "" +#~ "If you start the app for the first time, it will request that you scan " +#~ "the following code. The code tells the app all it needs about your event." +#~ msgstr "" +#~ "Wenn Sie die App das erste Mal starten wird sie darum bitten, den " +#~ "folgenden Code einzuscannen. Der Code enthält alle nötigen Informationen " +#~ "über die Veranstaltung." + +#~ msgctxt "subevent" +#~ msgid "Choose date" +#~ msgstr "Termin wählen" + +#~ msgid "Reset authentication token" +#~ msgstr "Authentifizierungstoken zurücksetzen" + #~ msgid "Download calendar as iCal file" #~ msgstr "Kalender als iCal herunterladen" diff --git a/src/pretix/locale/de/LC_MESSAGES/djangojs.po b/src/pretix/locale/de/LC_MESSAGES/djangojs.po index 793c98a87e..6291638e56 100644 --- a/src/pretix/locale/de/LC_MESSAGES/djangojs.po +++ b/src/pretix/locale/de/LC_MESSAGES/djangojs.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-08-27 07:31+0000\n" +"POT-Creation-Date: 2017-08-29 21:12+0000\n" "PO-Revision-Date: 2017-08-27 09:35+0200\n" "Last-Translator: Raphael Michel \n" "Language-Team: \n" diff --git a/src/pretix/locale/de_Informal/LC_MESSAGES/django.po b/src/pretix/locale/de_Informal/LC_MESSAGES/django.po index b97163444e..486e2eb8c0 100644 --- a/src/pretix/locale/de_Informal/LC_MESSAGES/django.po +++ b/src/pretix/locale/de_Informal/LC_MESSAGES/django.po @@ -7,8 +7,8 @@ msgid "" msgstr "" "Project-Id-Version: 1\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-08-27 07:30+0000\n" -"PO-Revision-Date: 2017-08-27 09:35+0200\n" +"POT-Creation-Date: 2017-08-29 21:12+0000\n" +"PO-Revision-Date: 2017-08-29 23:17+0200\n" "Last-Translator: Raphael Michel \n" "Language-Team: Raphael Michel \n" "Language: de\n" @@ -16,7 +16,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -"X-Generator: Poedit 2.0.3\n" +"X-Generator: Poedit 2.0.2\n" #: pretix/base/exporters/answers.py:18 msgid "Answers to file upload questions" @@ -1036,6 +1036,8 @@ msgstr "Antwort" #: pretix/control/templates/pretixcontrol/waitinglist/index.html:119 #: pretix/plugins/checkinlists/exporters.py:69 #: pretix/plugins/checkinlists/exporters.py:109 +#: pretix/plugins/pretixdroid/models.py:15 +#: pretix/plugins/pretixdroid/templates/pretixplugins/pretixdroid/configuration.html:46 #: pretix/plugins/reports/exporters.py:248 msgctxt "subevent" msgid "Date" @@ -1229,6 +1231,8 @@ msgstr "Das Produkt \"{item}\" ist nicht mehr verfügbar." #: pretix/base/models/orders.py:549 pretix/control/views/item.py:436 #: pretix/control/views/vouchers.py:81 pretix/control/views/vouchers.py:82 #: pretix/plugins/checkinlists/exporters.py:124 +#: pretix/plugins/pretixdroid/templates/pretixplugins/pretixdroid/configuration.html:71 +#: pretix/plugins/pretixdroid/templates/pretixplugins/pretixdroid/configuration.html:72 #: pretix/presale/checkoutflow.py:477 msgid "Yes" msgstr "Ja" @@ -1238,6 +1242,8 @@ msgstr "Ja" #: pretix/control/views/vouchers.py:82 #: pretix/plugins/checkinlists/exporters.py:124 #: pretix/plugins/paypal/templates/pretixplugins/paypal/action_refund.html:14 +#: pretix/plugins/pretixdroid/templates/pretixplugins/pretixdroid/configuration.html:71 +#: pretix/plugins/pretixdroid/templates/pretixplugins/pretixdroid/configuration.html:72 #: pretix/plugins/stripe/templates/pretixplugins/stripe/action_refund.html:14 #: pretix/presale/checkoutflow.py:479 msgid "No" @@ -3766,6 +3772,8 @@ msgstr "Alle Veranstalter" #: pretix/control/forms/filter.py:195 #: pretix/control/templates/pretixcontrol/organizers/teams.html:39 +#: pretix/plugins/pretixdroid/templates/pretixplugins/pretixdroid/configuration.html:59 +#: pretix/plugins/pretixdroid/templates/pretixplugins/pretixdroid/configuration.html:63 msgid "All" msgstr "Alle" @@ -6051,6 +6059,7 @@ msgid "The following products might be no longer available for sale:" msgstr "Die folgenden Produkte stehen möglicherweise nicht mehr zum Verkauf:" #: pretix/control/templates/pretixcontrol/items/quota_edit.html:27 +#: pretix/plugins/pretixdroid/templates/pretixplugins/pretixdroid/configuration.html:48 msgid "Items" msgstr "Produkte" @@ -8765,6 +8774,45 @@ msgid "" "This plugin allows you to use the pretixdroid Android app for your event." msgstr "Dieses Plugin erlaubt, die pretixdroid-Android-App zu verwenden." +#: pretix/plugins/pretixdroid/models.py:11 +msgid "Can scan all products" +msgstr "Kann alle Produkte scannen" + +#: pretix/plugins/pretixdroid/models.py:12 +msgid "Can scan these products" +msgstr "Nur diese Produkte" + +#: pretix/plugins/pretixdroid/models.py:13 +msgid "pretixdroid 1.6 or newer only" +msgstr "pretixdroid 1.6 oder neuer" + +#: pretix/plugins/pretixdroid/models.py:16 +#, fuzzy +#| msgid "Show configuration" +msgid "Show information" +msgstr "Informationen anzeigen" + +#: pretix/plugins/pretixdroid/models.py:17 +msgid "" +"If disabled, the device can not see how many tickets exist and how many are " +"already scanned. pretixdroid 1.6 or newer only." +msgstr "" +"Wenn diese Option deaktiviert ist, kann das Gerät nicht sehen wie viele " +"Tickets existieren und wie viele bereits gescannt wurden (ab pretixdroid " +"1.6)." + +#: pretix/plugins/pretixdroid/models.py:19 +msgid "Search allowed" +msgstr "Sucher erlaubt" + +#: pretix/plugins/pretixdroid/models.py:20 +msgid "" +"If disabled, the device can not search for attendees by name. pretixdroid " +"1.6 or newer only." +msgstr "" +"Wenn diese Option deaktiviert ist, kann mit dem Gerät nicht nach Namen " +"gesucht werden (ab pretixdroid 1.6)." + #: pretix/plugins/pretixdroid/signals.py:21 msgid "pretixdroid" msgstr "pretixdroid" @@ -8799,6 +8847,8 @@ msgstr "" #: pretix/plugins/pretixdroid/templates/pretixplugins/pretixdroid/configuration.html:5 #: pretix/plugins/pretixdroid/templates/pretixplugins/pretixdroid/configuration.html:7 +#: pretix/plugins/pretixdroid/templates/pretixplugins/pretixdroid/configuration_code.html:5 +#: pretix/plugins/pretixdroid/templates/pretixplugins/pretixdroid/configuration_code.html:8 msgid "pretixdroid configuration" msgstr "pretixdroid-Konfiguration" @@ -8811,45 +8861,76 @@ msgstr "" "die Tickets scannen und überprüfen kannst." #: pretix/plugins/pretixdroid/templates/pretixplugins/pretixdroid/configuration.html:11 -msgid "App download" -msgstr "App-Download" +msgid "Create app configuration" +msgstr "App-Konfiguration erstellen" -#: pretix/plugins/pretixdroid/templates/pretixplugins/pretixdroid/configuration.html:15 +#: pretix/plugins/pretixdroid/templates/pretixplugins/pretixdroid/configuration.html:13 +msgid "" +"To start scanning tickets with our Android app, first create a configuration " +"code here:" +msgstr "" +"Um mit unserer Android-App Tickets zu scannen, erstelle hier erst einen " +"Konfigurations-Code:" + +#: pretix/plugins/pretixdroid/templates/pretixplugins/pretixdroid/configuration.html:30 +msgid "Create" +msgstr "Erstellen" + +#: pretix/plugins/pretixdroid/templates/pretixplugins/pretixdroid/configuration.html:38 +msgid "Existing app configurations" +msgstr "Bestehende App-Konfigurationen" + +#: pretix/plugins/pretixdroid/templates/pretixplugins/pretixdroid/configuration.html:44 +msgid "ID" +msgstr "ID" + +#: pretix/plugins/pretixdroid/templates/pretixplugins/pretixdroid/configuration.html:49 +msgid "Show info" +msgstr "Infos anzeigen" + +#: pretix/plugins/pretixdroid/templates/pretixplugins/pretixdroid/configuration.html:50 +msgid "Allow search" +msgstr "Suche erlauben" + +#: pretix/plugins/pretixdroid/templates/pretixplugins/pretixdroid/configuration.html:75 +msgid "Show QR code" +msgstr "QR-Code anzeigen" + +#: pretix/plugins/pretixdroid/templates/pretixplugins/pretixdroid/configuration_code.html:10 +msgid "Back to overview" +msgstr "Zurück zur Übersicht" + +#: pretix/plugins/pretixdroid/templates/pretixplugins/pretixdroid/configuration_code.html:13 +msgid "1. Download app" +msgstr "1. App herunterladen" + +#: pretix/plugins/pretixdroid/templates/pretixplugins/pretixdroid/configuration_code.html:17 msgid "Download the app from the Google Play Store" msgstr "Lade die App aus dem App Store herunter" -#: pretix/plugins/pretixdroid/templates/pretixplugins/pretixdroid/configuration.html:20 +#: pretix/plugins/pretixdroid/templates/pretixplugins/pretixdroid/configuration_code.html:22 msgid "" "Android, Google Play and the Google Play logo are trademarks of Google Inc." msgstr "" "Android, Google Play und das Google Play-Logo sind eingetragene Marken von " "Google Inc." -#: pretix/plugins/pretixdroid/templates/pretixplugins/pretixdroid/configuration.html:25 -msgid "App configuration" -msgstr "App-Konfiguration" +#: pretix/plugins/pretixdroid/templates/pretixplugins/pretixdroid/configuration_code.html:27 +msgid "2. Scan code" +msgstr "2. Code scannen" -#: pretix/plugins/pretixdroid/templates/pretixplugins/pretixdroid/configuration.html:27 -msgid "" -"If you start the app for the first time, it will request that you scan the " -"following code. The code tells the app all it needs about your event." -msgstr "" -"Wenn du die App das erste Mal startest wird sie dich bitten, den folgenden " -"Code einzuscannen. Der Code enthält alle nötigen Informationen über die " -"Veranstaltung." +#: pretix/plugins/pretixdroid/templates/pretixplugins/pretixdroid/configuration_code.html:33 +msgid "3. Start scanning tickets" +msgstr "3. Los geht's – Tickets scannen" -#: pretix/plugins/pretixdroid/templates/pretixplugins/pretixdroid/configuration.html:37 -msgctxt "subevent" -msgid "Choose date" -msgstr "Termin wählen" +#: pretix/plugins/pretixdroid/views.py:42 +#: pretix/plugins/pretixdroid/views.py:102 +msgid "The selected configuration does not exist." +msgstr "Die ausgewählte Konfiguration existiert nicht." -#: pretix/plugins/pretixdroid/templates/pretixplugins/pretixdroid/configuration.html:46 -msgid "Show configuration" -msgstr "Konfiguration anzeigen" - -#: pretix/plugins/pretixdroid/templates/pretixplugins/pretixdroid/configuration.html:52 -msgid "Reset authentication token" -msgstr "Authentifizierungstoken zurücksetzen" +#: pretix/plugins/pretixdroid/views.py:100 +msgid "The selected configuration has been deleted." +msgstr "Die ausgewählte Konfiguration wurde gelöscht." #: pretix/plugins/reports/__init__.py:10 pretix/plugins/reports/__init__.py:13 msgid "Report exporter" @@ -10641,6 +10722,24 @@ msgstr "Deutsch" msgid "German (informal)" msgstr "Deutsch (Du)" +#~ msgid "App download" +#~ msgstr "App-Download" + +#~ msgid "" +#~ "If you start the app for the first time, it will request that you scan " +#~ "the following code. The code tells the app all it needs about your event." +#~ msgstr "" +#~ "Wenn du die App das erste Mal startest wird sie dich bitten, den " +#~ "folgenden Code einzuscannen. Der Code enthält alle nötigen Informationen " +#~ "über die Veranstaltung." + +#~ msgctxt "subevent" +#~ msgid "Choose date" +#~ msgstr "Termin wählen" + +#~ msgid "Reset authentication token" +#~ msgstr "Authentifizierungstoken zurücksetzen" + #~ msgid "Download calendar as iCal file" #~ msgstr "Kalender als iCal herunterladen" diff --git a/src/pretix/locale/de_Informal/LC_MESSAGES/djangojs.po b/src/pretix/locale/de_Informal/LC_MESSAGES/djangojs.po index abec5427e2..ade46d0517 100644 --- a/src/pretix/locale/de_Informal/LC_MESSAGES/djangojs.po +++ b/src/pretix/locale/de_Informal/LC_MESSAGES/djangojs.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-08-27 07:31+0000\n" +"POT-Creation-Date: 2017-08-29 21:12+0000\n" "PO-Revision-Date: 2017-08-27 09:35+0200\n" "Last-Translator: Raphael Michel \n" "Language-Team: \n" diff --git a/src/pretix/plugins/pretixdroid/forms.py b/src/pretix/plugins/pretixdroid/forms.py new file mode 100644 index 0000000000..5a616ae1ea --- /dev/null +++ b/src/pretix/plugins/pretixdroid/forms.py @@ -0,0 +1,23 @@ +from django import forms + +from pretix.plugins.pretixdroid.models import AppConfiguration + + +class AppConfigurationForm(forms.ModelForm): + class Meta: + model = AppConfiguration + fields = ('all_items', 'items', 'subevent', 'show_info', 'allow_search') + widgets = { + 'items': forms.CheckboxSelectMultiple(attrs={ + 'data-inverse-dependency': '#id_all_items' + }), + } + + def __init__(self, **kwargs): + self.event = kwargs.pop('event') + super().__init__(**kwargs) + self.fields['items'].queryset = self.event.items.all() + if self.event.has_subevents: + self.fields['subevent'].queryset = self.event.subevents.all() + else: + del self.fields['subevent'] diff --git a/src/pretix/plugins/pretixdroid/migrations/0003_appconfiguration.py b/src/pretix/plugins/pretixdroid/migrations/0003_appconfiguration.py new file mode 100644 index 0000000000..dbedd151b0 --- /dev/null +++ b/src/pretix/plugins/pretixdroid/migrations/0003_appconfiguration.py @@ -0,0 +1,48 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.4 on 2017-08-29 13:08 +from __future__ import unicode_literals + +import django.db.models.deletion +from django.db import migrations, models + + +def runfwd(app, schema_editor): + EventSettingsStore = app.get_model('pretixbase', 'Event_SettingsStore') + AppConfiguration = app.get_model('pretixdroid', 'AppConfiguration') + + for setting in EventSettingsStore.objects.filter(key='pretixdroid_key'): + AppConfiguration.objects.create( + event=setting.object, + key=setting.value, + all_items=True + ) + setting.delete() + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('pretixbase', '0074_auto_20170825_1258'), + ('pretixdroid', '0002_auto_20161208_1644'), + ] + + operations = [ + migrations.CreateModel( + name='AppConfiguration', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('key', models.CharField(db_index=True, max_length=190, unique=True)), + ('all_items', models.BooleanField(default=True)), + ('allow_search', models.BooleanField(default=True)), + ('show_info', models.BooleanField(default=True)), + ('event', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='pretixbase.Event')), + ('items', models.ManyToManyField(blank=True, to='pretixbase.Item')), + ('subevent', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='pretixbase.SubEvent')), + ], + ), + migrations.RunPython( + runfwd, migrations.RunPython.noop + ) + ] diff --git a/src/pretix/plugins/pretixdroid/models.py b/src/pretix/plugins/pretixdroid/models.py new file mode 100644 index 0000000000..340746cf34 --- /dev/null +++ b/src/pretix/plugins/pretixdroid/models.py @@ -0,0 +1,27 @@ +import string + +from django.db import models +from django.utils.crypto import get_random_string +from django.utils.translation import pgettext_lazy, ugettext_lazy as _ + + +class AppConfiguration(models.Model): + event = models.ForeignKey('pretixbase.Event') + key = models.CharField(max_length=190, unique=True, db_index=True) + all_items = models.BooleanField(default=True, verbose_name=_('Can scan all products')) + items = models.ManyToManyField('pretixbase.Item', blank=True, verbose_name=_('Can scan these products')) + subevent = models.ForeignKey('pretixbase.SubEvent', null=True, blank=True, + verbose_name=pgettext_lazy('subevent', 'Date')) + show_info = models.BooleanField(default=True, verbose_name=_('Show information'), + help_text=_('If disabled, the device can not see how many tickets exist and how ' + 'many are already scanned. pretixdroid 1.6 or newer only.')) + allow_search = models.BooleanField(default=True, verbose_name=_('Search allowed'), + help_text=_('If disabled, the device can not search for attendees by name. ' + 'pretixdroid 1.6 or newer only.')) + + def save(self, **kwargs): + if not self.key: + self.key = get_random_string( + length=32, allowed_chars=string.ascii_uppercase + string.ascii_lowercase + string.digits + ) + return super().save(**kwargs) diff --git a/src/pretix/plugins/pretixdroid/templates/pretixplugins/pretixdroid/configuration.html b/src/pretix/plugins/pretixdroid/templates/pretixplugins/pretixdroid/configuration.html index 55879dfdc8..4b311929b2 100644 --- a/src/pretix/plugins/pretixdroid/templates/pretixplugins/pretixdroid/configuration.html +++ b/src/pretix/plugins/pretixdroid/templates/pretixplugins/pretixdroid/configuration.html @@ -8,53 +8,81 @@

{% blocktrans trimmed %} pretixdroid is an Android app that you can use to control tickets at the entrance of your event. {% endblocktrans %}

-

{% trans "App download" %}

-

- - 
-{% trans - -

-

- - {% blocktrans trimmed %} - Android, Google Play and the Google Play logo are trademarks of Google Inc. - {% endblocktrans %} - -

-

{% trans "App configuration" %}

+

{% trans "Create app configuration" %}

{% blocktrans trimmed %} - If you start the app for the first time, it will request that you scan the following code. - The code tells the app all it needs about your event. + To start scanning tickets with our Android app, first create a configuration code here: {% endblocktrans %}

- {% if request.event.has_subevents %} -
-

- {% if request.event.has_subevents %} - - {% endif %} - -

+ + {% csrf_token %} + {% bootstrap_form_errors add_form %} + {% bootstrap_field add_form.all_items layout="horizontal" %} + {% bootstrap_field add_form.items layout="horizontal" %} + {% bootstrap_field add_form.show_info layout="horizontal" %} + {% bootstrap_field add_form.allow_search layout="horizontal" %} + {% if add_form.subevent %} + {% bootstrap_field add_form.subevent layout="horizontal" %} + {% endif %} +
+
+ +
+
+
+ + + {% if configs %} +

{% trans "Existing app configurations" %}

+
+ {% csrf_token %} + + + + + {% if request.event.has_subevents %} + + {% endif %} + + + + + + + + {% for ac in configs %} + + + {% if request.event.has_subevents %} + + {% endif %} + + + + + + {% endfor %} + +
{% trans "ID" %}{% trans "Date" context "subevent" %}{% trans "Items" %}{% trans "Show info" %}{% trans "Allow search" %}
{{ ac.key|slice:"0:8" }}…{% if ac.subevent %}{{ ac.subevent }}{% else %}{% trans "All" %}{% endif %} + {% if ac.all_items %} + {% trans "All" %} + {% else %} + {% for item in ac.items.all %} + {{ item.name }} + {% if loop.revindex0 > 0 %}
{% endif %} + {% endfor %} + {% endif %} +
{% if ac.show_info %}{% trans "Yes" %}{% else %}{% trans "No" %}{% endif %}{% if ac.allow_search %}{% trans "Yes" %}{% else %}{% trans "No" %}{% endif %} + + {% trans "Show QR code" %} + + +
{% endif %} - {% if not request.event.has_subevents or subevent %} -
- {% trans "Reset authentication token" %} - - {% endif %} - {% endblock %} diff --git a/src/pretix/plugins/pretixdroid/templates/pretixplugins/pretixdroid/configuration_code.html b/src/pretix/plugins/pretixdroid/templates/pretixplugins/pretixdroid/configuration_code.html new file mode 100644 index 0000000000..3f96b1992a --- /dev/null +++ b/src/pretix/plugins/pretixdroid/templates/pretixplugins/pretixdroid/configuration_code.html @@ -0,0 +1,36 @@ +{% extends "pretixcontrol/event/base.html" %} +{% load i18n %} +{% load bootstrap3 %} +{% load staticfiles %} +{% block title %}{% trans "pretixdroid configuration" %}{% endblock %} +{% block content %} +

+ {% trans "pretixdroid configuration" %} + + {% trans "Back to overview" %} + +

+

{% trans "1. Download app" %}

+

+ + 
+{% trans + +

+

+ + {% blocktrans trimmed %} + Android, Google Play and the Google Play logo are trademarks of Google Inc. + {% endblocktrans %} + +

+

{% trans "2. Scan code" %}

+
+ +

{% trans "3. Start scanning tickets" %}

+ +{% endblock %} + diff --git a/src/pretix/plugins/pretixdroid/urls.py b/src/pretix/plugins/pretixdroid/urls.py index 564c22f782..789a5387df 100644 --- a/src/pretix/plugins/pretixdroid/urls.py +++ b/src/pretix/plugins/pretixdroid/urls.py @@ -14,8 +14,10 @@ pretixdroid_api_patterns = [ ] urlpatterns = [ - url(r'^control/event/(?P[^/]+)/(?P[^/]+)/pretixdroid/', views.ConfigView.as_view(), + url(r'^control/event/(?P[^/]+)/(?P[^/]+)/pretixdroid/$', views.ConfigView.as_view(), name='config'), + url(r'^control/event/(?P[^/]+)/(?P[^/]+)/pretixdroid/(?P\d+)/$', + views.ConfigCodeView.as_view(), name='config.code'), url(r'^pretixdroid/api/(?P[^/]+)/(?P[^/]+)/(?P\d+)/', include(pretixdroid_api_patterns)), url(r'^pretixdroid/api/(?P[^/]+)/(?P[^/]+)/', include(pretixdroid_api_patterns)), diff --git a/src/pretix/plugins/pretixdroid/views.py b/src/pretix/plugins/pretixdroid/views.py index 63eba37eaf..57aec7b8f1 100644 --- a/src/pretix/plugins/pretixdroid/views.py +++ b/src/pretix/plugins/pretixdroid/views.py @@ -1,17 +1,19 @@ import json import logging -import string import dateutil.parser +from django.contrib import messages from django.db import transaction from django.db.models import Count, Q from django.http import ( HttpResponseForbidden, HttpResponseNotFound, JsonResponse, ) -from django.shortcuts import get_object_or_404 -from django.utils.crypto import get_random_string +from django.shortcuts import get_object_or_404, redirect +from django.urls import reverse from django.utils.decorators import method_decorator +from django.utils.functional import cached_property from django.utils.timezone import now +from django.utils.translation import ugettext_lazy as _ from django.views.decorators.csrf import csrf_exempt from django.views.generic import TemplateView, View @@ -22,44 +24,93 @@ from pretix.helpers.urls import build_absolute_uri from pretix.multidomain.urlreverse import ( build_absolute_uri as event_absolute_uri, ) +from pretix.plugins.pretixdroid.forms import AppConfigurationForm +from pretix.plugins.pretixdroid.models import AppConfiguration logger = logging.getLogger('pretix.plugins.pretixdroid') API_VERSION = 3 +class ConfigCodeView(EventPermissionRequiredMixin, TemplateView): + template_name = 'pretixplugins/pretixdroid/configuration_code.html' + permission = 'can_change_orders' + + def get(self, request, **kwargs): + try: + self.object = self.request.event.appconfiguration_set.get(pk=kwargs.get("config")) + except AppConfiguration.DoesNotExist: + messages.error(request, _('The selected configuration does not exist.')) + return redirect(reverse('plugins:pretixdroid:config', kwargs={ + 'organizer': self.request.event.organizer.slug, + 'event': self.request.event.slug, + })) + return super().get(request, **kwargs) + + def get_context_data(self, **kwargs): + ctx = super().get_context_data() + url = build_absolute_uri('plugins:pretixdroid:api.redeem', kwargs={ + 'organizer': self.request.event.organizer.slug, + 'event': self.request.event.slug + }) + if self.object.subevent: + url = build_absolute_uri('plugins:pretixdroid:api.redeem', kwargs={ + 'organizer': self.request.event.organizer.slug, + 'event': self.request.event.slug, + 'subevent': self.object.subevent.pk + }) + + ctx['qrdata'] = json.dumps({ + 'version': API_VERSION, + 'url': url[:-7], # the slice removes the redeem/ part at the end + 'key': self.object.key, + 'allow_search': self.object.allow_search, + 'show_info': self.object.show_info + }) + return ctx + + class ConfigView(EventPermissionRequiredMixin, TemplateView): template_name = 'pretixplugins/pretixdroid/configuration.html' permission = 'can_change_orders' + @cached_property + def add_form(self): + return AppConfigurationForm( + event=self.request.event, + instance=AppConfiguration(event=self.request.event), + data=self.request.POST if self.request.method == "POST" and "add" in self.request.POST else None + ) + + def post(self, request, *args, **kwargs): + if "add" in self.request.POST and self.add_form.is_valid(): + self.add_form.save() + self.request.event.log_action('pretix.plugins.pretixdroid.config.added', user=self.request.user, + data=dict(self.add_form.cleaned_data)) + return redirect(reverse('plugins:pretixdroid:config.code', kwargs={ + 'organizer': self.request.event.organizer.slug, + 'event': self.request.event.slug, + 'config': self.add_form.instance.pk + })) + elif "delete" in self.request.POST: + try: + ac = self.request.event.appconfiguration_set.get(pk=request.POST.get("delete")) + self.request.event.log_action('pretix.plugins.pretixdroid.config.deleted', user=self.request.user, + data={'id': ac.pk}) + ac.delete() + messages.success(request, _('The selected configuration has been deleted.')) + except AppConfiguration.DoesNotExist: + messages.error(request, _('The selected configuration does not exist.')) + return redirect(reverse('plugins:pretixdroid:config', kwargs={ + 'organizer': self.request.event.organizer.slug, + 'event': self.request.event.slug, + })) + else: + return self.get(request, *args, **kwargs) + def get_context_data(self, **kwargs): ctx = super().get_context_data() - key = self.request.event.settings.get('pretixdroid_key') - if not key or 'flush_key' in self.request.GET: - key = get_random_string(length=32, - allowed_chars=string.ascii_uppercase + string.ascii_lowercase + string.digits) - self.request.event.settings.set('pretixdroid_key', key) - - subevent = None - url = build_absolute_uri('plugins:pretixdroid:api.redeem', kwargs={ - 'organizer': self.request.event.organizer.slug, - 'event': self.request.event.slug - }) - if self.request.event.has_subevents: - if self.request.GET.get('subevent'): - subevent = get_object_or_404(SubEvent, event=self.request.event, pk=self.request.GET['subevent']) - url = build_absolute_uri('plugins:pretixdroid:api.redeem', kwargs={ - 'organizer': self.request.event.organizer.slug, - 'event': self.request.event.slug, - 'subevent': subevent.pk - }) - - ctx['subevent'] = subevent - - ctx['qrdata'] = json.dumps({ - 'version': API_VERSION, - 'url': url[:-7], # the slice removes the redeem/ part at the end - 'key': key, - }) + ctx['add_form'] = self.add_form + ctx['configs'] = self.request.event.appconfiguration_set.prefetch_related('items') return ctx @@ -75,13 +126,16 @@ class ApiView(View): except Event.DoesNotExist: return HttpResponseNotFound('Unknown event') - if (not self.event.settings.get('pretixdroid_key') - or self.event.settings.get('pretixdroid_key') != request.GET.get('key', '-unset-')): + try: + self.config = self.event.appconfiguration_set.get(key=request.GET.get("key", "-unset-")) + except AppConfiguration.DoesNotExist: return HttpResponseForbidden('Invalid key') self.subevent = None if self.event.has_subevents: - if 'subevent' in kwargs: + if self.config.subevent: + self.subevent = self.config.subevent + elif 'subevent' in kwargs: self.subevent = get_object_or_404(SubEvent, event=self.event, pk=kwargs['subevent']) else: return HttpResponseForbidden('No subevent selected.') @@ -112,7 +166,10 @@ class ApiRedeemView(ApiView): op = OrderPosition.objects.select_related('item', 'variation', 'order', 'addon_to').get( order__event=self.event, secret=secret, subevent=self.subevent ) - if op.order.status == Order.STATUS_PAID or force: + if not self.config.all_items and op.item_id not in [i.pk for i in self.config.items.all()]: + response['status'] = 'error' + response['reason'] = 'product' + elif op.order.status == Order.STATUS_PAID or force: ci, created = Checkin.objects.get_or_create(position=op, defaults={ 'datetime': dt, 'nonce': nonce, @@ -186,14 +243,20 @@ class ApiSearchView(ApiView): } if len(query) >= 4: - ops = OrderPosition.objects.select_related('item', 'variation', 'order', 'addon_to', 'order__invoice_address').filter( - Q(order__event=self.event) - & Q( - Q(secret__istartswith=query) | Q(attendee_name__icontains=query) | Q(order__code__istartswith=query) - | Q(order__invoice_address__name__icontains=query) - ) - & Q(subevent=self.subevent) - ).annotate(checkin_cnt=Count('checkins'))[:25] + qs = OrderPosition.objects.select_related('item', 'variation', 'order', 'addon_to', 'order__invoice_address') + if not self.config.allow_search: + ops = qs.filter( + Q(order__event=self.event) & Q(secret__istartswith=query) & Q(subevent=self.subevent) + ).annotate(checkin_cnt=Count('checkins'))[:25] + else: + ops = qs.filter( + Q(order__event=self.event) + & Q( + Q(secret__istartswith=query) | Q(attendee_name__icontains=query) | Q(order__code__istartswith=query) + | Q(order__invoice_address__name__icontains=query) + ) + & Q(subevent=self.subevent) + ).annotate(checkin_cnt=Count('checkins'))[:25] response['results'] = [serialize_op(op, bool(op.checkin_cnt)) for op in ops] else: @@ -210,7 +273,11 @@ class ApiDownloadView(ApiView): ops = OrderPosition.objects.select_related('item', 'variation', 'order', 'addon_to').filter( Q(order__event=self.event) & Q(subevent=self.subevent) - ).annotate(checkin_cnt=Count('checkins')) + ) + if not self.config.all_items: + ops = ops.filter(item__in=self.config.items.all()) + + ops = ops.annotate(checkin_cnt=Count('checkins')) response['results'] = [serialize_op(op, bool(op.checkin_cnt)) for op in ops] return JsonResponse(response) diff --git a/src/tests/plugins/pretixdroid/test_simple.py b/src/tests/plugins/pretixdroid/test_simple.py index 50e77fa4fc..650beae54c 100644 --- a/src/tests/plugins/pretixdroid/test_simple.py +++ b/src/tests/plugins/pretixdroid/test_simple.py @@ -8,6 +8,7 @@ from pretix.base.models import ( Checkin, Event, InvoiceAddress, Item, ItemVariation, Order, OrderPosition, Organizer, Team, User, ) +from pretix.plugins.pretixdroid.models import AppConfiguration from pretix.plugins.pretixdroid.views import API_VERSION @@ -44,23 +45,9 @@ def env(): return event, user, o1, op1, op2 -@pytest.mark.django_db -def test_flush_key(client, env): - env[0].settings.set('pretixdroid_key', 'abcdefg') - client.login(email='dummy@dummy.dummy', password='dummy') - - client.get('/control/event/%s/%s/pretixdroid/' % (env[0].organizer.slug, env[0].slug)) - env[0].settings.flush() - assert env[0].settings.get('pretixdroid_key') == 'abcdefg' - - client.get('/control/event/%s/%s/pretixdroid/?flush_key=1' % (env[0].organizer.slug, env[0].slug)) - env[0].settings.flush() - assert env[0].settings.get('pretixdroid_key') != 'abcdefg' - - @pytest.mark.django_db def test_custom_datetime(client, env): - env[0].settings.set('pretixdroid_key', 'abcdefg') + AppConfiguration.objects.create(event=env[0], key='abcdefg') dt = now() - timedelta(days=1) dt = dt.replace(microsecond=0) resp = client.post('/pretixdroid/api/%s/%s/redeem/?key=%s' % (env[0].organizer.slug, env[0].slug, 'abcdefg'), @@ -73,7 +60,7 @@ def test_custom_datetime(client, env): @pytest.mark.django_db def test_only_once(client, env): - env[0].settings.set('pretixdroid_key', 'abcdefg') + AppConfiguration.objects.create(event=env[0], key='abcdefg') resp = client.post('/pretixdroid/api/%s/%s/redeem/?key=%s' % (env[0].organizer.slug, env[0].slug, 'abcdefg'), data={'secret': '1234'}) @@ -87,9 +74,26 @@ def test_only_once(client, env): assert jdata['reason'] == 'already_redeemed' +@pytest.mark.django_db +def test_item_scope(client, env): + ac = AppConfiguration.objects.create(event=env[0], key='abcdefg', all_items=False) + ac.items.add(env[4].item) + + resp = client.post('/pretixdroid/api/%s/%s/redeem/?key=%s' % (env[0].organizer.slug, env[0].slug, 'abcdefg'), + data={'secret': env[4].secret}) + jdata = json.loads(resp.content.decode("utf-8")) + assert jdata['version'] == API_VERSION + assert jdata['status'] == 'ok' + resp = client.post('/pretixdroid/api/%s/%s/redeem/?key=%s' % (env[0].organizer.slug, env[0].slug, 'abcdefg'), + data={'secret': env[3].secret}) + jdata = json.loads(resp.content.decode("utf-8")) + assert jdata['status'] == 'error' + assert jdata['reason'] == 'product' + + @pytest.mark.django_db def test_reupload_same_nonce(client, env): - env[0].settings.set('pretixdroid_key', 'abcdefg') + AppConfiguration.objects.create(event=env[0], key='abcdefg') resp = client.post('/pretixdroid/api/%s/%s/redeem/?key=%s' % (env[0].organizer.slug, env[0].slug, 'abcdefg'), data={'secret': '1234', 'nonce': 'fooobar'}) @@ -105,7 +109,7 @@ def test_reupload_same_nonce(client, env): @pytest.mark.django_db def test_forced_multiple(client, env): - env[0].settings.set('pretixdroid_key', 'abcdefg') + AppConfiguration.objects.create(event=env[0], key='abcdefg') resp = client.post('/pretixdroid/api/%s/%s/redeem/?key=%s' % (env[0].organizer.slug, env[0].slug, 'abcdefg'), data={'secret': '1234'}) @@ -120,7 +124,7 @@ def test_forced_multiple(client, env): @pytest.mark.django_db def test_require_paid(client, env): - env[0].settings.set('pretixdroid_key', 'abcdefg') + AppConfiguration.objects.create(event=env[0], key='abcdefg') env[2].status = Order.STATUS_PENDING env[2].save() @@ -133,7 +137,7 @@ def test_require_paid(client, env): @pytest.mark.django_db def test_unknown(client, env): - env[0].settings.set('pretixdroid_key', 'abcdefg') + AppConfiguration.objects.create(event=env[0], key='abcdefg') resp = client.post('/pretixdroid/api/%s/%s/redeem/?key=%s' % (env[0].organizer.slug, env[0].slug, 'abcdefg'), data={'secret': '4321'}) @@ -158,17 +162,36 @@ def test_unknown_event(client, env): @pytest.mark.django_db def test_search(client, env): - env[0].settings.set('pretixdroid_key', 'abcdefg') + AppConfiguration.objects.create(event=env[0], key='abcdefg') resp = client.get('/pretixdroid/api/%s/%s/search/?key=%s&query=%s' % ( env[0].organizer.slug, env[0].slug, 'abcdefg', '567891')) jdata = json.loads(resp.content.decode("utf-8")) assert len(jdata['results']) == 1 assert jdata['results'][0]['secret'] == '5678910' + resp = client.get('/pretixdroid/api/%s/%s/search/?key=%s&query=%s' % ( + env[0].organizer.slug, env[0].slug, 'abcdefg', 'Peter')) + jdata = json.loads(resp.content.decode("utf-8")) + assert len(jdata['results']) == 1 + assert jdata['results'][0]['secret'] == '5678910' + + +@pytest.mark.django_db +def test_search_restricted(client, env): + AppConfiguration.objects.create(event=env[0], key='abcdefg', allow_search=False) + resp = client.get('/pretixdroid/api/%s/%s/search/?key=%s&query=%s' % ( + env[0].organizer.slug, env[0].slug, 'abcdefg', '567891')) + jdata = json.loads(resp.content.decode("utf-8")) + assert len(jdata['results']) == 1 + assert jdata['results'][0]['secret'] == '5678910' + resp = client.get('/pretixdroid/api/%s/%s/search/?key=%s&query=%s' % ( + env[0].organizer.slug, env[0].slug, 'abcdefg', 'Peter')) + jdata = json.loads(resp.content.decode("utf-8")) + assert len(jdata['results']) == 0 @pytest.mark.django_db def test_search_invoice_name(client, env): - env[0].settings.set('pretixdroid_key', 'abcdefg') + AppConfiguration.objects.create(event=env[0], key='abcdefg') InvoiceAddress.objects.create(order=env[2], name="John") resp = client.get('/pretixdroid/api/%s/%s/search/?key=%s&query=%s' % ( env[0].organizer.slug, env[0].slug, 'abcdefg', 'John')) @@ -179,7 +202,7 @@ def test_search_invoice_name(client, env): @pytest.mark.django_db def test_download_all_data(client, env): - env[0].settings.set('pretixdroid_key', 'abcdefg') + AppConfiguration.objects.create(event=env[0], key='abcdefg') resp = client.get('/pretixdroid/api/%s/%s/download/?key=%s' % (env[0].organizer.slug, env[0].slug, 'abcdefg')) jdata = json.loads(resp.content.decode("utf-8")) assert len(jdata['results']) == 2 @@ -187,9 +210,19 @@ def test_download_all_data(client, env): assert jdata['results'][1]['secret'] == '5678910' +@pytest.mark.django_db +def test_download_item_restriction(client, env): + ac = AppConfiguration.objects.create(event=env[0], key='abcdefg', all_items=False) + ac.items.add(env[4].item) + resp = client.get('/pretixdroid/api/%s/%s/download/?key=%s' % (env[0].organizer.slug, env[0].slug, 'abcdefg')) + jdata = json.loads(resp.content.decode("utf-8")) + assert len(jdata['results']) == 1 + assert jdata['results'][0]['secret'] == env[4].secret + + @pytest.mark.django_db def test_status(client, env): - env[0].settings.set('pretixdroid_key', 'abcdefg') + AppConfiguration.objects.create(event=env[0], key='abcdefg') Checkin.objects.create(position=env[3]) resp = client.get('/pretixdroid/api/%s/%s/status/?key=%s' % ( env[0].organizer.slug, env[0].slug, 'abcdefg')) diff --git a/src/tests/plugins/pretixdroid/test_subevents.py b/src/tests/plugins/pretixdroid/test_subevents.py index 7789aa3b12..dfd78e6919 100644 --- a/src/tests/plugins/pretixdroid/test_subevents.py +++ b/src/tests/plugins/pretixdroid/test_subevents.py @@ -8,6 +8,7 @@ from pretix.base.models import ( Checkin, Event, Item, ItemVariation, Order, OrderPosition, Organizer, Team, User, ) +from pretix.plugins.pretixdroid.models import AppConfiguration from pretix.plugins.pretixdroid.views import API_VERSION @@ -47,23 +48,9 @@ def env(): return event, user, o1, op1, op2, se1, se2 -@pytest.mark.django_db -def test_config(client, env): - env[0].settings.set('pretixdroid_key', 'abcdefg') - client.login(email='dummy@dummy.dummy', password='dummy') - - r = client.get('/control/event/%s/%s/pretixdroid/' % (env[0].organizer.slug, env[0].slug)) - print(r.content) - assert 'qrcodeCanvas' not in r.rendered_content - - r = client.get('/control/event/%s/%s/pretixdroid/?subevent=%d' % (env[0].organizer.slug, env[0].slug, env[5].pk)) - assert 'qrcodeCanvas' in r.rendered_content - assert '/%d/' % env[5].pk in r.rendered_content - - @pytest.mark.django_db def test_custom_datetime(client, env): - env[0].settings.set('pretixdroid_key', 'abcdefg') + AppConfiguration.objects.create(event=env[0], key='abcdefg', subevent=env[5]) dt = now() - timedelta(days=1) dt = dt.replace(microsecond=0) resp = client.post('/pretixdroid/api/%s/%s/%d/redeem/?key=%s' % ( @@ -77,7 +64,7 @@ def test_custom_datetime(client, env): @pytest.mark.django_db def test_wrong_subevent(client, env): - env[0].settings.set('pretixdroid_key', 'abcdefg') + AppConfiguration.objects.create(event=env[0], key='abcdefg') resp = client.post('/pretixdroid/api/%s/%s/%d/redeem/?key=%s' % ( env[0].organizer.slug, env[0].slug, env[5].pk, 'abcdefg' @@ -93,9 +80,30 @@ def test_wrong_subevent(client, env): assert jdata['status'] == 'ok' +@pytest.mark.django_db +def test_other_subevent_not_allowed(client, env): + ac = AppConfiguration.objects.create(event=env[0], key='abcdefg', subevent=env[5]) + + resp = client.post('/pretixdroid/api/%s/%s/%d/redeem/?key=%s' % ( + env[0].organizer.slug, env[0].slug, env[6].pk, 'abcdefg' + ), data={'secret': '5678910'}) + jdata = json.loads(resp.content.decode("utf-8")) + assert jdata['status'] == 'error' + assert jdata['reason'] == 'unknown_ticket' + + ac.subevent = env[6] + ac.save() + + resp = client.post('/pretixdroid/api/%s/%s/%d/redeem/?key=%s' % ( + env[0].organizer.slug, env[0].slug, env[6].pk, 'abcdefg' + ), data={'secret': '5678910'}) + jdata = json.loads(resp.content.decode("utf-8")) + assert jdata['status'] == 'ok' + + @pytest.mark.django_db def test_unknown_subevent(client, env): - env[0].settings.set('pretixdroid_key', 'abcdefg') + AppConfiguration.objects.create(event=env[0], key='abcdefg') resp = client.post('/pretixdroid/api/%s/%s/%d/redeem/?key=%s' % ( env[0].organizer.slug, env[0].slug, env[6].pk + 1000, 'abcdefg' ), data={'secret': '5678910'}) @@ -112,7 +120,7 @@ def test_no_subevent(client, env): @pytest.mark.django_db def test_search(client, env): - env[0].settings.set('pretixdroid_key', 'abcdefg') + AppConfiguration.objects.create(event=env[0], key='abcdefg') resp = client.get('/pretixdroid/api/%s/%s/%d/search/?key=%s&query=%s' % ( env[0].organizer.slug, env[0].slug, env[5].pk, 'abcdefg', '567891')) jdata = json.loads(resp.content.decode("utf-8")) @@ -126,7 +134,7 @@ def test_search(client, env): @pytest.mark.django_db def test_download_all_data(client, env): - env[0].settings.set('pretixdroid_key', 'abcdefg') + AppConfiguration.objects.create(event=env[0], key='abcdefg', subevent=env[5]) resp = client.get('/pretixdroid/api/%s/%s/%d/download/?key=%s' % ( env[0].organizer.slug, env[0].slug, env[5].pk, 'abcdefg')) jdata = json.loads(resp.content.decode("utf-8")) @@ -136,7 +144,7 @@ def test_download_all_data(client, env): @pytest.mark.django_db def test_status(client, env): - env[0].settings.set('pretixdroid_key', 'abcdefg') + AppConfiguration.objects.create(event=env[0], key='abcdefg', subevent=env[5]) Checkin.objects.create(position=env[3]) resp = client.get('/pretixdroid/api/%s/%s/%d/status/?key=%s' % ( env[0].organizer.slug, env[0].slug, env[5].pk, 'abcdefg'))