forked from beba/foerderbarometer
Merge branch 'WM-4' into 'cosmocode'
[WM-4] (3) E-Mail Versand Closes WM-4 See merge request wikimedia/foerderbarometer!9
This commit is contained in:
commit
c751a9fc37
|
|
@ -0,0 +1,33 @@
|
|||
TYPE_ALL = 'ALL'
|
||||
|
||||
TYPE_BIB = 'BIB' # Bibliotheksstipendium
|
||||
TYPE_ELIT = 'ELIT' # eLiteraturstipendium
|
||||
TYPE_SOFT = 'SOFT' # Softwarestipendium
|
||||
TYPE_MAIL = 'MAIL' # E-Mail-Adresse
|
||||
TYPE_IFG = 'IFG' # Kostenübernahme IFG-Anfrage
|
||||
TYPE_LIT = 'LIT' # Literaturstipendium
|
||||
TYPE_LIST = 'LIST' # Mailingliste
|
||||
TYPE_TRAV = 'TRAV' # Reisekosten
|
||||
TYPE_VIS = 'VIS' # Visitenkarten
|
||||
TYPE_PROJ = 'PROJ' # Projektförderung
|
||||
|
||||
TYPES = [
|
||||
TYPE_BIB,
|
||||
TYPE_ELIT,
|
||||
TYPE_SOFT,
|
||||
TYPE_MAIL,
|
||||
TYPE_IFG,
|
||||
TYPE_LIT,
|
||||
TYPE_LIST,
|
||||
TYPE_TRAV,
|
||||
TYPE_VIS,
|
||||
TYPE_PROJ,
|
||||
]
|
||||
|
||||
RECIPIENT_APPLICANT = 'applicant'
|
||||
RECIPIENT_STAFF = 'staff'
|
||||
|
||||
RECIPIENTS = [
|
||||
RECIPIENT_APPLICANT,
|
||||
RECIPIENT_STAFF,
|
||||
]
|
||||
|
|
@ -6,6 +6,8 @@ from dotenv import load_dotenv
|
|||
|
||||
from input.utils.settings import env, password_validators
|
||||
|
||||
from .constants import *
|
||||
|
||||
BASE_DIR = Path(__file__).parents[1]
|
||||
|
||||
load_dotenv(BASE_DIR / '.env')
|
||||
|
|
@ -165,3 +167,26 @@ NUTZUNGSBEDINGUNGEN_MAILINGLISTEN = 'static/input/nutzungsbedingungen-mailinglis
|
|||
NUTZUNGSBEDINGUNGEN_LITERATURSTIPENDIUM = 'static/input/nutzungsbedingungen-literaturstipendium.pdf'
|
||||
NUTZUNGSBEDINGUNGEN_OTRS = 'static/input/2025_Nutzungsvereinbarung_OTRS.docx.pdf'
|
||||
NUTZUNGSBEDINGUNGEN_VISITENKARTEN = 'static/input/nutzungsbedingungen-visitenkarten.pdf'
|
||||
|
||||
MAIL_ATTACHMENT_CACHE_DIR = env('MAIL_ATTACHMENT_CACHE_DIR', BASE_DIR / 'var' / 'mail-attachments')
|
||||
MAIL_ATTACHMENT_TTL_SECONDS = env('MAIL_ATTACHMENT_TTL_SECONDS', 24 * 60 * 60)
|
||||
MAIL_ATTACHMENT_URLS = {
|
||||
RECIPIENT_APPLICANT: {
|
||||
TYPE_ALL: [],
|
||||
TYPE_VIS: [
|
||||
'https://foerderung.wikimedia.de/static/input/nutzungsbedingungen-visitenkarten.pdf',
|
||||
],
|
||||
TYPE_MAIL: [
|
||||
'https://foerderung.wikimedia.de/static/input/nutzungsbedingungen-mail.pdf',
|
||||
],
|
||||
TYPE_LIST: [
|
||||
'https://foerderung.wikimedia.de/static/input/nutzungsbedingungen-mailinglisten.pdf',
|
||||
],
|
||||
TYPE_LIT: [
|
||||
'https://foerderung.wikimedia.de/static/input/nutzungsbedingungen-literaturstipendium.pdf',
|
||||
],
|
||||
},
|
||||
RECIPIENT_STAFF: {
|
||||
TYPE_ALL: [],
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
import csv
|
||||
|
||||
from django.contrib import admin
|
||||
from django.db import models
|
||||
from django.db import models, transaction
|
||||
from django.http import HttpResponse
|
||||
|
||||
from input.utils.mail import send_decision_mails
|
||||
|
||||
from .forms import BaseProjectForm
|
||||
from .models import (
|
||||
Account,
|
||||
|
|
@ -22,6 +24,7 @@ from .models import (
|
|||
BusinessCard,
|
||||
List,
|
||||
Literature,
|
||||
TYPE_PROJ,
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -83,7 +86,7 @@ class ProjectAdminForm(BaseProjectForm):
|
|||
super().__init__(*args, **kwargs)
|
||||
|
||||
for field, model in self.categories.items():
|
||||
if self.initial[f'{field}_other']:
|
||||
if self.initial.get(f'{field}_other'):
|
||||
self.initial[field] = [*self.initial[field], model.other]
|
||||
|
||||
def clean(self):
|
||||
|
|
@ -100,8 +103,7 @@ class ProjectAdminForm(BaseProjectForm):
|
|||
return cleaned_data
|
||||
|
||||
|
||||
@admin.register(Project)
|
||||
class ProjectAdmin(admin.ModelAdmin):
|
||||
class BaseProjectAdmin(admin.ModelAdmin):
|
||||
save_as = True
|
||||
form = ProjectAdminForm
|
||||
search_fields = ('name', 'pid','finance_id', 'realname', 'start', 'end', 'participants_estimated', 'participants_real', 'cost', 'status', 'end_quartal')
|
||||
|
|
@ -158,21 +160,42 @@ class ProjectAdmin(admin.ModelAdmin):
|
|||
class Media:
|
||||
js = ('dropdown/js/otrs_link.js',)
|
||||
|
||||
granted = True
|
||||
granted: bool
|
||||
|
||||
def get_queryset(self, request):
|
||||
return super().get_queryset(request).filter(granted=self.granted)
|
||||
|
||||
|
||||
@admin.register(Project)
|
||||
class ProjectAdmin(BaseProjectAdmin):
|
||||
granted = True
|
||||
|
||||
|
||||
@admin.register(ProjectRequest)
|
||||
class ProjectRequestAdmin(ProjectAdmin):
|
||||
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(ProjectAdmin):
|
||||
class ProjectDeclinedAdmin(BaseProjectAdmin):
|
||||
granted = False
|
||||
|
||||
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):
|
||||
|
|
|
|||
|
|
@ -2,13 +2,13 @@ from datetime import date, timedelta
|
|||
|
||||
from django.core.management import CommandError
|
||||
from django.core.management.base import BaseCommand
|
||||
from django.template.loader import get_template
|
||||
from django.core.mail import BadHeaderError
|
||||
from django.core.mail import EmailMultiAlternatives
|
||||
from django.conf import settings
|
||||
|
||||
from input.models import Project, Library, HonoraryCertificate, Travel, Email,\
|
||||
BusinessCard, List, IFG, Literature
|
||||
from input.utils.mail import send_email
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
''' mails will be sent here:
|
||||
|
|
@ -34,15 +34,11 @@ class Command(BaseCommand):
|
|||
'name': name,
|
||||
'pid': pid,
|
||||
'SURVEY_PREFIX': settings.SURVEY_PREFIX, }
|
||||
txt_mail_template = get_template('input/survey_mail.txt')
|
||||
html_mail_template = get_template('input/survey_mail.html')
|
||||
|
||||
subject = 'Dein Feedback zur Förderung durch Wikimedia Deutschland'
|
||||
|
||||
try:
|
||||
subject, from_email, to = 'Dein Feedback zur Förderung durch Wikimedia Deutschland', settings.IF_EMAIL, email
|
||||
text_content = txt_mail_template.render(context)
|
||||
html_content = html_mail_template.render(context)
|
||||
msg = EmailMultiAlternatives(subject, text_content, from_email, [to], bcc=[settings.SURVEY_EMAIL])
|
||||
msg.attach_alternative(html_content, "text/html")
|
||||
msg.send()
|
||||
send_email('survey_mail', context, subject, email, bcc=[settings.SURVEY_EMAIL])
|
||||
#print('survey mail would have been send')
|
||||
|
||||
#survey_mail = EmailMessage('Dein Feedback zur Förderung durch Wikimedia Deutschland',
|
||||
|
|
@ -70,21 +66,14 @@ class Command(BaseCommand):
|
|||
.exclude(end_mail_send = True)\
|
||||
.filter(mail_state = 'NONE')
|
||||
|
||||
txt_mail_template = get_template('input/if_end_of_project.txt')
|
||||
html_mail_template = get_template('input/if_end_of_project.html')
|
||||
|
||||
subject = 'Projektende erreicht'
|
||||
recipient = settings.IF_EMAIL
|
||||
|
||||
for project in old:
|
||||
context = {'project': project}
|
||||
context['URL_PREFIX'] = settings.EMAIL_URL_PREFIX
|
||||
context = {'project': project, 'URL_PREFIX': settings.EMAIL_URL_PREFIX}
|
||||
|
||||
try:
|
||||
subject, from_email, to = 'Projektende erreicht', settings.IF_EMAIL, settings.IF_EMAIL
|
||||
text_content = txt_mail_template.render(context)
|
||||
html_content = html_mail_template.render(context)
|
||||
msg = EmailMultiAlternatives(subject, text_content, from_email, [to])
|
||||
msg.attach_alternative(html_content, "text/html")
|
||||
msg.send()
|
||||
send_email('if_end_of_project', context, subject, recipient)
|
||||
#print('end of project mail would have been sent')
|
||||
|
||||
#send_mail('Projektende erreicht',
|
||||
|
|
@ -110,33 +99,19 @@ class Command(BaseCommand):
|
|||
approved_end = Project.objects.filter(status = 'END')\
|
||||
.exclude(end_mail_send = True)\
|
||||
.filter(mail_state = 'INF')
|
||||
txt_mail_template = get_template('input/if_end_of_project_approved.txt')
|
||||
html_mail_template = get_template('input/if_end_of_project_approved.html')
|
||||
|
||||
txt_informMail_template = get_template('input/if_end_of_project_orginformed.txt')
|
||||
html_informMail_template = get_template('input/if_end_of_project_orginformed.html')
|
||||
# send the mail to project.email, which would be the mail of the volunteer filling out the form
|
||||
|
||||
for project in approved_end:
|
||||
context = {'project': project}
|
||||
context['URL_PREFIX'] = settings.EMAIL_URL_PREFIX
|
||||
|
||||
context = {'project': project, 'URL_PREFIX': settings.EMAIL_URL_PREFIX}
|
||||
|
||||
try:
|
||||
subject, from_email, to = 'Projektende erreicht', settings.IF_EMAIL, project.email
|
||||
text_content = txt_mail_template.render(context)
|
||||
html_content = html_mail_template.render(context)
|
||||
msg = EmailMultiAlternatives(subject, text_content, from_email, [to])
|
||||
msg.attach_alternative(html_content, "text/html")
|
||||
msg.send()
|
||||
send_email('if_end_of_project_approved', context, 'Projektende erreicht', project.email)
|
||||
|
||||
#print('if and of project approved mail would have been sent')
|
||||
|
||||
inform_subject, inform_from_email, inform_to = 'Projektorganisator*in wurde informiert', settings.IF_EMAIL, settings.IF_EMAIL
|
||||
inform_text_content = txt_informMail_template.render(context)
|
||||
inform_html_content = html_informMail_template.render(context)
|
||||
inform_msg = EmailMultiAlternatives(inform_subject, inform_text_content, inform_from_email, [inform_to])
|
||||
inform_msg.attach_alternative(html_content, "text/html")
|
||||
inform_msg.send()
|
||||
send_email('if_end_of_project_orginformed', context, 'Projektorganisator*in wurde informiert', settings.IF_EMAIL)
|
||||
|
||||
#print('if end of project orginformed mail would have been sent')
|
||||
|
||||
#send_mail('Projektende erreicht',
|
||||
|
|
@ -168,25 +143,15 @@ class Command(BaseCommand):
|
|||
.exclude(end_mail_send = True)\
|
||||
.filter(mail_state = 'INF')
|
||||
|
||||
html_mail_template = get_template('input/if_not_of_project_approved.html')
|
||||
txt_mail_template = get_template('input/if_not_of_project_approved.txt')
|
||||
|
||||
txt_informMail_template = get_template('input/if_end_of_project_orginformed.txt')
|
||||
html_informMail_template = get_template('input/if_end_of_project_orginformed.html')
|
||||
# send the mail to project.email, which would be the mail of the volunteer that filled out the form
|
||||
|
||||
for project in approved_notHappened:
|
||||
context = {'project': project}
|
||||
context['URL_PREFIX'] = settings.EMAIL_URL_PREFIX
|
||||
try:
|
||||
subject, from_email, to = 'Projektende erreicht', settings.IF_EMAIL, project.email
|
||||
text_content = txt_mail_template.render(context)
|
||||
html_content = html_mail_template.render(context)
|
||||
msg = EmailMultiAlternatives(subject, text_content, from_email, [to])
|
||||
msg.attach_alternative(html_content, "text/html")
|
||||
msg.send()
|
||||
#print('if not of project approved end mail would have been sent')
|
||||
context = {'project': project, 'URL_PREFIX': settings.EMAIL_URL_PREFIX}
|
||||
|
||||
try:
|
||||
send_email('if_not_of_project_approved', context, 'Projektende erreicht', project.email)
|
||||
|
||||
#print('if not of project approved end mail would have been sent')
|
||||
|
||||
#send_mail('Projektende erreicht',
|
||||
# mail_template.render(context),
|
||||
|
|
@ -194,12 +159,8 @@ class Command(BaseCommand):
|
|||
# [project.email],
|
||||
# fail_silently=False)
|
||||
|
||||
inform_subject, inform_from_email, inform_to = 'Projektorganisator*in wurde informiert', settings.IF_EMAIL, settings.IF_EMAIL
|
||||
inform_text_content = txt_informMail_template.render(context)
|
||||
inform_html_content = html_informMail_template.render(context)
|
||||
inform_msg = EmailMultiAlternatives(inform_subject, inform_text_content, inform_from_email, [inform_to])
|
||||
inform_msg.attach_alternative(html_content, "text/html")
|
||||
inform_msg.send()
|
||||
send_email('if_end_of_project_orginformed', context, 'Projektorganisator*in wurde informiert', settings.IF_EMAIL)
|
||||
|
||||
#print('if not of project approved end mail orginformed would have been sent')
|
||||
|
||||
#send_mail('Projektorganisator*in wurde informiert',
|
||||
|
|
|
|||
|
|
@ -11,6 +11,9 @@ from django.utils.functional import cached_property, classproperty
|
|||
from django.utils.html import format_html
|
||||
from django.utils.safestring import mark_safe
|
||||
|
||||
from foerderbarometer.constants import *
|
||||
|
||||
|
||||
EMAIL_STATES = {
|
||||
'NONE': 'noch keine Mail versendet',
|
||||
'INF': 'die Benachrichtigung zur Projektabschlussmail wurde versendet',
|
||||
|
|
@ -403,17 +406,6 @@ def type_link(path, label):
|
|||
)
|
||||
|
||||
|
||||
TYPE_BIB = 'BIB'
|
||||
TYPE_ELIT = 'ELIT'
|
||||
TYPE_MAIL = 'MAIL'
|
||||
TYPE_IFG = 'IFG'
|
||||
TYPE_LIT = 'LIT'
|
||||
TYPE_LIST = 'LIST'
|
||||
TYPE_TRAV = 'TRAV'
|
||||
TYPE_SOFT = 'SOFT'
|
||||
TYPE_VIS = 'VIS'
|
||||
TYPE_PROJ = 'PROJ'
|
||||
|
||||
TYPE_CHOICES = {
|
||||
TYPE_BIB: type_link('Zugang_zu_Fachliteratur#Bibliotheksstipendium', 'Bibliotheksstipendium'),
|
||||
TYPE_ELIT: type_link('Zugang_zu_Fachliteratur#eLiteraturstipendium', 'eLiteraturstipendium'),
|
||||
|
|
|
|||
|
|
@ -1,34 +0,0 @@
|
|||
<html>
|
||||
<body>
|
||||
Hallo {{data.realname}},
|
||||
<br><br>
|
||||
wir haben Deine Anfrage ({{data.type_label|striptags}}) erhalten.<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>
|
||||
<br><br>
|
||||
Das Team Comunitys und Engagement wird sich um die Bearbeitung deiner Anfrage kümmern<br>
|
||||
und sich in den nächsten Tagen bei dir melden. Solltest du Rückfragen haben,<br>
|
||||
wende dich gern an <a href = "mailto: community@wikimedia.de">community@wikimedia.de</a>.<br>
|
||||
<br><br>
|
||||
Viele Grüße, dein freundliches aber komplett unmenschliches automatisches
|
||||
Formularbeantwortungssystem.
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<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>
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
Hallo {{ data.realname }},
|
||||
|
||||
deine Förderanfrage {{project.name}} wurde leider abgelehnt.
|
||||
|
||||
Fragen? community@wikimedia.de
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<html>
|
||||
<body>
|
||||
<p>Hallo Team Community-Konferenzen & Förderung,</p>
|
||||
|
||||
<p>Die Förderanfrage {{project.name}} von {{ data.realname }} wurde abgelehnt.</p>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
Hallo Team Community-Konferenzen & Förderung,
|
||||
|
||||
die Förderanfrage {{project.name}} von {{ data.realname }} wurde abgelehnt.
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
<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>
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
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
|
||||
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<html>
|
||||
<body>
|
||||
<p>Hallo Team Community-Konferenzen & Förderung,</p>
|
||||
|
||||
<p>Die Förderanfrage {{project.name}} von {{ data.realname }} wurde bewilligt.</p>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
Hallo Team Community-Konferenzen & Förderung,
|
||||
|
||||
die Förderanfrage {{project.name}} von {{ data.realname }} wurde bewilligt.
|
||||
|
|
@ -1,10 +1,11 @@
|
|||
<html>
|
||||
<body>
|
||||
Hallo Team Communitys und Engagement,
|
||||
<br><br>
|
||||
es gab einen neuen Antrag von {{data.realname}}.
|
||||
<br><br>
|
||||
Der Nutzer mit dem Username {{data.username}} ({{data.email}}) fragt ein_e {{data.type_label|striptags}} an.<br>
|
||||
<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>
|
||||
|
||||
{% if data.choice in data.grant %}<br>
|
||||
Vorraussichtliche Kosten: {{data.cost}}<br>
|
||||
Anmerkungen: {{data.notes}} {% endif %} {% if data.choice in data.domain %}<br>
|
||||
|
|
@ -24,9 +25,8 @@ Wikimedia-Projekt: {{data.project}}<br>
|
|||
Persönliche Daten: {{data.data}}<br>
|
||||
Variante: {{data.variant}}<br>
|
||||
Sendungsadrese: {{data.send_to}} <br> {% endif %}
|
||||
<br><br>
|
||||
|
||||
Zum Eintrag in der Förderdatenbank:
|
||||
<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'%}
|
||||
|
|
@ -46,13 +46,15 @@ Zum Eintrag in der Förderdatenbank:
|
|||
{% 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 %}
|
||||
<br><br>
|
||||
</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>
|
||||
<br><br>
|
||||
Zu Ablehnen hier klicken: <a href="{{data.url_prefix}}{% url 'deny' data.choice data.pk %}">{{data.url_prefix}}{% url 'deny' data.choice data.pk %}</a>
|
||||
<br><br>
|
||||
Stets zu Diensten, Deine Förderdatenbank
|
||||
<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>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
<html>
|
||||
<body>
|
||||
<p>Hallo {{ data.username|default:data.realname }},</p>
|
||||
|
||||
<p>vielen Dank für deine Anfrage ({{ data.type_label|striptags }}), 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>
|
||||
|
||||
<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>
|
||||
|
||||
<p>
|
||||
--<br>
|
||||
Wikimedia Deutschland e. V. | Tempelhofer Ufer 23–24 | 10963 Berlin<br>
|
||||
Zentrale: +49 30 5771162-0<br>
|
||||
<a href="https://wikimedia.de">wikimedia.de</a>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Unsere Vision ist eine Welt, in der alle Menschen am Wissen der Menschheit teilhaben, es nutzen und mehren können. Helfen Sie uns dabei!<br>
|
||||
<a href="https://spenden.wikimedia.de">spenden.wikimedia.de</a>
|
||||
</p>
|
||||
|
||||
<p>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.</p>
|
||||
|
||||
<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>
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
from django.conf import settings
|
||||
from django.core.mail import EmailMultiAlternatives
|
||||
from django.template.loader import get_template
|
||||
|
||||
from input.models import Project
|
||||
|
||||
from .attachments import collect_attachment_paths, attach_files
|
||||
|
||||
__all__ = [
|
||||
'build_email',
|
||||
'send_email',
|
||||
'collect_attachment_paths',
|
||||
'attach_files',
|
||||
'send_applicant_decision_mail',
|
||||
'send_staff_decision_mail',
|
||||
'send_decision_mails',
|
||||
]
|
||||
|
||||
|
||||
def build_email(template_name: str, context: dict, subject: str, *recipients: str, **kwargs):
|
||||
body = get_template(f'mails/{template_name}.txt').render(context)
|
||||
html = get_template(f'mails/{template_name}.html').render(context)
|
||||
|
||||
kwargs.setdefault('from_email', settings.IF_EMAIL)
|
||||
|
||||
kwargs['subject'] = subject
|
||||
kwargs['body'] = body
|
||||
kwargs['to'] = recipients
|
||||
|
||||
email = EmailMultiAlternatives(**kwargs)
|
||||
|
||||
email.attach_alternative(html, 'text/html')
|
||||
|
||||
return email
|
||||
|
||||
|
||||
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_{scope}_{decision}', 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)
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
import os
|
||||
import posixpath
|
||||
import time
|
||||
import mimetypes
|
||||
|
||||
from os import PathLike
|
||||
from pathlib import Path
|
||||
from urllib.parse import urlparse
|
||||
from urllib.request import urlretrieve
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.mail import EmailMultiAlternatives
|
||||
|
||||
from foerderbarometer.constants import *
|
||||
|
||||
PathList = list[Path]
|
||||
|
||||
|
||||
def ensure_dir(directory: PathLike) -> Path:
|
||||
"""
|
||||
Ensure that the given directory exists.
|
||||
Creates it recursively if it doesn't.
|
||||
"""
|
||||
|
||||
directory = Path(directory)
|
||||
|
||||
directory.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
return directory
|
||||
|
||||
|
||||
def is_fresh(path: Path, ttl_seconds: int) -> bool:
|
||||
"""
|
||||
Check if the cached file exists and is still fresh within TTL.
|
||||
"""
|
||||
|
||||
try:
|
||||
mtime = path.stat().st_mtime
|
||||
except FileNotFoundError:
|
||||
return False
|
||||
else:
|
||||
return time.time() - mtime < ttl_seconds
|
||||
|
||||
|
||||
def get_attachment(url: str) -> Path:
|
||||
filepath = urlparse(url).path
|
||||
filename = posixpath.basename(filepath)
|
||||
destination = ensure_dir(settings.MAIL_ATTACHMENT_CACHE_DIR) / filename
|
||||
|
||||
if is_fresh(destination, settings.MAIL_ATTACHMENT_TTL_SECONDS):
|
||||
return destination
|
||||
|
||||
return download_attachment(url, destination)
|
||||
|
||||
|
||||
def download_attachment(url: str, destination: Path) -> Path:
|
||||
filepath = destination.with_suffix('.tmp')
|
||||
|
||||
try:
|
||||
urlretrieve(url, filepath)
|
||||
os.replace(filepath, destination)
|
||||
finally:
|
||||
filepath.unlink(missing_ok=True)
|
||||
|
||||
return destination
|
||||
|
||||
|
||||
def collect_attachment_paths(recipient: str, type_code: str) -> PathList:
|
||||
assert recipient in RECIPIENTS
|
||||
assert type_code in TYPES
|
||||
|
||||
config = settings.MAIL_ATTACHMENT_URLS[recipient]
|
||||
urls = [*config[TYPE_ALL], *config.get(type_code, [])]
|
||||
|
||||
return [get_attachment(url) for url in urls]
|
||||
|
||||
|
||||
def get_mime_type(path: Path) -> str:
|
||||
mime_type, encoding = mimetypes.guess_type(path)
|
||||
|
||||
return mime_type or 'application/octet-stream'
|
||||
|
||||
|
||||
def attach_files(message: EmailMultiAlternatives, files: list[Path]):
|
||||
"""
|
||||
Attach files to the EmailMultiAlternatives message.
|
||||
MIME type is guessed from path; falls back to application/octet-stream.
|
||||
"""
|
||||
|
||||
for path in files:
|
||||
mime_type = get_mime_type(path)
|
||||
|
||||
with open(path, 'rb') as fp:
|
||||
message.attach(path.name, fp.read(), mime_type)
|
||||
|
|
@ -5,12 +5,14 @@ from django.shortcuts import render
|
|||
from django.http import HttpResponse, Http404
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.core.mail import BadHeaderError, EmailMultiAlternatives
|
||||
from django.template.loader import get_template
|
||||
from django.core.mail import BadHeaderError
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.views.generic import TemplateView
|
||||
from django.views.generic.edit import FormView
|
||||
from django.utils.html import strip_tags
|
||||
|
||||
from input.utils.mail import collect_attachment_paths, attach_files, build_email
|
||||
|
||||
from .forms import (
|
||||
BaseApplicationForm,
|
||||
|
|
@ -208,15 +210,28 @@ class ApplicationView(FormView):
|
|||
- Return the "done" response.
|
||||
"""
|
||||
|
||||
data = self.prepare_data(form)
|
||||
obj = self.save_obj(form, data)
|
||||
|
||||
if response := self.send_mail(obj, data):
|
||||
return response
|
||||
|
||||
return done(self.request)
|
||||
|
||||
def prepare_data(self, form):
|
||||
# Collect cleaned data and mark the current type
|
||||
data = form.cleaned_data.copy()
|
||||
data['choice'] = self.type_code
|
||||
|
||||
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
|
||||
|
||||
def save_obj(self, form, data):
|
||||
# Save model instance
|
||||
|
||||
modell = form.save(commit=False)
|
||||
|
||||
# Username from session if present
|
||||
|
|
@ -226,6 +241,7 @@ class ApplicationView(FormView):
|
|||
# Copy common fields if provided by the form
|
||||
if 'realname' in data:
|
||||
modell.realname = data['realname']
|
||||
|
||||
if 'email' in data:
|
||||
modell.email = data['email']
|
||||
|
||||
|
|
@ -238,39 +254,51 @@ class ApplicationView(FormView):
|
|||
modell.selfbuy_give_data = data['selfbuy_give_data']
|
||||
|
||||
modell.save()
|
||||
|
||||
if hasattr(form, 'save_m2m'):
|
||||
form.save_m2m()
|
||||
|
||||
return modell
|
||||
|
||||
def send_mail(self, obj, data):
|
||||
# Prepare minimal mail context and send mails
|
||||
data['pk'] = modell.pk
|
||||
|
||||
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'] = self.type_info.label
|
||||
data['type_label'] = type_label_html
|
||||
|
||||
context = {'data': data}
|
||||
|
||||
applicant_name = self.get_recipient_name(obj, data)
|
||||
applicant_subject = 'Deine Förderanfrage bei Wikimedia Deutschland'
|
||||
|
||||
staff_subject = f'Anfrage {type_label_plain} von {applicant_name}'
|
||||
|
||||
try:
|
||||
# Mail to applicant
|
||||
txt1 = get_template('input/ifg_volunteer_mail.txt').render(context)
|
||||
html1 = get_template('input/ifg_volunteer_mail.html').render(context)
|
||||
msg1 = EmailMultiAlternatives(
|
||||
'Formular ausgefüllt', txt1, settings.IF_EMAIL, [data['email']]
|
||||
)
|
||||
msg1.attach_alternative(html1, 'text/html')
|
||||
msg1.send()
|
||||
|
||||
# Mail to IF
|
||||
txt2 = get_template('input/if_mail.txt').render(context)
|
||||
html2 = get_template('input/if_mail.html').render(context)
|
||||
msg2 = EmailMultiAlternatives(
|
||||
'Formular ausgefüllt', txt2, settings.IF_EMAIL, [settings.IF_EMAIL]
|
||||
)
|
||||
msg2.attach_alternative(html2, 'text/html')
|
||||
msg2.send()
|
||||
|
||||
self.send_email('applicant', 'ifg_volunteer_mail', applicant_subject, data['email'], context)
|
||||
self.send_email('staff', 'if_mail', staff_subject, settings.IF_EMAIL, context)
|
||||
except BadHeaderError:
|
||||
modell.delete()
|
||||
obj.delete()
|
||||
return HttpResponse('Invalid header found. Data not saved!')
|
||||
except SMTPException:
|
||||
modell.delete()
|
||||
return HttpResponse('Error in sending mails (probably wrong adress?). Data not saved!')
|
||||
obj.delete()
|
||||
return HttpResponse('Error in sending mails (probably wrong address?). Data not saved!')
|
||||
|
||||
return done(self.request)
|
||||
def send_email(self, kind, template_name, subject, recipient, context, *, fail_silently=False):
|
||||
email = build_email(template_name, context, subject, recipient)
|
||||
applicant_files = collect_attachment_paths(kind, self.type_code)
|
||||
|
||||
attach_files(email, applicant_files)
|
||||
|
||||
return email.send(fail_silently)
|
||||
|
||||
@staticmethod
|
||||
def get_recipient_name(obj, data):
|
||||
for field in 'username', 'realname', 'email':
|
||||
if name := getattr(obj, field, None) or data.get(field):
|
||||
return name
|
||||
|
||||
return 'Unbekannt'
|
||||
|
|
|
|||
Loading…
Reference in New Issue