Merge branch 'feature/replace-radios-with-links' into 'cosmocode'

refactor(forms): Split Views and Template Restructure

See merge request wikimedia/foerderbarometer!5
This commit is contained in:
Oliver Zander 2025-09-29 16:31:43 +02:00
commit 38f6deee51
11 changed files with 428 additions and 199 deletions

View File

@ -1,9 +1,10 @@
from django.conf import settings 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.contrib.admin.widgets import AdminDateWidget
from django.forms.renderers import DjangoTemplates from django.forms.renderers import DjangoTemplates
from django.utils.html import format_html from django.utils.html import format_html
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django import forms
from .models import ( from .models import (
TYPE_CHOICES, TYPE_CHOICES,
@ -33,7 +34,6 @@ class FdbForm(ModelForm):
class ProjectForm(FdbForm): class ProjectForm(FdbForm):
# start = DateField(widget=AdminDateWidget()) # start = DateField(widget=AdminDateWidget())
class Meta: class Meta:
@ -47,6 +47,48 @@ class ProjectForm(FdbForm):
js = ('dropdown/js/otrs_link.js',) 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): class ExternForm(FdbForm):
choice = ChoiceField(choices=TYPE_CHOICES.items(), widget=RadioSelect, choice = ChoiceField(choices=TYPE_CHOICES.items(), widget=RadioSelect,
@ -79,7 +121,33 @@ HOTEL_CHOICES = {'TRUE': mark_safe('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 <br> Wikimedia Deutschland bei Rückfragen oder für <br> die Zusage kontaktieren kann.'
)
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))
class TravelForm(BaseApplicationForm, CommonOrderMixin):
# TODO: add some javascript to show/hide other-field # TODO: add some javascript to show/hide other-field
# this is the code, to change required to false if needed # 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: class Meta:
model = Library model = Library
@ -144,7 +212,7 @@ class HonoraryCertificateForm(FdbForm):
js = ('dropdown/js/otrs_link.js',) js = ('dropdown/js/otrs_link.js',)
class IFGForm(FdbForm): class IFGForm(BaseApplicationForm, CommonOrderMixin):
class Meta: class Meta:
model = IFG model = IFG
fields = ['cost', 'url', 'notes'] fields = ['cost', 'url', 'notes']
@ -168,7 +236,7 @@ class CheckForm(FdbForm):
) )
class LiteratureForm(CheckForm): class LiteratureForm(BaseApplicationForm, CommonOrderMixin):
termstoaccept = settings.NUTZUNGSBEDINGUNGEN_LITERATURSTIPENDIUM termstoaccept = settings.NUTZUNGSBEDINGUNGEN_LITERATURSTIPENDIUM
def __init__(self, *args, **kwargs): 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 termstoaccept = settings.NUTZUNGSBEDINGUNGEN_EMAIL_SERVICE
@ -210,7 +278,7 @@ class EmailForm(CheckForm):
class BusinessCardForm(CheckForm): class BusinessCardForm(BaseApplicationForm, CommonOrderMixin):
termstoaccept = settings.NUTZUNGSBEDINGUNGEN_VISITENKARTEN termstoaccept = settings.NUTZUNGSBEDINGUNGEN_VISITENKARTEN
# this is the code, to change required to false if needed # this is the code, to change required to false if needed
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
@ -226,7 +294,7 @@ class BusinessCardForm(CheckForm):
js = ('dropdown/js/businessCard.js',) js = ('dropdown/js/businessCard.js',)
class ListForm(CheckForm): class ListForm(BaseApplicationForm, CommonOrderMixin):
termstoaccept = settings.NUTZUNGSBEDINGUNGEN_MAILINGLISTEN termstoaccept = settings.NUTZUNGSBEDINGUNGEN_MAILINGLISTEN
class Meta: class Meta:
model = List model = List

View File

@ -0,0 +1,11 @@
.star {
color: red;
}
.wm-table {
margin: 0 auto;
}
.col-request {
width: 40%;
}

View File

