From eb4b723aa7606c8e286340e0bd1746cc580ebce8 Mon Sep 17 00:00:00 2001 From: Helge Jung <hej@c3pb.de> Date: Sat, 21 Dec 2024 02:48:49 +0100 Subject: [PATCH] (room) links: add 'nav' type and allow internal links, too Thereby, the handling of all links (assembly, projects, rooms) is now the same like in markdown. --- src/core/locale/de/LC_MESSAGES/django.po | 9 ++---- src/core/locale/en/LC_MESSAGES/django.po | 9 ++---- ...mlink_add_type_nav_remove_internal_link.py | 31 +++++++++++++++++++ src/core/models/links.py | 29 +++++++++++++---- src/core/models/rooms.py | 26 +++++++++++++--- src/plainui/jinja2.py | 1 + src/plainui/jinja2/plainui/assembly.html.j2 | 7 ++--- .../jinja2/plainui/projects/detail.html.j2 | 7 ++--- src/plainui/jinja2/plainui/room.html.j2 | 7 ++--- src/plainui/views/rooms.py | 2 +- 10 files changed, 92 insertions(+), 36 deletions(-) create mode 100644 src/core/migrations/0167_roomlink_add_type_nav_remove_internal_link.py diff --git a/src/core/locale/de/LC_MESSAGES/django.po b/src/core/locale/de/LC_MESSAGES/django.po index fc92843e2..f0434a686 100644 --- a/src/core/locale/de/LC_MESSAGES/django.po +++ b/src/core/locale/de/LC_MESSAGES/django.po @@ -1224,18 +1224,15 @@ msgstr "Video-Stream" msgid "RoomLink__type-audio" msgstr "Audio-Stream" +msgid "RoomLink__type-nav" +msgstr "Navigation" + msgid "RoomLink__type__help" msgstr "Was wird hier verlinkt?" msgid "RoomLink__type" msgstr "Typ" -msgid "RoomLink__conference_internal__help" -msgstr "Es handelt sich um einen Link der keine Vorschaltseite benötigt." - -msgid "RoomLink__conference_internal" -msgstr "interner Link" - msgid "Link__link__must_be_url" msgstr "Das Link-Ziel muss eine gültige URL sein." diff --git a/src/core/locale/en/LC_MESSAGES/django.po b/src/core/locale/en/LC_MESSAGES/django.po index dfb63b7f4..2ce7c5b43 100644 --- a/src/core/locale/en/LC_MESSAGES/django.po +++ b/src/core/locale/en/LC_MESSAGES/django.po @@ -1224,18 +1224,15 @@ msgstr "video stream" msgid "RoomLink__type-audio" msgstr "audio stream" +msgid "RoomLink__type-nav" +msgstr "navigation" + msgid "RoomLink__type__help" msgstr "What is being linked here?" msgid "RoomLink__type" msgstr "type" -msgid "RoomLink__conference_internal__help" -msgstr "This is an internal link which does not require a disclaimer page." - -msgid "RoomLink__conference_internal" -msgstr "internal link" - msgid "Link__link__must_be_url" msgstr "The link target must be a URL." diff --git a/src/core/migrations/0167_roomlink_add_type_nav_remove_internal_link.py b/src/core/migrations/0167_roomlink_add_type_nav_remove_internal_link.py new file mode 100644 index 000000000..92d338bd3 --- /dev/null +++ b/src/core/migrations/0167_roomlink_add_type_nav_remove_internal_link.py @@ -0,0 +1,31 @@ +# Generated by Django 5.1.3 on 2024-12-21 01:59 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0166_alter_room_official_room_order'), + ] + + operations = [ + migrations.RemoveField( + model_name='link', + name='conference_internal', + ), + migrations.RemoveField( + model_name='roomlink', + name='conference_internal', + ), + migrations.AlterField( + model_name='link', + name='link_type', + field=models.CharField(choices=[('website', 'RoomLink__type-website'), ('chat', 'RoomLink__type-chat'), ('bbb', 'RoomLink__type-bbb'), ('jitsi', 'RoomLink__type-jitsi'), ('pad', 'RoomLink__type-pad'), ('media.ccc.de', 'RoomLink__type-media_ccc_de'), ('video', 'RoomLink__type-video'), ('audio', 'RoomLink__type-audio'), ('nav', 'RoomLink__type-nav')], help_text='RoomLink__type__help', max_length=20, verbose_name='RoomLink__type'), + ), + migrations.AlterField( + model_name='roomlink', + name='link_type', + field=models.CharField(choices=[('website', 'RoomLink__type-website'), ('chat', 'RoomLink__type-chat'), ('bbb', 'RoomLink__type-bbb'), ('jitsi', 'RoomLink__type-jitsi'), ('pad', 'RoomLink__type-pad'), ('media.ccc.de', 'RoomLink__type-media_ccc_de'), ('video', 'RoomLink__type-video'), ('audio', 'RoomLink__type-audio'), ('nav', 'RoomLink__type-nav')], help_text='RoomLink__type__help', max_length=20, verbose_name='RoomLink__type'), + ), + ] diff --git a/src/core/models/links.py b/src/core/models/links.py index 2b7ae3e5f..86764e7e4 100644 --- a/src/core/models/links.py +++ b/src/core/models/links.py @@ -5,6 +5,8 @@ from django.core.validators import URLValidator from django.db import models from django.utils.translation import gettext_lazy as _ +from core.utils import resolve_link + class Link(models.Model): class Meta: @@ -19,8 +21,9 @@ class Link(models.Model): MEDIA_CCC_DE = 'media.ccc.de', _('RoomLink__type-media_ccc_de') VIDEO = 'video', _('RoomLink__type-video') AUDIO = 'audio', _('RoomLink__type-audio') + NAV = 'nav', _('RoomLink__type-nav') - URL_LINK_TYPES = [LinkType.WEBSITE, LinkType.CHAT, LinkType.BBB, LinkType.JITSI, LinkType.PAD, LinkType.VIDEO, LinkType.AUDIO] + URL_LINK_TYPES = [LinkType.WEBSITE, LinkType.CHAT, LinkType.BBB, LinkType.JITSI, LinkType.PAD, LinkType.VIDEO, LinkType.AUDIO, LinkType.NAV] """All LinkTypes which require the link to be a valid URL. The notable exception is media_ccc_de where only a global id is required.""" content_types = models.Q(app_label='core', model='project') @@ -32,9 +35,24 @@ class Link(models.Model): link_type = models.CharField(max_length=20, choices=LinkType.choices, help_text=_('RoomLink__type__help'), verbose_name=_('RoomLink__type')) link = models.CharField(max_length=255) - conference_internal = models.BooleanField( - default=False, help_text=_('RoomLink__conference_internal__help'), verbose_name=_('RoomLink__conference_internal') - ) + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self._resolved_url = None + + def _cache_resolved_url_if_necessary(self): + if self._resolved_url: + return + self._resolved_url = resolve_link(self.link) + + @property + def resolved_url(self) -> str: + self._cache_resolved_url_if_necessary() + return self._resolved_url[1] + + @property + def resolved_url_internal(self) -> bool: + self._cache_resolved_url_if_necessary() + return self._resolved_url[0] == 'internal' def clean(self): if self.link_type in Link.URL_LINK_TYPES: @@ -48,7 +66,6 @@ class Link(models.Model): return self.name def __repr__(self) -> str: - repr_str = f'<{self.__class__.__name__}: {self.name} ({self.link_type}' - repr_str += ':internal)' if self.conference_internal else ')' + repr_str = f'<{self.__class__.__name__}: {self.name} ({self.link_type})' repr_str += f': {self.link}>' if self.link != self.name else '>' return repr_str diff --git a/src/core/models/rooms.py b/src/core/models/rooms.py index 9a4c275f2..b54819c25 100644 --- a/src/core/models/rooms.py +++ b/src/core/models/rooms.py @@ -20,7 +20,7 @@ from core.models.base_managers import ConferenceManagerMixin from core.models.conference import Conference, ConferenceMember from core.models.shared import BackendMixin from core.models.tags import TagItem -from core.utils import resolve_internal_url, str2bool +from core.utils import resolve_internal_url, resolve_link, str2bool from core.validators import FileSizeValidator, ImageDimensionValidator @@ -464,10 +464,11 @@ class RoomLink(models.Model): MEDIA_CCC_DE = 'media.ccc.de', _('RoomLink__type-media_ccc_de') VIDEO = 'video', _('RoomLink__type-video') AUDIO = 'audio', _('RoomLink__type-audio') + NAV = 'nav', _('RoomLink__type-nav') objects = RoomLinkManager() - URL_LINKTYPES = [LinkType.WEBSITE, LinkType.CHAT, LinkType.BBB, LinkType.JITSI, LinkType.PAD, LinkType.VIDEO, LinkType.AUDIO] + URL_LINKTYPES = [LinkType.WEBSITE, LinkType.CHAT, LinkType.BBB, LinkType.JITSI, LinkType.PAD, LinkType.VIDEO, LinkType.AUDIO, LinkType.NAV] """All LinkTypes which require the link to be a valid URL. The notable exception is media_ccc_de where only a global id is required.""" room = models.ForeignKey(Room, related_name='links', on_delete=models.CASCADE) @@ -476,9 +477,24 @@ class RoomLink(models.Model): link_type = models.CharField(max_length=20, choices=LinkType.choices, help_text=_('RoomLink__type__help'), verbose_name=_('RoomLink__type')) link = models.CharField(max_length=255) - conference_internal = models.BooleanField( - default=False, help_text=_('RoomLink__conference_internal__help'), verbose_name=_('RoomLink__conference_internal') - ) + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self._resolved_url = None + + def _cache_resolved_url_if_necessary(self): + if self._resolved_url: + return + self._resolved_url = resolve_link(self.link) + + @property + def resolved_url(self) -> str: + self._cache_resolved_url_if_necessary() + return self._resolved_url[1] + + @property + def resolved_url_internal(self) -> bool: + self._cache_resolved_url_if_necessary() + return self._resolved_url[0] == 'internal' def clean(self): if self.link_type in RoomLink.URL_LINKTYPES: diff --git a/src/plainui/jinja2.py b/src/plainui/jinja2.py index 7365c3a32..1094fa4df 100644 --- a/src/plainui/jinja2.py +++ b/src/plainui/jinja2.py @@ -159,6 +159,7 @@ link_icon_map = { 'media.ccc.de': 'play-circle-fill', 'video': 'camera-reels-fill', 'audio': 'volume-up-fill', + 'nav': 'map', } diff --git a/src/plainui/jinja2/plainui/assembly.html.j2 b/src/plainui/jinja2/plainui/assembly.html.j2 index b5472b45e..9fa0bb821 100644 --- a/src/plainui/jinja2/plainui/assembly.html.j2 +++ b/src/plainui/jinja2/plainui/assembly.html.j2 @@ -47,14 +47,13 @@ {% endif %} {% if assembly.assembly_link %} - {% set assembly_url = '/' + assembly.assembly_link if assembly.assembly_link.conference_internal else url('plainui:dereferrer', assembly.assembly_link) %} <div class="hub-tags"> {{ tagboxMacro.tag(_("Website") , icon="globe", - link=assembly_url, + link=assembly.assembly_link.resolved_url, style="secondary", - target=('_self' if assembly.assembly_link.conference_internal else '_blank'), - rel=('' if assembly.assembly_link.conference_internal else 'external') + target=('_self' if assembly.assembly_link.resolved_url_internal else '_blank'), + rel=('' if assembly.assembly_link.resolved_url_internal else 'external') ) }} </div> {% endif %} diff --git a/src/plainui/jinja2/plainui/projects/detail.html.j2 b/src/plainui/jinja2/plainui/projects/detail.html.j2 index 541805773..4e9b51722 100644 --- a/src/plainui/jinja2/plainui/projects/detail.html.j2 +++ b/src/plainui/jinja2/plainui/projects/detail.html.j2 @@ -68,13 +68,12 @@ {% if project.links %} <div class="hub-tags" role="list"> {% for link in project.links.all() %} - {% set url = '/' + link.link if link.conference_internal else url('plainui:dereferrer', dst=link.link) %} {{ tagMacros.tag(link.name, - link=url, + link=link.resolved_url, icon=link_icon(link) , style='secondary', - target=('_self' if link.conference_internal else '_blank'), - rel=('' if link.conference_internal else 'external, noreferrer') + target=('_self' if link.resolved_url_internal else '_blank'), + rel=('' if link.resolved_url_internal else 'external, noreferrer') ) }} {% endfor %} </div> diff --git a/src/plainui/jinja2/plainui/room.html.j2 b/src/plainui/jinja2/plainui/room.html.j2 index d37cae1bb..de12e4b29 100644 --- a/src/plainui/jinja2/plainui/room.html.j2 +++ b/src/plainui/jinja2/plainui/room.html.j2 @@ -41,13 +41,12 @@ {% if links %} <div class="hub-tags" role="list"> {% for link in links %} - {% set url = '/' + link.link if link.conference_internal else url('plainui:dereferrer', dst=link.link) %} {{ tagMacros.tag(link.name, - link=url, + link=link.resolved_url, icon=link_icon(link) , style='secondary', - target=('_self' if link.conference_internal else '_blank'), - rel=('' if link.conference_internal else 'external, noreferrer') + target=('_self' if link.resolved_url_internal else '_blank'), + rel=('' if link.resolved_url_internal else 'external, noreferrer') ) }} {% endfor %} </div> diff --git a/src/plainui/views/rooms.py b/src/plainui/views/rooms.py index ae1b7ce71..0de04f39d 100644 --- a/src/plainui/views/rooms.py +++ b/src/plainui/views/rooms.py @@ -56,7 +56,7 @@ class RoomView(ConferenceRequiredMixin, DetailView): for link in self.room.links.all(): if link.link_type == RoomLink.LinkType.MEDIA_CCC_DE: media_link = link.link - elif link.link_type in {RoomLink.LinkType.WEBSITE, RoomLink.LinkType.CHAT, RoomLink.LinkType.JITSI, RoomLink.LinkType.PAD}: + elif link.link_type in [RoomLink.LinkType.WEBSITE, RoomLink.LinkType.CHAT, RoomLink.LinkType.JITSI, RoomLink.LinkType.PAD, RoomLink.LinkType.NAV]: linking_links.append(link) context['voc_stream'] = media_link -- GitLab