From 433ce7c75cca360b36f72fa93e90529c18786dea Mon Sep 17 00:00:00 2001 From: cubicroot <cccv@cubicroot.xyz> Date: Fri, 20 Dec 2024 10:38:13 +0000 Subject: [PATCH] Tags beim erstellen lowercasen --- src/backoffice/forms/assemblies.py | 20 ++----------------- src/backoffice/forms/events.py | 4 ++-- .../locale/de/LC_MESSAGES/django.po | 6 +++--- .../locale/en/LC_MESSAGES/django.po | 6 +++--- src/backoffice/views/assemblies/assemblies.py | 2 +- src/core/forms/projects.py | 4 ++-- src/core/locale/de/LC_MESSAGES/django.po | 6 +++--- src/core/locale/en/LC_MESSAGES/django.po | 6 +++--- src/core/models/tags.py | 18 ----------------- src/core/utils.py | 19 ++++++++++++++++++ src/plainui/views/general.py | 13 ++++++------ 11 files changed, 45 insertions(+), 59 deletions(-) diff --git a/src/backoffice/forms/assemblies.py b/src/backoffice/forms/assemblies.py index 810ef2452..cb3d92f4d 100644 --- a/src/backoffice/forms/assemblies.py +++ b/src/backoffice/forms/assemblies.py @@ -1,8 +1,6 @@ import logging from django import forms -from django.core.exceptions import ValidationError -from django.core.validators import validate_slug from django.utils.translation import gettext_lazy as _ from core.base_forms import TranslatedFieldsForm @@ -11,6 +9,7 @@ from core.models import ( Assembly, AssemblyMember, ) +from core.utils import tags_from_string logger = logging.getLogger(__name__) @@ -99,22 +98,7 @@ class AssemblyEditForm(TranslatedFieldsForm): tags = forms.CharField(required=False) def clean_tags(self): - # try to detect people delimiting tags with space instead of comma - - tags = self.cleaned_data['tags'] - # allow empty tags - if tags.strip() == '': - return '' - - split_tags = tags.split(',') - if tags is not None and len(split_tags) == 1 and len(tags.split(' ')) > 2: - raise ValidationError(_('Assembly__tags__split_with_comma')) - - for tag in split_tags: - stripped_tag = tag.strip() - validate_slug(stripped_tag) - - return tags + return ','.join(tags_from_string(self.cleaned_data['tags'])) def clean(self): # call original .clean() which e.g. removes 'slug' from cleaned_data if that isn't a slug diff --git a/src/backoffice/forms/events.py b/src/backoffice/forms/events.py index 91695593d..6430fce6f 100644 --- a/src/backoffice/forms/events.py +++ b/src/backoffice/forms/events.py @@ -11,7 +11,7 @@ from core.models import ( PlatformUser, Room, ) -from core.models.tags import clean_tags +from core.utils import tags_from_string logger = logging.getLogger(__name__) @@ -82,7 +82,7 @@ class EventForm(TranslatedFieldsForm): self.initial['tags_list'] = ', '.join(self.instance.sorted_tags) def clean_tags_list(self): - return clean_tags(self.cleaned_data['tags_list']) + return tags_from_string(self.cleaned_data['tags_list']) def clean(self): if self.cleaned_data.get('schedule_duration', None) is None: diff --git a/src/backoffice/locale/de/LC_MESSAGES/django.po b/src/backoffice/locale/de/LC_MESSAGES/django.po index 64ffad6a4..0502f1c5d 100644 --- a/src/backoffice/locale/de/LC_MESSAGES/django.po +++ b/src/backoffice/locale/de/LC_MESSAGES/django.po @@ -23,9 +23,6 @@ msgstr "Bitte beachte den Hinweis und bestätige, dass du ihn gelesen und versta msgid "Assembly__slug__already_exists" msgstr "Dieser Kurzname wird bereits von einer anderen Assembly benutzt." -msgid "Assembly__tags__split_with_comma" -msgstr "Mehrere Tags bitte mit einem Komma trennen." - msgid "username" msgstr "Benutzername" @@ -434,6 +431,9 @@ msgstr "öffnet in neuem Fenster/Tab" msgid "Assembly__edit__children" msgstr "Zugeordnete Assemblies" +msgid "Assembly__tags__split_with_comma" +msgstr "Mehrere Tags bitte mit einem Komma trennen." + msgid "Assembly__edit__submit_btn" msgstr "Speichern" diff --git a/src/backoffice/locale/en/LC_MESSAGES/django.po b/src/backoffice/locale/en/LC_MESSAGES/django.po index b04ff9a20..7f2de2b6b 100644 --- a/src/backoffice/locale/en/LC_MESSAGES/django.po +++ b/src/backoffice/locale/en/LC_MESSAGES/django.po @@ -23,9 +23,6 @@ msgstr "Please read the disclaimer and acknowledge that you have understood and msgid "Assembly__slug__already_exists" msgstr "This slug is already used by another assembly." -msgid "Assembly__tags__split_with_comma" -msgstr "Split multiple tags by comma." - msgid "username" msgstr "" @@ -434,6 +431,9 @@ msgstr "opens in a new tab or window" msgid "Assembly__edit__children" msgstr "Grouped Assemblies" +msgid "Assembly__tags__split_with_comma" +msgstr "Split multiple tags by comma." + msgid "Assembly__edit__submit_btn" msgstr "Save" diff --git a/src/backoffice/views/assemblies/assemblies.py b/src/backoffice/views/assemblies/assemblies.py index 9269bf662..daf9e1124 100644 --- a/src/backoffice/views/assemblies/assemblies.py +++ b/src/backoffice/views/assemblies/assemblies.py @@ -255,7 +255,7 @@ class AssemblyUpdateView(AssemblyMixin, UpdateView): # update tags: go through supplied list of tags given_tags = form.cleaned_data.get('tags').split(',') for raw_tag in given_tags: - tag = raw_tag.strip() # type: str + tag = raw_tag.strip().lower() # type: str if len(tag) == 0: # skip empty ones continue diff --git a/src/core/forms/projects.py b/src/core/forms/projects.py index 7f07da402..264a69b4f 100644 --- a/src/core/forms/projects.py +++ b/src/core/forms/projects.py @@ -6,7 +6,7 @@ from django.utils.translation import gettext_lazy as _ from core.base_forms import TranslatedFieldsForm from core.models import Assembly, PlatformUser, Project -from core.models.tags import clean_tags +from core.utils import tags_from_string class ProjectForm(TranslatedFieldsForm): @@ -66,7 +66,7 @@ class ProjectForm(TranslatedFieldsForm): return self.cleaned_data['name'] def clean_tags_list(self): - return clean_tags(self.cleaned_data['tags_list']) + return tags_from_string(self.cleaned_data['tags_list']) def clean(self) -> dict[str, Any]: if not self.create: diff --git a/src/core/locale/de/LC_MESSAGES/django.po b/src/core/locale/de/LC_MESSAGES/django.po index 4649f5476..fc92843e2 100644 --- a/src/core/locale/de/LC_MESSAGES/django.po +++ b/src/core/locale/de/LC_MESSAGES/django.po @@ -1969,9 +1969,6 @@ msgstr "erklärender Begleittext des Tags" msgid "ConferenceTag__description" msgstr "Beschreibung" -msgid "Tags__split_with_comma" -msgstr "Tags müssen mit Komma getrennt werden." - msgid "ConferenceMemberTicket__token_wrong_conference" msgstr "Das Ticket ist nicht für diese Konferenz." @@ -2469,6 +2466,9 @@ msgstr "Aktiviere dein %(safe_site_name)s Konto" msgid "Conference-day" msgstr "Tag" +msgid "Tags__split_with_comma" +msgstr "Tags müssen mit Komma getrennt werden." + #, python-format msgid "Validation__error_file_size_MB_exceeded %(max_size)d" msgstr "Die Datei darf nicht größer als %(max_size)dMb sein" diff --git a/src/core/locale/en/LC_MESSAGES/django.po b/src/core/locale/en/LC_MESSAGES/django.po index 794b6c1f7..dfb63b7f4 100644 --- a/src/core/locale/en/LC_MESSAGES/django.po +++ b/src/core/locale/en/LC_MESSAGES/django.po @@ -1967,9 +1967,6 @@ msgstr "additional explanation of this tag" msgid "ConferenceTag__description" msgstr "description" -msgid "Tags__split_with_comma" -msgstr "tags must be split with comma" - msgid "ConferenceMemberTicket__token_wrong_conference" msgstr "This ticket is for another conference." @@ -2451,6 +2448,9 @@ msgstr "" msgid "Conference-day" msgstr "day" +msgid "Tags__split_with_comma" +msgstr "tags must be split with comma" + #, python-format msgid "Validation__error_file_size_MB_exceeded %(max_size)d" msgstr "You aren't allowed to upload a file larger than %(max_size)dMb" diff --git a/src/core/models/tags.py b/src/core/models/tags.py index 630e5512f..82dfa11ce 100644 --- a/src/core/models/tags.py +++ b/src/core/models/tags.py @@ -3,7 +3,6 @@ import contextlib from django.contrib.contenttypes.fields import GenericForeignKey from django.contrib.contenttypes.models import ContentType from django.core.exceptions import ValidationError -from django.core.validators import validate_slug from django.db import models from django.db.models import QuerySet from django.utils.functional import cached_property @@ -193,20 +192,3 @@ class TaggedItemMixin: # TODO: check user's permission with contextlib.suppress(TagItem.DoesNotExist): self.tags.remove(self.tags.get(tag=tag)) - - -def clean_tags(tags: str): - # try to detect people delimiting tags with space instead of comma - - # allow empty tags - if tags.strip() == '': - return '' - - split_tags = tags.split(',') - if tags is not None and len(split_tags) == 1 and len(tags.split(' ')) > 2: - raise ValidationError(_('Tags__split_with_comma')) - stripped_tags = [tag.strip() for tag in split_tags] - for tag in stripped_tags: - validate_slug(tag) - - return stripped_tags diff --git a/src/core/utils.py b/src/core/utils.py index c084a7b45..3ec4c560b 100644 --- a/src/core/utils.py +++ b/src/core/utils.py @@ -12,10 +12,13 @@ from urllib.parse import parse_qs, urlparse, urlunparse import requests +from django.core.exceptions import ValidationError from django.core.files.base import ContentFile +from django.core.validators import validate_slug from django.urls import NoReverseMatch from django.utils.functional import cached_property from django.utils.html import strip_tags +from django.utils.translation import gettext_lazy as _ logger = logging.getLogger(__name__) @@ -228,6 +231,22 @@ def mail2uuid(mail: str, prefix: str = 'acct:', suffix: str = '') -> uuid.UUID: return uuid.uuid5(uuid.NAMESPACE_URL, uri) +def tags_from_string(value: str) -> list[str]: + # allow empty tags + if value.strip() == '': + return [] + + split_tags = value.split(',') + if value is not None and len(split_tags) == 1 and len(value.split(' ')) > 2: + raise ValidationError(_('Tags__split_with_comma')) + + tags = [tag.strip().lower() for tag in split_tags] + for tag in tags: + validate_slug(tag) + + return tags + + class GitCloneError(Exception): pass diff --git a/src/plainui/views/general.py b/src/plainui/views/general.py index bc3f1074e..bbe2e23d7 100644 --- a/src/plainui/views/general.py +++ b/src/plainui/views/general.py @@ -11,7 +11,7 @@ __all__ = ( from datetime import timedelta from django.contrib.contenttypes.models import ContentType -from django.shortcuts import get_object_or_404, redirect +from django.shortcuts import get_list_or_404, redirect from django.urls import reverse from django.utils import timezone from django.utils.timezone import now @@ -122,24 +122,25 @@ class TagView(ConferenceRequiredMixin, TemplateView): context = super().get_context_data(tag_slug=tag_slug, **kwargs) context['conf'] = self.conf - tag = get_object_or_404(ConferenceTag, slug=tag_slug) - context['tag'] = tag + # workaround: there can be multiple tags with different casing + tags = get_list_or_404(ConferenceTag, slug__iexact=tag_slug) + context['tag'] = tags[0] # TODO other types. What should we link here? # TODO: consider using views.utils.event_filter() here context['events'] = ( Event.objects.conference_accessible(self.conf) - .filter(id__in=TagItem.objects.filter(tag=tag, target_type=ContentType.objects.get_for_model(Event)).values_list('target_id')) + .filter(id__in=TagItem.objects.filter(tag__in=tags, target_type=ContentType.objects.get_for_model(Event)).values_list('target_id')) .filter(schedule_start__isnull=False, schedule_end__isnull=False) .order_by('schedule_start', 'schedule_end') ) context['my_favorite_events'] = session_get_favorite_events(self.request.session, self.request.user) context['assemblies'] = Assembly.objects.conference_accessible(self.conf).filter( - id__in=TagItem.objects.filter(tag=tag, target_type=ContentType.objects.get_for_model(Assembly)).values_list('target_id') + id__in=TagItem.objects.filter(tag__in=tags, target_type=ContentType.objects.get_for_model(Assembly)).values_list('target_id') ) context['my_favorite_assemblies'] = session_get_favorite_assemblies(self.request.session, self.request.user) - context['projects'] = Project.objects.conference_accessible(self.conf).filter(tags__tag=tag) + context['projects'] = Project.objects.conference_accessible(self.conf).filter(tags__tag__in=tags) context['my_favorite_projects'] = session_get_favorite_projects(self.request.session, self.request.user) return context -- GitLab