From cf654570f92a2ec42098458568c68f2b9632be5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20H=C3=A4ttasch?= <benjamin.haettasch@fachschaft.informatik.tu-darmstadt.de> Date: Tue, 16 Feb 2021 15:46:46 +0100 Subject: [PATCH] Introduce and use wizard for event creation and configuration The wizard is used instead of the normal built-in creation workflow It consists of multiple steps to specify the most fundamental settings (especially the timezone), further settings, copying AKCategories and AKRequirements from existing events and activating the event once the basic configuration is done The steps after the creation (first two steps) can be skipped --- AKModel/admin.py | 32 ++- AKModel/forms.py | 67 +++++ AKModel/locale/de_DE/LC_MESSAGES/django.po | 250 +++++++++++++----- .../admin/AKModel/event_wizard/activate.html | 36 +++ .../event_wizard/created_prepare_import.html | 52 ++++ .../admin/AKModel/event_wizard/finish.html | 24 ++ .../admin/AKModel/event_wizard/import.html | 28 ++ .../admin/AKModel/event_wizard/settings.html | 34 +++ .../admin/AKModel/event_wizard/start.html | 26 ++ .../AKModel/event_wizard/wizard_steps.html | 15 ++ AKModel/views.py | 96 ++++++- locale/de_DE/LC_MESSAGES/django.po | 14 +- 12 files changed, 591 insertions(+), 83 deletions(-) create mode 100644 AKModel/forms.py create mode 100644 AKModel/templates/admin/AKModel/event_wizard/activate.html create mode 100644 AKModel/templates/admin/AKModel/event_wizard/created_prepare_import.html create mode 100644 AKModel/templates/admin/AKModel/event_wizard/finish.html create mode 100644 AKModel/templates/admin/AKModel/event_wizard/import.html create mode 100644 AKModel/templates/admin/AKModel/event_wizard/settings.html create mode 100644 AKModel/templates/admin/AKModel/event_wizard/start.html create mode 100644 AKModel/templates/admin/AKModel/event_wizard/wizard_steps.html diff --git a/AKModel/admin.py b/AKModel/admin.py index 48c1f878..81ab14a6 100644 --- a/AKModel/admin.py +++ b/AKModel/admin.py @@ -3,7 +3,7 @@ from django.contrib import admin from django.contrib.admin import SimpleListFilter from django.db.models import Count, F from django import forms -from django.shortcuts import render +from django.shortcuts import render, redirect from django.urls import path, reverse_lazy from django.utils import timezone from django.utils.html import format_html @@ -16,7 +16,9 @@ from AKModel.availability.forms import AvailabilitiesFormMixin from AKModel.availability.models import Availability from AKModel.models import Event, AKOwner, AKCategory, AKTrack, AKTag, AKRequirement, AK, AKSlot, Room, AKOrgaMessage, \ ConstraintViolation -from AKModel.views import EventStatusView, AKCSVExportView, AKWikiExportView, AKMessageDeleteView +from AKModel.views import EventStatusView, AKCSVExportView, AKWikiExportView, AKMessageDeleteView, \ + NewEventWizardStartView, NewEventWizardSettingsView, NewEventWizardPrepareImportView, NewEventWizardFinishView, \ + NewEventWizardImportView, NewEventWizardActivateView @admin.register(Event) @@ -27,9 +29,29 @@ class EventAdmin(admin.ModelAdmin): list_editable = ['active'] ordering = ['-start'] + def add_view(self, request, form_url='', extra_context=None): + # Always use wizard to create new events + # (the built-in form wouldn't work anyways since the timezone cannot be specified before starting to fill the form) + return redirect("admin:new_event_wizard_start") + def get_urls(self): urls = super().get_urls() custom_urls = [ + path('add/wizard/start/', self.admin_site.admin_view(NewEventWizardStartView.as_view()), + name="new_event_wizard_start"), + path('add/wizard/settings/', self.admin_site.admin_view(NewEventWizardSettingsView.as_view()), + name="new_event_wizard_settings"), + path('add/wizard/created/<slug:event_slug>/', self.admin_site.admin_view(NewEventWizardPrepareImportView.as_view()), + name="new_event_wizard_prepare_import"), + path('add/wizard/import/<slug:event_slug>/from/<slug:import_slug>/', + self.admin_site.admin_view(NewEventWizardImportView.as_view()), + name="new_event_wizard_import"), + path('add/wizard/activate/<slug:slug>/', + self.admin_site.admin_view(NewEventWizardActivateView.as_view()), + name="new_event_wizard_activate"), + path('add/wizard/finish/<slug:slug>/', + self.admin_site.admin_view(NewEventWizardFinishView.as_view()), + name="new_event_wizard_finish"), path('<slug:slug>/status/', self.admin_site.admin_view(EventStatusView.as_view()), name="event_status"), path('<slug:event_slug>/ak-csv-export/', self.admin_site.admin_view(AKCSVExportView.as_view()), name="ak_csv_export"), path('<slug:event_slug>/ak-wiki-export/', self.admin_site.admin_view(AKWikiExportView.as_view()), name="ak_wiki_export"), @@ -45,11 +67,7 @@ class EventAdmin(admin.ModelAdmin): def get_form(self, request, obj=None, change=False, **kwargs): # Use timezone of event - if obj is not None and obj.timezone: - timezone.activate(obj.timezone) - # No timezone available? Use UTC - else: - timezone.activate("UTC") + timezone.activate(obj.timezone) return super().get_form(request, obj, change, **kwargs) diff --git a/AKModel/forms.py b/AKModel/forms.py new file mode 100644 index 00000000..3e39dcaa --- /dev/null +++ b/AKModel/forms.py @@ -0,0 +1,67 @@ +from bootstrap_datepicker_plus import DateTimePickerInput +from django import forms +from django.forms.utils import ErrorList +from django.utils.translation import ugettext_lazy as _ + +from AKModel.models import Event, AKCategory, AKRequirement + + +class NewEventWizardStartForm(forms.ModelForm): + class Meta: + model = Event + fields = ['name', 'slug', 'timezone'] + + is_init = forms.BooleanField(initial=True, widget=forms.HiddenInput) + + +class NewEventWizardSettingsForm(forms.ModelForm): + class Meta: + model = Event + exclude = [] + widgets = { + 'name': forms.HiddenInput(), + 'slug': forms.HiddenInput(), + 'timezone': forms.HiddenInput(), + 'active': forms.HiddenInput(), + 'plan_hidden': forms.HiddenInput(), + 'start': DateTimePickerInput(options={"format": "YYYY-MM-DD HH:mm"}), + 'end': DateTimePickerInput(options={"format": "YYYY-MM-DD HH:mm"}), + 'reso_deadline': DateTimePickerInput(options={"format": "YYYY-MM-DD HH:mm"}), + } + + +class NewEventWizardPrepareImportForm(forms.Form): + import_event = forms.ModelChoiceField( + queryset=Event.objects.all(), + label=_("Copy ak requirements and ak categories of existing event"), + help_text=_("You can choose what to copy in the next step") + ) + +class NewEventWizardImportForm(forms.Form): + import_categories = forms.ModelMultipleChoiceField( + queryset=AKCategory.objects.all(), + widget=forms.CheckboxSelectMultiple, + label=_("Copy ak categories"), + required=False, + ) + + import_requirements = forms.ModelMultipleChoiceField( + queryset=AKRequirement.objects.all(), + widget=forms.CheckboxSelectMultiple, + label=_("Copy ak requirements"), + required=False, + ) + + def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None, initial=None, error_class=ErrorList, + label_suffix=None, empty_permitted=False, field_order=None, use_required_attribute=None, + renderer=None): + super().__init__(data, files, auto_id, prefix, initial, error_class, label_suffix, empty_permitted, field_order, + use_required_attribute, renderer) + self.fields["import_categories"].queryset = self.fields["import_categories"].queryset.filter(event=self.initial["import_event"]) + self.fields["import_requirements"].queryset = self.fields["import_requirements"].queryset.filter(event=self.initial["import_event"]) + + +class NewEventWizardActivateForm(forms.ModelForm): + class Meta: + fields = ["active"] + model = Event diff --git a/AKModel/locale/de_DE/LC_MESSAGES/django.po b/AKModel/locale/de_DE/LC_MESSAGES/django.po index ebcaa384..5d16986b 100644 --- a/AKModel/locale/de_DE/LC_MESSAGES/django.po +++ b/AKModel/locale/de_DE/LC_MESSAGES/django.po @@ -2,7 +2,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-11-03 20:41+0000\n" +"POT-Creation-Date: 2021-02-16 14:42+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" @@ -11,28 +11,35 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: AKModel/admin.py:41 AKModel/admin.py:42 +#: AKModel/admin.py:65 AKModel/admin.py:66 +#: AKModel/templates/admin/AKModel/event_wizard/activate.html:32 +#: AKModel/templates/admin/AKModel/event_wizard/created_prepare_import.html:48 +#: AKModel/templates/admin/AKModel/event_wizard/finish.html:21 #: AKModel/templates/admin/AKModel/status.html:7 #: AKModel/templates/admin/ak_index.html:15 msgid "Status" msgstr "Status" -#: AKModel/admin.py:120 +#: AKModel/admin.py:140 msgid "Wish" msgstr "AK-Wunsch" -#: AKModel/admin.py:126 +#: AKModel/admin.py:146 msgid "Is wish" msgstr "Ist ein Wunsch" -#: AKModel/admin.py:127 +#: AKModel/admin.py:147 msgid "Is not a wish" msgstr "Ist kein Wunsch" -#: AKModel/admin.py:154 +#: AKModel/admin.py:174 msgid "Export to wiki syntax" msgstr "In Wiki-Syntax exportieren" +#: AKModel/admin.py:270 +msgid "AK Details" +msgstr "AK-Details" + #: AKModel/availability/forms.py:20 AKModel/availability/models.py:239 msgid "Availability" msgstr "Verfügbarkeit" @@ -60,13 +67,13 @@ msgstr "Bitte Verfügbarkeiten eintragen!" #: AKModel/availability/models.py:38 AKModel/models.py:47 AKModel/models.py:76 #: AKModel/models.py:128 AKModel/models.py:147 AKModel/models.py:179 #: AKModel/models.py:233 AKModel/models.py:292 AKModel/models.py:324 -#: AKModel/models.py:410 +#: AKModel/models.py:431 msgid "Event" msgstr "Event" #: AKModel/availability/models.py:39 AKModel/models.py:77 AKModel/models.py:129 #: AKModel/models.py:148 AKModel/models.py:180 AKModel/models.py:234 -#: AKModel/models.py:293 AKModel/models.py:325 AKModel/models.py:411 +#: AKModel/models.py:293 AKModel/models.py:325 AKModel/models.py:432 msgid "Associated event" msgstr "Zugehöriges Event" @@ -79,7 +86,7 @@ msgid "Person whose availability this is" msgstr "Person deren Verfügbarkeit hier abgebildet wird" #: AKModel/availability/models.py:56 AKModel/models.py:296 -#: AKModel/models.py:315 AKModel/models.py:419 +#: AKModel/models.py:315 AKModel/models.py:440 msgid "Room" msgstr "Raum" @@ -88,7 +95,7 @@ msgid "Room whose availability this is" msgstr "Raum dessen Verfügbarkeit hier abgebildet wird" #: AKModel/availability/models.py:65 AKModel/models.py:239 -#: AKModel/models.py:314 AKModel/models.py:366 +#: AKModel/models.py:314 AKModel/models.py:387 msgid "AK" msgstr "AK" @@ -97,7 +104,7 @@ msgid "AK whose availability this is" msgstr "Verfügbarkeiten" #: AKModel/availability/models.py:74 AKModel/models.py:132 -#: AKModel/models.py:425 +#: AKModel/models.py:446 msgid "AK Category" msgstr "AK-Kategorie" @@ -109,6 +116,23 @@ msgstr "AK-Kategorie, deren Verfügbarkeit hier abgebildet wird" msgid "Availabilities" msgstr "Verfügbarkeiten" +#: AKModel/forms.py:36 +msgid "Copy ak requirements and ak categories of existing event" +msgstr "AK-Anforderungen und AK-Kategorien eines existierenden Events kopieren" + +#: AKModel/forms.py:37 +msgid "You can choose what to copy in the next step" +msgstr "" +"Im nächsten Schritt kann ausgewählt werden, was genau kopiert werden soll" + +#: AKModel/forms.py:44 +msgid "Copy ak categories" +msgstr "AK-Kategorien kopieren" + +#: AKModel/forms.py:51 +msgid "Copy ak requirements" +msgstr "AK-Anforderungen kopieren" + #: AKModel/models.py:16 AKModel/models.py:123 AKModel/models.py:144 #: AKModel/models.py:163 AKModel/models.py:177 AKModel/models.py:195 #: AKModel/models.py:285 @@ -145,7 +169,7 @@ msgstr "Zeitzone" msgid "Time Zone where this event takes place in" msgstr "Zeitzone in der das Event stattfindet" -#: AKModel/models.py:25 +#: AKModel/models.py:25 AKModel/views.py:188 msgid "Start" msgstr "Start" @@ -261,7 +285,7 @@ msgstr "Internet Link" msgid "Link to Homepage" msgstr "Link zu Homepage oder Webseite" -#: AKModel/models.py:80 AKModel/models.py:418 +#: AKModel/models.py:80 AKModel/models.py:439 msgid "AK Owner" msgstr "AK-Leitung" @@ -321,7 +345,7 @@ msgstr "AK-Tags" msgid "Name of the Requirement" msgstr "Name der Anforderung" -#: AKModel/models.py:183 AKModel/models.py:422 +#: AKModel/models.py:183 AKModel/models.py:443 msgid "AK Requirement" msgstr "AK-Anforderung" @@ -458,7 +482,7 @@ msgstr "Interessenszähler" msgid "People who have indicated interest online" msgstr "Anzahl Personen, die online Interesse bekundet haben" -#: AKModel/models.py:240 AKModel/models.py:413 +#: AKModel/models.py:240 AKModel/models.py:434 #: AKModel/templates/admin/AKModel/status.html:49 #: AKModel/templates/admin/AKModel/status.html:56 msgid "AKs" @@ -536,163 +560,168 @@ msgstr "Letzte Aktualisierung" msgid "AK Slot" msgstr "AK-Slot" -#: AKModel/models.py:331 AKModel/models.py:415 +#: AKModel/models.py:331 AKModel/models.py:436 msgid "AK Slots" msgstr "AK-Slot" -#: AKModel/models.py:345 +#: AKModel/models.py:353 AKModel/models.py:362 msgid "Not scheduled yet" msgstr "Noch nicht geplant" -#: AKModel/models.py:367 +#: AKModel/models.py:388 #, fuzzy #| msgid "Track the AK belongs to" msgid "AK this message belongs to" msgstr "Track zu dem der AK gehört" -#: AKModel/models.py:368 +#: AKModel/models.py:389 msgid "Message text" msgstr "Nachrichtentext" -#: AKModel/models.py:369 +#: AKModel/models.py:390 msgid "Message to the organizers. This is not publicly visible." msgstr "" "Nachricht an die Organisator*innen. Diese ist nicht öffentlich sichtbar." -#: AKModel/models.py:373 +#: AKModel/models.py:394 msgid "AK Orga Message" msgstr "AK-Organachricht" -#: AKModel/models.py:374 +#: AKModel/models.py:395 msgid "AK Orga Messages" msgstr "AK-Organachrichten" -#: AKModel/models.py:383 +#: AKModel/models.py:404 msgid "Constraint Violation" msgstr "Constraintverletzung" -#: AKModel/models.py:384 +#: AKModel/models.py:405 msgid "Constraint Violations" msgstr "Constraintverletzungen" -#: AKModel/models.py:388 +#: AKModel/models.py:409 msgid "Owner has two parallel slots" msgstr "Leitung hat zwei Slots parallel" -#: AKModel/models.py:389 +#: AKModel/models.py:410 msgid "AK Slot was scheduled outside the AK's availabilities" msgstr "AK Slot wurde außerhalb der Verfügbarkeit des AKs platziert" -#: AKModel/models.py:390 +#: AKModel/models.py:411 msgid "Room has two AK slots scheduled at the same time" msgstr "Raum hat AK Slots gleichzeitig" -#: AKModel/models.py:391 +#: AKModel/models.py:412 msgid "Room does not satisfy the requirement of the scheduled AK" msgstr "Room erfüllt die Anforderungen des platzierten AKs nicht" -#: AKModel/models.py:392 +#: AKModel/models.py:413 msgid "AK Slot is scheduled at the same time as an AK listed as a conflict" -msgstr "AK Slot wurde wurde zur gleichen Zeit wie ein Konflikt des AKs platziert" +msgstr "" +"AK Slot wurde wurde zur gleichen Zeit wie ein Konflikt des AKs platziert" -#: AKModel/models.py:393 +#: AKModel/models.py:414 msgid "AK Slot is scheduled before an AK listed as a prerequisite" msgstr "AK Slot wurde vor einem als Voraussetzung gelisteten AK platziert" -#: AKModel/models.py:395 -msgid "AK Slot for AK with intention to submit a resolution is scheduled after " +#: AKModel/models.py:416 +msgid "" +"AK Slot for AK with intention to submit a resolution is scheduled after " "resolution deadline" -msgstr "AK Slot eines AKs mit Resoabsicht wurde nach der Resodeadline platziert" +msgstr "" +"AK Slot eines AKs mit Resoabsicht wurde nach der Resodeadline platziert" -#: AKModel/models.py:396 +#: AKModel/models.py:417 msgid "AK Slot in a category is outside that categories availabilities" msgstr "AK Slot wurde außerhalb der Verfügbarkeiten seiner Kategorie" -#: AKModel/models.py:397 +#: AKModel/models.py:418 msgid "Two AK Slots for the same AK scheduled at the same time" msgstr "Zwei AK Slots eines AKs wurden zur selben Zeit platziert" -#: AKModel/models.py:398 +#: AKModel/models.py:419 msgid "AK Slot is scheduled in a room with less space than interest" -msgstr "AK Slot wurde in einem Raum mit weniger Plätzen als am AK Interessierten platziert" +msgstr "" +"AK Slot wurde in einem Raum mit weniger Plätzen als am AK Interessierten " +"platziert" -#: AKModel/models.py:399 +#: AKModel/models.py:420 msgid "AK Slot is scheduled outside the event's availabilities" msgstr "AK Slot wurde außerhalb der Verfügbarkeit des Events platziert" -#: AKModel/models.py:402 +#: AKModel/models.py:423 msgid "Warning" msgstr "Warnung" -#: AKModel/models.py:403 +#: AKModel/models.py:424 msgid "Violation" msgstr "Verletzung" -#: AKModel/models.py:405 +#: AKModel/models.py:426 msgid "Type" msgstr "Art" -#: AKModel/models.py:406 +#: AKModel/models.py:427 msgid "Type of violation, i.e. what kind of constraint was violated" msgstr "Art der Verletzung, gibt an welche Art Constraint verletzt wurde" -#: AKModel/models.py:407 +#: AKModel/models.py:428 msgid "Level" msgstr "Level" -#: AKModel/models.py:408 +#: AKModel/models.py:429 msgid "Severity level of the violation" msgstr "Schweregrad der Verletzung" -#: AKModel/models.py:414 +#: AKModel/models.py:435 msgid "AK(s) belonging to this constraint" msgstr "AK(s), die zu diesem Constraint gehören" -#: AKModel/models.py:416 +#: AKModel/models.py:437 msgid "AK Slot(s) belonging to this constraint" msgstr "AK Slot(s), die zu diesem Constraint gehören" -#: AKModel/models.py:418 +#: AKModel/models.py:439 msgid "AK Owner belonging to this constraint" msgstr "AK Leitung(en), die zu diesem Constraint gehören" -#: AKModel/models.py:420 +#: AKModel/models.py:441 msgid "Room belonging to this constraint" msgstr "Raum, der zu diesem Constraint gehört" -#: AKModel/models.py:423 +#: AKModel/models.py:444 msgid "AK Requirement belonging to this constraint" msgstr "AK Anforderung, die zu diesem Constraint gehört" -#: AKModel/models.py:425 +#: AKModel/models.py:446 msgid "AK Category belonging to this constraint" msgstr "AK Kategorie, di zu diesem Constraint gehört" -#: AKModel/models.py:427 +#: AKModel/models.py:448 msgid "Comment" msgstr "Kommentar" -#: AKModel/models.py:427 +#: AKModel/models.py:448 msgid "Comment or further details for this violation" msgstr "Kommentar oder weitere Details zu dieser Vereletzung" -#: AKModel/models.py:430 +#: AKModel/models.py:451 msgid "Timestamp" msgstr "Timestamp" -#: AKModel/models.py:430 +#: AKModel/models.py:451 msgid "Time of creation" msgstr "Zeitpunkt der ERstellung" -#: AKModel/models.py:431 +#: AKModel/models.py:452 msgid "Manually Resolved" msgstr "Manuell behoben" -#: AKModel/models.py:432 +#: AKModel/models.py:453 msgid "Mark this violation manually as resolved" msgstr "Markiere diese Verletzung manuell als behoben" -#: AKModel/models.py:454 +#: AKModel/models.py:475 msgid "Details" msgstr "Details" @@ -717,6 +746,71 @@ msgstr "" msgid "Logout" msgstr "Ausloggen" +#: AKModel/templates/admin/AKModel/event_wizard/activate.html:9 +#: AKModel/templates/admin/AKModel/event_wizard/created_prepare_import.html:9 +#: AKModel/templates/admin/AKModel/event_wizard/finish.html:9 +#: AKModel/templates/admin/AKModel/event_wizard/import.html:9 +#: AKModel/templates/admin/AKModel/event_wizard/settings.html:9 +#: AKModel/templates/admin/AKModel/event_wizard/start.html:8 +#: AKModel/templates/admin/AKModel/event_wizard/wizard_steps.html:3 +msgid "New event wizard" +msgstr "Assistent zum Anlegen eines neuen Events" + +#: AKModel/templates/admin/AKModel/event_wizard/activate.html:18 +msgid "Successfully imported.<br><br>Do you want to activate your event now?" +msgstr "Erfolgreich importiert.<br><br>Soll das Event jetzt aktiviert werden?" + +#: AKModel/templates/admin/AKModel/event_wizard/activate.html:27 +#: AKModel/views.py:193 +msgid "Finish" +msgstr "Abschluss" + +#: AKModel/templates/admin/AKModel/event_wizard/created_prepare_import.html:16 +msgid "New event:" +msgstr "Neues Event:" + +#: AKModel/templates/admin/AKModel/event_wizard/created_prepare_import.html:30 +msgid "Your event was created and can now be further configured." +msgstr "Das Event wurde angelegt und kann nun weiter konfiguriert werden." + +#: AKModel/templates/admin/AKModel/event_wizard/created_prepare_import.html:39 +msgid "Skip Import" +msgstr "Import überspringen" + +#: AKModel/templates/admin/AKModel/event_wizard/created_prepare_import.html:43 +#: AKModel/templates/admin/AKModel/event_wizard/import.html:20 +#: AKModel/templates/admin/AKModel/event_wizard/settings.html:22 +#: AKModel/templates/admin/AKModel/event_wizard/start.html:19 +msgid "Continue" +msgstr "Fortfahren" + +#: AKModel/templates/admin/AKModel/event_wizard/finish.html:18 +msgid "Congratulations. Everything is set up!" +msgstr "Herzlichen Glückwunsch. Alles ist eingerichtet!" + +#: AKModel/templates/admin/AKModel/event_wizard/import.html:24 +#: AKModel/templates/admin/AKModel/event_wizard/settings.html:29 +#: AKModel/templates/admin/AKModel/event_wizard/start.html:23 +#: AKModel/templates/admin/AKModel/message_delete.html:21 +msgid "Cancel" +msgstr "Abbrechen" + +#: AKModel/templates/admin/AKModel/event_wizard/settings.html:26 +msgid "Back" +msgstr "Zurück" + +#: AKModel/templates/admin/AKModel/event_wizard/start.html:13 +msgid "" +"Add a new event. Please start by filling these basic properties. You can " +"specify more settings later." +msgstr "" +"Neues Event anlegen. Bitte zunächst diese Grundeinstellungen ausfüllen, " +"weitere Einstellungen können später gesetzt werden." + +#: AKModel/templates/admin/AKModel/event_wizard/wizard_steps.html:15 +msgid "Step" +msgstr "Schritt" + #: AKModel/templates/admin/AKModel/message_delete.html:7 msgid "Delete Orga-Messages" msgstr "Organachrichten löschen" @@ -739,10 +833,6 @@ msgstr "" msgid "Delete" msgstr "Löschen" -#: AKModel/templates/admin/AKModel/message_delete.html:21 -msgid "Cancel" -msgstr "Abbrechen" - #: AKModel/templates/admin/AKModel/status.html:16 msgid "Categories" msgstr "Kategorien" @@ -810,22 +900,50 @@ msgstr "Alle Nachrichten löschen" msgid "Active Events" msgstr "Aktive Events" -#: AKModel/views.py:130 +#: AKModel/views.py:131 msgid "Event Status" msgstr "Eventstatus" -#: AKModel/views.py:144 +#: AKModel/views.py:145 msgid "AK CSV Export" msgstr "AK-CSV-Export" -#: AKModel/views.py:158 +#: AKModel/views.py:159 msgid "AK Wiki Export" msgstr "AK-Wiki-Export" -#: AKModel/views.py:178 +#: AKModel/views.py:179 msgid "AK Orga Messages successfully deleted" msgstr "AK-Organachrichten erfolgreich gelöscht" +#: AKModel/views.py:189 +msgid "Settings" +msgstr "Einstellungen" + +#: AKModel/views.py:190 +msgid "Event created, Prepare Import" +msgstr "Event angelegt, Import vorbereiten" + +#: AKModel/views.py:191 +msgid "Import categories & requirements" +msgstr "Kategorien & Anforderungen kopieren" + +#: AKModel/views.py:192 +#, fuzzy +#| msgid "Active State" +msgid "Activate?" +msgstr "Aktivieren?" + +#: AKModel/views.py:250 +#, python-format +msgid "Copied '%(obj)s'" +msgstr "'%(obj)s' kopiert" + +#: AKModel/views.py:252 +#, python-format +msgid "Could not copy '%(obj)s' (%(error)s)" +msgstr "'%(obj)s' konnte nicht kopiert werden (%(error)s)" + #~ msgid "Confirm" #~ msgstr "Bestätigen" diff --git a/AKModel/templates/admin/AKModel/event_wizard/activate.html b/AKModel/templates/admin/AKModel/event_wizard/activate.html new file mode 100644 index 00000000..eb460420 --- /dev/null +++ b/AKModel/templates/admin/AKModel/event_wizard/activate.html @@ -0,0 +1,36 @@ +{% extends "admin/base_site.html" %} +{% load tags_AKModel %} + +{% load i18n %} +{% load bootstrap4 %} +{% load fontawesome_5 %} +{% load tz %} + +{% block title %}{% trans "New event wizard" %}: {{ wizard_step_text }}{% endblock %} + +{% block content %} + {% include "admin/AKModel/event_wizard/wizard_steps.html" %} + + <div class="text-center btn-success disabled mt-3 mb-3" style="font-size: 8em;"> + {% fa5_icon "copy" "fas" %} + </div> + + <h5 class="mb-3">{% trans "Successfully imported.<br><br>Do you want to activate your event now?" %}</h5> + + {{ form.media }} + + <form method="post">{% csrf_token %} + {% bootstrap_form form %} + + <div class="float-right"> + <button type="submit" class="save btn btn-success" value="Submit"> + {% fa5_icon "check" 'fas' %} {% trans "Finish" %} + </button> + </div> + + <a href="{% url 'admin:event_status' event.slug %}" class="btn btn-info"> + {% fa5_icon "info" 'fas' %} {% trans "Status" %} + </a> + </form> + +{% endblock %} diff --git a/AKModel/templates/admin/AKModel/event_wizard/created_prepare_import.html b/AKModel/templates/admin/AKModel/event_wizard/created_prepare_import.html new file mode 100644 index 00000000..e1fc7165 --- /dev/null +++ b/AKModel/templates/admin/AKModel/event_wizard/created_prepare_import.html @@ -0,0 +1,52 @@ +{% extends "admin/base_site.html" %} +{% load tags_AKModel %} + +{% load i18n %} +{% load bootstrap4 %} +{% load fontawesome_5 %} +{% load tz %} + +{% block title %}{% trans "New event wizard" %}: {{ wizard_step_text }}{% endblock %} + +{% block content %} + {% include "admin/AKModel/event_wizard/wizard_steps.html" %} + + {% timezone event.timezone %} + <div class="card border-success mt-3 mb-3" style="max-width: 100%;"> + <div class="card-header">{% trans "New event:" %}</div> + <div class="card-body"> + <h4 class="card-title">{{event}}</h4> + <p class="card-text">{{ event.start }} - {{ event.end }}</p> + </div> + </div> + {% endtimezone %} + + <div class="text-center btn-success disabled mb-3" style="font-size: 8em;"> + {% fa5_icon "calendar-plus" "fas" %} + </div> + + + + <h5 class="mb-3">{% trans "Your event was created and can now be further configured." %}</h5> + + {{ form.media }} + + <form method="post">{% csrf_token %} + {% bootstrap_form form %} + + <div class="float-right"> + <a href="{% url 'admin:new_event_wizard_activate' event.slug %}" class="btn btn-info"> + {% fa5_icon "forward" 'fas' %} {% trans "Skip Import" %} + </a> + + <button type="submit" class="save btn btn-success" value="Submit"> + {% fa5_icon "check" 'fas' %} {% trans "Continue" %} + </button> + </div> + + <a href="{% url 'admin:event_status' event.slug %}" class="btn btn-info"> + {% fa5_icon "info" 'fas' %} {% trans "Status" %} + </a> + </form> + +{% endblock %} diff --git a/AKModel/templates/admin/AKModel/event_wizard/finish.html b/AKModel/templates/admin/AKModel/event_wizard/finish.html new file mode 100644 index 00000000..16d3090b --- /dev/null +++ b/AKModel/templates/admin/AKModel/event_wizard/finish.html @@ -0,0 +1,24 @@ +{% extends "admin/base_site.html" %} +{% load tags_AKModel %} + +{% load i18n %} +{% load bootstrap4 %} +{% load fontawesome_5 %} +{% load tz %} + +{% block title %}{% trans "New event wizard" %}: {{ wizard_step_text }}{% endblock %} + +{% block content %} + {% include "admin/AKModel/event_wizard/wizard_steps.html" %} + + <div class="text-center btn-success disabled mt-3 mb-3" style="font-size: 8em;"> + {% fa5_icon "check-circle" "fas" %} + </div> + + <h5>{% trans "Congratulations. Everything is set up!" %}</h5> + + <a href="{% url 'admin:event_status' event.slug %}" class="btn btn-info float-right"> + {% fa5_icon "info" 'fas' %} {% trans "Status" %} + </a> + +{% endblock %} diff --git a/AKModel/templates/admin/AKModel/event_wizard/import.html b/AKModel/templates/admin/AKModel/event_wizard/import.html new file mode 100644 index 00000000..d899a2d0 --- /dev/null +++ b/AKModel/templates/admin/AKModel/event_wizard/import.html @@ -0,0 +1,28 @@ +{% extends "admin/base_site.html" %} +{% load tags_AKModel %} + +{% load i18n %} +{% load bootstrap4 %} +{% load fontawesome_5 %} +{% load tz %} + +{% block title %}{% trans "New event wizard" %}: {{ wizard_step_text }}{% endblock %} + +{% block content %} + {% include "admin/AKModel/event_wizard/wizard_steps.html" %} + + {{ form.media }} + + <form method="post">{% csrf_token %} + {% bootstrap_form form %} + + <button type="submit" class="save btn btn-success float-right" value="Submit"> + {% fa5_icon "check" 'fas' %} {% trans "Continue" %} + </button> + + <a href="{% url 'admin:index' %}" class="btn btn-info"> + {% fa5_icon "times" 'fas' %} {% trans "Cancel" %} + </a> + </form> + +{% endblock %} diff --git a/AKModel/templates/admin/AKModel/event_wizard/settings.html b/AKModel/templates/admin/AKModel/event_wizard/settings.html new file mode 100644 index 00000000..0b306d7b --- /dev/null +++ b/AKModel/templates/admin/AKModel/event_wizard/settings.html @@ -0,0 +1,34 @@ +{% extends "admin/base_site.html" %} +{% load tags_AKModel %} + +{% load i18n %} +{% load bootstrap4 %} +{% load fontawesome_5 %} +{% load tz %} + +{% block title %}{% trans "New event wizard" %}: {{ wizard_step_text }}{% endblock %} + +{% block content %} + {% include "admin/AKModel/event_wizard/wizard_steps.html" %} + + {{ form.media }} + + {% timezone timezone %} + + <form method="post">{% csrf_token %} + {% bootstrap_form form %} + + <button type="submit" class="save btn btn-success float-right" value="Submit"> + {% fa5_icon "check" 'fas' %} {% trans "Continue" %} + </button> + + <a href="{% url 'admin:new_event_wizard_start' %}" class="btn btn-info"> + {% fa5_icon "chevron-left" 'fas' %} {% trans "Back" %} + </a> + <a href="{% url 'admin:index' %}" class="btn btn-warning"> + {% fa5_icon "times" 'fas' %} {% trans "Cancel" %} + </a> + </form> + + {% endtimezone %} +{% endblock %} diff --git a/AKModel/templates/admin/AKModel/event_wizard/start.html b/AKModel/templates/admin/AKModel/event_wizard/start.html new file mode 100644 index 00000000..762cadb5 --- /dev/null +++ b/AKModel/templates/admin/AKModel/event_wizard/start.html @@ -0,0 +1,26 @@ +{% extends "admin/base_site.html" %} +{% load tags_AKModel %} + +{% load i18n %} +{% load bootstrap4 %} +{% load fontawesome_5 %} + +{% block title %}{% trans "New event wizard" %}: {{ wizard_step_text }}{% endblock %} + +{% block content %} + {% include "admin/AKModel/event_wizard/wizard_steps.html" %} + + {% trans "Add a new event. Please start by filling these basic properties. You can specify more settings later." %} + + <form method="post" action="{% url 'admin:new_event_wizard_settings' %}">{% csrf_token %} + {% bootstrap_form form %} + + <button type="submit" class="save btn btn-success float-right" value="Submit"> + {% fa5_icon "check" 'fas' %} {% trans "Continue" %} + </button> + + <a href="{% url 'admin:index' %}" class="btn btn-info"> + {% fa5_icon "times" 'fas' %} {% trans "Cancel" %} + </a> + </form> +{% endblock %} diff --git a/AKModel/templates/admin/AKModel/event_wizard/wizard_steps.html b/AKModel/templates/admin/AKModel/event_wizard/wizard_steps.html new file mode 100644 index 00000000..a006f3d3 --- /dev/null +++ b/AKModel/templates/admin/AKModel/event_wizard/wizard_steps.html @@ -0,0 +1,15 @@ +{% load i18n %} + +<h2>{% trans "New event wizard" %}</h2> + +<div> + <ul class="pagination pagination-sm"> + {% for step in wizard_steps %} + <li class="page-item {% if forloop.counter == wizard_step %}active{% else %}disabled{% endif %}"> + <a class="page-link" href="#">{{ step }}</a> + </li> + {% endfor %} + </ul> +</div> + +<h3>{% trans "Step" %} {{ wizard_step }}: {{ wizard_step_text }}</h3> diff --git a/AKModel/views.py b/AKModel/views.py index d560e9f6..49269dad 100644 --- a/AKModel/views.py +++ b/AKModel/views.py @@ -1,12 +1,13 @@ from django.contrib import admin, messages from django.http import HttpResponseRedirect -from django.shortcuts import get_object_or_404 +from django.shortcuts import get_object_or_404, redirect from django.urls import reverse_lazy from django.utils.translation import gettext_lazy as _ - -from django.views.generic import TemplateView, DetailView, ListView, DeleteView +from django.views.generic import TemplateView, DetailView, ListView, DeleteView, CreateView, FormView, UpdateView from rest_framework import viewsets, permissions, mixins +from AKModel.forms import NewEventWizardStartForm, NewEventWizardSettingsForm, NewEventWizardPrepareImportForm, \ + NewEventWizardImportForm, NewEventWizardActivateForm from AKModel.models import Event, AK, AKSlot, Room, AKTrack, AKCategory, AKOwner, AKOrgaMessage from AKModel.serializers import AKSerializer, AKSlotSerializer, RoomSerializer, AKTrackSerializer, AKCategorySerializer, \ AKOwnerSerializer @@ -177,3 +178,92 @@ class AKMessageDeleteView(AdminViewMixin, DeleteView): self.get_orga_messages_for_event(self.get_object()).delete() messages.add_message(self.request, messages.SUCCESS, _("AK Orga Messages successfully deleted")) return HttpResponseRedirect(reverse_lazy('admin:event_status', kwargs={'slug': self.get_object().slug})) + + +class WizardViewMixin: + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + context["wizard_step"] = self.wizard_step + context["wizard_steps"] = [ + _("Start"), + _("Settings"), + _("Event created, Prepare Import"), + _("Import categories & requirements"), + _("Activate?"), + _("Finish") + ] + context["wizard_step_text"] = context["wizard_steps"][self.wizard_step - 1] + context["wizard_steps_total"] = len(context["wizard_steps"]) + return context + + +class NewEventWizardStartView(AdminViewMixin, WizardViewMixin, CreateView): + model = Event + form_class = NewEventWizardStartForm + template_name = "admin/AKModel/event_wizard/start.html" + wizard_step = 1 + + +class NewEventWizardSettingsView(AdminViewMixin, WizardViewMixin, CreateView): + model = Event + form_class = NewEventWizardSettingsForm + template_name = "admin/AKModel/event_wizard/settings.html" + wizard_step = 2 + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + context["timezone"] = context["form"].cleaned_data["timezone"] + return context + + def get_success_url(self): + return reverse_lazy("admin:new_event_wizard_prepare_import", kwargs={"event_slug": self.object.slug}) + + +class NewEventWizardPrepareImportView(WizardViewMixin, EventSlugMixin, FormView): + form_class = NewEventWizardPrepareImportForm + template_name = "admin/AKModel/event_wizard/created_prepare_import.html" + wizard_step = 3 + + def form_valid(self, form): + # Selected a valid event to import from? Use this to go to next step of wizard + return redirect("admin:new_event_wizard_import", event_slug=self.event.slug, import_slug=form.cleaned_data["import_event"].slug) + + +class NewEventWizardImportView(EventSlugMixin, WizardViewMixin, FormView): + form_class = NewEventWizardImportForm + template_name = "admin/AKModel/event_wizard/import.html" + wizard_step = 4 + + def get_initial(self): + initial = super().get_initial() + initial["import_event"] = Event.objects.get(slug=self.kwargs["import_slug"]) + return initial + + def form_valid(self, form): + for import_type in ["import_categories", "import_requirements"]: + for import_obj in form.cleaned_data.get(import_type): + # clone existing entry + try: + import_obj.event = self.event + import_obj.pk = None + import_obj.save() + messages.add_message(self.request, messages.SUCCESS, _("Copied '%(obj)s'" % {'obj': import_obj})) + except BaseException as e: + messages.add_message(self.request, messages.ERROR, _("Could not copy '%(obj)s' (%(error)s)" % {'obj': import_obj, "error": str(e)})) + return redirect("admin:new_event_wizard_activate", slug=self.event.slug) + + +class NewEventWizardActivateView(WizardViewMixin, UpdateView): + model = Event + template_name = "admin/AKModel/event_wizard/activate.html" + form_class = NewEventWizardActivateForm + wizard_step = 5 + + def get_success_url(self): + return reverse_lazy("admin:new_event_wizard_finish", kwargs={"slug": self.object.slug}) + + +class NewEventWizardFinishView(WizardViewMixin, DetailView): + model = Event + template_name = "admin/AKModel/event_wizard/finish.html" + wizard_step = 6 diff --git a/locale/de_DE/LC_MESSAGES/django.po b/locale/de_DE/LC_MESSAGES/django.po index 2f8b8562..25b6fc42 100644 --- a/locale/de_DE/LC_MESSAGES/django.po +++ b/locale/de_DE/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-11-03 17:40+0000\n" +"POT-Creation-Date: 2021-02-16 14:42+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" @@ -38,27 +38,27 @@ msgstr "Virtuelle Räume" msgid "Scheduling for" msgstr "Scheduling für" -#: AKScheduling/templates/admin/AKScheduling/scheduling.html:94 +#: AKScheduling/templates/admin/AKScheduling/scheduling.html:87 msgid "Day (Horizontal)" msgstr "Tag (horizontal)" -#: AKScheduling/templates/admin/AKScheduling/scheduling.html:101 +#: AKScheduling/templates/admin/AKScheduling/scheduling.html:94 msgid "Day (Vertical)" msgstr "Tag (vertikal)" -#: AKScheduling/templates/admin/AKScheduling/scheduling.html:112 +#: AKScheduling/templates/admin/AKScheduling/scheduling.html:105 msgid "Event (Horizontal)" msgstr "Event (horizontal)" -#: AKScheduling/templates/admin/AKScheduling/scheduling.html:121 +#: AKScheduling/templates/admin/AKScheduling/scheduling.html:114 msgid "Event (Vertical)" msgstr "Event (vertikal)" -#: AKScheduling/templates/admin/AKScheduling/scheduling.html:148 +#: AKScheduling/templates/admin/AKScheduling/scheduling.html:141 msgid "Room" msgstr "Raum" -#: AKScheduling/templates/admin/AKScheduling/scheduling.html:204 +#: AKScheduling/templates/admin/AKScheduling/scheduling.html:197 #: AKScheduling/templates/admin/AKScheduling/unscheduled.html:34 msgid "Event Status" msgstr "Event-Status" -- GitLab