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