forked from beba/foerderbarometer
Compare commits
27 Commits
ebd7ebd3fd
...
3fe9b08925
| Author | SHA1 | Date |
|---|---|---|
|
|
3fe9b08925 | |
|
|
7fd35636b1 | |
|
|
6932b9413c | |
|
|
3e9859c1a8 | |
|
|
5ba6195a82 | |
|
|
db8f5b1bef | |
|
|
feafada61d | |
|
|
361e3252f2 | |
|
|
bf16383c3f | |
|
|
45b910a768 | |
|
|
e92029db83 | |
|
|
d9091c818a | |
|
|
9e30415003 | |
|
|
7bb4f1a841 | |
|
|
5e77900daf | |
|
|
984c7807cb | |
|
|
c828964ee6 | |
|
|
341c24e651 | |
|
|
966bb8e7a6 | |
|
|
3356bcef4f | |
|
|
5d6295358b | |
|
|
513671bef0 | |
|
|
336428bcb6 | |
|
|
ec60aa0550 | |
|
|
03dc9c0775 | |
|
|
a084eb6664 | |
|
|
1a299fd10b |
|
|
@ -121,6 +121,9 @@ AUTH_PASSWORD_VALIDATORS = password_validators(
|
|||
USE_I18N = True
|
||||
USE_L10N = True
|
||||
LANGUAGE_CODE = env('LANGUAGE_CODE', 'de')
|
||||
LANGUAGES = [
|
||||
('de', 'Deutsch'),
|
||||
]
|
||||
|
||||
USE_TZ = True
|
||||
TIME_ZONE = env('TIME_ZONE', 'Europe/Berlin')
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import csv
|
||||
|
||||
from django.contrib import admin
|
||||
from django.db import models, transaction
|
||||
from django.db import models
|
||||
from django.http import HttpResponse
|
||||
|
||||
from input.utils.mail import send_decision_mails
|
||||
from input.utils.list import reorder_value
|
||||
|
||||
from .forms import BaseProjectForm
|
||||
from .models import (
|
||||
|
|
@ -24,24 +24,21 @@ from .models import (
|
|||
BusinessCard,
|
||||
List,
|
||||
Literature,
|
||||
TYPE_PROJ,
|
||||
)
|
||||
|
||||
|
||||
class RequestURLBeforeInternNotesMixin:
|
||||
"""
|
||||
Ensures that 'request_url' appears directly before 'intern_notes'.
|
||||
Works whether 'fields' is explicitly defined or derived from the Model/Form.
|
||||
"""
|
||||
class WMDEAdmin(admin.ModelAdmin):
|
||||
|
||||
def get_fields(self, request, obj=None):
|
||||
fields = [*super().get_fields(request, obj)]
|
||||
fields = super().get_fields(request, obj=obj)
|
||||
|
||||
fields.remove('request_url')
|
||||
if 'username' in fields:
|
||||
fields = reorder_value(fields, 'username', after='email')
|
||||
|
||||
index = fields.index('intern_notes')
|
||||
fields = reorder_value(fields, 'request_url', before='intern_notes')
|
||||
|
||||
fields.insert(index, 'request_url')
|
||||
if 'terms_accepted' in fields:
|
||||
fields = reorder_value(fields, 'terms_accepted', before='request_url')
|
||||
|
||||
return fields
|
||||
|
||||
|
|
@ -114,6 +111,7 @@ class BaseProjectAdmin(admin.ModelAdmin):
|
|||
('Kontakt', {'fields': (
|
||||
'realname',
|
||||
'email',
|
||||
'username',
|
||||
)}),
|
||||
('Projekt', {'fields': (
|
||||
'name',
|
||||
|
|
@ -175,16 +173,6 @@ class ProjectAdmin(BaseProjectAdmin):
|
|||
class ProjectRequestAdmin(BaseProjectAdmin):
|
||||
granted = None
|
||||
|
||||
def save_model(self, request, obj: ProjectRequest, form: ProjectAdminForm, change: bool):
|
||||
super().save_model(request, obj, form, change)
|
||||
|
||||
if obj.granted is None:
|
||||
return None
|
||||
|
||||
transaction.on_commit(lambda: send_decision_mails(obj))
|
||||
|
||||
return obj.granted
|
||||
|
||||
|
||||
@admin.register(ProjectDeclined)
|
||||
class ProjectDeclinedAdmin(BaseProjectAdmin):
|
||||
|
|
@ -193,12 +181,9 @@ class ProjectDeclinedAdmin(BaseProjectAdmin):
|
|||
def has_add_permission(self, request):
|
||||
return False
|
||||
|
||||
def has_change_permission(self, request, obj=None):
|
||||
return False
|
||||
|
||||
|
||||
@admin.register(BusinessCard)
|
||||
class BusinessCardAdmin(RequestURLBeforeInternNotesMixin, admin.ModelAdmin):
|
||||
class BusinessCardAdmin(WMDEAdmin):
|
||||
save_as = True
|
||||
search_fields = ('realname', 'service_id', 'granted', 'granted_date', 'project')
|
||||
list_display = ('realname', 'service_id', 'granted', 'granted_date', 'project', 'terms_accepted')
|
||||
|
|
@ -212,7 +197,7 @@ class BusinessCardAdmin(RequestURLBeforeInternNotesMixin, admin.ModelAdmin):
|
|||
|
||||
|
||||
@admin.register(Literature)
|
||||
class LiteratureAdmin(RequestURLBeforeInternNotesMixin, admin.ModelAdmin):
|
||||
class LiteratureAdmin(WMDEAdmin):
|
||||
save_as = True
|
||||
search_fields = ('realname', 'service_id', 'granted', 'granted_date')
|
||||
list_display = ('realname', 'service_id', 'granted', 'granted_date', 'terms_accepted')
|
||||
|
|
@ -242,7 +227,7 @@ class HonoraryCertificateAdmin(admin.ModelAdmin):
|
|||
|
||||
|
||||
@admin.register(Library, ELiterature, Software)
|
||||
class LibraryAdmin(RequestURLBeforeInternNotesMixin, admin.ModelAdmin):
|
||||
class LibraryAdmin(WMDEAdmin):
|
||||
save_as = True
|
||||
search_fields = ('realname', 'service_id', 'granted', 'granted_date')
|
||||
list_display = ('realname', 'service_id', 'granted', 'granted_date')
|
||||
|
|
@ -269,7 +254,7 @@ class LibraryAdmin(RequestURLBeforeInternNotesMixin, admin.ModelAdmin):
|
|||
|
||||
|
||||
@admin.register(IFG)
|
||||
class IFGAdmin(RequestURLBeforeInternNotesMixin, admin.ModelAdmin):
|
||||
class IFGAdmin(WMDEAdmin):
|
||||
save_as = True
|
||||
search_fields = ('realname', 'service_id', 'granted', 'granted_date')
|
||||
list_display = ('realname', 'service_id', 'granted', 'granted_date')
|
||||
|
|
@ -282,7 +267,7 @@ class IFGAdmin(RequestURLBeforeInternNotesMixin, admin.ModelAdmin):
|
|||
|
||||
|
||||
@admin.register(Travel)
|
||||
class TravelAdmin(admin.ModelAdmin):
|
||||
class TravelAdmin(WMDEAdmin):
|
||||
save_as = True
|
||||
search_fields = ['realname', 'service_id', 'granted_date', 'project__name', 'project__pid']
|
||||
list_display = ('realname', 'service_id', 'granted', 'granted_date', 'project_end', 'project',
|
||||
|
|
@ -297,7 +282,7 @@ class TravelAdmin(admin.ModelAdmin):
|
|||
|
||||
|
||||
@admin.register(Email)
|
||||
class EmailAdmin(RequestURLBeforeInternNotesMixin, admin.ModelAdmin):
|
||||
class EmailAdmin(WMDEAdmin):
|
||||
save_as = True
|
||||
search_fields = ('realname', 'service_id', 'granted', 'granted_date')
|
||||
list_display = ('realname', 'service_id', 'granted', 'granted_date', 'terms_accepted')
|
||||
|
|
@ -311,7 +296,7 @@ class EmailAdmin(RequestURLBeforeInternNotesMixin, admin.ModelAdmin):
|
|||
|
||||
|
||||
@admin.register(List)
|
||||
class ListAdmin(RequestURLBeforeInternNotesMixin, admin.ModelAdmin):
|
||||
class ListAdmin(WMDEAdmin):
|
||||
save_as = True
|
||||
search_fields = ('realname', 'service_id', 'granted', 'granted_date')
|
||||
list_display = ('realname', 'service_id', 'granted', 'granted_date', 'terms_accepted')
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ from django.forms import ModelForm
|
|||
from django.forms.renderers import DjangoTemplates
|
||||
from django.utils.html import format_html
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.utils.translation import gettext_lazy as trans
|
||||
|
||||
from .models import (
|
||||
Project,
|
||||
|
|
@ -125,6 +126,11 @@ class ProjectForm(BaseProjectForm, BaseApplicationForm):
|
|||
'insurance',
|
||||
'notes',
|
||||
]
|
||||
labels = {
|
||||
'cost': 'Kosten in Euro',
|
||||
'insurance': 'Haftpflicht- und Unfallversicherung gewünscht',
|
||||
'participants_estimated': 'Voraussichtliche Zahl der Teilnehmenden',
|
||||
}
|
||||
widgets = {
|
||||
'start': AdminDateWidget,
|
||||
'end': AdminDateWidget,
|
||||
|
|
@ -161,6 +167,7 @@ class TravelForm(BaseApplicationForm):
|
|||
self.fields['project_name'].required = True
|
||||
self.fields['transport'].required = True
|
||||
self.fields['travelcost'].required = True
|
||||
self.fields['travelcost'].initial = None
|
||||
self.fields['checkin'].required = True
|
||||
self.fields['checkout'].required = True
|
||||
self.fields['hotel'].required = True
|
||||
|
|
@ -178,6 +185,10 @@ class TravelForm(BaseApplicationForm):
|
|||
'hotel',
|
||||
'notes',
|
||||
]
|
||||
labels = {
|
||||
'checkin': 'Datum der Anreise',
|
||||
'checkout': 'Datum der Abreise',
|
||||
}
|
||||
widgets = {
|
||||
'checkin': AdminDateWidget,
|
||||
'checkout': AdminDateWidget,
|
||||
|
|
@ -202,6 +213,9 @@ class LibraryForm(BaseApplicationForm):
|
|||
'duration',
|
||||
'notes',
|
||||
]
|
||||
labels = {
|
||||
'cost': 'Kosten in Euro',
|
||||
}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
|
@ -250,10 +264,6 @@ class TermsForm(BaseApplicationForm):
|
|||
class LiteratureForm(TermsForm):
|
||||
terms_accepted_url = settings.NUTZUNGSBEDINGUNGEN_LITERATURSTIPENDIUM
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.fields['selfbuy_give_data'].required = True
|
||||
|
||||
class Meta:
|
||||
model = Literature
|
||||
fields = [
|
||||
|
|
@ -272,6 +282,24 @@ class LiteratureForm(TermsForm):
|
|||
class Media:
|
||||
js = ('dropdown/js/literature.js',)
|
||||
|
||||
def clean(self):
|
||||
cleaned_data = TermsForm.clean(self)
|
||||
|
||||
if self.errors:
|
||||
return cleaned_data
|
||||
|
||||
if cleaned_data['selfbuy'] == 'TRUE':
|
||||
cleaned_data['selfbuy_data'] = ''
|
||||
cleaned_data['selfbuy_give_data'] = False
|
||||
|
||||
return cleaned_data
|
||||
|
||||
for field in 'selfbuy_data', 'selfbuy_give_data':
|
||||
if not cleaned_data.get(field):
|
||||
self.add_error(field, trans('This field is required.'))
|
||||
|
||||
return cleaned_data
|
||||
|
||||
|
||||
ADULT_CHOICES = {
|
||||
'TRUE': mark_safe('Ich bin volljährig.'),
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ class Migration(migrations.Migration):
|
|||
('survey_mail_send', models.BooleanField(null=True)),
|
||||
('username', models.CharField(max_length=200, null=True)),
|
||||
('domain', models.CharField(choices=[('PEDIA', '@wikipedia.de'), ('BOOKS', '@wikibooks.de'), ('QUOTE', '@wikiquote.de'), ('SOURCE', '@wikisource.de'), ('VERSITY', '@wikiversity.de')], default='PEDIA', max_length=10)),
|
||||
('adress', models.CharField(choices=[('REALNAME', 'Vorname.Nachname'), ('USERNAME', 'Username'), ('OTHER', 'Sonstiges:')], default='USERNAME', max_length=50)),
|
||||
('adress', models.CharField(choices=[('REALNAME', 'Vorname.Nachname'), ('USERNAME', 'Username'), ('OTHER', 'Sonstiges')], default='USERNAME', max_length=50)),
|
||||
('other', models.CharField(blank=True, max_length=50, null=True)),
|
||||
],
|
||||
options={
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ class Migration(migrations.Migration):
|
|||
migrations.AddField(
|
||||
model_name='email',
|
||||
name='address',
|
||||
field=models.CharField(choices=[('REALNAME', 'Vorname.Nachname'), ('USERNAME', 'Username'), ('OTHER', 'Sonstiges:')], default='USERNAME', max_length=50, verbose_name='Adressbestandteil'),
|
||||
field=models.CharField(choices=[('REALNAME', 'Vorname.Nachname'), ('USERNAME', 'Username'), ('OTHER', 'Sonstiges')], default='USERNAME', max_length=50, verbose_name='Adressbestandteil'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='list',
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ class Migration(migrations.Migration):
|
|||
migrations.AlterField(
|
||||
model_name='email',
|
||||
name='address',
|
||||
field=models.CharField(choices=[('REALNAME', 'Vorname.Nachname'), ('USERNAME', 'Username'), ('OTHER', 'Sonstiges:')], default='USERNAME', help_text='Bitte gib hier den gewünschten Adressbestandteil an,<br>der sich vor der Domain befinden soll.', max_length=50, verbose_name='Adressbestandteil'),
|
||||
field=models.CharField(choices=[('REALNAME', 'Vorname.Nachname'), ('USERNAME', 'Username'), ('OTHER', 'Sonstiges')], default='USERNAME', help_text='Bitte gib hier den gewünschten Adressbestandteil an,<br>der sich vor der Domain befinden soll.', max_length=50, verbose_name='Adressbestandteil'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='email',
|
||||
|
|
|
|||
|
|
@ -18,12 +18,12 @@ class Migration(migrations.Migration):
|
|||
migrations.AlterField(
|
||||
model_name='ifg',
|
||||
name='notes',
|
||||
field=models.TextField(blank=True, help_text='Bitte gib an wofür Du das Stipendium verwenden willst.', max_length=1000, verbose_name='Anmerkungen'),
|
||||
field=models.TextField(blank=True, help_text='Bitte gib an, wofür Du das Stipendium verwenden willst.', max_length=1000, verbose_name='Anmerkungen'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='library',
|
||||
name='notes',
|
||||
field=models.TextField(blank=True, help_text='Bitte gib an wofür Du das Stipendium verwenden willst.', max_length=1000, verbose_name='Anmerkungen'),
|
||||
field=models.TextField(blank=True, help_text='Bitte gib an, wofür Du das Stipendium verwenden willst.', max_length=1000, verbose_name='Anmerkungen'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='library',
|
||||
|
|
@ -33,7 +33,7 @@ class Migration(migrations.Migration):
|
|||
migrations.AlterField(
|
||||
model_name='literature',
|
||||
name='notes',
|
||||
field=models.TextField(blank=True, help_text='Bitte gib an wofür Du das Stipendium verwenden willst.', max_length=1000, verbose_name='Anmerkungen'),
|
||||
field=models.TextField(blank=True, help_text='Bitte gib an, wofür Du das Stipendium verwenden willst.', max_length=1000, verbose_name='Anmerkungen'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='project',
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ class Migration(migrations.Migration):
|
|||
migrations.AddField(
|
||||
model_name='travel',
|
||||
name='project_name',
|
||||
field=models.CharField(blank=True, max_length=50, null=True, verbose_name='Projektname:'),
|
||||
field=models.CharField(blank=True, max_length=50, null=True, verbose_name='Projektname'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='library',
|
||||
|
|
|
|||
|
|
@ -13,6 +13,6 @@ class Migration(migrations.Migration):
|
|||
migrations.AddField(
|
||||
model_name='travel',
|
||||
name='hotel',
|
||||
field=models.BooleanField(default=False, verbose_name='Hotelzimmer benötigt:'),
|
||||
field=models.BooleanField(default=False, verbose_name='Hotelzimmer benötigt'),
|
||||
),
|
||||
]
|
||||
|
|
|
|||
|
|
@ -13,6 +13,6 @@ class Migration(migrations.Migration):
|
|||
migrations.AlterField(
|
||||
model_name='travel',
|
||||
name='transport',
|
||||
field=models.CharField(choices=[('BAHN', 'Bahn'), ('NONE', 'Keine Fahrtkosten'), ('OTHER', 'Sonstiges (mit Begründung)')], default='BAHN', max_length=5, verbose_name='Transportmittel:'),
|
||||
field=models.CharField(choices=[('BAHN', 'Bahn'), ('NONE', 'Keine Fahrtkosten'), ('OTHER', 'Sonstiges (mit Begründung)')], default='BAHN', max_length=5, verbose_name='Transportmittel'),
|
||||
),
|
||||
]
|
||||
|
|
|
|||
|
|
@ -13,6 +13,6 @@ class Migration(migrations.Migration):
|
|||
migrations.AlterField(
|
||||
model_name='travel',
|
||||
name='hotel',
|
||||
field=models.CharField(choices=[('TRUE', 'Hotelzimmer benötigt'), ('FALSE', 'Kein Hotelzimmer benötigt')], max_length=10, verbose_name='Hotelzimmer benötigt:'),
|
||||
field=models.CharField(choices=[('TRUE', 'Hotelzimmer benötigt'), ('FALSE', 'Kein Hotelzimmer benötigt')], max_length=10, verbose_name='Hotelzimmer benötigt'),
|
||||
),
|
||||
]
|
||||
|
|
|
|||
|
|
@ -65,8 +65,8 @@ class Migration(migrations.Migration):
|
|||
('order', models.PositiveIntegerField(verbose_name='Reihenfolge')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Wikimedia Projekt',
|
||||
'verbose_name_plural': 'Wikimedia Projekte',
|
||||
'verbose_name': 'Wikimedia-Projekt',
|
||||
'verbose_name_plural': 'Wikimedia-Projekte',
|
||||
'ordering': ['order'],
|
||||
},
|
||||
),
|
||||
|
|
|
|||
|
|
@ -24,11 +24,11 @@ class Migration(migrations.Migration):
|
|||
migrations.AddField(
|
||||
model_name='project',
|
||||
name='wikimedia_projects',
|
||||
field=input.models.ProjectCategoryField(related_name='projects', to='input.wikimediaproject', verbose_name='Wikimedia Projekte'),
|
||||
field=input.models.ProjectCategoryField(related_name='projects', to='input.wikimediaproject', verbose_name='Wikimedia-Projekte'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='project',
|
||||
name='wikimedia_projects_other',
|
||||
field=models.CharField(blank=True, max_length=200, verbose_name='Wikimedia Projekte (Anderes)'),
|
||||
field=models.CharField(blank=True, max_length=200, verbose_name='Wikimedia-Projekte (Anderes)'),
|
||||
),
|
||||
]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,57 @@
|
|||
# Generated by Django 5.2.5 on 2025-11-07 15:26
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('input', '0104_alter_project_required_fields'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='account',
|
||||
options={'verbose_name': 'Kostenstelle', 'verbose_name_plural': 'Kostenstellen'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='businesscard',
|
||||
options={'verbose_name': 'Visitenkarte', 'verbose_name_plural': 'Visitenkarten'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='eliterature',
|
||||
options={'verbose_name': 'eLiteraturstipendium', 'verbose_name_plural': 'eLiteraturstipendien'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='email',
|
||||
options={'verbose_name': 'E-Mail-Adresse', 'verbose_name_plural': 'E-Mail-Adressen'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='honorarycertificate',
|
||||
options={'verbose_name': 'Bescheinigung', 'verbose_name_plural': 'Bescheinigungen'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='ifg',
|
||||
options={'verbose_name': 'IFG-Anfrage', 'verbose_name_plural': 'IFG-Anfragen'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='library',
|
||||
options={'verbose_name': 'Bibliotheksstipendium', 'verbose_name_plural': 'Bibliotheksstipendien'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='list',
|
||||
options={'verbose_name': 'Mailingliste', 'verbose_name_plural': 'Mailinglisten'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='literature',
|
||||
options={'verbose_name': 'Literaturstipendium', 'verbose_name_plural': 'Literaturstipendien'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='software',
|
||||
options={'verbose_name': 'Softwarestipendium', 'verbose_name_plural': 'Softwarestipendien'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='travel',
|
||||
options={'verbose_name': 'Reisekosten', 'verbose_name_plural': 'Reisekosten'},
|
||||
),
|
||||
]
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 5.2.5 on 2025-11-07 15:43
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('input', '0105_add_verbose_names'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='project',
|
||||
name='username',
|
||||
field=models.CharField(help_text='Wikimedia Benutzer_innenname', max_length=200, blank=True, verbose_name='Benutzer_innenname'),
|
||||
),
|
||||
]
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
# Generated by Django 5.2.5 on 2025-11-10 10:42
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('input', '0106_project_username'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='project',
|
||||
name='categories_other',
|
||||
field=models.TextField(blank=True, verbose_name='Projektkategorien (Sonstiges)'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='project',
|
||||
name='wikimedia_projects_other',
|
||||
field=models.TextField(blank=True, verbose_name='Wikimedia-Projekte (Anderes)'),
|
||||
),
|
||||
]
|
||||
|
|
@ -100,6 +100,10 @@ class Account(models.Model):
|
|||
description = models.CharField('Beschreibung', max_length=60, default='NO DESCRIPTION')
|
||||
intern_notes = models.TextField(max_length=1000, blank=True, verbose_name='interne Anmerkungen')
|
||||
|
||||
class Meta:
|
||||
verbose_name = 'Kostenstelle'
|
||||
verbose_name_plural = 'Kostenstellen'
|
||||
|
||||
def __str__(self):
|
||||
return f'{self.code} {self.description}'
|
||||
|
||||
|
|
@ -142,8 +146,8 @@ class WikimediaProject(BaseProjectCategory):
|
|||
OTHER = 'Anderes'
|
||||
|
||||
class Meta(BaseProjectCategory.Meta):
|
||||
verbose_name = 'Wikimedia Projekt'
|
||||
verbose_name_plural = 'Wikimedia Projekte'
|
||||
verbose_name = 'Wikimedia-Projekt'
|
||||
verbose_name_plural = 'Wikimedia-Projekte'
|
||||
|
||||
|
||||
class ProductCategoryChoiceIterator(ModelMultipleChoiceField.iterator):
|
||||
|
|
@ -184,7 +188,7 @@ class ProjectCategoryField(models.ManyToManyField):
|
|||
|
||||
super().__init__(**kwargs)
|
||||
|
||||
self.other_field = models.CharField(max_length=200, blank=True)
|
||||
self.other_field = models.TextField(blank=True)
|
||||
|
||||
def contribute_to_class(self, cls, name, **kwargs):
|
||||
super().contribute_to_class(cls, name, **kwargs)
|
||||
|
|
@ -213,6 +217,8 @@ class ProjectCategoryField(models.ManyToManyField):
|
|||
|
||||
|
||||
class Project(Volunteer):
|
||||
username = models.CharField(max_length=200, blank=True, verbose_name='Benutzer_innenname',
|
||||
help_text=mark_safe('Wikimedia Benutzer_innenname'))
|
||||
end_mail_send = models.BooleanField(default=False, verbose_name='Keine Projektabschlussmail schicken')
|
||||
name = models.CharField(max_length=200, verbose_name='Name des Projekts')
|
||||
description = models.CharField(max_length=500, verbose_name='Kurzbeschreibung', null=True)
|
||||
|
|
@ -347,8 +353,12 @@ class HonoraryCertificate(Intern):
|
|||
|
||||
project = models.ForeignKey(Project, null=True, blank=True, on_delete=models.SET_NULL)
|
||||
|
||||
class Meta:
|
||||
verbose_name = 'Bescheinigung'
|
||||
verbose_name_plural = 'Bescheinigungen'
|
||||
|
||||
def __str__(self):
|
||||
return f'Certificate for {self.realname}'
|
||||
return f'Bescheinigung für {self.realname}'
|
||||
|
||||
|
||||
TRANSPORT_CHOICES = {
|
||||
|
|
@ -371,15 +381,15 @@ HOTEL_CHOICES = {
|
|||
class Travel(Extern):
|
||||
# project variable is now null true and blank true, which means it can be saved without project id to be later on filled out by admins
|
||||
project = models.ForeignKey(Project, on_delete=models.CASCADE, null=True, blank=True)
|
||||
project_name = models.CharField(max_length=50, null=True, blank=True, verbose_name='Projektname:')
|
||||
transport = models.CharField(max_length=5, choices=TRANSPORT_CHOICES.items(), default='BAHN', verbose_name='Transportmittel:')
|
||||
project_name = models.CharField(max_length=50, null=True, blank=True, verbose_name='Projektname')
|
||||
transport = models.CharField(max_length=5, choices=TRANSPORT_CHOICES.items(), default='BAHN', verbose_name='Transportmittel')
|
||||
other_transport = models.CharField(max_length=200, null=True, blank=True, verbose_name='Sonstige Transportmittel (mit Begründung)')
|
||||
travelcost = models.CharField(max_length=10, default='0', verbose_name='Fahrtkosten')
|
||||
checkin = models.DateField(blank=True, null=True, verbose_name='Anreise')
|
||||
checkout = models.DateField(blank=True, null=True, verbose_name='Abreise')
|
||||
payed_for_hotel_by = models.CharField(max_length=4, choices=PAYEDBY_CHOICES.items(), blank=True, null=True, verbose_name='Kostenauslage Hotel durch')
|
||||
payed_for_travel_by = models.CharField(max_length=4, choices=PAYEDBY_CHOICES.items(), blank=True, null=True, verbose_name='Kostenauslage Fahrt durch')
|
||||
hotel = models.CharField(max_length=10, choices=HOTEL_CHOICES.items(), verbose_name='Hotelzimmer benötigt:')
|
||||
hotel = models.CharField(max_length=10, choices=HOTEL_CHOICES.items(), verbose_name='Hotelzimmer benötigt')
|
||||
notes = models.TextField(max_length=1000, blank=True, verbose_name='Anmerkungen')
|
||||
request_url = models.URLField(max_length=2000, verbose_name='Antrag (URL)')
|
||||
intern_notes = models.TextField(max_length=1000, blank=True, verbose_name='interne Anmerkungen')
|
||||
|
|
@ -387,6 +397,21 @@ class Travel(Extern):
|
|||
# use content type model to get the end date for the project foreign key
|
||||
project_end_quartal = models.CharField(max_length=15, null=True, blank=True, verbose_name='Quartal Projekt Ende')
|
||||
|
||||
class Meta:
|
||||
verbose_name = 'Reisekosten'
|
||||
verbose_name_plural = 'Reisekosten'
|
||||
|
||||
def __str__(self):
|
||||
return f'Reisekosten für {self.realname}'
|
||||
|
||||
def clean(self):
|
||||
if (self.checkin and self.checkout) and (self.checkout < self.checkin):
|
||||
raise forms.ValidationError({
|
||||
'checkout': [
|
||||
forms.ValidationError('Das Datum der Abreise muss nach dem Datum der Anreise liegen.'),
|
||||
],
|
||||
})
|
||||
|
||||
|
||||
@receiver(pre_save, sender=Travel, dispatch_uid='get_project_end')
|
||||
def get_project_end(sender, instance, **kwargs):
|
||||
|
|
@ -400,7 +425,7 @@ class Grant(RequestUrlMixin, Extern):
|
|||
cost = models.CharField(max_length=10, verbose_name='Kosten',
|
||||
help_text='Bitte gib die ungefähr zu erwartenden Kosten in Euro an.')
|
||||
notes = models.TextField(max_length=1000, blank=True, verbose_name='Anmerkungen',
|
||||
help_text='Bitte gib an wofür Du das Stipendium verwenden willst.')
|
||||
help_text='Bitte gib an, wofür Du das Stipendium verwenden willst.')
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
|
@ -443,6 +468,10 @@ class Library(Grant):
|
|||
duration = models.CharField(max_length=100, verbose_name='Dauer')
|
||||
intern_notes = models.TextField(max_length=1000, blank=True, verbose_name='interne Anmerkungen')
|
||||
|
||||
class Meta:
|
||||
verbose_name = 'Bibliotheksstipendium'
|
||||
verbose_name_plural = 'Bibliotheksstipendien'
|
||||
|
||||
def __str__(self):
|
||||
return self.library
|
||||
|
||||
|
|
@ -460,6 +489,8 @@ class ELiterature(Library):
|
|||
|
||||
class Meta:
|
||||
proxy = True
|
||||
verbose_name = 'eLiteraturstipendium'
|
||||
verbose_name_plural = 'eLiteraturstipendien'
|
||||
|
||||
|
||||
class Software(Library):
|
||||
|
|
@ -470,6 +501,8 @@ class Software(Library):
|
|||
|
||||
class Meta:
|
||||
proxy = True
|
||||
verbose_name = 'Softwarestipendium'
|
||||
verbose_name_plural = 'Softwarestipendien'
|
||||
|
||||
|
||||
SELFBUY_CHOICES = {
|
||||
|
|
@ -492,14 +525,22 @@ class Literature(TermsConsentMixin, Grant):
|
|||
Telefonnummer, E-Mail-Adresse usw.). Trenne die einzelnen Angaben durch Zeilenumbrüche.'))
|
||||
intern_notes = models.TextField(max_length=1000, blank=True, verbose_name='interne Anmerkungen')
|
||||
|
||||
class Meta:
|
||||
verbose_name = 'Literaturstipendium'
|
||||
verbose_name_plural = 'Literaturstipendien'
|
||||
|
||||
|
||||
class IFG(Grant):
|
||||
url = models.URLField(max_length=2000, verbose_name='URL',
|
||||
help_text='Bitte gib den Link zu deiner Anfrage bei Frag den Staat an.')
|
||||
intern_notes = models.TextField(max_length=1000, blank=True, verbose_name='interne Anmerkungen')
|
||||
|
||||
class Meta:
|
||||
verbose_name = 'IFG-Anfrage'
|
||||
verbose_name_plural = 'IFG-Anfragen'
|
||||
|
||||
def __str__(self):
|
||||
return 'IFG-Anfrage von ' + self.realname
|
||||
return f'IFG-Anfrage von {self.realname}'
|
||||
|
||||
|
||||
DOMAIN_CHOICES = {
|
||||
|
|
@ -523,7 +564,7 @@ class Domain(RequestUrlMixin, Extern):
|
|||
MAIL_CHOICES = {
|
||||
'REALNAME': 'Vorname.Nachname',
|
||||
'USERNAME': 'Username',
|
||||
'OTHER': 'Sonstiges:',
|
||||
'OTHER': 'Sonstiges',
|
||||
}
|
||||
|
||||
ADULT_CHOICES = {
|
||||
|
|
@ -542,6 +583,10 @@ class Email(TermsConsentMixin, Domain):
|
|||
adult = models.CharField(max_length=10, verbose_name='Volljährigkeit', choices=ADULT_CHOICES.items(), default='FALSE')
|
||||
intern_notes = models.TextField(max_length=1000, blank=True, verbose_name='interne Anmerkungen')
|
||||
|
||||
class Meta:
|
||||
verbose_name = 'E-Mail-Adresse'
|
||||
verbose_name_plural = 'E-Mail-Adressen'
|
||||
|
||||
|
||||
class List(TermsConsentMixin, Domain):
|
||||
address = models.CharField(max_length=50, default='NO_ADDRESS',
|
||||
|
|
@ -549,6 +594,10 @@ class List(TermsConsentMixin, Domain):
|
|||
help_text=mark_safe('Bitte gib hier den gewünschten Adressbestandteil an,<br>der sich vor der Domain befinden soll.'))
|
||||
intern_notes = models.TextField(max_length=1000, blank=True, verbose_name='interne Anmerkungen')
|
||||
|
||||
class Meta:
|
||||
verbose_name = 'Mailingliste'
|
||||
verbose_name_plural = 'Mailinglisten'
|
||||
|
||||
|
||||
PROJECT_CHOICE = {
|
||||
'PEDIA': 'Wikipedia',
|
||||
|
|
@ -593,6 +642,10 @@ class BusinessCard(RequestUrlMixin, TermsConsentMixin, Extern):
|
|||
send_data_to_print = models.BooleanField(default=False, verbose_name=mark_safe('Datenweitergabe erlauben'), help_text=mark_safe('Hiermit erlaube ich die Weitergabe meiner Daten (Name, Postadresse) an den von Wikimedia<br> Deutschland ausgewählten Dienstleister (z. B. <a href="wir-machen-druck.de">wir-machen-druck.de</a>) zum Zwecke des direkten <br> Versands der Druckerzeugnisse an mich.'))
|
||||
intern_notes = models.TextField(max_length=1000, blank=True, verbose_name='interne Anmerkungen')
|
||||
|
||||
class Meta:
|
||||
verbose_name = 'Visitenkarte'
|
||||
verbose_name_plural = 'Visitenkarten'
|
||||
|
||||
|
||||
MODELS = {
|
||||
TYPE_BIB: Library,
|
||||
|
|
|
|||
|
|
@ -6,6 +6,16 @@
|
|||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.wm-table.start {
|
||||
td, th {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.applications {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
.col-request {
|
||||
width: 40%;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
const otherInput = $(otherInputSelector);
|
||||
const otherLabelSelector = 'label'.concat('[for="', this.id, '_other"]');
|
||||
const otherLabel = $(otherLabelSelector);
|
||||
const otherTableRow = otherInput.parents('tr');
|
||||
|
||||
const toggle = function () {
|
||||
const checked = otherCheckbox.prop('checked');
|
||||
|
|
@ -14,6 +15,7 @@
|
|||
otherInput.prop('required', checked);
|
||||
otherLabel.toggleClass('required', checked);
|
||||
otherLabel.css('opacity', checked ? 1 : 0.3);
|
||||
otherTableRow.css('visibility', checked ? 'visible' : 'collapse');
|
||||
|
||||
if (checked) {
|
||||
otherInput.focus();
|
||||
|
|
|
|||
|
|
@ -1,32 +1,44 @@
|
|||
{% extends "input/base.html" %}
|
||||
{% 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="{% url 'extern' type='projektfoerderung' %}">Projektförderung</a>
|
||||
mit einer Gesamtsumme unter 1.000,— EUR
|
||||
</li>
|
||||
<li>
|
||||
<a href="{% url 'projektfoerderung-ab-1000' %}">Projektförderung</a>
|
||||
mit einer Gesamtsumme ab 1.000,— EUR
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<strong>Serviceleistungen</strong>
|
||||
<ul>
|
||||
{% for info in services %}
|
||||
<li><a href="{% url 'extern' type=info.path %}">{{ info.label|striptags }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
{% block head_extra %}
|
||||
<title>Was möchtest du beantragen?</title>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<form class="page-centered" method="post">
|
||||
{% csrf_token %}
|
||||
|
||||
<table class="wm-table start">
|
||||
<tbody class="applications">
|
||||
<tr>
|
||||
<th class="col-request">Was möchtest du beantragen?</th>
|
||||
<td>
|
||||
{% for title, services in applications %}
|
||||
<strong>{{ title }}</strong>
|
||||
<ul>
|
||||
{% for service in services %}
|
||||
<li>
|
||||
<label>
|
||||
<input type="radio" name="url" value="{% url 'extern' type=service.path %}" />
|
||||
<span>{{ service.label|striptags }}</span>
|
||||
</label>
|
||||
<span>(<a href="{{ service.url }}" target="_blank">mehr erfahren</a>)</span>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endfor %}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<button type="submit">Beantragen</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</form>
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
{% extends "input/base.html" %}
|
||||
{% load static %}
|
||||
|
||||
{% block head_extra %}
|
||||
<title>{{ type_label|striptags }}</title>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{{ form.media }}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,4 +21,7 @@
|
|||
Für Fragen steht dir das Team Community-Konferenzen & Förderung gern unter
|
||||
<a href="mailto:community@wikimedia.de">community@wikimedia.de</a> zur Verfügung.
|
||||
</p>
|
||||
<div class="page-centered">
|
||||
<button type="button" onclick="history.back()">Zurück</button>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -1,9 +0,0 @@
|
|||
<html>
|
||||
<body>
|
||||
<p>Hallo {{ data.realname }},</p>
|
||||
|
||||
<p>Deine Förderanfrage {{project.name}} wurde leider abgelehnt.</p>
|
||||
|
||||
<p>Fragen? <a href="mailto:community@wikimedia.de">community@wikimedia.de</a></p>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
Hallo {{ data.realname }},
|
||||
|
||||
deine Förderanfrage {{project.name}} wurde leider abgelehnt.
|
||||
|
||||
Fragen? community@wikimedia.de
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
<html>
|
||||
<body>
|
||||
<p>Hallo Team Community-Konferenzen & Förderung,</p>
|
||||
|
||||
<p>Die Förderanfrage {{project.name}} von {{ data.realname }} wurde abgelehnt.</p>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
Hallo Team Community-Konferenzen & Förderung,
|
||||
|
||||
die Förderanfrage {{project.name}} von {{ data.realname }} wurde abgelehnt.
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
<html>
|
||||
<body>
|
||||
<p>Hallo {{ data.realname }},</p>
|
||||
|
||||
<p>Deine Förderanfrage {{project.name}} wurde bewilligt.</p>
|
||||
|
||||
<p>Das Team Community-Konferenzen & Förderung meldet sich bald bei dir.<br>
|
||||
Fragen? <a href="mailto:community@wikimedia.de">community@wikimedia.de</a></p>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
Hallo {{ data.realname }},
|
||||
|
||||
deine Förderanfrage {{project.name}} wurde bewilligt.
|
||||
|
||||
Das Team Community-Konferenzen & Förderung meldet sich bald bei dir.
|
||||
Fragen? community@wikimedia.de
|
||||
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
<html>
|
||||
<body>
|
||||
<p>Hallo Team Community-Konferenzen & Förderung,</p>
|
||||
|
||||
<p>Die Förderanfrage {{project.name}} von {{ data.realname }} wurde bewilligt.</p>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
Hallo Team Community-Konferenzen & Förderung,
|
||||
|
||||
die Förderanfrage {{project.name}} von {{ data.realname }} wurde bewilligt.
|
||||
|
|
@ -1,60 +1,26 @@
|
|||
<html>
|
||||
<html lang="de">
|
||||
<body>
|
||||
<p>Hallo Team Community-Konferenzen & Förderung,</p>
|
||||
|
||||
<p>es gibt eine neue Anfrage von {{ data.realname }}.</p>
|
||||
|
||||
<p>{{ data.username|default:data.realname }} ({{ data.email }}) fragt an: {{ data.type_label|striptags }}</p>
|
||||
<p>{{ data.username|default:data.realname }} ({{ data.email }}) fragt an: {{ type_label }}</p>
|
||||
|
||||
{% if data.choice in data.grant %}<br>
|
||||
Vorraussichtliche Kosten: {{data.cost}}<br>
|
||||
Anmerkungen: {{data.notes}} {% endif %} {% if data.choice in data.domain %}<br>
|
||||
Domain: <a href="{{data.domain}}">{{data.domain}}</a><br>
|
||||
Adressenbestandteil: {{data.address}} <br> {% endif %} {% if data.choice == 'BIB' %}
|
||||
Bibliothek: {{data.library}}<br>
|
||||
Dauer: {{data.duration}} <br> {% elif data.choice == 'ELIT' %}
|
||||
Datenbank: {{data.library}}<br>
|
||||
Dauer: {{data.duration}} <br> {% elif data.choice == 'SOFT' %}
|
||||
Software: {{data.library}}<br>
|
||||
Dauer: {{data.duration}} <br> {% elif data.choice == 'IFG'%}
|
||||
Anfrage-URL: <a href="{{data.url}}">{{data.url}}</a> <br> {% elif data.choice == 'LIT'%}
|
||||
Info zum Werk: {{data.info}}<br>
|
||||
Bezugsquelle: {{data.source}} <br> {% elif data.choice == 'MAIL'%}
|
||||
Adressenbestandteil frei gewählt: {{data.other}} <br> {% elif data.choice == 'VIS'%}
|
||||
Wikimedia-Projekt: {{data.project}}<br>
|
||||
Persönliche Daten: {{data.data}}<br>
|
||||
Variante: {{data.variant}}<br>
|
||||
Sendungsadrese: {{data.send_to}} <br> {% endif %}
|
||||
<p>
|
||||
{% for label, value in form_data.items %}
|
||||
{{ label }}: {{ value }} <br />
|
||||
{% endfor %}
|
||||
</p>
|
||||
|
||||
<p>Zum Eintrag in der Förderdatenbank:
|
||||
{% if data.choice == 'BIB' %}
|
||||
<a href="{{data.url_prefix}}/admin/input/library/{{data.pk}}/change">{{data.url_prefix}}/admin/input/library/{{data.pk}}/change</a>
|
||||
{% elif data.choice == 'ELIT'%}
|
||||
<a href="{{data.url_prefix}}/admin/input/library/{{data.pk}}/change">{{data.url_prefix}}/admin/input/library/{{data.pk}}/change</a>
|
||||
{% elif data.choice == 'LIT'%}
|
||||
<a href="{{data.url_prefix}}/admin/input/literature/{{data.pk}}/change">{{data.url_prefix}}/admin/input/literature/{{data.pk}}/change</a>
|
||||
{% elif data.choice == 'MAIL'%}
|
||||
<a href="{{data.url_prefix}}/admin/input/email/{{data.pk}}/change">{{data.url_prefix}}/admin/input/email/{{data.pk}}/change</a>
|
||||
{% elif data.choice == 'IFG'%}
|
||||
<a href="{{data.url_prefix}}/admin/input/ifg/{{data.pk}}/change">{{data.url_prefix}}/admin/input/ifg/{{data.pk}}/change</a>
|
||||
{% elif data.choice == 'LIST'%}
|
||||
<a href="{{data.url_prefix}}/admin/input/list/{{data.pk}}/change">{{data.url_prefix}}/admin/input/list/{{data.pk}}/change</a>
|
||||
{% elif data.choice == 'TRAV'%}
|
||||
<a href="{{data.url_prefix}}/admin/input/travel/{{data.pk}}/change">{{data.url_prefix}}/admin/input/travel/{{data.pk}}/change</a>
|
||||
{% elif data.choice == 'SOFT'%}
|
||||
<a href="{{data.url_prefix}}/admin/input/library/{{data.pk}}/change">{{data.url_prefix}}/admin/input/library/{{data.pk}}/change</a>
|
||||
{% elif data.choice == 'VIS'%}
|
||||
<a href="{{data.url_prefix}}/admin/input/businesscard/{{data.pk}}/change">{{data.url_prefix}}/admin/input/businesscard/{{data.pk}}/change</a>
|
||||
<p>Zum Eintrag in der Förderdatenbank: <a href="{{ urls.admin }}">{{ urls.admin }}</a></p>
|
||||
|
||||
{% if urls.authorize %}
|
||||
<p>Zum Genehmigen hier klicken: <a href="{{ urls.authorize }}">{{ urls.authorize }}</a></p>
|
||||
{% endif %}
|
||||
</p>
|
||||
|
||||
<p>Zum Genehmigen hier klicken:
|
||||
<a href="{{data.url_prefix}}{% url 'authorize' data.choice data.pk %}">{{data.url_prefix}}{% url 'authorize' data.choice data.pk %}</a>
|
||||
</p>
|
||||
|
||||
<p>Zum Ablehnen hier klicken:
|
||||
<a href="{{data.url_prefix}}{% url 'deny' data.choice data.pk %}">{{data.url_prefix}}{% url 'deny' data.choice data.pk %}</a>
|
||||
</p>
|
||||
{% if urls.deny %}
|
||||
<p>Zum Ablehnen hier klicken: <a href="{{ urls.deny }}">{{ urls.deny }}</a></p>
|
||||
{% endif %}
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -1,52 +1,12 @@
|
|||
Hallo Team Communitys und Engagement,
|
||||
Hallo Team Community-Konferenzen & Förderung,
|
||||
|
||||
es gab einen neuen Antrag von {{data.realname}}.
|
||||
es gibt eine neue Anfrage von {{ data.realname }}.
|
||||
|
||||
Der Nutzer mit dem Username {{data.username}} ({{data.email}}) fragt ein_e {{data.type_label|striptags}} an.
|
||||
{% if data.choice in data.grant %}
|
||||
Vorraussichtliche Kosten: {{data.cost}}
|
||||
Anmerkungen: {{data.notes}} {% endif %} {% if data.choice in data.domain %}
|
||||
Domain: {{data.domain}}
|
||||
Adressenbestandteil: {{data.address}} {% endif %} {% if data.choice == 'BIB' %}
|
||||
Bibliothek: {{data.library}}
|
||||
Dauer: {{data.duration}} {% elif data.choice == 'ELIT' %}
|
||||
Datenbank: {{data.library}}
|
||||
Dauer: {{data.duration}} {% elif data.choice == 'SOFT' %}
|
||||
Software: {{data.library}}
|
||||
Dauer: {{data.duration}} {% elif data.choice == 'IFG'%}
|
||||
Anfrage-URL: {{data.url}} {% elif data.choice == 'LIT'%}
|
||||
Info zum Werk: {{data.info}}
|
||||
Bezugsquelle: {{data.source}} {% elif data.choice == 'MAIL'%}
|
||||
Adressenbestandteil frei gewählt: {{data.other}} {% elif data.choice == 'VIS'%}
|
||||
Wikimedia-Projekt: {{data.project}}
|
||||
Persönliche Daten: {{data.data}}
|
||||
Variante: {{data.variant}}
|
||||
Sendungsadrese: {{data.send_to}} {% endif %}
|
||||
{{ data.username|default:data.realname }} ({{ data.email }}) fragt an: {{ type_label }}
|
||||
|
||||
Zum Eintrag in der Förderdatenbank:
|
||||
{% if data.choice == 'BIB' %}
|
||||
<a href="{{data.url_prefix}}/admin/input/library/{{data.pk}}/change">{{data.url_prefix}}/admin/input/library/{{data.pk}}/change</a>
|
||||
{% elif data.choice == 'ELIT'%}
|
||||
<a href="{{data.url_prefix}}/admin/input/library/{{data.pk}}/change">{{data.url_prefix}}/admin/input/library/{{data.pk}}/change</a>
|
||||
{% elif data.choice == 'LIT'%}
|
||||
<a href="{{data.url_prefix}}/admin/input/literature/{{data.pk}}/change">{{data.url_prefix}}/admin/input/literature/{{data.pk}}/change</a>
|
||||
{% elif data.choice == 'MAIL'%}
|
||||
<a href="{{data.url_prefix}}/admin/input/email/{{data.pk}}/change">{{data.url_prefix}}/admin/input/email/{{data.pk}}/change</a>
|
||||
{% elif data.choice == 'IFG'%}
|
||||
<a href="{{data.url_prefix}}/admin/input/ifg/{{data.pk}}/change">{{data.url_prefix}}/admin/input/ifg/{{data.pk}}/change</a>
|
||||
{% elif data.choice == 'LIST'%}
|
||||
<a href="{{data.url_prefix}}/admin/input/list/{{data.pk}}/change">{{data.url_prefix}}/admin/input/list/{{data.pk}}/change</a>
|
||||
{% elif data.choice == 'TRAV'%}
|
||||
<a href="{{data.url_prefix}}/admin/input/travel/{{data.pk}}/change">{{data.url_prefix}}/admin/input/travel/{{data.pk}}/change</a>
|
||||
{% elif data.choice == 'SOFT'%}
|
||||
<a href="{{data.url_prefix}}/admin/input/library/{{data.pk}}/change">{{data.url_prefix}}/admin/input/library/{{data.pk}}/change</a>
|
||||
{% elif data.choice == 'VIS'%}
|
||||
<a href="{{data.url_prefix}}/admin/input/businesscard/{{data.pk}}/change">{{data.url_prefix}}/admin/input/businesscard/{{data.pk}}/change</a>
|
||||
{% endif %}
|
||||
{% for label, value in form_data.items %}{{ label }}: {{ value }}
|
||||
{% endfor %}
|
||||
|
||||
|
||||
Zum Genehmigen hier klicken: {{data.url_prefix}}{% url 'authorize' data.choice data.pk %}
|
||||
|
||||
Zu Ablehnen hier klicken: {{data.url_prefix}}{% url 'deny' data.choice data.pk %}
|
||||
|
||||
Stets zu Diensten, Deine Förderdatenbank
|
||||
Zum Eintrag in der Förderdatenbank: {{ urls.admin }}
|
||||
{% if urls.authorize %}Zum Genehmigen hier klicken: {{ urls.authorize }}{% endif %}
|
||||
{% if urls.deny %}Zum Ablehnen hier klicken: {{ urls.deny }}{% endif %}
|
||||
|
|
|
|||
|
|
@ -1,29 +1,14 @@
|
|||
<html>
|
||||
<html lang="de">
|
||||
<body>
|
||||
<p>Hallo {{ data.username|default:data.realname }},</p>
|
||||
<p>Hallo {{ applicant_name }},</p>
|
||||
|
||||
<p>vielen Dank für deine Anfrage ({{ data.type_label|striptags }}), die bei uns eingegangen ist.</p>
|
||||
<p>vielen Dank für deine Anfrage ({{ type_label }}), die bei uns eingegangen ist.</p>
|
||||
|
||||
Dies ist eine automatisch generierte E-Mail. Im Folgenden findest du deine Formulareingaben nochmals zu deiner Übersicht:<br>
|
||||
{% if data.choice in data.grant %}<br>
|
||||
Vorraussichtliche Kosten: {{data.cost}}<br>
|
||||
Anmerkungen: {{data.notes}} {% endif %} {% if data.choice in data.domain %}<br>
|
||||
Domain: <a href="{{data.domain}}">{{data.domain}}</a><br>
|
||||
Adressenbestandteil: {{data.address}} {% endif %} {% if data.choice == 'BIB' %}<br>
|
||||
Bibliothek: {{data.library}}<br>
|
||||
Dauer: {{data.duration}} {% elif data.choice == 'ELIT' %}<br>
|
||||
Datenbank: {{data.library}}<br>
|
||||
Dauer: {{data.duration}} {% elif data.choice == 'SOFT' %}<br>
|
||||
Software: {{data.library}}<br>
|
||||
Dauer: {{data.duration}} {% elif data.choice == 'IFG'%}<br>
|
||||
Anfrage-URL: <a href="{{data.url}}">{{data.url}}</a> {% elif data.choice == 'LIT'%}<br>
|
||||
Info zum Werk: {{data.info}}<br>
|
||||
Bezugsquelle: {{data.source}} {% elif data.choice == 'MAIL'%}<br>
|
||||
Adressenbestandteil frei gewählt: {{data.other}} {% elif data.choice == 'VIS'%}<br>
|
||||
Wikimedia-Projekt: {{data.project}}<br>
|
||||
Persönliche Daten: {{data.data}}<br>
|
||||
Variante: {{data.variant}}<br>
|
||||
Sendungsadrese: {{data.send_to}} {% endif %}<br>
|
||||
|
||||
{% for label, value in form_data.items %}
|
||||
{{ label }}: {{ value }} <br />
|
||||
{% endfor %}
|
||||
|
||||
<p>Das Team Community-Konferenzen & Förderung wird sich um deine Anfrage kümmern und sich in den nächsten Tagen bei dir melden. Wenn du Fragen hast, wende dich gern jederzeit an <a href="mailto:community@wikimedia.de">community@wikimedia.de</a>.</p>
|
||||
|
||||
|
|
@ -44,4 +29,4 @@ Sendungsadrese: {{data.send_to}} {% endif %}<br>
|
|||
<p>Datenschutzerklärung:<br>
|
||||
Soweit Sie uns personenbezogene Daten mitteilen, verarbeiten wir diese Daten gemäß unserer <a href="https://www.wikimedia.de/datenschutz/">Datenschutzerklärung </a>.</p>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -1,29 +1,22 @@
|
|||
Hallo {{data.realname}},
|
||||
Hallo {{ applicant_name }},
|
||||
|
||||
wir haben Deine Anfrage ({{data.type_label|striptags}}) erhalten.
|
||||
{% if data.choice in data.grant %}
|
||||
Vorraussichtliche Kosten: {{data.cost}}
|
||||
Anmerkungen: {{data.notes}} {% endif %} {% if data.choice in data.domain %}
|
||||
Domain: {{data.domain}}
|
||||
Adressenbestandteil: {{data.address}} {% endif %} {% if data.choice == 'BIB' %}
|
||||
Bibliothek: {{data.library}}
|
||||
Dauer: {{data.duration}} {% elif data.choice == 'ELIT' %}
|
||||
Datenbank: {{data.library}}
|
||||
Dauer: {{data.duration}} {% elif data.choice == 'SOFT' %}
|
||||
Software: {{data.library}}
|
||||
Dauer: {{data.duration}} {% elif data.choice == 'IFG'%}
|
||||
Anfrage-URL: {{data.url}} {% elif data.choice == 'LIT'%}
|
||||
Info zum Werk: {{data.info}}
|
||||
Bezugsquelle: {{data.source}} {% elif data.choice == 'MAIL'%}
|
||||
Adressenbestandteil frei gewählt: {{data.other}} {% elif data.choice == 'VIS'%}
|
||||
Wikimedia-Projekt: {{data.project}}
|
||||
Persönliche Daten: {{data.data}}
|
||||
Variante: {{data.variant}}
|
||||
Sendungsadrese: {{data.send_to}} {% endif %}
|
||||
vielen Dank für deine Anfrage ({{type_label}}), die bei uns eingegangen ist.
|
||||
|
||||
Das Team Comunitys und Engagement wird sich um die Bearbeitung deiner Anfrage kümmern
|
||||
und sich in den nächsten Tagen bei dir melden. Solltest du Rückfragen haben,
|
||||
wende dich gern an community@wikimedia.de.
|
||||
{% for label, value in form_data.items %}{{ label }}: {{ value }}
|
||||
{% endfor %}
|
||||
|
||||
Viele Grüße, dein freundliches aber komplett unmenschliches automatisches
|
||||
Formularbeantwortungssystem.
|
||||
Das Team Community-Konferenzen & Förderung wird sich um deine Anfrage kümmern und sich in den nächsten Tagen bei dir melden. Wenn du Fragen hast, wende dich gern jederzeit an community@wikimedia.de.
|
||||
|
||||
--
|
||||
|
||||
Wikimedia Deutschland e. V. | Tempelhofer Ufer 23–24 | 10963 Berlin
|
||||
Zentrale: +49 30 5771162-0
|
||||
https://wikimedia.de
|
||||
|
||||
Unsere Vision ist eine Welt, in der alle Menschen am Wissen der Menschheit teilhaben, es nutzen und mehren können. Helfen Sie uns dabei!
|
||||
https://spenden.wikimedia.de
|
||||
|
||||
Wikimedia Deutschland – Gesellschaft zur Förderung Freien Wissens e. V. Eingetragen im Vereinsregister des Amtsgerichts Charlottenburg, VR 23855 B. Als gemeinnützig anerkannt durch das Finanzamt für Körperschaften I Berlin, Steuernummer 27/029/42207. Geschäftsführende Vorständin: Franziska Heine.
|
||||
|
||||
Datenschutzerklärung:
|
||||
Soweit Sie uns personenbezogene Daten mitteilen, verarbeiten wir diese Daten gemäß unserer Datenschutzerklärung (https://www.wikimedia.de/datenschutz/).
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import datetime
|
||||
|
||||
from django.core import mail
|
||||
from django.forms import model_to_dict
|
||||
from django.test import TestCase
|
||||
|
||||
|
|
@ -109,7 +108,4 @@ class AdminTestCase(TestCase):
|
|||
if data[key] is None:
|
||||
data.pop(key)
|
||||
|
||||
with self.captureOnCommitCallbacks(execute=True):
|
||||
request(self, url, expected_url=expected_url, data=data)
|
||||
|
||||
self.assertEqual(len(mail.outbox), 2)
|
||||
request(self, url, expected_url=expected_url, data=data)
|
||||
|
|
|
|||
|
|
@ -1,11 +1,17 @@
|
|||
import random
|
||||
|
||||
from django.forms import model_to_dict
|
||||
from django.shortcuts import resolve_url
|
||||
from django.test import TestCase
|
||||
|
||||
from input.models import Library, TYPE_PROJ
|
||||
from foerderbarometer.constants import *
|
||||
from input.models import Library, ProjectCategory, WikimediaProject
|
||||
from input.utils.testing import create_superuser, login, request
|
||||
from input.views import TYPES
|
||||
from input.views import PROJECT_FUNDING, TYPES, ApplicationView
|
||||
|
||||
PATHS = {TYPES[path].code: path for path in TYPES}
|
||||
PATHS[TYPE_PROJ] = PROJECT_FUNDING[0].path
|
||||
CODES = list(PATHS)
|
||||
|
||||
|
||||
class AnonymousViewTestCase(TestCase):
|
||||
|
|
@ -18,6 +24,15 @@ class AnonymousViewTestCase(TestCase):
|
|||
def test_extern(self):
|
||||
request(self, 'extern')
|
||||
|
||||
def test_extern_post(self):
|
||||
code = random.choice(CODES)
|
||||
url = self.helper_url(code)
|
||||
|
||||
request(self, 'extern', expected_url=url, data={'url': url})
|
||||
|
||||
def test_extern_invalid_url(self):
|
||||
request(self, 'extern', data={'url': 'https://domain.not/allowed/to/be/redirected/'})
|
||||
|
||||
@classmethod
|
||||
def get_step_data(cls, choice, **data):
|
||||
return {
|
||||
|
|
@ -50,15 +65,15 @@ class AnonymousViewTestCase(TestCase):
|
|||
|
||||
def test_extern_types(self):
|
||||
types = [
|
||||
('BIB', 'Bibliotheksausweis'),
|
||||
('ELIT', 'Online-Ressource'),
|
||||
('MAIL', 'Mailadresse beantragen'),
|
||||
('IFG', 'gewonnenen Informationen'),
|
||||
('LIT', 'Literatur verwenden'),
|
||||
('LIST', 'Mailingliste beantragen'),
|
||||
('TRAV', 'Transportmittel'),
|
||||
('SOFT', 'Lizenz'),
|
||||
('VIS', 'DIN 5008'),
|
||||
(TYPE_BIB, 'Bibliotheksausweis'),
|
||||
(TYPE_ELIT, 'Online-Ressource'),
|
||||
(TYPE_MAIL, 'Mailadresse beantragen'),
|
||||
(TYPE_IFG, 'gewonnenen Informationen'),
|
||||
(TYPE_LIT, 'Literatur verwenden'),
|
||||
(TYPE_LIST, 'Mailingliste beantragen'),
|
||||
(TYPE_TRAV, 'Transportmittel'),
|
||||
(TYPE_SOFT, 'Lizenz'),
|
||||
(TYPE_VIS, 'DIN 5008'),
|
||||
(TYPE_PROJ, 'Projektförderung'),
|
||||
]
|
||||
|
||||
|
|
@ -70,7 +85,7 @@ class AnonymousViewTestCase(TestCase):
|
|||
self.assertContains(response, text)
|
||||
|
||||
def test_extern_travel(self):
|
||||
self.helper_extern('TRAV', 'Transportmittel', {
|
||||
self.helper_extern(TYPE_TRAV, 'Transportmittel', {
|
||||
'project_name': 'Test',
|
||||
'transport': 'BAHN',
|
||||
'travelcost': 10,
|
||||
|
|
@ -81,27 +96,27 @@ class AnonymousViewTestCase(TestCase):
|
|||
})
|
||||
|
||||
def test_extern_lit(self):
|
||||
self.helper_extern('LIT', 'Literatur verwenden', {
|
||||
self.helper_extern(TYPE_LIT, 'Literatur verwenden', {
|
||||
'cost': 20,
|
||||
'info': 'Test',
|
||||
'source': 'Test',
|
||||
'notes': '',
|
||||
'selfbuy': 'TRUE',
|
||||
'selfbuy_data': 'NONE',
|
||||
'selfbuy': 'FALSE',
|
||||
'selfbuy_data': 'Test',
|
||||
'selfbuy_give_data': True,
|
||||
'check': True,
|
||||
'terms_accepted': True,
|
||||
})
|
||||
|
||||
def test_extern_lit_without_consent_fails(self):
|
||||
response = self.helper_extern_base('LIT', 'Literatur verwenden', {
|
||||
response = self.helper_extern_base(TYPE_LIT, 'Literatur verwenden', {
|
||||
'cost': 20,
|
||||
'info': 'Test',
|
||||
'source': 'Test',
|
||||
'notes': '',
|
||||
'selfbuy': 'TRUE',
|
||||
'selfbuy_data': 'NONE',
|
||||
'selfbuy_give_data': True,
|
||||
'selfbuy_data': '',
|
||||
'selfbuy_give_data': False,
|
||||
'check': False,
|
||||
})
|
||||
|
||||
|
|
@ -115,9 +130,32 @@ class AnonymousViewTestCase(TestCase):
|
|||
'notes': '',
|
||||
})
|
||||
|
||||
def test_extern_proj(self):
|
||||
category = ProjectCategory.objects.order_by('?').first()
|
||||
wikimedia_project = WikimediaProject.objects.order_by('?').first()
|
||||
|
||||
self.helper_extern(TYPE_PROJ, 'Projektförderung', {
|
||||
'name': 'Test',
|
||||
'description': 'Test',
|
||||
'categories': [category.id, 0],
|
||||
'categories_other': 'Test',
|
||||
'wikimedia_projects': [wikimedia_project.id, 0],
|
||||
'wikimedia_projects_other': 'Test',
|
||||
'start': '2025-01-01',
|
||||
'end': '2025-01-02',
|
||||
'participants_estimated': 1,
|
||||
'cost': 20,
|
||||
})
|
||||
|
||||
def test_extern_invalid_code(self):
|
||||
request(self, 'extern', args=['invalid'], status_code=404)
|
||||
|
||||
def test_unknown_name(self):
|
||||
obj = Library(type=Library.TYPE)
|
||||
data = model_to_dict(obj)
|
||||
name = ApplicationView.get_recipient_name(obj, data)
|
||||
|
||||
self.assertEqual(name, 'Unbekannt')
|
||||
|
||||
|
||||
class AuthenticatedViewTestCase(TestCase):
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
from typing import Iterable
|
||||
|
||||
|
||||
def reorder_value(values: Iterable, value, *, after=None, before=None):
|
||||
"""
|
||||
Reorders a value after or before another value in the given list.
|
||||
Does not work properly for duplicate or None values.
|
||||
Raises ValueError when any of the values is not contained in the list.
|
||||
"""
|
||||
|
||||
assert (after is None) != (before is None), 'Either after or before is needed but not both.'
|
||||
|
||||
values = list(values)
|
||||
|
||||
values.remove(value)
|
||||
|
||||
if after is None:
|
||||
index = values.index(before)
|
||||
else:
|
||||
index = values.index(after) + 1
|
||||
|
||||
values.insert(index, value)
|
||||
|
||||
return values
|
||||
|
|
@ -10,9 +10,6 @@ __all__ = [
|
|||
'build_email',
|
||||
'send_email',
|
||||
'collect_and_attach',
|
||||
'send_applicant_decision_mail',
|
||||
'send_staff_decision_mail',
|
||||
'send_decision_mails',
|
||||
]
|
||||
|
||||
|
||||
|
|
@ -35,49 +32,3 @@ def build_email(template_name: str, context: dict, subject: str, *recipients: st
|
|||
|
||||
def send_email(template_name: str, context: dict, subject: str, *recipients: str, fail_silently=False, **kwargs):
|
||||
return build_email(template_name, context, subject, *recipients, **kwargs).send(fail_silently)
|
||||
|
||||
|
||||
def get_decision_mail_context(obj: Project):
|
||||
"""
|
||||
Build a minimal, consistent context for decision mails (applicant & staff).
|
||||
"""
|
||||
|
||||
return {
|
||||
'project': obj,
|
||||
'data': {
|
||||
'realname': obj.realname or obj.email,
|
||||
'name': obj.name,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def send_base_decision_mail(obj: Project, scope: str, subject: str, recipient: str):
|
||||
context = get_decision_mail_context(obj)
|
||||
decision = 'granted' if obj.granted else 'denied'
|
||||
decision_label = 'bewilligt' if obj.granted else 'abgelehnt'
|
||||
subject = subject.format(name=obj.name, decision=decision_label)
|
||||
|
||||
return send_email(f'approval_{decision}_{scope}', context, subject, recipient)
|
||||
|
||||
|
||||
def send_applicant_decision_mail(obj: Project):
|
||||
"""
|
||||
Send a decision email to the applicant after manual approval/denial.
|
||||
"""
|
||||
|
||||
if recipient := obj.email:
|
||||
return send_base_decision_mail(obj, 'applicant', 'Deine Förderanfrage „{name}“ – {decision}', recipient)
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
def send_staff_decision_mail(obj: Project):
|
||||
"""
|
||||
Send a decision email to the internal team (staff) after approval/denial.
|
||||
"""
|
||||
|
||||
return send_base_decision_mail(obj, 'staff', 'Entscheidung: {name} ({decision})', settings.IF_EMAIL)
|
||||
|
||||
|
||||
def send_decision_mails(obj: Project):
|
||||
return send_applicant_decision_mail(obj) + send_staff_decision_mail(obj)
|
||||
|
|
|
|||
154
input/views.py
154
input/views.py
|
|
@ -1,10 +1,20 @@
|
|||
from smtplib import SMTPException
|
||||
from typing import NamedTuple
|
||||
import datetime
|
||||
|
||||
from django.shortcuts import render
|
||||
from dataclasses import dataclass
|
||||
from smtplib import SMTPException
|
||||
from typing import Optional
|
||||
from urllib.parse import urljoin
|
||||
|
||||
from django.forms import ChoiceField, Field
|
||||
from django.shortcuts import render, redirect
|
||||
from django.http import HttpResponse, Http404
|
||||
from django.urls import reverse
|
||||
from django.utils.choices import flatten_choices
|
||||
from django.utils.formats import date_format
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.http import url_has_allowed_host_and_scheme
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.utils.text import get_text_list
|
||||
from django.core.mail import BadHeaderError
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.decorators import login_required
|
||||
|
|
@ -12,6 +22,7 @@ from django.views.generic import TemplateView
|
|||
from django.views.generic.edit import FormView
|
||||
from django.utils.html import strip_tags
|
||||
|
||||
from input.utils.admin import admin_url_name
|
||||
from input.utils.mail import build_email, collect_and_attach
|
||||
|
||||
from .forms import (
|
||||
|
|
@ -41,6 +52,8 @@ from .models import (
|
|||
TYPE_SOFT,
|
||||
TYPE_TRAV,
|
||||
TYPE_VIS,
|
||||
Project,
|
||||
ProductCategoryFormField,
|
||||
)
|
||||
|
||||
HELP_TEXTS = {
|
||||
|
|
@ -67,35 +80,49 @@ HELP_TEXTS = {
|
|||
},
|
||||
}
|
||||
|
||||
|
||||
class ApplicationType(NamedTuple):
|
||||
@dataclass
|
||||
class ApplicationType:
|
||||
code: str
|
||||
path: str
|
||||
form_class: type[BaseApplicationForm]
|
||||
link: str
|
||||
label: Optional[str] = None
|
||||
help_texts: Optional[dict] = None
|
||||
|
||||
def __post_init__(self):
|
||||
if self.label is None:
|
||||
self.label = TYPE_CHOICES[self.code]
|
||||
|
||||
if self.help_texts is None: # pragma: no branch
|
||||
self.help_texts = HELP_TEXTS.get(self.code)
|
||||
|
||||
@property
|
||||
def label(self):
|
||||
return TYPE_CHOICES[self.code]
|
||||
|
||||
@property
|
||||
def help_texts(self):
|
||||
return HELP_TEXTS.get(self.code)
|
||||
def url(self):
|
||||
return f'https://de.wikipedia.org/wiki/Wikipedia:F%C3%B6rderung/{self.link}'
|
||||
|
||||
|
||||
PROJECT_FUNDING = [
|
||||
ApplicationType(TYPE_PROJ, 'projektfoerderung', ProjectForm),
|
||||
ApplicationType(TYPE_PROJ, 'projektfoerderung', ProjectForm, 'Projektplanung',
|
||||
'Projektförderung mit einer Gesamtsumme unter 1.000,— EUR'),
|
||||
ApplicationType(TYPE_PROJ, 'projektfoerderung-ab-1000', ProjectForm, 'Projektplanung',
|
||||
'Projektförderung mit einer Gesamtsumme ab 1.000,— EUR'),
|
||||
]
|
||||
|
||||
SERVICES = [
|
||||
ApplicationType(TYPE_BIB, 'bibliotheksstipendium', LibraryForm),
|
||||
ApplicationType(TYPE_ELIT, 'eliteraturstipendium', ELiteratureForm),
|
||||
ApplicationType(TYPE_MAIL, 'email', EmailForm),
|
||||
ApplicationType(TYPE_IFG, 'ifg', IFGForm),
|
||||
ApplicationType(TYPE_LIT, 'literaturstipendium', LiteratureForm),
|
||||
ApplicationType(TYPE_LIST, 'mailingliste', ListForm),
|
||||
ApplicationType(TYPE_TRAV, 'reisekosten', TravelForm),
|
||||
ApplicationType(TYPE_SOFT, 'softwarestipendium', SoftwareForm),
|
||||
ApplicationType(TYPE_VIS, 'visitenkarten', BusinessCardForm),
|
||||
ApplicationType(TYPE_BIB, 'bibliotheksstipendium', LibraryForm, 'Zugang_zu_Fachliteratur#Bibliotheksstipendium'),
|
||||
ApplicationType(TYPE_ELIT, 'eliteraturstipendium', ELiteratureForm, 'Zugang_zu_Fachliteratur#eLiteraturstipendium'),
|
||||
ApplicationType(TYPE_MAIL, 'email', EmailForm, 'E-Mail-Adressen_und_Visitenkarten#E-Mail-Adressen'),
|
||||
ApplicationType(TYPE_IFG, 'ifg', IFGForm, 'Geb%C3%BChrenerstattungen_f%C3%BCr_Beh%C3%B6rdenanfragen'),
|
||||
ApplicationType(TYPE_LIT, 'literaturstipendium', LiteratureForm, 'Zugang_zu_Fachliteratur#Literaturstipendium'),
|
||||
ApplicationType(TYPE_LIST, 'mailingliste', ListForm, 'E-Mail-Adressen_und_Visitenkarten#Mailinglisten'),
|
||||
ApplicationType(TYPE_TRAV, 'reisekosten', TravelForm, 'Reisekostenerstattungen'),
|
||||
ApplicationType(TYPE_SOFT, 'softwarestipendium', SoftwareForm, 'Software-Stipendien'),
|
||||
ApplicationType(TYPE_VIS, 'visitenkarten', BusinessCardForm, 'E-Mail-Adressen_und_Visitenkarten#Visitenkarten'),
|
||||
]
|
||||
|
||||
APPLICATIONS = [
|
||||
('Projektförderung', PROJECT_FUNDING),
|
||||
('Serviceleistungen', SERVICES),
|
||||
]
|
||||
|
||||
TYPES = {info.path: info for info in PROJECT_FUNDING + SERVICES}
|
||||
|
|
@ -147,7 +174,14 @@ def index(request):
|
|||
|
||||
class ApplicationStartView(TemplateView):
|
||||
template_name = 'input/forms/extern.html'
|
||||
extra_context = {'services': SERVICES}
|
||||
extra_context = {'applications': APPLICATIONS}
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
if url := request.POST.get('url'):
|
||||
if url_has_allowed_host_and_scheme(url, None):
|
||||
return redirect(url)
|
||||
|
||||
return self.get(request, *args, **kwargs)
|
||||
|
||||
|
||||
class ProjectFundingInfoView(TemplateView):
|
||||
|
|
@ -220,14 +254,7 @@ class ApplicationView(FormView):
|
|||
|
||||
def prepare_data(self, form):
|
||||
# Collect cleaned data and mark the current type
|
||||
|
||||
data = {**form.cleaned_data, 'choice': self.type_code}
|
||||
|
||||
# Special rule for literature applications
|
||||
if self.type_code == TYPE_LIT and data.get('selfbuy') == 'TRUE':
|
||||
data['selfbuy_give_data'] = 'False'
|
||||
|
||||
return data
|
||||
return {**form.cleaned_data, 'choice': self.type_code}
|
||||
|
||||
def save_obj(self, form, data):
|
||||
# Save model instance
|
||||
|
|
@ -263,19 +290,9 @@ class ApplicationView(FormView):
|
|||
def send_mail(self, obj, data):
|
||||
# Prepare minimal mail context and send mails
|
||||
|
||||
type_label_html = self.type_info.label
|
||||
type_label_plain = strip_tags(type_label_html)
|
||||
|
||||
data['pk'] = obj.pk
|
||||
data['url_prefix'] = settings.EMAIL_URL_PREFIX
|
||||
data['type_label'] = type_label_html
|
||||
|
||||
context = {'data': data}
|
||||
|
||||
applicant_name = self.get_recipient_name(obj, data)
|
||||
context = self.get_email_context(obj, data)
|
||||
applicant_subject = 'Deine Förderanfrage bei Wikimedia Deutschland'
|
||||
|
||||
staff_subject = f'Anfrage {type_label_plain} von {applicant_name}'
|
||||
staff_subject = 'Anfrage {type_label} von {applicant_name}'.format(**context)
|
||||
|
||||
try:
|
||||
self.send_email('applicant', 'ifg_volunteer_mail', applicant_subject, data['email'], context)
|
||||
|
|
@ -287,6 +304,59 @@ class ApplicationView(FormView):
|
|||
obj.delete()
|
||||
return HttpResponse('Error in sending mails (probably wrong address?). Data not saved!')
|
||||
|
||||
def get_email_context(self, obj, data):
|
||||
return {
|
||||
'data': data,
|
||||
'urls': self.get_urls(obj),
|
||||
'form_data': self.get_form_data(obj, data),
|
||||
'applicant_name': self.get_recipient_name(obj, data),
|
||||
'type_label': self.sanitize_label(self.type_info.label),
|
||||
}
|
||||
|
||||
def get_urls(self, obj, **urls):
|
||||
urls['admin'] = self.get_absolute_url(admin_url_name(obj, 'change'), obj.id)
|
||||
|
||||
if isinstance(obj, Project):
|
||||
urls['authorize'] = urls['deny'] = None
|
||||
else:
|
||||
urls['authorize'] = self.get_absolute_url('authorize', self.type_info.code, obj.id)
|
||||
urls['deny'] = self.get_absolute_url('deny', self.type_info.code, obj.id)
|
||||
|
||||
return urls
|
||||
|
||||
@staticmethod
|
||||
def get_absolute_url(view, *args):
|
||||
return urljoin(settings.EMAIL_URL_PREFIX, reverse(view, args=args))
|
||||
|
||||
def get_form_data(self, obj, data):
|
||||
return {
|
||||
self.sanitize_label(field.label): self.format_value(field.field, field.initial)
|
||||
for field in self.type_info.form_class(initial=data)
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def sanitize_label(label: str):
|
||||
label = strip_tags(label)
|
||||
words = str.split(label)
|
||||
|
||||
return ' '.join(words)
|
||||
|
||||
@staticmethod
|
||||
def format_value(field: Field, value):
|
||||
if isinstance(field, ProductCategoryFormField):
|
||||
value = get_text_list(value, 'und')
|
||||
elif isinstance(field, ChoiceField):
|
||||
choices = flatten_choices(field.choices)
|
||||
value = dict(choices).get(value, value)
|
||||
elif isinstance(value, bool):
|
||||
value = '✓' if value else '✗'
|
||||
elif isinstance(value, datetime.date):
|
||||
value = date_format(value)
|
||||
elif value in field.empty_values:
|
||||
value = '–'
|
||||
|
||||
return value
|
||||
|
||||
def send_email(self, kind, template_name, subject, recipient, context, *, fail_silently=False):
|
||||
email = build_email(template_name, context, subject, recipient)
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue