diff --git a/input/forms.py b/input/forms.py index 32d40d6..cdfa6ba 100755 --- a/input/forms.py +++ b/input/forms.py @@ -1,9 +1,10 @@ from django.conf import settings -from django.forms import ModelForm, ChoiceField, RadioSelect, BooleanField +from django.forms import ModelForm, ChoiceField, RadioSelect, BooleanField, CharField, EmailField from django.contrib.admin.widgets import AdminDateWidget from django.forms.renderers import DjangoTemplates from django.utils.html import format_html from django.utils.safestring import mark_safe +from django import forms from .models import ( TYPE_CHOICES, @@ -33,20 +34,61 @@ class FdbForm(ModelForm): class ProjectForm(FdbForm): - # start = DateField(widget=AdminDateWidget()) class Meta: model = Project - exclude = ('pid', 'project_of_year', 'finance_id','granted', 'granted_date', 'realname', 'email',\ + exclude = ('pid', 'project_of_year', 'finance_id', 'granted', 'granted_date', 'realname', 'email', \ 'end_mail_send', 'status', 'persons', 'survey_mail_date', 'mail_state') widgets = {'start': AdminDateWidget(), - 'end': AdminDateWidget(),} + 'end': AdminDateWidget(), } class Media: js = ('dropdown/js/otrs_link.js',) +class CommonOrderMixin(forms.Form): + """ + Ensures a consistent field order for all forms that inherit from this mixin. + + The goal is to always render: + - "realname" and "email" fields at the top, + - all other fields in the middle (in the order Django provides), + - "check" (terms/privacy checkbox) at the bottom. + + This keeps the UX consistent across all forms without repeating field_order logic. + """ + + # Fields that should always appear first in the form + field_order_head = ('realname', 'email') + + # Fields that should always appear last in the form + field_order_tail = ('check',) # rename if your checkbox field is called differently + + def __init__(self, *args, **kwargs): + """ + Override the default form initialization to reorder fields. + The method: + 1. Collects all existing field names. + 2. Puts the 'head' fields first (if they exist in this form). + 3. Keeps all other fields in the middle. + 4. Puts the 'tail' fields last (if they exist in this form). + """ + super().__init__(*args, **kwargs) + + existing = list(self.fields.keys()) + + # Select only fields that actually exist in this form + head = [f for f in self.field_order_head if f in self.fields] + tail = [f for f in self.field_order_tail if f in self.fields] + + # All other fields that are not explicitly in head or tail + middle = [f for f in existing if f not in (*head, *tail)] + + # Apply the new order to the form fields + self.order_fields(head + middle + tail) + + class ExternForm(FdbForm): choice = ChoiceField(choices=TYPE_CHOICES.items(), widget=RadioSelect, @@ -75,11 +117,37 @@ class InternForm(FdbForm): HOTEL_CHOICES = {'TRUE': mark_safe('Hotelzimmer benötigt'), - 'FALSE': mark_safe('Kein Hotelzimmer benötigt') - } + 'FALSE': mark_safe('Kein Hotelzimmer benötigt') + } -class TravelForm(FdbForm): +class BaseApplicationForm(FdbForm): + """ + Base form for all external applications. + + - Adds standard fields that must appear in every form: + * realname (applicant's full name) + * email (contact address for notifications) + * check (confirmation of data protection and funding policy) + - Ensures consistency across all application types. + """ + realname = CharField( + label="Realname", + required=True, + help_text="Bitte gib deinen Vor- und Nachnamen ein." + ) + email = EmailField( + label="E-Mail-Adresse", + required=True, + help_text="Bitte gib deine E-Mail-Adresse ein, damit dich
Wikimedia Deutschland bei Rückfragen oder für
die Zusage kontaktieren kann." + ) + check = BooleanField(required=True, + label=format_html( + "Ich stimme den Datenschutzbestimmungen und der
Richtlinie zur Förderung der Communitys zu", + settings.DATAPROTECTION, settings.FOERDERRICHTLINIEN)) + + +class TravelForm(BaseApplicationForm, CommonOrderMixin): # TODO: add some javascript to show/hide other-field # this is the code, to change required to false if needed @@ -107,7 +175,7 @@ class TravelForm(FdbForm): } -class LibraryForm(FdbForm): +class LibraryForm(BaseApplicationForm, CommonOrderMixin): class Meta: model = Library @@ -144,7 +212,7 @@ class HonoraryCertificateForm(FdbForm): js = ('dropdown/js/otrs_link.js',) -class IFGForm(FdbForm): +class IFGForm(BaseApplicationForm, CommonOrderMixin): class Meta: model = IFG fields = ['cost', 'url', 'notes'] @@ -174,7 +242,7 @@ class CheckForm(FdbForm): # NUTZUNGSBEDINGUNGEN)) -class LiteratureForm(CheckForm): +class LiteratureForm(BaseApplicationForm, CommonOrderMixin): termstoaccept = settings.NUTZUNGSBEDINGUNGEN_LITERATURSTIPENDIUM def __init__(self, *args, **kwargs): @@ -192,7 +260,7 @@ ADULT_CHOICES = {'TRUE': mark_safe('Ich bin volljährig.'), } -class EmailForm(CheckForm): +class EmailForm(BaseApplicationForm, CommonOrderMixin): termstoaccept = settings.NUTZUNGSBEDINGUNGEN_EMAIL_SERVICE @@ -216,7 +284,7 @@ class EmailForm(CheckForm): -class BusinessCardForm(CheckForm): +class BusinessCardForm(BaseApplicationForm, CommonOrderMixin): termstoaccept = settings.NUTZUNGSBEDINGUNGEN_VISITENKARTEN # this is the code, to change required to false if needed def __init__(self, *args, **kwargs): @@ -232,7 +300,7 @@ class BusinessCardForm(CheckForm): js = ('dropdown/js/businessCard.js',) -class ListForm(CheckForm): +class ListForm(BaseApplicationForm, CommonOrderMixin): termstoaccept = settings.NUTZUNGSBEDINGUNGEN_MAILINGLISTEN class Meta: model = List