From adbff17e176a1c750d3ac354a8f9f94c129a7ebc Mon Sep 17 00:00:00 2001 From: Vincent Porte Date: Thu, 14 Nov 2024 13:55:43 +0100 Subject: [PATCH] feat: ajout d'une balise youtube dans le markdown (#810) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description 🎸 Permettre l'ajout d'une video Youtube en iframe, en utilisant une balise de style markdown `[youtube:12345abc]` ## Type de changement 🎢 Nouvelle fonctionnalité (changement non cassant qui ajoute une fonctionnalité). 🚧 technique ### Points d'attention 🦺 rationalisation des rendus de champs markdown via le gabarit `partials/rendered_md.html` 🦺 rationalisation des vues `TopicContentView` et `TopicCertifiedPostView` en utilisant directement le nouveau gabarit 🦺 suppression du gabarit `post_preview.html` (code mort) --- lacommunaute/forum/forms.py | 2 - .../tests/__snapshots__/tests_views.ambr | 8 ++- lacommunaute/forum/tests/tests_forms.py | 17 ------- lacommunaute/forum_conversation/views_htmx.py | 10 ++-- lacommunaute/partner/forms.py | 10 ---- .../tests_partner_detailview.ambr | 40 ++++++++++++--- lacommunaute/partner/tests/tests_forms.py | 16 ------ .../partner/tests/tests_partner_createview.py | 4 +- .../templates/forum/forum_detail.html | 5 +- .../templates/forum/forum_documentation.html | 5 +- lacommunaute/templates/forum/index.html | 5 +- .../partials/post_certified.html | 3 +- .../partials/post_feed.html | 3 +- .../partials/posts_list.html | 3 +- .../partials/topic_certified_post.html | 2 - .../partials/topic_content.html | 2 - .../forum_conversation/post_preview.html | 22 -------- .../forum_conversation/topic_detail.html | 3 +- .../forum_conversation/topic_list.html | 3 +- .../templates/forum_upvote/upvote_list.html | 3 +- .../templates/partials/rendered_md.html | 6 +++ lacommunaute/templates/partner/detail.html | 5 +- lacommunaute/utils/html.py | 16 ------ .../utils/templatetags/str_filters.py | 21 ++++++++ lacommunaute/utils/tests/tests_utils.py | 50 +++++++++---------- 25 files changed, 116 insertions(+), 148 deletions(-) delete mode 100644 lacommunaute/templates/forum_conversation/partials/topic_certified_post.html delete mode 100644 lacommunaute/templates/forum_conversation/partials/topic_content.html delete mode 100644 lacommunaute/templates/forum_conversation/post_preview.html create mode 100644 lacommunaute/templates/partials/rendered_md.html delete mode 100644 lacommunaute/utils/html.py diff --git a/lacommunaute/forum/forms.py b/lacommunaute/forum/forms.py index 24e6ee178..268ba8810 100644 --- a/lacommunaute/forum/forms.py +++ b/lacommunaute/forum/forms.py @@ -5,7 +5,6 @@ from lacommunaute.forum.models import Forum from lacommunaute.partner.models import Partner -from lacommunaute.utils.html import wrap_iframe_in_div_tag class ForumForm(forms.ModelForm): @@ -45,7 +44,6 @@ def __init__(self, *args, **kwargs): def save(self, commit=True): forum = super().save(commit=False) - forum.description = wrap_iframe_in_div_tag(self.cleaned_data.get("description")) if commit: forum.save() diff --git a/lacommunaute/forum/tests/__snapshots__/tests_views.ambr b/lacommunaute/forum/tests/__snapshots__/tests_views.ambr index 997623353..18b4e423e 100644 --- a/lacommunaute/forum/tests/__snapshots__/tests_views.ambr +++ b/lacommunaute/forum/tests/__snapshots__/tests_views.ambr @@ -206,7 +206,13 @@
-

Test description

