members/waitinglist: allow editing of invite text

pull/94/head
Christian Merten 12 months ago
parent c2bfd85eb4
commit 31b31a3eb3
Signed by: christian.merten
GPG Key ID: D953D69721B948B3

@ -26,7 +26,7 @@ GROUP_TIME_AVAILABLE_TEXT = """Die Gruppenstunde findet jeden {weekday} von {sta
GROUP_TIME_UNAVAILABLE_TEXT = """Bitte erfrage die Gruppenzeiten bei der Gruppenleitung ({contact_email}).""" GROUP_TIME_UNAVAILABLE_TEXT = """Bitte erfrage die Gruppenzeiten bei der Gruppenleitung ({contact_email})."""
INVITE_TEXT = """Hallo {name}, INVITE_TEXT = """Hallo {{name}},
wir haben gute Neuigkeiten für dich. Es ist ein Platz in der Jugendgruppe {group_name} {group_link}freigeworden. wir haben gute Neuigkeiten für dich. Es ist ein Platz in der Jugendgruppe {group_name} {group_link}freigeworden.
{group_time} {group_time}
@ -38,7 +38,7 @@ Informationen und deine Anmeldebestätigung von dir. Die lädst du herunter
(siehe %(REGISTRATION_FORM_DOWNLOAD_LINK)s ), lässt sie von deinen Eltern ausfüllen, unterschreiben (siehe %(REGISTRATION_FORM_DOWNLOAD_LINK)s ), lässt sie von deinen Eltern ausfüllen, unterschreiben
und lädst ein Foto davon in unserem Anmeldeformular hoch. Das kannst du alles über folgenden Link erledigen: und lädst ein Foto davon in unserem Anmeldeformular hoch. Das kannst du alles über folgenden Link erledigen:
{link} {{link}}
Du siehst dort auch die Daten, die du bei deiner Eintragung auf die Warteliste angegeben hast. Bitte Du siehst dort auch die Daten, die du bei deiner Eintragung auf die Warteliste angegeben hast. Bitte
überprüfe, ob die Daten noch stimmen und ändere sie bei Bedarf ab. überprüfe, ob die Daten noch stimmen und ändere sie bei Bedarf ab.
@ -46,7 +46,7 @@ Du siehst dort auch die Daten, die du bei deiner Eintragung auf die Warteliste a
Falls du zu dem obigen Termin keine Zeit hast oder dich ganz von der Warteliste abmelden möchtest, Falls du zu dem obigen Termin keine Zeit hast oder dich ganz von der Warteliste abmelden möchtest,
lehne bitte diese Einladung unter folgendem Link ab: lehne bitte diese Einladung unter folgendem Link ab:
{invitation_reject_link} {{invitation_reject_link}}
Bei Fragen, wende dich gerne an %(RESPONSIBLE_MAIL)s. Bei Fragen, wende dich gerne an %(RESPONSIBLE_MAIL)s.

@ -561,6 +561,12 @@ class WaiterInviteForm(forms.Form):
label=_('Group')) label=_('Group'))
class WaiterInviteTextForm(forms.Form):
_selected_action = forms.CharField(widget=forms.MultipleHiddenInput)
text_template = forms.CharField(label=_('Invitation text'),
widget=forms.Textarea(attrs={'rows': 30, 'cols': 100}))
class InvitationToGroupAdmin(admin.TabularInline): class InvitationToGroupAdmin(admin.TabularInline):
model = InvitationToGroup model = InvitationToGroup
fields = ['group', 'date', 'status'] fields = ['group', 'date', 'status']
@ -594,7 +600,7 @@ class MemberWaitingListAdmin(CommonAdminMixin, admin.ModelAdmin):
'confirmed_mail', 'waiting_confirmed', 'sent_reminders') 'confirmed_mail', 'waiting_confirmed', 'sent_reminders')
search_fields = ('prename', 'lastname', 'email') search_fields = ('prename', 'lastname', 'email')
list_filter = ['confirmed_mail', 'gender', InvitedToGroupFilter] list_filter = ['confirmed_mail', 'gender', InvitedToGroupFilter]
actions = ['ask_for_registration', 'ask_for_wait_confirmation'] actions = ['ask_for_registration_action', 'ask_for_wait_confirmation']
inlines = [InvitationToGroupAdmin] inlines = [InvitationToGroupAdmin]
readonly_fields= ['application_date', 'sent_reminders'] readonly_fields= ['application_date', 'sent_reminders']
@ -609,38 +615,6 @@ class MemberWaitingListAdmin(CommonAdminMixin, admin.ModelAdmin):
_("Successfully asked %(name)s to confirm their waiting status.") % {'name': waiter.name}) _("Successfully asked %(name)s to confirm their waiting status.") % {'name': waiter.name})
ask_for_wait_confirmation.short_description = _('Ask selected waiters to confirm their waiting status') ask_for_wait_confirmation.short_description = _('Ask selected waiters to confirm their waiting status')
def ask_for_registration(self, request, queryset):
"""Asks the waiting person to register with all required data."""
if "apply" in request.POST:
try:
group = Group.objects.get(pk=request.POST['group'])
except Group.DoesNotExist:
messages.error(request,
_("An error occurred while trying to invite said members. Please try again."))
return HttpResponseRedirect(request.get_full_path())
if not group.contact_email:
messages.error(request,
_('The selected group does not have a contact email. Please first set a contact email and then try again.'))
return HttpResponseRedirect(request.get_full_path())
for waiter in queryset:
waiter.invited_for_group = group
waiter.save()
waiter.invite_to_group(group)
messages.success(request,
_("Successfully invited %(name)s to %(group)s.") % {'name': waiter.name, 'group': waiter.invited_for_group.name})
return HttpResponseRedirect(request.get_full_path())
context = dict(self.admin_site.each_context(request),
title=_('Select group for invitation'),
opts=self.opts,
waiters=queryset.all(),
form=WaiterInviteForm(initial={'_selected_action': queryset.values_list('id', flat=True)}))
return render(request,
'admin/invite_selected_for_group.html',
context=context)
ask_for_registration.short_description = _('Offer waiter a place in a group.')
def response_change(self, request, waiter): def response_change(self, request, waiter):
ret = super(MemberWaitingListAdmin, self).response_change(request, waiter) ret = super(MemberWaitingListAdmin, self).response_change(request, waiter)
if "_invite" in request.POST: if "_invite" in request.POST:
@ -672,8 +646,19 @@ class MemberWaitingListAdmin(CommonAdminMixin, admin.ModelAdmin):
queryset = super().get_queryset(request) queryset = super().get_queryset(request)
return queryset.prefetch_related('invitationtogroup_set') return queryset.prefetch_related('invitationtogroup_set')
def ask_for_registration_action(self, request, queryset):
return self.invite_view(request, queryset)
ask_for_registration_action.short_description = _('Offer waiter a place in a group.')
def invite_view(self, request, object_id): def invite_view(self, request, object_id):
if type(object_id) == str:
waiter = MemberWaitingList.objects.get(pk=object_id) waiter = MemberWaitingList.objects.get(pk=object_id)
queryset = [waiter]
id_list = [waiter.pk]
else:
waiter = None
queryset = object_id
id_list = queryset.values_list('id', flat=True)
if "apply" in request.POST: if "apply" in request.POST:
try: try:
@ -687,22 +672,49 @@ class MemberWaitingListAdmin(CommonAdminMixin, admin.ModelAdmin):
messages.error(request, messages.error(request,
_('The selected group does not have a contact email. Please first set a contact email and then try again.')) _('The selected group does not have a contact email. Please first set a contact email and then try again.'))
return HttpResponseRedirect(request.get_full_path()) return HttpResponseRedirect(request.get_full_path())
context = dict(self.admin_site.each_context(request),
title=_('Select group for invitation'),
opts=self.opts,
group=group,
queryset=queryset,
form=WaiterInviteTextForm(initial={
'_selected_action': id_list,
'text_template': group.get_invitation_text_template()
}))
if waiter:
context = dict(context, object=waiter, waiter=waiter)
return render(request,
'admin/invite_for_group_text.html',
context=context)
waiter.invited_for_group = group if "send" in request.POST:
waiter.save() try:
waiter.invite_to_group(group) group = Group.objects.get(pk=request.POST['group'])
text_template = request.POST['text_template']
except (Group.DoesNotExist, KeyError):
messages.error(request,
_("An error occurred while trying to invite said members. Please try again."))
return HttpResponseRedirect(request.get_full_path())
for w in queryset:
w.invite_to_group(group, text_template=text_template)
messages.success(request, messages.success(request,
_("Successfully invited %(name)s to %(group)s.") % {'name': waiter.name, 'group': waiter.invited_for_group.name}) _("Successfully invited %(name)s to %(group)s.") % {'name': w.name, 'group': w.invited_for_group.name})
return HttpResponseRedirect(reverse('admin:%s_%s_change' % (waiter._meta.app_label, waiter._meta.model_name), if waiter:
return HttpResponseRedirect(reverse('admin:%s_%s_change' % (self.opts.app_label, self.opts.model_name),
args=(object_id,))) args=(object_id,)))
else:
return HttpResponseRedirect(reverse('admin:%s_%s_changelist' % (self.opts.app_label, self.opts.model_name)))
context = dict(self.admin_site.each_context(request), context = dict(self.admin_site.each_context(request),
title=_('Select group for invitation'), title=_('Select group for invitation'),
opts=self.opts, opts=self.opts,
object=waiter, queryset=queryset,
waiter=waiter, form=WaiterInviteForm(initial={
form=WaiterInviteForm(initial={'_selected_action': [waiter.pk]})) '_selected_action': id_list
}))
if waiter:
context = dict(context, object=waiter, waiter=waiter)
return render(request, return render(request,
'admin/invite_for_group.html', 'admin/invite_for_group.html',
context=context) context=context)

File diff suppressed because it is too large Load Diff

@ -97,6 +97,24 @@ class Group(models.Model):
# return if the group has all relevant time slot information filled # return if the group has all relevant time slot information filled
return self.weekday and self.start_time and self.end_time return self.weekday and self.start_time and self.end_time
def get_invitation_text_template(self):
"""The text template used to invite waiters to this group. This contains
placeholders for the name of the waiter and personalized links."""
if self.show_website:
group_link = '({url}) '.format(url=prepend_base_url(reverse('startpage:gruppe_detail', args=[self.name])))
else:
group_link = ''
if self.has_time_info():
group_time = settings.GROUP_TIME_AVAILABLE_TEXT.format(weekday=WEEKDAYS[self.weekday][1],
start_time=self.start_time.strftime('%H:%M'),
end_time=self.end_time.strftime('%H:%M'))
else:
group_time = settings.GROUP_TIME_UNAVAILABLE_TEXT.format(contact_email=self.contact_email)
return settings.INVITE_TEXT.format(group_time=group_time,
group_name=self.name,
group_link=group_link,
contact_email=self.contact_email)
class MemberManager(models.Manager): class MemberManager(models.Manager):
def get_queryset(self): def get_queryset(self):
@ -945,25 +963,19 @@ class MemberWaitingList(Person):
except InvitationToGroup.DoesNotExist: except InvitationToGroup.DoesNotExist:
return False return False
def invite_to_group(self, group): def invite_to_group(self, group, text_template=None):
if group.show_website: """
group_link = '({url}) '.format(url=prepend_base_url(reverse('startpage:gruppe_detail', args=[group.name]))) Invite waiter to given group. Stores a new group invitation
else: and sends a personalized e-mail based on the passed template.
group_link = '' """
if group.has_time_info(): self.invited_for_group = group
group_time = settings.GROUP_TIME_AVAILABLE_TEXT.format(weekday=WEEKDAYS[group.weekday][1], self.save()
start_time=group.start_time.strftime('%H:%M'), if not text_template:
end_time=group.end_time.strftime('%H:%M')) text_template = group.get_invitation_text_template()
else:
group_time = settings.GROUP_TIME_UNAVAILABLE_TEXT.format(contact_email=group.contact_email)
invitation = InvitationToGroup(group=group, waiter=self) invitation = InvitationToGroup(group=group, waiter=self)
invitation.save() invitation.save()
self.send_mail(_("Invitation to trial group meeting"), self.send_mail(_("Invitation to trial group meeting"),
settings.INVITE_TEXT.format(name=self.prename, text_template.format(name=self.prename,
group_time=group_time,
group_name=group.name,
group_link=group_link,
contact_email=group.contact_email,
link=get_registration_link(invitation.key), link=get_registration_link(invitation.key),
invitation_reject_link=get_invitation_reject_link(invitation.key)), invitation_reject_link=get_invitation_reject_link(invitation.key)),
cc=group.contact_email.email) cc=group.contact_email.email)

@ -17,7 +17,9 @@
<a href="{% url 'admin:index' %}">{% translate 'Home' %}</a> <a href="{% url 'admin:index' %}">{% translate 'Home' %}</a>
&rsaquo; <a href="{% url 'admin:app_list' app_label=opts.app_label %}">{{ opts.app_config.verbose_name }}</a> &rsaquo; <a href="{% url 'admin:app_list' app_label=opts.app_label %}">{{ opts.app_config.verbose_name }}</a>
&rsaquo; <a href="{% url opts|admin_urlname:'changelist' %}">{{ opts.verbose_name_plural|capfirst }}</a> &rsaquo; <a href="{% url opts|admin_urlname:'changelist' %}">{{ opts.verbose_name_plural|capfirst }}</a>
{% if object %}
&rsaquo; <a href="{% url opts|admin_urlname:'change' object.pk|admin_urlquote %}">{{ object|truncatewords:"18" }}</a> &rsaquo; <a href="{% url opts|admin_urlname:'change' object.pk|admin_urlquote %}">{{ object|truncatewords:"18" }}</a>
{% endif %}
&rsaquo; {% translate 'Invite to group' %} &rsaquo; {% translate 'Invite to group' %}
</div> </div>
{% endblock %} {% endblock %}
@ -25,18 +27,28 @@
{% block content %} {% block content %}
<h2>{% translate "Invite to a group" %}</h2> <h2>{% translate "Invite to a group" %}</h2>
<p> <p>
{% if waiter %}
{% trans "You are inviting:" %} {% trans "You are inviting:" %}
{% else %}
{% trans "You are inviting the following waiters for registration:" %}
{% endif %}
</p> </p>
<p> <p>
<ul> <ul>
{% for waiter in queryset %}
<li> <li>
<a href="{% url 'admin:members_memberwaitinglist_change' waiter.id %}">{{ waiter }}</a> <a href="{% url 'admin:members_memberwaitinglist_change' waiter.id %}">{{ waiter }}</a>
</li> </li>
{% endfor %}
</ul> </ul>
</p> </p>
<p> <p>
{% if waiter %}
{% blocktrans %}Please choose the group that you want to invite {{ waiter }} to.{% endblocktrans %} {% blocktrans %}Please choose the group that you want to invite {{ waiter }} to.{% endblocktrans %}
{% else %}
{% blocktrans %}To which group do you want to invite these waiters?{% endblocktrans %}
{% endif %}
</p> </p>
<form action="" method="post"> <form action="" method="post">
@ -47,7 +59,7 @@
<br> <br>
<div> <div>
<p> <p>
<input type="hidden" name="action" value="ask_for_registration"> <input type="hidden" name="action" value="ask_for_registration_action">
<input class="default" style="color: $default-link-color" type="submit" name="apply" value="{% translate 'Invite' %}"> <input class="default" style="color: $default-link-color" type="submit" name="apply" value="{% translate 'Invite' %}">
<a href="#" class="button cancel-link">{% translate "Cancel" %}</a> <a href="#" class="button cancel-link">{% translate "Cancel" %}</a>
</p> </p>

@ -0,0 +1,68 @@
{% extends "admin/base_site.html" %}
{% load i18n admin_urls static %}
{% block extrahead %}
{{ block.super }}
{{ media }}
<script src="{% static 'admin/js/cancel.js' %}" async></script>
<script type="text/javascript" src="{% static "admin/js/vendor/jquery/jquery.js" %}"></script>
<script type="text/javascript" src="{% static "admin/js/jquery.init.js" %}"></script>
{% endblock %}
{% block bodyclass %}{{ block.super }} app-{{ opts.app_label }} model-{{ opts.model_name }} invite-waiter
{% endblock %}
{% block breadcrumbs %}
<div class="breadcrumbs">
<a href="{% url 'admin:index' %}">{% translate 'Home' %}</a>
&rsaquo; <a href="{% url 'admin:app_list' app_label=opts.app_label %}">{{ opts.app_config.verbose_name }}</a>
&rsaquo; <a href="{% url opts|admin_urlname:'changelist' %}">{{ opts.verbose_name_plural|capfirst }}</a>
{% if object %}
&rsaquo; <a href="{% url opts|admin_urlname:'change' object.pk|admin_urlquote %}">{{ object|truncatewords:"18" }}</a>
{% endif %}
&rsaquo; {% translate 'Invite to group' %}
</div>
{% endblock %}
{% block content %}
<h2>{% translate "Invite to a group" %}</h2>
<p>
{% if waiter %}
{% blocktrans %}You are inviting the following waiter for registration in group {{ group }}.{% endblocktrans %}
{% else %}
{% blocktrans %}You are inviting the following waiters for registration in group {{ group }}.{% endblocktrans %}
{% endif %}
</p>
<p>
<ul>
{% for waiter in queryset %}
<li>
<a href="{% url 'admin:members_memberwaitinglist_change' waiter.id %}">{{ waiter }}</a>
</li>
{% endfor %}
</ul>
</p>
<p>
{% blocktrans %}The following text will be sent as an invitation email. The patterns
{name}, {link} and {invitation_reject_link} will be automatically replaced by personalized
data upon sending. Please adapt if needed and confirm.{% endblocktrans %}
</p>
<form action="" method="post">
{% csrf_token %}
<p>
{{form}}
</p>
<br>
<div>
<p>
<input type="hidden" name="group" value="{{ group.pk }}">
<input type="hidden" name="action" value="ask_for_registration_action">
<input class="default" style="color: $default-link-color" type="submit" name="send" value="{% translate 'Send' %}">
<a href="#" class="button cancel-link">{% translate "Back" %}</a>
</p>
</div>
</form>
{% endblock %}
Loading…
Cancel
Save