From 4bd8f15e9707218d2953ac8aa9bbca0e4af98ef2 Mon Sep 17 00:00:00 2001 From: Christian Merten Date: Sat, 22 Feb 2025 22:18:33 +0100 Subject: [PATCH 1/3] fix(members/crisis_intervention_list): escape emergency contact information --- .../members/templates/members/crisis_intervention_list.tex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/jdav_web/members/templates/members/crisis_intervention_list.tex b/jdav_web/members/templates/members/crisis_intervention_list.tex index 2f99039..1e07831 100644 --- a/jdav_web/members/templates/members/crisis_intervention_list.tex +++ b/jdav_web/members/templates/members/crisis_intervention_list.tex @@ -90,8 +90,8 @@ {{ m.member.address|esc_all }} & {{ m.member.contact_phone_number|esc_all }} & {% for c in m.member.emergencycontact_set.all %} - {{ c.name }} \newline - Tel.: {{ c.phone_number }} + {{ c.name|esc_all }} \newline + Tel.: {{ c.phone_number|esc_all }} {% endfor %} \\ {% endfor %} \bottomrule From 2761efecaa0f7e533e9a25f4858feb6f67552b71 Mon Sep 17 00:00:00 2001 From: Christian Merten Date: Sat, 22 Feb 2025 22:35:19 +0100 Subject: [PATCH 2/3] fix(members/templatetags): escape all non-ascii characters in esc_all --- jdav_web/members/models.py | 7 +------ jdav_web/members/templatetags/tex_extras.py | 3 ++- jdav_web/utils.py | 9 +++++++++ 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/jdav_web/members/models.py b/jdav_web/members/models.py index fda3b41..b81e583 100644 --- a/jdav_web/members/models.py +++ b/jdav_web/members/models.py @@ -14,7 +14,7 @@ from django.utils.html import format_html from django.urls import reverse from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation from django.contrib.contenttypes.models import ContentType -from utils import RestrictedFileField +from utils import RestrictedFileField, normalize_name import os from mailer.mailutils import send as send_mail, get_mail_confirmation_link,\ prepend_base_url, get_registration_link, get_wait_confirmation_link,\ @@ -2012,8 +2012,3 @@ def import_from_csv_waitinglist(path): for row in rows: transform_row(row) - - -def normalize_name(raw): - noumlaut = raw.replace('ö', 'oe').replace('ä', 'ae').replace('ü', 'ue').replace(' ', '_') - return unicodedata.normalize('NFKD', noumlaut).encode('ascii', 'ignore').decode('ascii') diff --git a/jdav_web/members/templatetags/tex_extras.py b/jdav_web/members/templatetags/tex_extras.py index beb678d..4f954a0 100644 --- a/jdav_web/members/templatetags/tex_extras.py +++ b/jdav_web/members/templatetags/tex_extras.py @@ -1,5 +1,6 @@ from django import template from django.utils.safestring import mark_safe +from utils import normalize_name register = template.Library() @@ -12,7 +13,7 @@ def checked_if_true(name, value): @register.filter def esc_all(val): - return mark_safe(str(val).replace('_', '\\_').replace('&', '\\&')) + return mark_safe(str(normalize_name(val, False)).replace('_', '\\_').replace('&', '\\&')) @register.filter diff --git a/jdav_web/utils.py b/jdav_web/utils.py index b6131bc..db3e93d 100644 --- a/jdav_web/utils.py +++ b/jdav_web/utils.py @@ -2,6 +2,7 @@ from django.db import models from django.core.exceptions import ValidationError from django.utils.translation import gettext_lazy as _ from decimal import Decimal, ROUND_HALF_DOWN +import unicodedata def file_size_validator(max_upload_size): @@ -60,3 +61,11 @@ def get_member(request): return None else: return request.user.member + + +def normalize_name(raw, nospaces=True): + if nospaces: + noumlaut = raw.replace('ö', 'oe').replace('ä', 'ae').replace('ü', 'ue').replace(' ', '_') + else: + noumlaut = raw.replace('ö', 'oe').replace('ä', 'ae').replace('ü', 'ue') + return unicodedata.normalize('NFKD', noumlaut).encode('ascii', 'ignore').decode('ascii') From bffacf7808c68259172e22910b13199fecabba90 Mon Sep 17 00:00:00 2001 From: Christian Merten Date: Sat, 22 Feb 2025 23:17:02 +0100 Subject: [PATCH 3/3] fix(members/models): replace `auto_now=True` by `default=timezone.now` --- ...roup_date_alter_member_created_and_more.py | 34 +++++++++++++++++++ jdav_web/members/models.py | 10 +++--- jdav_web/members/tests.py | 1 - 3 files changed, 39 insertions(+), 6 deletions(-) create mode 100644 jdav_web/members/migrations/0038_alter_invitationtogroup_date_alter_member_created_and_more.py diff --git a/jdav_web/members/migrations/0038_alter_invitationtogroup_date_alter_member_created_and_more.py b/jdav_web/members/migrations/0038_alter_invitationtogroup_date_alter_member_created_and_more.py new file mode 100644 index 0000000..01fabb9 --- /dev/null +++ b/jdav_web/members/migrations/0038_alter_invitationtogroup_date_alter_member_created_and_more.py @@ -0,0 +1,34 @@ +# Generated by Django 4.0.1 on 2025-02-22 21:59 + +from django.db import migrations, models +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + ('members', '0037_memberwaitinglist_leave_key'), + ] + + operations = [ + migrations.AlterField( + model_name='invitationtogroup', + name='date', + field=models.DateField(default=django.utils.timezone.now, verbose_name='Invitation date'), + ), + migrations.AlterField( + model_name='member', + name='created', + field=models.DateField(default=django.utils.timezone.now, verbose_name='created'), + ), + migrations.AlterField( + model_name='memberwaitinglist', + name='last_reminder', + field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='Last reminder'), + ), + migrations.AlterField( + model_name='memberwaitinglist', + name='last_wait_confirmation', + field=models.DateField(default=django.utils.timezone.now, verbose_name='Last wait confirmation'), + ), + ] diff --git a/jdav_web/members/models.py b/jdav_web/members/models.py index b81e583..ba9e5fd 100644 --- a/jdav_web/members/models.py +++ b/jdav_web/members/models.py @@ -312,7 +312,7 @@ class Member(Person): default=True) unsubscribe_key = models.CharField(max_length=32, default="") unsubscribe_expire = models.DateTimeField(default=timezone.now) - created = models.DateField(auto_now=True, verbose_name=_('created')) + created = models.DateField(default=timezone.now, verbose_name=_('created')) active = models.BooleanField(default=True, verbose_name=_('Active')) registration_form = RestrictedFileField(verbose_name=_('registration form'), upload_to='registration_forms', @@ -858,7 +858,7 @@ class InvitationToGroup(models.Model): """An invitation of a waiter to a group.""" waiter = models.ForeignKey('MemberWaitingList', verbose_name=_('Waiter'), on_delete=models.CASCADE) group = models.ForeignKey(Group, verbose_name=_('Group'), on_delete=models.CASCADE) - date = models.DateField(auto_now=True, verbose_name=_('Invitation date')) + 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) @@ -890,13 +890,13 @@ class MemberWaitingList(Person): application_text = models.TextField(_('Do you want to tell us something else?'), default='', blank=True) application_date = models.DateTimeField(verbose_name=_('application date'), default=timezone.now) - last_wait_confirmation = models.DateField(auto_now=True, verbose_name=_('Last wait confirmation')) + last_wait_confirmation = models.DateField(default=timezone.now, verbose_name=_('Last wait confirmation')) wait_confirmation_key = models.CharField(max_length=32, default="") wait_confirmation_key_expire = models.DateTimeField(default=timezone.now) leave_key = models.CharField(max_length=32, default="") - last_reminder = models.DateTimeField(auto_now=True, verbose_name=_('Last reminder')) + last_reminder = models.DateTimeField(default=timezone.now, verbose_name=_('Last reminder')) sent_reminders = models.IntegerField(default=0, verbose_name=_('Missed reminders')) registration_key = models.CharField(max_length=32, default="") @@ -940,7 +940,7 @@ class MemberWaitingList(Person): def ask_for_wait_confirmation(self): """Sends an email to the person asking them to confirm their intention to wait.""" - self.last_reminder = datetime.now() + self.last_reminder = timezone.now() self.sent_reminders += 1 self.leave_key = gen_key() self.save() diff --git a/jdav_web/members/tests.py b/jdav_web/members/tests.py index c73a5ef..4484518 100644 --- a/jdav_web/members/tests.py +++ b/jdav_web/members/tests.py @@ -1473,7 +1473,6 @@ class ConfirmWaitingViewTestCase(BasicMemberTestCase): waiter = MemberWaitingList.objects.get(pk=self.waiter.pk) self.assertEqual(waiter.leave_key, '') - @skip("This currently fails, because `last_wait_confirmation` has `auto_now=True`, which is wrong.") def test_get_expired(self): self.waiter.last_wait_confirmation = datetime.date(1900, 1, 1) self.waiter.save()