@ -0,0 +1,37 @@
{% load static %}
<!doctype html>
<html lang="de">
<head>
<meta charset="utf-8">
<!-- Admin i18n -->
<script src="{% url 'jsi18n' %}"></script>
<!-- Admin Assets -->
<script src="{% static 'admin/js/core.js' %}"></script>
<script src="{% static 'admin/js/vendor/jquery/jquery.js' %}"></script>
<script src="{% static 'admin/js/jquery.init.js' %}"></script>
<link rel="stylesheet" href="{% static 'admin/css/base.css' %}">
<link rel="stylesheet" href="{% static 'admin/css/widgets.css' %}">
<!-- Project Styles -->
<link rel="stylesheet" type="text/css" href="{% static 'css/forms.css' %}">
<link rel="stylesheet" type="text/css" href="{% static 'css/base.css' %}">
{% block head_extra %}{% endblock %}
</head>
<body>
{% include "input/partials/_header.html" %}
<main class="wm-main">
{% block pre_content %}{% endblock %}
{% block content %}{% endblock %}
{% block post_content %}{% endblock %}
</main>
{% include "input/partials/_footer.html" %}
</body>
</html>

View File

@ -1,71 +0,0 @@
{% load static %}
<script type="text/javascript" src="/admin/jsi18n/"></script>
<script type="text/javascript" src="{% static 'admin/js/core.js' %}"></script>
<script type="text/javascript" src="{% static 'admin/js/vendor/jquery/jquery.js' %}"></script>
<script type="text/javascript" src="{% static 'admin/js/jquery.init.js' %}"></script>
{{ form.media }}
<link rel="stylesheet" type="text/css" href="{% static 'admin/css/base.css' %}" />
<link rel="stylesheet" type="text/css" href="{% static 'admin/css/widgets.css' %}" />
{% load i18n %}
{% block content %}
<center>
<style>
ul > li {
list-style-type: none;
}
ul {
padding-left: 10;
}
label.required::after {
content: ' *';
color: red;
}
</style>
<img src="{% static 'input/logo.png' %}" />
<p>Schritt {{ wizard.steps.step1 }} von {{ wizard.steps.count }}</p>
<form action="" method="post">
{% csrf_token %}
<table>
{% if choice %}
Du hast {{choice}} ausgewählt.
{% endif %}
{{ wizard.management_form }}
{% if wizard.form.forms %}
{{ wizard.form.management_form }}
{% for form in wizard.form.forms %}
{{ form }}
{% endfor %}
{% else %}
{{ wizard.form }}
{% endif %}
</table>
<p>
<span style="color: red">*</span> Pflichtfeld
<p>
{% if wizard.steps.prev %}
<button formnovalidate="formnovalidate" name="wizard_goto_step" type="submit" value="{{ wizard.steps.prev }}">Zurück</button>
{% endif %}
{% if wizard.steps.current == wizard.steps.last %}
<button type="submit" value="{% trans "Weiter" %}">Absenden</button>
{% else %}
<button type="submit" value="{% trans "Weiter" %}">Weiter</button>
{% endif %}
</form>
<p>
<img src="https://upload.wikimedia.org/wikipedia/commons/c/c4/Figuren_klein.jpg"><p>
Eine Übersicht aller Förderangebote von Wikimedia Deutschland findest du im <a href="https://de.wikipedia.org/wiki/Wikipedia:Förderung/Förderangebote">
Förderportal in der deutschsprachigen Wikipedia</a>.
<br>Für alle Fragen wende dich gern an das <a href="https://de.wikipedia.org/wiki/Wikipedia:Förderung/Wikimedia_Deutschland">Team Communitys und Engagement</a>.
<p>
Für interessierte Hacker gibts auch den <a href="https://srcsrv.wikimedia.de/beba/foerderbarometer">Sourcecode</a> zum Formular und was damit passiert.
<p>
<a href="https://www.wikimedia.de/impressum/">Impressum</a>
</center>{% endblock %}

View File

@ -0,0 +1,38 @@
{% extends "input/base.html" %}
{% load i18n %}
{% block content %}
<div class="page-centered">
<table class="wm-table">
<tr>
<th class="col-request">Was möchtest du beantragen?</th>
<td>
<strong>Projektförderung</strong>
<ul>
<li>
<a href="#">Projektförderung</a>
mit einer Gesamtsumme unter 1.000,— EUR
</li>
<li>
<a href="{% url 'info-foerderprojekt-ab-1000' %}">Projektförderung</a>
mit einer Gesamtsumme ab 1.000,— EUR
</li>
</ul>
<strong>Serviceleistungen</strong>
<ul>
<li><a href="{% url 'bibliotheksstipendium' %}">Bibliotheksstipendium</a></li>
<li><a href="{% url 'eliteraturstipendium' %}">eLiteraturstipendium</a></li>
<li><a href="{% url 'email' %}">E-Mail-Adresse</a></li>
<li><a href="{% url 'ifg' %}">Kostenübernahme IFG-Anfrage</a></li>
<li><a href="{% url 'literatur' %}">Literaturstipendium</a></li>
<li><a href="{% url 'mailingliste' %}">Mailingliste</a></li>
<li><a href="{% url 'reisekosten' %}">Reisekosten</a></li>
<li><a href="{% url 'softwarestipendium' %}">Softwarestipendium</a></li>
<li><a href="{% url 'visitenkarten' %}">Visitenkarten</a></li>
</ul>
</td>
</tr>
</table>
</div>
{% endblock %}

