diff --git a/input/forms.py b/input/forms.py
index bdc71ee..8c2b6a5 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']
@@ -168,7 +236,7 @@ class CheckForm(FdbForm):
)
-class LiteratureForm(CheckForm):
+class LiteratureForm(BaseApplicationForm, CommonOrderMixin):
termstoaccept = settings.NUTZUNGSBEDINGUNGEN_LITERATURSTIPENDIUM
def __init__(self, *args, **kwargs):
@@ -186,7 +254,7 @@ ADULT_CHOICES = {'TRUE': mark_safe('Ich bin volljährig.'),
}
-class EmailForm(CheckForm):
+class EmailForm(BaseApplicationForm, CommonOrderMixin):
termstoaccept = settings.NUTZUNGSBEDINGUNGEN_EMAIL_SERVICE
@@ -210,7 +278,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):
@@ -226,7 +294,7 @@ class BusinessCardForm(CheckForm):
js = ('dropdown/js/businessCard.js',)
-class ListForm(CheckForm):
+class ListForm(BaseApplicationForm, CommonOrderMixin):
termstoaccept = settings.NUTZUNGSBEDINGUNGEN_MAILINGLISTEN
class Meta:
model = List
diff --git a/input/static/css/forms.css b/input/static/css/forms.css
new file mode 100644
index 0000000..548ddc7
--- /dev/null
+++ b/input/static/css/forms.css
@@ -0,0 +1,11 @@
+.star {
+ color: red;
+}
+
+.wm-table {
+ margin: 0 auto;
+}
+
+.col-request {
+ width: 40%;
+}
\ No newline at end of file
diff --git a/input/templates/input/base.html b/input/templates/input/base.html
new file mode 100644
index 0000000..7a08389
--- /dev/null
+++ b/input/templates/input/base.html
@@ -0,0 +1,37 @@
+{% load static %}
+
+
+
Projektförderung mit einer Gesamtsumme ab 1.000,— EUR
+
Vielen Dank für dein Interesse an einer Projektförderung!
+ Für Projektförderungen mit einer Gesamtsumme ab 1.000,— EUR ist ein öffentlicher Projektplan
+ erforderlich. Weitere Informationen zu diesem Prozess findest du unter
+
+ Wikipedia:Förderung/Projektplanung.
+ Für Fragen steht dir das Team Community-Konferenzen & Förderung gern unter
+ community@wikimedia.de zur Verfügung.
+