diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 69747db..4c4adc2 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -8,17 +8,17 @@ permissions: contents: read # to fetch code (actions/checkout) env: - PYTHON_LATEST: "3.11" + PYTHON_LATEST: "3.12" jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 0 - - uses: actions/setup-python@v4 + - uses: actions/setup-python@v5 with: python-version: ${{env.PYTHON_LATEST}} cache: "pip" @@ -32,7 +32,7 @@ jobs: - name: ๐Ÿ—๏ธ Build run: python -Im flit build - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: path: ./dist @@ -47,7 +47,7 @@ jobs: # Mandatory for trusted publishing id-token: write steps: - - uses: actions/download-artifact@v3 + - uses: actions/download-artifact@v4 - name: ๐Ÿš€ Publish package distributions to PyPI uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 260738f..e666fa1 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -20,7 +20,7 @@ env: TOX_TESTENV_PASSENV: FORCE_COLOR PIP_DISABLE_PIP_VERSION_CHECK: "1" PIP_NO_PYTHON_VERSION_WARNING: "1" - PYTHON_LATEST: "3.11" + PYTHON_LATEST: "3.12" jobs: @@ -28,10 +28,10 @@ jobs: lint: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 0 - - uses: actions/setup-python@v4 + - uses: actions/setup-python@v5 with: python-version: ${{env.PYTHON_LATEST}} - uses: pre-commit/action@v3.0.0 @@ -44,9 +44,9 @@ jobs: python: ["3.8", "3.9", "3.10", "3.11", "3.12"] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: ๐Ÿ Setup Python ${{ matrix.python }} - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python }} - name: ๐Ÿ“ฆ Install dependencies @@ -57,13 +57,13 @@ jobs: - name: ๐Ÿ—๏ธ Build wheel run: python -Im flit build --format wheel - - name: ๐Ÿงช Run tox targets for Python ${{ matrix.python-version }} + - name: ๐Ÿงช Run tox targets for Python ${{ matrix.python }} run: tox --installpkg ./dist/*.whl - name: โฌ†๏ธ Upload coverage data - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - name: coverage-data + name: coverage-data-${{ matrix.python }} path: .coverage.* if-no-files-found: ignore retention-days: 1 @@ -72,10 +72,10 @@ jobs: runs-on: ubuntu-latest needs: tests steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 0 - - uses: actions/setup-python@v4 + - uses: actions/setup-python@v5 with: # Use latest Python, so it understands all syntax. python-version: ${{env.PYTHON_LATEST}} @@ -83,9 +83,10 @@ jobs: - run: python -Im pip install --upgrade "coverage[toml]>=7.2,<8.0" - name: โฌ‡๏ธ Download coverage data - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: - name: coverage-data + pattern: coverage-data-* + merge-multiple: true - name: ๏ผ‹ Combine coverage run: | @@ -95,7 +96,7 @@ jobs: echo "## Coverage summary" >> $GITHUB_STEP_SUMMARY python -Im coverage report --format=markdown >> $GITHUB_STEP_SUMMARY - name: ๐Ÿ“ˆ Upload HTML report if check failed. - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: html-report path: htmlcov diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0073f0d..bf319be 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,7 +2,7 @@ ci: autofix_prs: false default_language_version: - python: python3.11 + python: python3.12 repos: - repo: https://github.com/pre-commit/pre-commit-hooks diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d57724..7b22602 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,8 +2,10 @@ ## Unreleased -- Add support for Wagtail 5.2 -- Add Wagtail 5.2 and Python 3.12 in test matrices @katdom13 +- Drop support for Wagtail < 5.2 @katdom13 +- Add support for Wagtail 6.0 @katdom13 +- Add support for Django 5.0 by @katdom13 +- Add compatibility tests for Wagtail 6.0 and Python 3.12 @katdom13 ## 0.10.0 diff --git a/README.md b/README.md index ae63f46..dbcdb02 100644 --- a/README.md +++ b/README.md @@ -110,11 +110,11 @@ tox To run tests for a specific environment: ```shell -tox -e python3.11-django4.2-wagtail5.2 +tox -e python3.12-django5.0-wagtail6.0 ``` To run a single test method in a specific environment: ```shell -tox -e python3.11-django4.2-wagtail5.2 -- tests.test.test_blocks.TestBlocks.test_block_with_features +tox -e python3.12-django5.0-wagtail6.0 -- tests.test.test_blocks.TestBlocks.test_block_with_features ``` diff --git a/pyproject.toml b/pyproject.toml index 01ff6f3..5dfb0e0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,20 +23,18 @@ classifiers = [ "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Framework :: Django", - "Framework :: Django :: 3", "Framework :: Django :: 3.2", - "Framework :: Django :: 4", - "Framework :: Django :: 4.1", "Framework :: Django :: 4.2", + "Framework :: Django :: 5.0", "Framework :: Wagtail", - "Framework :: Wagtail :: 4", "Framework :: Wagtail :: 5", + "Framework :: Wagtail :: 6", ] dynamic = ["version"] # will read __version__ from wagtail_footnotes/__init__.py requires-python = ">=3.8" dependencies = [ - "Wagtail>=4.1", + "Wagtail>=5.2", "Django>=3.2", ] @@ -47,6 +45,7 @@ testing = [ "black==23.7.0", "ruff==0.0.280", "coverage[toml]>=7.2,<8.0", + "wagtail-modeladmin>=2.0.0", ] [project.urls] diff --git a/tests/settings.py b/tests/settings.py index f51d490..fe65fd4 100644 --- a/tests/settings.py +++ b/tests/settings.py @@ -38,7 +38,7 @@ "wagtail.images", "wagtail.search", "wagtail.admin", - "wagtail.contrib.modeladmin", + "wagtail_modeladmin", "wagtail.sites", "wagtail", "taggit", diff --git a/tests/test/test_widgets.py b/tests/test/test_widgets.py index ce2b163..a65878f 100644 --- a/tests/test/test_widgets.py +++ b/tests/test/test_widgets.py @@ -1,5 +1,6 @@ from django import forms from django.test import TestCase +from wagtail import VERSION as WAGTAIL_VERSION from wagtail_footnotes.widgets import ReadonlyUUIDInput @@ -8,11 +9,18 @@ class TestWidgets(TestCase): def test_read_only_uuid_input(self): form = forms.Form() form.fields["uuid"] = forms.CharField(widget=ReadonlyUUIDInput) - self.assertInHTML( - '', - form.as_p(), - ) - self.assertInHTML( - '', - form.as_p(), - ) + + if WAGTAIL_VERSION >= (6, 0): + self.assertInHTML( + '', + form.as_p(), + ) + else: + self.assertInHTML( + '', + form.as_p(), + ) + self.assertInHTML( + '', + form.as_p(), + ) diff --git a/tox.ini b/tox.ini index c2fd6d5..d167ec6 100644 --- a/tox.ini +++ b/tox.ini @@ -2,10 +2,9 @@ min_version = 4.0 envlist = - python{3.8,3.9,3.10}-django{3.2}-wagtail{4.1,5.1,5.2} - python{3.9,3.10,3.11}-django{4.1}-wagtail{4.1,5.1,5.2} - python{3.10,3.11}-django{4.2}-wagtail{5.1,5.2} - python3.12-django4.2-wagtail5.2 + python{3.8,3.9,3.10,3.11,3.12}-django3.2-wagtail5.2 + python{3.8,3.9,3.10,3.11}-django4.2-wagtail{5.2,6.0} + python{3.10,3.11,3.12}-django5.0-wagtail{5.2,6.0} [gh-actions] python = @@ -28,14 +27,14 @@ deps = coverage[toml]>=7.2,<8.0 django3.2: Django>=3.2,<4.0 - django4.1: Django>=4.1,<4.2 django4.2: Django>=4.2,<4.3 + django5.0: Django>=5.0,<5.1 - wagtail4.1: wagtail>=4.1,<4.2 - wagtail5.1: wagtail>=5.1,<5.2 wagtail5.2: wagtail>=5.2,<5.3 + wagtail6.0: wagtail>=6.0,<6.1 +extras = testing install_command = python -Im pip install -U {opts} {packages} commands = python -Im coverage run testmanage.py test --deprecation all {posargs: -v 2} diff --git a/wagtail_footnotes/blocks.py b/wagtail_footnotes/blocks.py index 4157de1..1211b9e 100644 --- a/wagtail_footnotes/blocks.py +++ b/wagtail_footnotes/blocks.py @@ -2,7 +2,6 @@ from django.core.exceptions import ValidationError from django.utils.safestring import mark_safe -from wagtail import VERSION as WAGTAIL_VERSION from wagtail.blocks import RichTextBlock from wagtail.models import Page @@ -54,9 +53,7 @@ def replace_tag(match): return mark_safe(FIND_FOOTNOTE_TAG.sub(replace_tag, html)) # noqa: S308 def render(self, value, context=None): - kwargs = {"value": value} if WAGTAIL_VERSION >= (5, 2) else {} - - if not self.get_template(context=context, **kwargs): + if not self.get_template(value=value, context=context): return self.render_basic(value, context=context) html = super().render(value, context=context) diff --git a/wagtail_footnotes/static/footnotes/js/read-only-uuid-controller.js b/wagtail_footnotes/static/footnotes/js/read-only-uuid-controller.js new file mode 100644 index 0000000..5f13e0b --- /dev/null +++ b/wagtail_footnotes/static/footnotes/js/read-only-uuid-controller.js @@ -0,0 +1,9 @@ +// myapp/static/js/read-only-uuid-controller.js + +class CustomEditorController extends window.StimulusModule.Controller { + connect() { + setUUID(this.element.id); + } +} + +window.wagtail.app.register('read-only-uuid', CustomEditorController); diff --git a/wagtail_footnotes/widgets.py b/wagtail_footnotes/widgets.py index 0fd8c86..7345461 100644 --- a/wagtail_footnotes/widgets.py +++ b/wagtail_footnotes/widgets.py @@ -1,20 +1,64 @@ from django.forms import HiddenInput -from wagtail.utils.widgets import WidgetWithScript +from wagtail import VERSION as WAGTAIL_VERSION -class ReadonlyUUIDInput(WidgetWithScript, HiddenInput): - """ - This isn't really read-only. It's a hidden input with an an adjacent div - showing the current value; that way we can set the value in JavaScript, but - the user can't easily change it. - """ +if WAGTAIL_VERSION and WAGTAIL_VERSION >= (6, 0): + from django.forms import Media + from django.utils.safestring import mark_safe - def render_html(self, name, value, attrs): - """Render the HTML (non-JS) portion of the field markup""" - hidden = super(WidgetWithScript, self).render(name, value, attrs) - display_value = value[:6] if value is not None else value - shown = f'
{display_value}
' - return shown + hidden + class ReadonlyUUIDInput(HiddenInput): + """ + This isn't really read-only. It's a hidden input with an an adjacent div + showing the current value; that way we can set the value in JavaScript, but + the user can't easily change it. + """ - def render_js_init(self, id_, name, value): - return f'setUUID("{id_}");' + def render(self, name, value, attrs=None, renderer=None): + # no point trying to come up with sensible semantics for when 'id' is missing from attrs, + # so let's make sure it fails early in the process + try: + attrs["id"] + except (KeyError, TypeError) as exc: + raise TypeError( + "ReadonlyUUIDInput cannot be rendered without an 'id' attribute" + ) from exc + + widget_html = self.render_html(name, value, attrs) + + return mark_safe(widget_html) # noqa: S308 + + def render_html(self, name, value, attrs): + """Render the HTML (non-JS) portion of the field markup""" + hidden = super().render(name, value, attrs) + display_value = value[:6] if value is not None else value + shown = f'
{display_value}
' + return shown + hidden + + def build_attrs(self, *args, **kwargs): + attrs = super().build_attrs(*args, **kwargs) + attrs["data-controller"] = "read-only-uuid" + return attrs + + @property + def media(self): + return Media(js=["footnotes/js/read-only-uuid-controller.js"]) + +else: + from wagtail.utils.widgets import WidgetWithScript + + class ReadonlyUUIDInput(WidgetWithScript, HiddenInput): + """ + This isn't really read-only. It's a hidden input with an an adjacent div + showing the current value; that way we can set the value in JavaScript, but + the user can't easily change it. + """ + + def render_html(self, name, value, attrs): + """Render the HTML (non-JS) portion of the field markup""" + hidden = super(WidgetWithScript, self).render(name, value, attrs) + display_value = value[:6] if value is not None else value + shown = f'
{display_value}
' + return shown + hidden + + def render_js_init(self, id_, name, value): + return f'setUUID("{id_}");'