View File

@ -0,0 +1,30 @@
{% extends "input/base.html" %}
{% load static %}
{% block content %}
{{ form.media }}
<div class="page-centered">
<p>Du hast {{ typestring }} ausgewählt.</p>
</div>
<form method="post" class="wm-form" {% if form.is_multipart %}enctype="multipart/form-data"{% endif %}>
{% csrf_token %}
{% block pre_table %}{% endblock %}
<table class="wm-table">
{{ form.non_field_errors }}
{{ form.as_table }}
</table>
{% block post_table %}{% endblock %}
<p class="page-centered"><span class="star">*</span> Pflichtfeld</p>
<div class="page-centered">
<button type="button" onclick="history.back()">Zurück</button>
<button type="submit">Absenden</button>
</div>
</form>
{% endblock %}

View File

@ -0,0 +1,18 @@
<!doctype html>
<html lang="de">
<head>
<meta charset="utf-8">
<title>Projektförderung ab 1.000,— EUR</title>
</head>
<body>
<h1>Projektförderung mit einer Gesamtsumme ab 1.000,— EUR</h1>
<p>Vielen Dank für dein Interesse an einer Projektförderung!<br>
Für Projektförderungen mit einer Gesamtsumme ab 1.000,— EUR ist ein öffentlicher Projektplan
erforderlich. Weitere Informationen zu diesem Prozess findest du unter
<a href="https://de.wikipedia.org/wiki/Wikipedia:F%C3%B6rderung/Projektplanung" target="_blank" rel="noopener">
Wikipedia:Förderung/Projektplanung</a>.<br>
Für Fragen steht dir das Team Community-Konferenzen &amp; Förderung gern unter
<a href="mailto:community@wikimedia.de">community@wikimedia.de</a> zur Verfügung.
</p>
</body>
</html>

View File

@ -0,0 +1,21 @@
<div class="wm-footer page-centered">
<p>
<img src="https://upload.wikimedia.org/wikipedia/commons/c/c4/Figuren_klein.jpg" alt=""/>
</p>
<p>
Eine Übersicht aller Förderangebote von Wikimedia Deutschland findest du im
<a href="https://de.wikipedia.org/wiki/Wikipedia:Förderung/Förderangebote">Förderportal in der deutschsprachigen
Wikipedia</a>.
<br>
Für alle Fragen wende dich gern an das
<a href="https://de.wikipedia.org/wiki/Wikipedia:Förderung/Wikimedia_Deutschland">Team Communitys und
Engagement</a>.
</p>
<p>
Für interessierte Hacker gibts auch den
<a href="https://srcsrv.wikimedia.de/beba/foerderbarometer">Sourcecode</a> zum Formular und was damit passiert.
</p>
<p>
<a href="https://www.wikimedia.de/impressum/">Impressum</a>
</p>
</div>

View File

@ -0,0 +1,4 @@
{% load static %}
<div class="wm-header page-centered">
<img src="{% static 'input/logo.png' %}" alt="Wikimedia Deutschland"/>
</div>

View File

