diff --git a/jdav_web/jdav_web/settings/components/texts.py b/jdav_web/jdav_web/settings/components/texts.py index c094966..e5366b9 100644 --- a/jdav_web/jdav_web/settings/components/texts.py +++ b/jdav_web/jdav_web/settings/components/texts.py @@ -22,6 +22,24 @@ bestätige falls möglich. Zu der Registrierung kommst du hier: Viele Grüße Dein KOMPASS""") +GROUP_INVITATION_LEFT_WAITINGLIST = get_text('group_invitation_left_waitinglist', + default="""Hallo {name}, + +der*die kürzlich zu einer Schnupperstunde für die Gruppe {group} eingeladene Wartende {waiter} +hat die Warteliste verlassen. + +Viele Grüße +Dein KOMPASS""") + +GROUP_INVITATION_REJECTED = get_text('group_invitation_rejected', + default="""Hallo {name}, + +{waiter} hat die Einladung zu einer Schnupperstunde bei der Gruppe {group} abgelehnt, ist +aber weiterhin auf der Warteliste. + +Viele Grüße +Dein KOMPASS""") + GROUP_TIME_AVAILABLE_TEXT = get_text('group_time_available', default="""Die Gruppenstunde findet jeden {weekday} von {start_time} bis {end_time} Uhr statt.""") diff --git a/jdav_web/members/admin.py b/jdav_web/members/admin.py index ca21539..ea45863 100644 --- a/jdav_web/members/admin.py +++ b/jdav_web/members/admin.py @@ -712,7 +712,12 @@ class MemberWaitingListAdmin(CommonAdminMixin, admin.ModelAdmin): def invite_view(self, request, object_id): if type(object_id) == str: - waiter = MemberWaitingList.objects.get(pk=object_id) + try: + waiter = MemberWaitingList.objects.get(pk=object_id) + except MemberWaitingList.DoesNotExist: + messages.error(request, + _("A waiter with this ID does not exist.")) + return HttpResponseRedirect(reverse('admin:members_memberwaitinglist_changelist')) queryset = [waiter] id_list = [waiter.pk] else: @@ -756,7 +761,8 @@ class MemberWaitingListAdmin(CommonAdminMixin, admin.ModelAdmin): _("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) + w.invite_to_group(group, text_template=text_template, + creator=request.user.member if hasattr(request.user, 'member') else None) messages.success(request, _("Successfully invited %(name)s to %(group)s.") % {'name': w.name, 'group': w.invited_for_group.name}) diff --git a/jdav_web/members/locale/de/LC_MESSAGES/django.po b/jdav_web/members/locale/de/LC_MESSAGES/django.po index 1187b87..9b46b2b 100644 --- a/jdav_web/members/locale/de/LC_MESSAGES/django.po +++ b/jdav_web/members/locale/de/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-03-08 16:16+0100\n" +"POT-Creation-Date: 2025-04-06 14:02+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -226,6 +226,10 @@ msgstr "Wartende auffordern den Wartelistenplatz zu bestätigen" msgid "Offer waiter a place in a group." msgstr "Personen auf der Warteliste einen Gruppenplatz anbieten." +#: members/admin.py +msgid "A waiter with this ID does not exist." +msgstr "Es existiert keine wartende Person mit dieser ID." + #: members/admin.py msgid "" "An error occurred while trying to invite said members. Please try again." @@ -763,6 +767,10 @@ msgstr "Einladungsdatum" msgid "Invitation rejected" msgstr "Einladung abgelehnt" +#: members/models.py +msgid "Created by" +msgstr "Erstellt von" + #: members/models.py msgid "Invitation to group" msgstr "Gruppeneinladung" @@ -787,6 +795,16 @@ msgstr "Ausstehend" msgid "Status" msgstr "Status" +#: members/models.py +#, python-format +msgid "%(waiter)s left the waiting list" +msgstr "%(waiter)s hat die Warteliste verlassen" + +#: members/models.py +#, python-format +msgid "Group invitation rejected by %(waiter)s" +msgstr "Einladung zur Schnupperstunde von %(waiter)s abgelehnt" + #: members/models.py msgid "Do you want to tell us something else?" msgstr "Möchtest du uns noch etwas mitteilen?" diff --git a/jdav_web/members/migrations/0040_invitationtogroup_created_by.py b/jdav_web/members/migrations/0040_invitationtogroup_created_by.py new file mode 100644 index 0000000..c017371 --- /dev/null +++ b/jdav_web/members/migrations/0040_invitationtogroup_created_by.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.20 on 2025-04-06 11:30 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('members', '0039_membertraining_certificate_attendance'), + ] + + operations = [ + migrations.AddField( + model_name='invitationtogroup', + name='created_by', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='created_group_invitations', to='members.member', verbose_name='Created by'), + ), + ] diff --git a/jdav_web/members/models.py b/jdav_web/members/models.py index d18b06f..a1f179d 100644 --- a/jdav_web/members/models.py +++ b/jdav_web/members/models.py @@ -874,6 +874,11 @@ class InvitationToGroup(models.Model): date = models.DateField(default=timezone.now, verbose_name=_('Invitation date')) rejected = models.BooleanField(verbose_name=_('Invitation rejected'), default=False) key = models.CharField(max_length=32, default=gen_key) + created_by = models.ForeignKey(Member, verbose_name=_('Created by'), + blank=True, + null=True, + on_delete=models.SET_NULL, + related_name='created_group_invitations') class Meta: verbose_name = _('Invitation to group') @@ -891,6 +896,42 @@ class InvitationToGroup(models.Model): return _('Undecided') status.short_description = _('Status') + def send_left_waitinglist_notification_to(self, recipient): + send_mail(_('%(waiter)s left the waiting list') % {'waiter': self.waiter}, + settings.GROUP_INVITATION_LEFT_WAITINGLIST.format(name=recipient.prename, + waiter=self.waiter, + group=self.group), + settings.DEFAULT_SENDING_MAIL, + recipient.email) + + def send_reject_notification_to(self, recipient): + send_mail(_('Group invitation rejected by %(waiter)s') % {'waiter': self.waiter}, + settings.GROUP_INVITATION_REJECTED.format(name=recipient.prename, + waiter=self.waiter, + group=self.group), + settings.DEFAULT_SENDING_MAIL, + recipient.email) + + def notify_left_waitinglist(self): + """ + Inform youth leaders of the group and the inviter that the waiter left the waitinglist, + prompted by this group invitation. + """ + if self.created_by: + self.send_left_waitinglist_notification_to(self.created_by) + for jl in self.group.leiters.all(): + self.send_left_waitinglist_notification_to(jl) + + def reject(self): + """Reject this invitation. Informs the youth leaders of the group of the rejection.""" + self.rejected = True + self.save() + # send notifications + if self.created_by: + self.send_reject_notification_to(self.created_by) + for jl in self.group.leiters.all(): + self.send_reject_notification_to(jl) + class MemberWaitingList(Person): """A participant on the waiting list""" @@ -1008,7 +1049,7 @@ class MemberWaitingList(Person): except InvitationToGroup.DoesNotExist: return False - def invite_to_group(self, group, text_template=None): + def invite_to_group(self, group, text_template=None, creator=None): """ Invite waiter to given group. Stores a new group invitation and sends a personalized e-mail based on the passed template. @@ -1017,7 +1058,7 @@ class MemberWaitingList(Person): self.save() if not text_template: text_template = group.get_invitation_text_template() - invitation = InvitationToGroup(group=group, waiter=self) + invitation = InvitationToGroup(group=group, waiter=self, created_by=creator) invitation.save() self.send_mail(_("Invitation to trial group meeting"), text_template.format(name=self.prename, diff --git a/jdav_web/members/views.py b/jdav_web/members/views.py index 6280153..3cb22ea 100644 --- a/jdav_web/members/views.py +++ b/jdav_web/members/views.py @@ -470,10 +470,10 @@ def reject_invitation(request): except InvitationToGroup.DoesNotExist: return render_reject_invalid(request) if 'reject_invitation' in request.POST: - invitation.rejected = True - invitation.save() + invitation.reject() return render_reject_success(request, invitation) elif 'leave_waitinglist' in request.POST: + invitation.notify_left_waitinglist() invitation.waiter.unregister() return render_reject_success(request, invitation, leave_waitinglist=True) return render_reject_invalid(request)