From 7560511a95b63b8912c66256e772db2142327667 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Freitag?= Date: Mon, 2 Dec 2024 16:35:15 +0100 Subject: [PATCH] easymde: Vendor the editor instead of using unmaintained fork Makes the dependencies clearer, avoid the dependency on setuptools which has been an issue with dependabot, and remove some unused support from the library, as well as using Python3-only helpers. --- config/settings/base.py | 1 - itou/templates/layout/base.html | 3 +++ itou/utils/staticfiles.py | 15 ++++++++++++ itou/utils/widgets.py | 17 +++++++++----- itou/www/companies_views/forms.py | 12 +++++----- itou/www/prescribers_views/forms.py | 4 ++-- requirements/base.in | 3 --- requirements/base.txt | 8 ------- requirements/dev.txt | 10 -------- requirements/test.txt | 10 -------- tests/utils/test_widgets.py | 36 +++++++++++++++++++++++++++++ 11 files changed, 73 insertions(+), 46 deletions(-) create mode 100644 tests/utils/test_widgets.py diff --git a/config/settings/base.py b/config/settings/base.py index d9020e14eb2..89619fab25a 100644 --- a/config/settings/base.py +++ b/config/settings/base.py @@ -51,7 +51,6 @@ "citext", "django_bootstrap5", "django_select2", - "easymde", "formtools", "huey.contrib.djhuey", "markdownify", diff --git a/itou/templates/layout/base.html b/itou/templates/layout/base.html index 1e0eddb7b40..c81bf42b14c 100644 --- a/itou/templates/layout/base.html +++ b/itou/templates/layout/base.html @@ -35,6 +35,7 @@ + {% block extra_head %}{% endblock %} @@ -105,6 +106,7 @@ + @@ -114,6 +116,7 @@ {% endif %} + diff --git a/itou/utils/staticfiles.py b/itou/utils/staticfiles.py index a914251b22c..d8bd211c3b9 100644 --- a/itou/utils/staticfiles.py +++ b/itou/utils/staticfiles.py @@ -76,6 +76,21 @@ ], }, }, + "easymde": { + "download": { + "url": "https://registry.npmjs.org/easymde/-/easymde-2.18.0.tgz", + "sha256": "8b2747047c8381f1eade4533bcb67ad70daf98b6bcf5ca5ac3514ecde0f6552d", + }, + "extract": { + "origin": "package", + "destination": "vendor/easymde", + "files": [ + "LICENSE", + ("dist/easymde.min.js", "easymde.min.js"), + ("dist/easymde.min.css", "easymde.min.css"), + ], + }, + }, "tiny-slider": { "download": { "url": "https://github.com/ganlanyuan/tiny-slider/archive/refs/tags/v2.9.4.zip", diff --git a/itou/utils/widgets.py b/itou/utils/widgets.py index a8a533ac435..5a421861f35 100644 --- a/itou/utils/widgets.py +++ b/itou/utils/widgets.py @@ -11,7 +11,6 @@ from django.db.models import Q from django.forms.models import ModelChoiceIterator from django_select2.forms import Select2Widget -from easymde.widgets import EasyMDEEditor from itou.utils.validators import get_max_birthdate, get_min_birthdate @@ -196,8 +195,14 @@ def create_option(self, name, value, *args, **kwargs): return option -class EasyMDEEditorWithConfig(EasyMDEEditor): - class Media: - extend = False - js = ("easymde/easymde.min.js", "js/easymde_config.js") - css = {"all": ("easymde/easymde.min.css",)} +class EasyMDEEditor(forms.Textarea): + def build_attrs(self, base_attrs, extra_attrs=None): + attrs = super().build_attrs(base_attrs, extra_attrs) + try: + classes = attrs["class"].split(" ") + except KeyError: + classes = [] + if "easymde-box" not in classes: + classes.append("easymde-box") + attrs["class"] = " ".join(classes) + return attrs diff --git a/itou/www/companies_views/forms.py b/itou/www/companies_views/forms.py index a77dd77959e..bfa111d212e 100644 --- a/itou/www/companies_views/forms.py +++ b/itou/www/companies_views/forms.py @@ -10,7 +10,7 @@ from itou.companies.models import Company, CompanyMembership, JobDescription from itou.jobs.models import Appellation from itou.utils.urls import get_external_link_markup -from itou.utils.widgets import EasyMDEEditorWithConfig, RemoteAutocompleteSelect2Widget +from itou.utils.widgets import EasyMDEEditor, RemoteAutocompleteSelect2Widget class CreateCompanyForm(forms.ModelForm): @@ -39,7 +39,7 @@ class Meta: "description": "Texte de présentation de votre structure.", } widgets = { - "description": EasyMDEEditorWithConfig, + "description": EasyMDEEditor, } def __init__(self, current_company, current_user, *args, **kwargs): @@ -151,8 +151,8 @@ class Meta: } widgets = { - "description": EasyMDEEditorWithConfig, - "provided_support": EasyMDEEditorWithConfig, + "description": EasyMDEEditor, + "provided_support": EasyMDEEditor, } def __init__(self, *args, **kwargs): @@ -353,8 +353,8 @@ class Meta: "bénéficier en priorité aux publics résidant en Quartier Prioritaire de la Ville.", } widgets = { - "description": EasyMDEEditorWithConfig, - "profile_description": EasyMDEEditorWithConfig, + "description": EasyMDEEditor, + "profile_description": EasyMDEEditor, } def __init__(self, current_company: Company, *args, **kwargs): diff --git a/itou/www/prescribers_views/forms.py b/itou/www/prescribers_views/forms.py index 42e5a26ee68..ab7a6a879c6 100644 --- a/itou/www/prescribers_views/forms.py +++ b/itou/www/prescribers_views/forms.py @@ -2,7 +2,7 @@ from itou.prescribers.enums import PrescriberOrganizationKind from itou.prescribers.models import PrescriberOrganization -from itou.utils.widgets import EasyMDEEditorWithConfig +from itou.utils.widgets import EasyMDEEditor class EditPrescriberOrganizationForm(forms.ModelForm): @@ -56,7 +56,7 @@ def __init__(self, *args, **kwargs): for field in self.fields.values(): field.disabled = True else: - self.fields["description"].widget = EasyMDEEditorWithConfig() + self.fields["description"].widget = EasyMDEEditor() def clean_siret(self): siret = self.cleaned_data["siret"] diff --git a/requirements/base.in b/requirements/base.in index de6ade37f0c..2c0414034d3 100644 --- a/requirements/base.in +++ b/requirements/base.in @@ -54,9 +54,6 @@ django-redis # https://github.com/jazzband/django-redis # django-markdownify (to render only a set of HTML tags) django-markdownify # https://github.com/erwinmatijsen/django-markdownify -# Markdown editor -django-easymde # https://github.com/WPI-LNL/django-easymde - # Pillow (image manipulation) pillow # https://python-pillow.org/ diff --git a/requirements/base.txt b/requirements/base.txt index 2b3f454967c..a4660d4b9ab 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -327,10 +327,6 @@ django-datadog-logger==0.7.3 \ --hash=sha256:4361bb068a4b188fa14135398f9b747728464a291757e6adf8c95c9215dcd602 \ --hash=sha256:87838cd868f407e050831c536413de6b2ece31433b28c952c0fd90be1d66486a # via -r requirements/base.in -django-easymde==1.0.4 \ - --hash=sha256:0690902bde591461138e174e47f58363f1f7b7fb637ebae7401e7a6449177e26 \ - --hash=sha256:5b2fd94953b76cb69aebd5d6169056084188e079609bee1300174ad54cfa346a - # via -r requirements/base.in django-filter==24.3 \ --hash=sha256:c4852822928ce17fb699bcfccd644b3574f1a2d80aeb2b4ff4f16b02dd49dc64 \ --hash=sha256:d8ccaf6732afd21ca0542f6733b11591030fa98669f8d15599b358e24a2cd9c3 @@ -1187,10 +1183,6 @@ sentry-sdk==2.19.0 \ --hash=sha256:7b0b3b709dee051337244a09a30dbf6e95afe0d34a1f8b430d45e0982a7c125b \ --hash=sha256:ee4a4d2ae8bfe3cac012dcf3e4607975904c137e1738116549fc3dbbb6ff0e36 # via -r requirements/base.in -setuptools==75.6.0 \ - --hash=sha256:8199222558df7c86216af4f84c30e9b34a61d8ba19366cc914424cdbd28252f6 \ - --hash=sha256:ce74b49e8f7110f9bf04883b730f4765b774ef3ef28f722cce7c273d253aaf7d - # via django-easymde six==1.16.0 \ --hash=sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926 \ --hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254 diff --git a/requirements/dev.txt b/requirements/dev.txt index 3c87ed44bb2..8208a7103dc 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -389,10 +389,6 @@ django-debug-toolbar==4.4.6 \ --hash=sha256:36e421cb908c2f0675e07f9f41e3d1d8618dc386392ec82d23bcfcd5d29c7044 \ --hash=sha256:3beb671c9ec44ffb817fad2780667f172bd1c067dbcabad6268ce39a81335f45 # via -r requirements/dev.in -django-easymde==1.0.4 \ - --hash=sha256:0690902bde591461138e174e47f58363f1f7b7fb637ebae7401e7a6449177e26 \ - --hash=sha256:5b2fd94953b76cb69aebd5d6169056084188e079609bee1300174ad54cfa346a - # via -r requirements/test.txt django-extensions==3.2.3 \ --hash=sha256:44d27919d04e23b3f40231c4ab7af4e61ce832ef46d610cc650d53e68328410a \ --hash=sha256:9600b7562f79a92cbf1fde6403c04fee314608fefbb595502e34383ae8203401 @@ -1608,12 +1604,6 @@ sentry-sdk==2.19.0 \ --hash=sha256:7b0b3b709dee051337244a09a30dbf6e95afe0d34a1f8b430d45e0982a7c125b \ --hash=sha256:ee4a4d2ae8bfe3cac012dcf3e4607975904c137e1738116549fc3dbbb6ff0e36 # via -r requirements/test.txt -setuptools==75.6.0 \ - --hash=sha256:8199222558df7c86216af4f84c30e9b34a61d8ba19366cc914424cdbd28252f6 \ - --hash=sha256:ce74b49e8f7110f9bf04883b730f4765b774ef3ef28f722cce7c273d253aaf7d - # via - # -r requirements/test.txt - # django-easymde shellcheck-py==0.10.0.1 \ --hash=sha256:390826b340b8c19173922b0da5ef7b66ef34d4d087dc48aad3e01f7e77e164d9 \ --hash=sha256:48f08965cafbb3363b265c4ef40628ffced19cb6fc7c4bb5ce72d32cbcfb4bb9 \ diff --git a/requirements/test.txt b/requirements/test.txt index 52805f16b21..3f6be326f1f 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -359,10 +359,6 @@ django-datadog-logger==0.7.3 \ --hash=sha256:4361bb068a4b188fa14135398f9b747728464a291757e6adf8c95c9215dcd602 \ --hash=sha256:87838cd868f407e050831c536413de6b2ece31433b28c952c0fd90be1d66486a # via -r requirements/base.txt -django-easymde==1.0.4 \ - --hash=sha256:0690902bde591461138e174e47f58363f1f7b7fb637ebae7401e7a6449177e26 \ - --hash=sha256:5b2fd94953b76cb69aebd5d6169056084188e079609bee1300174ad54cfa346a - # via -r requirements/base.txt django-filter==24.3 \ --hash=sha256:c4852822928ce17fb699bcfccd644b3574f1a2d80aeb2b4ff4f16b02dd49dc64 \ --hash=sha256:d8ccaf6732afd21ca0542f6733b11591030fa98669f8d15599b358e24a2cd9c3 @@ -1491,12 +1487,6 @@ sentry-sdk==2.19.0 \ --hash=sha256:7b0b3b709dee051337244a09a30dbf6e95afe0d34a1f8b430d45e0982a7c125b \ --hash=sha256:ee4a4d2ae8bfe3cac012dcf3e4607975904c137e1738116549fc3dbbb6ff0e36 # via -r requirements/base.txt -setuptools==75.6.0 \ - --hash=sha256:8199222558df7c86216af4f84c30e9b34a61d8ba19366cc914424cdbd28252f6 \ - --hash=sha256:ce74b49e8f7110f9bf04883b730f4765b774ef3ef28f722cce7c273d253aaf7d - # via - # -r requirements/base.txt - # django-easymde shellcheck-py==0.10.0.1 \ --hash=sha256:390826b340b8c19173922b0da5ef7b66ef34d4d087dc48aad3e01f7e77e164d9 \ --hash=sha256:48f08965cafbb3363b265c4ef40628ffced19cb6fc7c4bb5ce72d32cbcfb4bb9 \ diff --git a/tests/utils/test_widgets.py b/tests/utils/test_widgets.py new file mode 100644 index 00000000000..a2ee5c04d62 --- /dev/null +++ b/tests/utils/test_widgets.py @@ -0,0 +1,36 @@ +from django import forms +from pytest_django.asserts import assertHTMLEqual + +from itou.utils.widgets import EasyMDEEditor + + +class TestEasyMDEEditor: + def test_basic(self): + class MyForm(forms.Form): + description = forms.CharField(widget=EasyMDEEditor) + + form = MyForm() + assertHTMLEqual( + str(form), + """ +
+ +