@ -1,12 +1,41 @@
from django.urls import path from django.urls import path
from django.views.generic import TemplateView
from .views import ExternView, index, done, authorize, deny, export from django.views.i18n import JavaScriptCatalog
from .views import (
index, done, export, authorize, deny,
TravelApplicationView, IFGApplicationView, EmailApplicationView,
LiteratureApplicationView, ListApplicationView, BusinessCardApplicationView,
LibraryApplicationView, ELiteratureApplicationView, SoftwareApplicationView,
)
urlpatterns = [ urlpatterns = [
path('', index, name='index'), path('', index, name='index'),
path('extern', ExternView.as_view(), name='extern'), path(
'extern/',
TemplateView.as_view(template_name='input/forms/extern.html'),
name='extern',
),
path('saved', done, name='done'), path('saved', done, name='done'),
path('export', export, name='export'), path('export', export, name='export'),
path('authorize/<str:choice>/<int:pk>', authorize, name='authorize'), path('authorize/<str:choice>/<int:pk>', authorize, name='authorize'),
path('deny/<str:choice>/<int:pk>', deny, name='deny'), path('deny/<str:choice>/<int:pk>', deny, name='deny'),
# Static info page for project funding above 1000 EUR
path('extern/info/projektfoerderung-ab-1000/',
TemplateView.as_view(template_name='input/info_project_funding_gt_1000.html'),
name='info-foerderprojekt-ab-1000'),
# New single-page application views
path('extern/reisekosten/', TravelApplicationView.as_view(), name='reisekosten'),
path('extern/ifg/', IFGApplicationView.as_view(), name='ifg'),
path('extern/email/', EmailApplicationView.as_view(), name='email'),
path('extern/literaturstipendium/', LiteratureApplicationView.as_view(), name='literatur'),
path('extern/mailingliste/', ListApplicationView.as_view(), name='mailingliste'),
path('extern/visitenkarten/', BusinessCardApplicationView.as_view(), name='visitenkarten'),
path('extern/bibliotheksstipendium/', LibraryApplicationView.as_view(), name='bibliotheksstipendium'),
path('extern/eliteraturstipendium/', ELiteratureApplicationView.as_view(), name='eliteraturstipendium'),
path('extern/softwarestipendium/', SoftwareApplicationView.as_view(), name='softwarestipendium'),
# JavaScript translations for date widgets, etc.
path('jsi18n/', JavaScriptCatalog.as_view(), name='jsi18n'),
] ]

View File

