2025-10-15 12:46:28 +00:00
from django import forms
2025-08-19 07:45:14 +00:00
from django . conf import settings
2020-09-29 10:16:10 +00:00
from django . contrib . admin . widgets import AdminDateWidget
2025-10-15 12:46:28 +00:00
from django . forms import ModelForm , ChoiceField , RadioSelect , BooleanField , CharField , EmailField
2025-08-20 12:28:46 +00:00
from django . forms . renderers import DjangoTemplates
2020-10-20 12:12:12 +00:00
from django . utils . html import format_html
2025-08-19 14:08:48 +00:00
from django . utils . safestring import mark_safe
2025-10-15 12:46:28 +00:00
2025-08-20 10:06:43 +00:00
from . models import (
TYPE_CHOICES ,
Project ,
2025-10-16 10:16:08 +00:00
ProjectCategory ,
WikimediaProject ,
2025-08-20 10:06:43 +00:00
ConcreteVolunteer ,
ConcreteExtern ,
IFG ,
Library ,
ELiterature ,
Software ,
HonoraryCertificate ,
Travel ,
Email ,
Literature ,
List ,
BusinessCard ,
)
2020-09-29 07:54:31 +00:00
2020-11-17 15:11:10 +00:00
2025-08-20 12:28:46 +00:00
class TableFormRenderer ( DjangoTemplates ) :
form_template_name = ' django/forms/table.html '
2020-11-17 15:11:10 +00:00
class FdbForm ( ModelForm ) :
''' this base class provides the required css class for all forms '''
required_css_class = ' required '
2025-08-31 21:38:22 +00:00
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 .
2025-10-14 09:15:50 +00:00
2025-08-31 21:38:22 +00:00
The method :
2025-10-14 09:15:50 +00:00
1. Put the ' head ' fields first .
2. Put all other fields in the middle .
3. Put the ' tail ' fields last .
2025-08-31 21:38:22 +00:00
2025-10-14 09:15:50 +00:00
Non - existing fields are ignored by ` order_fields ` .
"""
2025-08-31 21:38:22 +00:00
2025-10-14 09:15:50 +00:00
super ( ) . __init__ ( * args , * * kwargs )
2025-08-31 21:38:22 +00:00
2025-10-14 09:15:50 +00:00
ordered = { * self . field_order_head , * self . field_order_tail }
2025-08-31 21:38:22 +00:00
2025-10-14 09:15:50 +00:00
self . order_fields ( [
* self . field_order_head ,
* [ field for field in self . fields if field not in ordered ] ,
* self . field_order_tail ,
] )
2025-08-31 21:38:22 +00:00
2020-11-17 15:11:10 +00:00
class ExternForm ( FdbForm ) :
2020-10-21 11:27:05 +00:00
choice = ChoiceField ( choices = TYPE_CHOICES . items ( ) , widget = RadioSelect ,
2020-10-19 12:46:58 +00:00
label = ' Was möchtest Du beantragen? ' )
2020-10-01 08:51:19 +00:00
2020-10-20 12:12:12 +00:00
check = BooleanField ( required = True ,
2025-06-16 09:43:08 +00:00
label = format_html ( " Ich stimme den <a href= ' {} ' target= ' _blank ' rel= ' noopener ' >Datenschutzbestimmungen</a> und der<br> <a href= ' {} ' target= ' _blank ' rel= ' noopener ' >Richtlinie zur Förderung der Communitys</a> zu " ,
2025-08-19 07:45:14 +00:00
settings . DATAPROTECTION , settings . FOERDERRICHTLINIEN ) )
2020-10-20 11:16:03 +00:00
2020-10-01 08:51:19 +00:00
class Meta :
2021-07-06 11:00:34 +00:00
model = ConcreteExtern
2023-10-25 15:50:40 +00:00
exclude = ( ' username ' , ' granted ' , ' granted_date ' , ' survey_mail_send ' , ' service_id ' , ' survey_mail_date ' , ' mail_state ' )
2020-10-01 08:51:19 +00:00
2023-02-27 17:09:29 +00:00
2025-10-15 12:46:28 +00:00
INTERN_CHOICES = {
' PRO ' : ' Projektsteckbrief ' ,
' HON ' : ' Ehrenamtsbescheinigung, Akkreditierung oder Redaktionsbestätigung ' ,
' TRAV ' : ' Reisekostenerstattung ' ,
}
2020-10-21 07:54:12 +00:00
2020-11-17 15:11:10 +00:00
class InternForm ( FdbForm ) :
2025-10-15 12:46:28 +00:00
choice = ChoiceField ( choices = INTERN_CHOICES . items ( ) , widget = RadioSelect ,
label = ' Was möchtest Du eingeben? ' )
2020-10-21 07:54:12 +00:00
class Meta :
2021-07-07 07:42:51 +00:00
model = ConcreteVolunteer
2023-02-27 17:09:29 +00:00
exclude = ( ' granted ' , ' granted_date ' , ' survey_mail_send ' , ' survey_mail_date ' , ' mail_state ' )
2020-10-21 07:54:12 +00:00
2023-02-27 17:09:29 +00:00
2025-08-31 21:38:22 +00:00
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 (
2025-09-01 11:24:59 +00:00
label = ' Realname ' ,
2025-08-31 21:38:22 +00:00
required = True ,
2025-09-01 11:24:59 +00:00
help_text = ' Bitte gib deinen Vor- und Nachnamen ein. '
2025-08-31 21:38:22 +00:00
)
email = EmailField (
2025-09-01 11:24:59 +00:00
label = ' E-Mail-Adresse ' ,
2025-08-31 21:38:22 +00:00
required = True ,
2025-09-01 11:24:59 +00:00
help_text = ' Bitte gib deine E-Mail-Adresse ein, damit dich <br> Wikimedia Deutschland bei Rückfragen oder für <br> die Zusage kontaktieren kann. '
2025-08-31 21:38:22 +00:00
)
check = BooleanField ( required = True ,
label = format_html (
" Ich stimme den <a href= ' {} ' target= ' _blank ' rel= ' noopener ' >Datenschutzbestimmungen</a> und der<br> <a href= ' {} ' target= ' _blank ' rel= ' noopener ' >Richtlinie zur Förderung der Communitys</a> zu " ,
settings . DATAPROTECTION , settings . FOERDERRICHTLINIEN ) )
2023-02-27 17:09:29 +00:00
2025-10-16 13:38:41 +00:00
PROJECT_COST_GT_1000_MESSAGE = format_html (
""" Bitte beachte, dass für Projektkosten über 1.000 € ein öffentlicher Projektplan erforderlich
ist ( siehe < a href = " {0} " target = " blank_ " > Wikipedia : Förderung / Projektplanung ) < / a > . """ ,
' https://de.wikipedia.org/wiki/Wikipedia:F % C3 % B6rderung/Projektplanung '
)
2025-10-16 10:16:08 +00:00
class BaseProjectForm ( ModelForm ) :
categories = {
' categories ' : ProjectCategory ,
' wikimedia_projects ' : WikimediaProject ,
}
class Media :
js = ( ' dropdown/js/otrs_link.js ' , ' js/project-categories.js ' )
def clean ( self ) :
cleaned_data = ModelForm . clean ( self )
if self . errors :
return cleaned_data
for field , model in self . categories . items ( ) :
field_other = f ' { field } _other '
values = cleaned_data [ field ]
if model . other in values :
if not cleaned_data [ field_other ] :
2025-10-16 15:00:40 +00:00
self . add_error ( field_other , f ' Bitte gib einen Wert an oder deselektiere " { model . OTHER } " . ' )
2025-10-16 10:16:08 +00:00
else :
cleaned_data [ field_other ] = ' '
return cleaned_data
class ProjectForm ( CommonOrderMixin , BaseProjectForm , BaseApplicationForm ) :
OPTIONAL_FIELDS = {
' categories_other ' ,
' wikimedia_projects_other ' ,
' page ' ,
' group ' ,
' location ' ,
' insurance ' ,
' notes ' ,
}
def __init__ ( self , * args , * * kwargs ) :
super ( ) . __init__ ( * args , * * kwargs )
for field in set ( self . fields ) - self . OPTIONAL_FIELDS :
self . fields [ field ] . required = True
class Meta :
model = Project
fields = [
' name ' ,
' description ' ,
' categories ' ,
' categories_other ' ,
' wikimedia_projects ' ,
' wikimedia_projects_other ' ,
' start ' ,
' end ' ,
' participants_estimated ' ,
' page ' ,
' group ' ,
' location ' ,
' cost ' ,
' insurance ' ,
' notes ' ,
]
widgets = {
' start ' : AdminDateWidget ,
' end ' : AdminDateWidget ,
}
class Media :
css = {
' all ' : ( ' css/dateFieldNoNowShortcutInTravels.css ' , )
}
2025-10-16 13:38:41 +00:00
def clean_cost ( self ) :
cost = self . cleaned_data [ ' cost ' ]
if cost > 1000 :
raise forms . ValidationError ( PROJECT_COST_GT_1000_MESSAGE , code = ' cost-gt-1000 ' )
return cost
2025-10-16 10:16:08 +00:00
2025-10-15 12:46:28 +00:00
HOTEL_CHOICES = {
' TRUE ' : mark_safe ( ' Hotelzimmer benötigt ' ) ,
' FALSE ' : mark_safe ( ' Kein Hotelzimmer benötigt ' ) ,
}
2025-08-31 21:38:22 +00:00
class TravelForm ( BaseApplicationForm , CommonOrderMixin ) :
2020-11-03 11:56:40 +00:00
# TODO: add some javascript to show/hide other-field
2025-08-19 12:10:15 +00:00
2025-10-15 12:46:28 +00:00
hotel = ChoiceField ( label = ' Hotelzimmer benötigt: ' , choices = HOTEL_CHOICES . items ( ) , widget = RadioSelect ( ) )
2023-02-27 17:09:29 +00:00
# this is the code, to change required to false if needed
def __init__ ( self , * args , * * kwargs ) :
super ( ) . __init__ ( * args , * * kwargs )
self . fields [ ' project_name ' ] . required = True
self . fields [ ' transport ' ] . required = True
self . fields [ ' travelcost ' ] . required = True
self . fields [ ' checkin ' ] . required = True
self . fields [ ' checkout ' ] . required = True
2023-02-27 17:09:29 +00:00
self . fields [ ' hotel ' ] . required = True
2023-02-27 17:09:29 +00:00
2020-10-26 10:38:56 +00:00
class Meta :
model = Travel
2023-02-27 17:09:29 +00:00
fields = [ ' project_name ' , ' transport ' , ' travelcost ' , ' checkin ' , ' checkout ' , ' hotel ' , ' notes ' ]
2025-10-15 12:46:28 +00:00
exclude = ( ' granted ' , ' granted_date ' , ' survey_mail_send ' , ' realname ' , ' email ' , ' survey_mail_date ' , ' project ' ,
' request_url ' , ' payed_for_hotel_by ' , ' payed_for_travel_by ' , ' intern_notes ' , ' mail_state ' )
widgets = {
' checkin ' : AdminDateWidget ,
' checkout ' : AdminDateWidget ,
}
2020-10-21 07:54:12 +00:00
2023-02-27 17:09:29 +00:00
class Media :
2025-02-26 12:13:46 +00:00
js = ( ' dropdown/js/otrs_link.js ' , )
2023-02-27 17:09:29 +00:00
css = {
2025-10-15 12:46:28 +00:00
' all ' : ( ' css/dateFieldNoNowShortcutInTravels.css ' , )
2023-02-27 17:09:29 +00:00
}
2025-08-20 10:06:43 +00:00
2025-08-31 21:38:22 +00:00
class LibraryForm ( BaseApplicationForm , CommonOrderMixin ) :
2020-10-19 11:29:36 +00:00
2020-10-01 08:51:19 +00:00
class Meta :
model = Library
2023-02-27 17:09:29 +00:00
fields = [ ' cost ' , ' library ' , ' duration ' , ' notes ' , ' survey_mail_send ' ]
2023-02-27 17:09:29 +00:00
exclude = [ ' intern_notes ' , ' survey_mail_send ' , ' mail_state ' ]
2020-10-01 08:51:19 +00:00
2025-08-20 10:06:43 +00:00
def __init__ ( self , * args , * * kwargs ) :
super ( ) . __init__ ( * args , * * kwargs )
self . fields [ ' library ' ] . label = self . _meta . model . LIBRARY_LABEL
self . fields [ ' library ' ] . help_text = self . _meta . model . LIBRARY_HELP_TEXT
self . fields [ ' duration ' ] . help_text = self . _meta . model . DURATION_HELP_TEXT
class ELiteratureForm ( LibraryForm ) :
class Meta ( LibraryForm . Meta ) :
model = ELiterature
class SoftwareForm ( LibraryForm ) :
class Meta ( LibraryForm . Meta ) :
model = Software
2020-11-18 17:02:23 +00:00
class HonoraryCertificateForm ( FdbForm ) :
2025-08-19 12:10:15 +00:00
2020-11-18 17:02:23 +00:00
class Meta :
model = HonoraryCertificate
fields = [ ' request_url ' , ' project ' ]
2023-02-27 17:09:29 +00:00
exclude = [ ' intern_notes ' ]
2025-10-15 12:46:28 +00:00
2025-02-27 09:15:25 +00:00
class Media :
js = ( ' dropdown/js/otrs_link.js ' , )
2025-08-19 12:10:15 +00:00
2020-11-18 17:02:23 +00:00
2025-08-31 21:38:22 +00:00
class IFGForm ( BaseApplicationForm , CommonOrderMixin ) :
2020-10-01 08:51:19 +00:00
class Meta :
2020-10-01 10:08:02 +00:00
model = IFG
2020-11-02 14:41:08 +00:00
fields = [ ' cost ' , ' url ' , ' notes ' ]
2023-02-27 17:09:29 +00:00
exclude = [ ' intern_notes ' , ' survey_mail_send ' , ' mail_state ' ]
2020-10-21 07:54:12 +00:00
2020-10-27 10:00:58 +00:00
2020-11-18 17:06:00 +00:00
class CheckForm ( FdbForm ) :
2025-08-19 07:45:14 +00:00
termstoaccept = settings . NUTZUNGSBEDINGUNGEN
2025-06-16 09:43:08 +00:00
def __init__ ( self , * args , * * kwargs ) :
super ( ) . __init__ ( * args , * * kwargs )
2025-08-26 20:48:13 +00:00
# Check if the model field 'terms_accepted' is present
if ' terms_accepted ' in self . fields :
# Make the field required (HTML5 validation)
self . fields [ ' terms_accepted ' ] . required = True
# Set custom label with link to terms
self . fields [ ' terms_accepted ' ] . label = format_html (
2025-06-16 09:43:08 +00:00
" Ich stimme den <a href= ' {} ' >Nutzungsbedingungen</a> zu " ,
self . termstoaccept
)
2020-11-18 17:02:23 +00:00
2023-02-27 17:09:29 +00:00
2025-08-31 21:38:22 +00:00
class LiteratureForm ( BaseApplicationForm , CommonOrderMixin ) :
2025-06-16 09:43:08 +00:00
termstoaccept = settings . NUTZUNGSBEDINGUNGEN_LITERATURSTIPENDIUM
2023-02-27 17:09:28 +00:00
def __init__ ( self , * args , * * kwargs ) :
super ( ) . __init__ ( * args , * * kwargs )
2023-02-27 17:09:29 +00:00
self . fields [ ' selfbuy_give_data ' ] . required = True
2025-10-15 12:46:28 +00:00
2020-10-27 12:47:46 +00:00
class Meta :
model = Literature
2025-08-26 20:48:13 +00:00
fields = [ ' cost ' , ' info ' , ' source ' , ' notes ' , ' selfbuy ' , ' selfbuy_data ' , ' selfbuy_give_data ' , ' terms_accepted ' ]
2023-02-27 17:09:29 +00:00
exclude = [ ' intern_notes ' , ' survey_mail_send ' , ' mail_state ' ]
2025-10-15 12:46:28 +00:00
2023-02-27 17:09:28 +00:00
class Media :
2023-02-27 17:09:29 +00:00
js = ( ' dropdown/js/literature.js ' , )
2020-10-27 12:47:46 +00:00
2023-02-27 17:09:29 +00:00
2025-10-15 12:46:28 +00:00
ADULT_CHOICES = {
' TRUE ' : mark_safe ( ' Ich bin volljährig. ' ) ,
' FALSE ' : mark_safe ( ' Ich bin noch nicht volljährig. ' ) ,
}
2023-02-27 17:09:29 +00:00
2025-06-16 09:43:08 +00:00
2025-10-15 12:46:28 +00:00
class EmailForm ( BaseApplicationForm , CommonOrderMixin ) :
2025-06-16 09:43:08 +00:00
termstoaccept = settings . NUTZUNGSBEDINGUNGEN_EMAIL_SERVICE
2025-08-19 12:10:15 +00:00
2023-02-27 17:09:29 +00:00
# this is the code, to change required to false if needed
def __init__ ( self , * args , * * kwargs ) :
super ( ) . __init__ ( * args , * * kwargs )
self . fields [ ' adult ' ] . required = True
2023-02-27 17:09:29 +00:00
self . fields [ ' other ' ] . required = True
2023-02-27 17:09:29 +00:00
2023-02-27 17:09:29 +00:00
adult = ChoiceField ( label = ' Volljährigkeit ' , choices = ADULT_CHOICES . items ( ) , widget = RadioSelect ( ) )
2020-10-27 10:00:58 +00:00
# TODO: add some javascript to show/hide other-field
class Meta :
model = Email
2025-08-26 20:48:13 +00:00
fields = [ ' domain ' , ' address ' , ' other ' , ' adult ' , ' terms_accepted ' ]
2023-02-27 17:09:29 +00:00
exclude = [ ' intern_notes ' , ' survey_mail_send ' , ' mail_state ' ]
2025-10-15 12:46:28 +00:00
2023-02-27 17:09:28 +00:00
class Media :
2023-02-27 17:09:29 +00:00
js = ( ' dropdown/js/mail.js ' , )
2023-02-27 17:09:28 +00:00
2025-08-31 21:38:22 +00:00
class BusinessCardForm ( BaseApplicationForm , CommonOrderMixin ) :
2025-06-16 09:43:08 +00:00
termstoaccept = settings . NUTZUNGSBEDINGUNGEN_VISITENKARTEN
2025-10-15 12:46:28 +00:00
2025-06-16 09:43:08 +00:00
# this is the code, to change required to false if needed
def __init__ ( self , * args , * * kwargs ) :
2023-02-27 17:09:28 +00:00
super ( ) . __init__ ( * args , * * kwargs )
self . fields [ ' url_of_pic ' ] . required = True
2023-02-27 17:09:29 +00:00
self . fields [ ' send_data_to_print ' ] . required = True
2023-02-27 17:09:28 +00:00
2025-06-16 09:43:08 +00:00
class Meta :
2020-10-27 12:47:46 +00:00
model = BusinessCard
2025-08-19 12:10:15 +00:00
exclude = [ ' intern_notes ' , ' survey_mail_send ' , ' mail_state ' ]
2025-08-26 20:48:13 +00:00
fields = [ ' project ' , ' data ' , ' variant ' , ' url_of_pic ' , ' send_data_to_print ' , ' sent_to ' , ' terms_accepted ' ]
2025-10-15 12:46:28 +00:00
2025-06-16 09:43:08 +00:00
class Media :
2023-02-27 17:09:29 +00:00
js = ( ' dropdown/js/businessCard.js ' , )
2020-10-27 12:47:46 +00:00
2023-02-27 17:09:29 +00:00
2025-08-31 21:38:22 +00:00
class ListForm ( BaseApplicationForm , CommonOrderMixin ) :
2025-06-16 09:43:08 +00:00
termstoaccept = settings . NUTZUNGSBEDINGUNGEN_MAILINGLISTEN
2025-10-15 12:46:28 +00:00
2020-10-27 12:47:46 +00:00
class Meta :
model = List
2025-08-26 20:48:13 +00:00
fields = [ ' domain ' , ' address ' , ' terms_accepted ' ]
2025-10-15 12:46:28 +00:00
exclude = [ ' intern_notes ' , ' survey_mail_send ' , ' mail_state ' ]
2025-10-14 09:16:13 +00:00
def __init__ ( self , * args , * * kwargs ) :
super ( ) . __init__ ( * args , * * kwargs )
self . fields [ ' address ' ] . initial = ' '