+
+ + +

Test description

+ + +
diff --git a/lacommunaute/forum/tests/tests_forms.py b/lacommunaute/forum/tests/tests_forms.py index a99656af6..c71c34f4c 100644 --- a/lacommunaute/forum/tests/tests_forms.py +++ b/lacommunaute/forum/tests/tests_forms.py @@ -2,23 +2,6 @@ from lacommunaute.forum.models import Forum -def test_saved_forum_description(db): - form = ForumForm( - data={ - "name": "test", - "short_description": "test", - "description": "Text\n\ntext\n
\nbye", - } - ) - assert form.is_valid() - form.instance.type = Forum.FORUM_POST - forum = form.save() - assert forum.description.rendered == ( - "

Text

\n\n
\n\n" - "

text

\n\n
\n\n

bye

" - ) - - def test_form_field(): form = ForumForm() assert form.Meta.model == Forum diff --git a/lacommunaute/forum_conversation/views_htmx.py b/lacommunaute/forum_conversation/views_htmx.py index d17cfb0b6..ff228a532 100644 --- a/lacommunaute/forum_conversation/views_htmx.py +++ b/lacommunaute/forum_conversation/views_htmx.py @@ -19,7 +19,7 @@ class TopicContentView(PermissionRequiredMixin, View): - template = "forum_conversation/partials/topic_content.html" + template = "partials/rendered_md.html" permission_required = [ "can_read_forum", ] @@ -32,6 +32,9 @@ def get_topic(self): ) return self.topic + def get_content(self): + return self.get_topic().first_post.content + def get(self, request, **kwargs): topic = self.get_topic() @@ -40,7 +43,7 @@ def get(self, request, **kwargs): return render( request, self.template, - context={"topic": topic}, + context={"content": self.get_content()}, ) def get_controlled_object(self): @@ -48,7 +51,8 @@ def get_controlled_object(self): class TopicCertifiedPostView(TopicContentView): - template = "forum_conversation/partials/topic_certified_post.html" + def get_content(self): + return self.get_topic().certified_post.post.content class PostListView(PermissionRequiredMixin, View): diff --git a/lacommunaute/partner/forms.py b/lacommunaute/partner/forms.py index e3aae2f5e..6b45bc314 100644 --- a/lacommunaute/partner/forms.py +++ b/lacommunaute/partner/forms.py @@ -2,7 +2,6 @@ from django.conf import settings from lacommunaute.partner.models import Partner -from lacommunaute.utils.html import wrap_iframe_in_div_tag class PartnerForm(forms.ModelForm): @@ -12,15 +11,6 @@ class PartnerForm(forms.ModelForm): widget=forms.FileInput(attrs={"accept": settings.SUPPORTED_IMAGE_FILE_TYPES.keys()}), ) - def save(self, commit=True): - partner = super().save(commit=False) - partner.description = wrap_iframe_in_div_tag(self.cleaned_data.get("description")) - - if commit: - partner.save() - - return partner - class Meta: model = Partner fields = ("name", "short_description", "description", "logo", "url") diff --git a/lacommunaute/partner/tests/__snapshots__/tests_partner_detailview.ambr b/lacommunaute/partner/tests/__snapshots__/tests_partner_detailview.ambr index 53ad13619..7b873c028 100644 --- a/lacommunaute/partner/tests/__snapshots__/tests_partner_detailview.ambr +++ b/lacommunaute/partner/tests/__snapshots__/tests_partner_detailview.ambr @@ -41,9 +41,15 @@
-

h3 long MD description

+
+ + +

h3 long MD description

+ +

lorem ipsum dolor sit amet, consectetur adipiscing elit.

+ -

lorem ipsum dolor sit amet, consectetur adipiscing elit.

+
https://www.best-partner-ever.com @@ -103,9 +109,15 @@
-

h3 long MD description

+
+ + +

h3 long MD description

+ +

lorem ipsum dolor sit amet, consectetur adipiscing elit.

+ -

lorem ipsum dolor sit amet, consectetur adipiscing elit.

+
https://www.best-partner-ever.com @@ -163,9 +175,15 @@
-

h3 long MD description

+
+ + +

h3 long MD description

+ +

lorem ipsum dolor sit amet, consectetur adipiscing elit.

+ -

lorem ipsum dolor sit amet, consectetur adipiscing elit.

+
https://www.best-partner-ever.com @@ -223,9 +241,15 @@
-

h3 long MD description

+
+ + +

h3 long MD description

+ +

lorem ipsum dolor sit amet, consectetur adipiscing elit.

+ -

lorem ipsum dolor sit amet, consectetur adipiscing elit.

+
https://www.best-partner-ever.com diff --git a/lacommunaute/partner/tests/tests_forms.py b/lacommunaute/partner/tests/tests_forms.py index 21fc7ff29..bfb1e6464 100644 --- a/lacommunaute/partner/tests/tests_forms.py +++ b/lacommunaute/partner/tests/tests_forms.py @@ -2,22 +2,6 @@ from lacommunaute.partner.models import Partner -def test_saved_partner_description(db): - form = PartnerForm( - data={ - "name": "test", - "short_description": "test", - "description": "Text\n\ntext\n
\nbye", - } - ) - assert form.is_valid() - partner = form.save() - assert partner.description.rendered == ( - "

Text

\n\n
\n\n" - "

text

\n\n
\n\n

bye

" - ) - - def test_form_field(): form = PartnerForm() assert form.Meta.model == Partner diff --git a/lacommunaute/partner/tests/tests_partner_createview.py b/lacommunaute/partner/tests/tests_partner_createview.py index e7ce8a435..876d7ff9d 100644 --- a/lacommunaute/partner/tests/tests_partner_createview.py +++ b/lacommunaute/partner/tests/tests_partner_createview.py @@ -39,7 +39,7 @@ def test_post_partner(client, db, url, superuser): data = { "name": "Test", "short_description": "Short description", - "description": "# Titre\n", + "description": "# Titre\ntext", "url": "https://www.example.com", } response = client.post(url, data) @@ -47,4 +47,4 @@ def test_post_partner(client, db, url, superuser): partner = Partner.objects.get() assert response.url == reverse("partner:detail", kwargs={"pk": partner.pk, "slug": partner.slug}) - assert partner.description.raw == "# Titre\n
" + assert partner.description.raw == "# Titre\ntext" diff --git a/lacommunaute/templates/forum/forum_detail.html b/lacommunaute/templates/forum/forum_detail.html index c9c20a5c8..cec82303c 100644 --- a/lacommunaute/templates/forum/forum_detail.html +++ b/lacommunaute/templates/forum/forum_detail.html @@ -5,7 +5,6 @@ {% load forum_conversation_tags %} {% load forum_permission_tags %} {% load forum_tracking_tags %} -{% load str_filters %} {% block title %}{{ forum.name }}{{ block.super }}{% endblock %} {% block meta_description %} {{ forum.short_description }} @@ -34,7 +33,9 @@

{{ forum.name }}

{% if forum.description %}
-
{{ forum.description.rendered|urlizetrunc_target_blank:30|img_fluid }}
+
+ {% include 'partials/rendered_md.html' with content=forum.description only %} +
{% endif %} diff --git a/lacommunaute/templates/forum/forum_documentation.html b/lacommunaute/templates/forum/forum_documentation.html index 070683850..2d4e63e29 100644 --- a/lacommunaute/templates/forum/forum_documentation.html +++ b/lacommunaute/templates/forum/forum_documentation.html @@ -1,6 +1,5 @@ {% extends "forum/forum_detail.html" %} {% load i18n %} -{% load str_filters %} {% block forum_head_content %}
@@ -27,7 +26,9 @@
{% endif %}
-
{{ forum.description.rendered|urlizetrunc_target_blank:30|img_fluid }}
+
+ {% include 'partials/rendered_md.html' with content=forum.description only %} +
{% if forum.partner %}
diff --git a/lacommunaute/templates/forum/index.html b/lacommunaute/templates/forum/index.html index 14256719f..079be2360 100644 --- a/lacommunaute/templates/forum/index.html +++ b/lacommunaute/templates/forum/index.html @@ -2,7 +2,6 @@ {% load i18n %} {% load mptt_tags %} {% load forum_tags %} -{% load str_filters %} {% block title %} {% trans "Index" %}{{ block.super }} {% endblock %} @@ -27,7 +26,9 @@

- {% if node.obj.description.rendered %}{{ node.obj.description.rendered|urlizetrunc_target_blank:30 }}{% endif %} + {% if node.obj.description.rendered %} + {% include 'partials/rendered_md.html' with content=node.obj.description only %} + {% endif %} {% if node.children %}
@@ -14,7 +13,7 @@ {% include "partials/upvotes.html" with obj=post kind="post" %}
- {{ post.content.rendered|urlizetrunc_target_blank:30|img_fluid }} + {% include 'partials/rendered_md.html' with content=post.content only %} {% include "forum_conversation/forum_attachments/attachments_images.html" %}
{% include "forum_conversation/forum_attachments/attachments_detail.html" with post=post user_can_download_files=user_can_download_files %} diff --git a/lacommunaute/templates/forum_conversation/partials/posts_list.html b/lacommunaute/templates/forum_conversation/partials/posts_list.html index e4adb5614..26cf3aed9 100644 --- a/lacommunaute/templates/forum_conversation/partials/posts_list.html +++ b/lacommunaute/templates/forum_conversation/partials/posts_list.html @@ -1,7 +1,6 @@ {% load i18n %} {% load forum_member_tags %} {% load forum_permission_tags %} -{% load str_filters %} {% get_permission 'can_download_files' topic.forum request.user as user_can_download_files %}
{% for post in posts %} @@ -20,7 +19,7 @@
- {{ post.content.rendered|urlizetrunc_target_blank:30|img_fluid }} + {% include 'partials/rendered_md.html' with content=post.content only %} {% include "forum_conversation/forum_attachments/attachments_images.html" %}
{% include "forum_conversation/forum_attachments/attachments_detail.html" with post=post user_can_download_files=user_can_download_files %} diff --git a/lacommunaute/templates/forum_conversation/partials/topic_certified_post.html b/lacommunaute/templates/forum_conversation/partials/topic_certified_post.html deleted file mode 100644 index c591ef39c..000000000 --- a/lacommunaute/templates/forum_conversation/partials/topic_certified_post.html +++ /dev/null @@ -1,2 +0,0 @@ -{% load str_filters %} -{{ topic.certified_post.post.content.rendered|urlizetrunc_target_blank:30|img_fluid }} diff --git a/lacommunaute/templates/forum_conversation/partials/topic_content.html b/lacommunaute/templates/forum_conversation/partials/topic_content.html deleted file mode 100644 index d88222fcc..000000000 --- a/lacommunaute/templates/forum_conversation/partials/topic_content.html +++ /dev/null @@ -1,2 +0,0 @@ -{% load str_filters %} -{{ topic.first_post.content.rendered|urlizetrunc_target_blank:30|img_fluid }} diff --git a/lacommunaute/templates/forum_conversation/post_preview.html b/lacommunaute/templates/forum_conversation/post_preview.html deleted file mode 100644 index 6349cee53..000000000 --- a/lacommunaute/templates/forum_conversation/post_preview.html +++ /dev/null @@ -1,22 +0,0 @@ -{% load i18n %} -{% load forum_markup_tags %} -{% load str_filters %} -
-
-
-
-
-
-

Votre {% trans "Preview"|lower %}

-
-
- {% if post_form.subject.value %}

{{ post_form.subject.value }}

{% endif %} - {{ post_form.content.value|safe|rendered|urlizetrunc_target_blank:30 }} -
- {% include "forum_conversation/forum_attachments/attachments_preview.html" %} -
-
-
-
-
-
diff --git a/lacommunaute/templates/forum_conversation/topic_detail.html b/lacommunaute/templates/forum_conversation/topic_detail.html index f0eab84c8..54503c5a5 100644 --- a/lacommunaute/templates/forum_conversation/topic_detail.html +++ b/lacommunaute/templates/forum_conversation/topic_detail.html @@ -2,7 +2,6 @@ {% load i18n %} {% load forum_conversation_tags %} {% load forum_permission_tags %} -{% load str_filters %} {% block title %}{{ topic.subject }} ({{ topic.forum.name }}){% endblock %} {% block meta_description %} {{ topic.first_post.content.raw }} @@ -36,7 +35,7 @@

{{ topic.subject }}

- {{ post.content.rendered|urlizetrunc_target_blank:30|img_fluid }} + {% include 'partials/rendered_md.html' with content=post.content only %} {% include "forum_conversation/forum_attachments/attachments_images.html" %}
diff --git a/lacommunaute/templates/forum_conversation/topic_list.html b/lacommunaute/templates/forum_conversation/topic_list.html index 1a52a9916..594b1b45b 100644 --- a/lacommunaute/templates/forum_conversation/topic_list.html +++ b/lacommunaute/templates/forum_conversation/topic_list.html @@ -3,7 +3,6 @@ {% load forum_member_tags %} {% load forum_tracking_tags %} {% load forum_permission_tags %} -{% load str_filters %} {% load url_add_query %} {% if forum %} {% get_permission 'can_download_files' forum request.user as user_can_download_files %} @@ -49,7 +48,7 @@
- {{ topic.first_post.content.rendered|urlizetrunc_target_blank:30|img_fluid|truncatechars_html:200 }} + {% include 'partials/rendered_md.html' with content=topic.first_post.content truncatechars=1 only %} {% if topic.first_post.content.rendered|length > 200 %} {% trans "My UpVotes" %}
{% include "forum_conversation/partials/poster.html" with post=upvote.content_object topic=upvote.content_object.topic %} -
{{ upvote.content_object.content.rendered|urlizetrunc_target_blank:30|img_fluid|truncatechars_html:200 }}
+
{% include 'partials/rendered_md.html' with content=upvote.content_object.content truncatechars=1 only %}
{% endif %} diff --git a/lacommunaute/templates/partials/rendered_md.html b/lacommunaute/templates/partials/rendered_md.html new file mode 100644 index 000000000..9d7d45855 --- /dev/null +++ b/lacommunaute/templates/partials/rendered_md.html @@ -0,0 +1,6 @@ +{% load str_filters %} +{% if truncatechars %} + {{ content.rendered|urlizetrunc_target_blank:30|img_fluid|youtube_embed|truncatechars_html:200 }} +{% else %} + {{ content.rendered|urlizetrunc_target_blank:30|img_fluid|youtube_embed }} +{% endif %} diff --git a/lacommunaute/templates/partner/detail.html b/lacommunaute/templates/partner/detail.html index 7910893cb..0c7047c68 100644 --- a/lacommunaute/templates/partner/detail.html +++ b/lacommunaute/templates/partner/detail.html @@ -1,5 +1,4 @@ {% extends "layouts/base.html" %} -{% load str_filters %} {% block title %}{{ partner.name }} {{ block.super }}{% endblock %} {% block meta_description %} {{ partner.short_description }} @@ -30,7 +29,9 @@

{{ partner.short_description }}

-
{{ partner.description.rendered|urlizetrunc_target_blank:30|img_fluid }}
+
+ {% include 'partials/rendered_md.html' with content=partner.description only %} +
{% if partner.url %}
{{ partner.url }} diff --git a/lacommunaute/utils/html.py b/lacommunaute/utils/html.py deleted file mode 100644 index 2d3644e17..000000000 --- a/lacommunaute/utils/html.py +++ /dev/null @@ -1,16 +0,0 @@ -import re - - -def wrap_iframe_in_div_tag(text): - """ - given a markdown text, wrap all iframe tags in a div tag - this is required for iframes to be displayed correctly - """ - - iframe_regex = r"((
)?(]*><\/iframe>)(<\/div>)?)" - - for match, starts_with, iframe, ends_with in re.findall(iframe_regex, text): - if not (starts_with and ends_with): - text = text.replace(match, f"
{iframe}
") - - return text diff --git a/lacommunaute/utils/templatetags/str_filters.py b/lacommunaute/utils/templatetags/str_filters.py index 77b7a7f43..35027cb5f 100644 --- a/lacommunaute/utils/templatetags/str_filters.py +++ b/lacommunaute/utils/templatetags/str_filters.py @@ -2,6 +2,8 @@ https://docs.djangoproject.com/en/dev/howto/custom-template-tags/ """ +import re + from django import template from django.template.defaultfilters import stringfilter from django.urls import reverse @@ -57,3 +59,22 @@ def urlizetrunc_target_blank(value, limit, autoescape=True): @stringfilter def img_fluid(value): return mark_safe(value.replace("
' + ), + ) + + return text diff --git a/lacommunaute/utils/tests/tests_utils.py b/lacommunaute/utils/tests/tests_utils.py index d90bbeb6e..5912218e8 100644 --- a/lacommunaute/utils/tests/tests_utils.py +++ b/lacommunaute/utils/tests/tests_utils.py @@ -24,7 +24,6 @@ from lacommunaute.stats.models import ForumStat from lacommunaute.users.factories import UserFactory from lacommunaute.utils.date import get_last_sunday -from lacommunaute.utils.html import wrap_iframe_in_div_tag from lacommunaute.utils.math import percent from lacommunaute.utils.matomo import ( collect_forum_stats_from_matomo_api, @@ -156,6 +155,29 @@ def test_convert_seconds_into_hours(self, value, expected_result): template = Template("{% load date_filters %}{{ value|convert_seconds_into_hours }}") assert template.render(Context({"value": value})) == expected_result + @pytest.mark.parametrize( + "text,expected", + [ + ( + "[youtube:123456abc]", + ( + "<div><iframe width="560" height="315" src="" + "https://www.youtube.com/embed/123456abc" frameborder="0" allow="" + "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; " + "web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen> " + "</iframe></div>" + ), + ), + ("[ youtube:123456abc]", "[ youtube:123456abc]"), + ("[youtube:123456abc ]", "[youtube:123456abc ]"), + ("[youtube:123456abc youtube:123456abc]", "[youtube:123456abc youtube:123456abc]"), + ], + ) + def test_youtube_embed(self, text, expected): + template = Template("{% load str_filters %}{{ text|youtube_embed }}") + out = template.render(Context({"text": text})) + assert out == expected + class UtilsTemplateTagsTestCase(TestCase): def test_pluralizefr(self): @@ -717,29 +739,3 @@ class TestTheLastSunday: ) def test_the_last_sunday(self, day, expected_sunday): assert get_last_sunday(datetime(2024, 5, day)) == expected_sunday - - -class TestWrapIframeInDiv: - @pytest.mark.parametrize( - "input,output", - [ - ("", "
"), - ( - "markdown text markdown text", - "markdown text
markdown text", - ), - ("
", "
"), - ("
text", "
text"), - ("
", "
"), - ( - "", - "
", - ), - ( - "
", - "
", - ), - ], - ) - def test_wrap_iframe_in_div_tag(self, input, output): - assert wrap_iframe_in_div_tag(input) == output