@ -8,6 +8,7 @@ from django.core.mail import BadHeaderError, EmailMultiAlternatives
from django.template.loader import get_template from django.template.loader import get_template
from django.conf import settings from django.conf import settings
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.views.generic.edit import FormView
from .forms import ( from .forms import (
ExternForm, ExternForm,
@ -29,6 +30,30 @@ LIBRARY_FORMS = {
TYPE_SOFT: SoftwareForm, TYPE_SOFT: SoftwareForm,
} }
HELP_TEXTS = {
'IFG': {
'notes': (
'Bitte gib an, wie die gewonnenen Informationen den<br>'
'Wikimedia-Projekten zugute kommen sollen.'
)
},
'MAIL': {
'domain': (
'Mit welcher Domain, bzw. für welches Wikimedia-Projekt,<br>'
'möchtest du eine Mailadresse beantragen?'
)
},
'LIT': {
'notes': 'Bitte gib an, wofür du die Literatur verwenden möchtest.'
},
'LIST': {
'domain': (
'Mit welcher Domain, bzw. für welches Wikimedia-Projekt,<br>'
'möchtest du eine Mailingliste beantragen?'
)
},
}
def auth_deny(choice, pk, auth): def auth_deny(choice, pk, auth):
if choice not in MODELS: if choice not in MODELS:
@ -51,7 +76,7 @@ def authorize(request, choice, pk):
if ret := auth_deny(choice, pk, True): if ret := auth_deny(choice, pk, True):
return ret return ret
else: else:
return HttpResponse(f"AUTHORIZED! choice: {choice}, pk: {pk}") return HttpResponse(f'AUTHORIZED! choice: {choice}, pk: {pk}')
@login_required @login_required
@ -62,143 +87,118 @@ def deny(request, choice, pk):
if ret := auth_deny(choice, pk, False): if ret := auth_deny(choice, pk, False):
return ret return ret
else: else:
return HttpResponse(f"DENIED! choice: {choice}, pk: {pk}") return HttpResponse(f'DENIED! choice: {choice}, pk: {pk}')
def done(request): def done(request):
return HttpResponse("Deine Anfrage wurde gesendet. Du erhältst in Kürze eine E-Mail-Benachrichtigung mit deinen Angaben. Für alle Fragen kontaktiere bitte das Team Communitys und Engagement unter community@wikimedia.de.") return HttpResponse(
'Deine Anfrage wurde gesendet. Du erhältst in Kürze eine E-Mail-Benachrichtigung mit deinen Angaben. Für alle Fragen kontaktiere bitte das Team Communitys und Engagement unter community@wikimedia.de.')
def index(request): def index(request):
return render(request, 'input/index.html') return render(request, 'input/index.html')
class ExternView(CookieWizardView): class BaseApplicationView(FormView):
'''This View is for Volunteers''' """
Base view for all application types.
template_name = "input/extern.html" - Each application type (travel, literature, email, etc.) gets its own subclass.
form_list = [ExternForm, LibraryForm] - Renders the generic form template.
- Handles saving the submitted form to the database.
def get_form(self, step=None, data=None, files=None): - Adds extra fields from the session or request type if needed.
'''this function determines which part of the multipart form is - Applies optional help_text overrides for certain fields.
displayed next''' - Sends confirmation mail to the applicant.
- Sends notification mail to the internal IF address.
if step is None: - Returns the "done" response after successful processing.
step = self.steps.current """
print ("get_form() step " + step) template_name = 'input/forms/form_generic.html'
type_code: str = ''
if step == '1':
prev_data = self.get_cleaned_data_for_step('0')
choice = prev_data.get('choice')
print(f'choice detection in ExternView: {TYPE_CHOICES[choice]}')
if choice == 'IFG':
form = IFGForm(data)
form.fields['notes'].help_text = mark_safe("Bitte gib an, wie die gewonnenen Informationen den<br>Wikimedia-Projekten zugute kommen sollen.")
elif choice in LIBRARY_FORMS:
form = LIBRARY_FORMS[choice](data)
elif choice == 'MAIL':
form = EmailForm(data)
form.fields['domain'].help_text = mark_safe("Mit welcher Domain, bzw. für welches Wikimedia-Projekt,<br>möchtest du eine Mailadresse beantragen?")
elif choice == 'LIT':
form = LiteratureForm(data)
form.fields['notes'].help_text = "Bitte gib an, wofür du die Literatur verwenden möchtest."
elif choice == 'VIS':
form = BusinessCardForm(data)
elif choice == 'LIST':
form = ListForm(data)
form.fields['domain'].help_text = mark_safe("Mit welcher Domain, bzw. für welches Wikimedia-Projekt,<br>möchtest du eine Mailingliste beantragen?")
elif choice == 'TRAV':
form = TravelForm(data)
else: # pragma: no cover
raise RuntimeError(f'ERROR! UNKNOWN FORMTYPE {choice} in ExternView')
self.choice = choice
else:
form = super().get_form(step, data, files)
return form
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) """Add the human-readable type string (from TYPE_CHOICES) to the template context."""
if hasattr(self, 'choice'): ctx = super().get_context_data(**kwargs)
context["choice"] = TYPE_CHOICES[self.choice] ctx['typestring'] = TYPE_CHOICES.get(self.type_code, self.type_code)
return context return ctx
def done(self, form_list, **kwargs): def get_form(self, form_class=None):
print('ExternView.done() reached') """Return the form instance and inject custom help_texts if defined for this type."""
# gather data from all forms form = super().get_form(form_class)
data = {}
for form in form_list:
data = {**data, **form.cleaned_data}
if data['choice'] == 'LIT': # Apply help_text overrides if defined for this type_code
if data['selfbuy'] == 'TRUE': if self.type_code in HELP_TEXTS:
for field, text in HELP_TEXTS[self.type_code].items():
if field in form.fields:
form.fields[field].help_text = mark_safe(text)
return form
def form_valid(self, form):
"""
Process a valid form submission:
- Enrich form data (e.g., set type_code, handle special rules).
- Save the model instance and related data.
- Send confirmation and notification mails.
- Return the "done" response.
"""
# Collect cleaned data and mark the current type
data = form.cleaned_data.copy()
data['choice'] = self.type_code
# Special rule for literature applications
if self.type_code == 'LIT' and data.get('selfbuy') == 'TRUE':
data['selfbuy_give_data'] = 'False' data['selfbuy_give_data'] = 'False'
# write data to database # Save model instance
modell = form.save(commit=False) modell = form.save(commit=False)
# we have to copy the data from the first form here
# this is a bit ugly code. can we copy this without explicit writing?
if data['choice'] == 'LIT': # Username from session if present
if user := self.request.session.get('user'):
modell.username = user.get('username')
# Copy common fields if provided by the form
if 'realname' in data:
modell.realname = data['realname']
if 'email' in data:
modell.email = data['email']
# Set model.type for specific request types
if self.type_code in ('BIB', 'ELIT', 'SOFT'):
modell.type = self.type_code
# Literature-specific extra field
if self.type_code == 'LIT' and 'selfbuy_give_data' in data:
modell.selfbuy_give_data = data['selfbuy_give_data'] modell.selfbuy_give_data = data['selfbuy_give_data']
if user := self.request.session.get('user'): modell.save()
modell.username = user['username'] if hasattr(form, 'save_m2m'):
form.save_m2m()
modell.realname = data['realname'] # Prepare minimal mail context and send mails
modell.email = data['email']
# write type of form in some cases
if data['choice'] in ('BIB', 'ELIT', 'SOFT'):
modell.type = data['choice']
form.save()
# add some data to context for mail templates
data['pk'] = modell.pk data['pk'] = modell.pk
data['url_prefix'] = settings.EMAIL_URL_PREFIX data['url_prefix'] = settings.EMAIL_URL_PREFIX
data['grant'] = ('LIT', 'SOFT', 'ELIT', 'BIB', 'IFG') data['typestring'] = TYPE_CHOICES.get(self.type_code, self.type_code)
data['DOMAIN'] = ('MAIL', 'LIST')
data['typestring'] = TYPE_CHOICES[data['choice']]
# we need to send the following mails here:
context = {'data': data} context = {'data': data}
try: try:
# - mail with entered data to the Volunteer # Mail to applicant
txt1 = get_template('input/ifg_volunteer_mail.txt').render(context)
txt_mail_template1 = get_template('input/ifg_volunteer_mail.txt') html1 = get_template('input/ifg_volunteer_mail.html').render(context)
html_mail_template1 = get_template('input/ifg_volunteer_mail.html') msg1 = EmailMultiAlternatives(
'Formular ausgefüllt', txt1, settings.IF_EMAIL, [data['email']]
subject1, from_email1, to1 = 'Formular ausgefüllt', settings.IF_EMAIL, data['email'] )
text_content1 = txt_mail_template1.render(context) msg1.attach_alternative(html1, 'text/html')
html_content1 = html_mail_template1.render(context)
msg1 = EmailMultiAlternatives(subject1, text_content1, from_email1, [to1])
msg1.attach_alternative(html_content1, "text/html")
msg1.send() msg1.send()
#print('ifg volunteer mail would have been sent')
#send_mail(
# 'Formular ausgefüllt',
# txt_mail_template1.render(context),
# IF_EMAIL,
# [data['email']],
# fail_silently=False)
## - mail to IF with link to accept/decline
txt_mail_template = get_template('input/if_mail.txt') # Mail to IF
html_mail_template = get_template('input/if_mail.html') txt2 = get_template('input/if_mail.txt').render(context)
html2 = get_template('input/if_mail.html').render(context)
subject, from_email, to = 'Formular ausgefüllt', settings.IF_EMAIL, settings.IF_EMAIL msg2 = EmailMultiAlternatives(
text_content = txt_mail_template.render(context) 'Formular ausgefüllt', txt2, settings.IF_EMAIL, [settings.IF_EMAIL]
html_content = html_mail_template.render(context) )
msg2 = EmailMultiAlternatives(subject, text_content, from_email, [to]) msg2.attach_alternative(html2, 'text/html')
msg2.attach_alternative(html_content, "text/html")
msg2.send() msg2.send()
#print('if mail would have been sent')
#send_mail(
# 'Formular ausgefüllt',
# txt_mail_template.render(context),
# IF_EMAIL,
# [IF_EMAIL],
# fail_silently=False)
## raise SMTPException("testing pupose only")
except BadHeaderError: except BadHeaderError:
modell.delete() modell.delete()
@ -207,5 +207,49 @@ class ExternView(CookieWizardView):
modell.delete() modell.delete()
return HttpResponse('Error in sending mails (probably wrong adress?). Data not saved!') return HttpResponse('Error in sending mails (probably wrong adress?). Data not saved!')
return done(self.request) return done(self.request)
class TravelApplicationView(BaseApplicationView):
form_class = TravelForm
type_code = 'TRAV'
class LibraryApplicationView(BaseApplicationView):
form_class = LibraryForm
type_code = 'BIB'
class ELiteratureApplicationView(BaseApplicationView):
form_class = ELiteratureForm
type_code = 'ELIT'
class SoftwareApplicationView(BaseApplicationView):
form_class = SoftwareForm
type_code = 'SOFT'
class IFGApplicationView(BaseApplicationView):
form_class = IFGForm
type_code = 'IFG'
class EmailApplicationView(BaseApplicationView):
form_class = EmailForm
type_code = 'MAIL'
class LiteratureApplicationView(BaseApplicationView):
form_class = LiteratureForm
type_code = 'LIT'
class ListApplicationView(BaseApplicationView):
form_class = ListForm
type_code = 'LIST'
class BusinessCardApplicationView(BaseApplicationView):
form_class = BusinessCardForm
type_code = 'VIS'