Skip to content

Commit

Permalink
Merge pull request #7 from torchbox-forks/support/wagtail-60
Browse files Browse the repository at this point in the history
Wagtail 6.0
  • Loading branch information
katdom13 authored Mar 6, 2024
2 parents d30f77e + d32d7db commit aa5e3d7
Show file tree
Hide file tree
Showing 7 changed files with 170 additions and 67 deletions.
21 changes: 21 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,26 @@ jobs:
name: flake8
command: venv/bin/tox -e flake8

test_py312:
working_directory: ~/wagtail-autocomplete-repo
docker:
- image: cimg/python:3.12
steps:
- checkout
- run:
name: install dependencies
command: |
python -m venv venv
. venv/bin/activate
pip install -U pip
pip install ".[test]"
- run:
name: tests
command: venv/bin/tox -f py312
- run:
name: flake8
command: venv/bin/tox -e flake8

workflows:
version: 2
build:
Expand All @@ -88,3 +108,4 @@ workflows:
- test_py39
- test_py310
- test_py311
- test_py312
11 changes: 7 additions & 4 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,20 @@ Changelog
Unreleased
----------

* Remove tests for Wagtail 4.2 and 5.0 as they have reached their EOL
* Added Wagtail 5.x compatibility
* Added tests for Python 3.10 and 3.11
* Remove support for versions of Wagtail < 4.1 (Wagtail 4.1 or later now required)
* Added Wagtail 6.0 compatibility
* Added tests for Django 5.0
* Added tests for Python 3.12

0.11 Release
------------

* Add handling of validation errors during creation of objects.
* Fix bug where searches failed if Django's CSRF cookie was configured with ``CSRF_COOKIE_HTTPONLY`` set to ``True``
* Update Javascript dependencies to remove security vulnerabilities.
* Remove tests for Wagtail 4.2 and 5.0 as they have reached their EOL
* Added Wagtail 5.x compatibility
* Added tests for Python 3.10 and 3.11
* Remove support for versions of Wagtail < 4.1 (Wagtail 4.1 or later now required)

0.10 Release
------------
Expand Down
5 changes: 4 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,15 +54,18 @@
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'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',
'Framework :: Django :: 5.0',
'Framework :: Wagtail',
'Framework :: Wagtail :: 4',
'Framework :: Wagtail :: 5',
'Framework :: Wagtail :: 6',
'Topic :: Internet :: WWW/HTTP',
'Topic :: Internet :: WWW/HTTP :: Dynamic Content',
],
Expand Down
15 changes: 8 additions & 7 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@
skipsdist = True
usedevelop = True
envlist =
py{38,39,310}-dj{32,41}-wt{41,51,52}
py311-dj41-wt{41,51,52}
py311-dj42-wt{51,52}
py{38,39,310,311}-dj32-wt{41,52}
py312-dj32-wt52
py{38,39,310,311,312}-dj42-wt{52,60}
py{310,311,312}-dj50-wt{52,60}

[testenv]
install_command = pip install -e ".[test]" -U {opts} {packages}
Expand All @@ -14,21 +15,21 @@ basepython =
py39: python3.9
py310: python3.10
py311: python3.11
py312: python3.12
deps =
dj32: django>=3.2,<4.0
dj41: django>=4.1,<4.2
dj42: django>=4.2,<4.3
dj50: django>=5.0,<5.1
wt41: wagtail>=4.1,<4.2
wt42: wagtail>=4.2,<5.0
wt50: wagtail>=5.0,<5.1
wt51: wagtail>=5.1,<5.2
wt52: wagtail>=5.2,<5.3
wt60: wagtail>=6.0,<6.1

[testenv:flake8]
basepython =
py38: python3.8
py39: python3.9
py310: python3.10
py311: python3.11
py312: python3.12
deps = flake8>3.7
commands = flake8 wagtailautocomplete
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
class AutoCompleteWidgetController extends window.StimulusModule.Controller {
connect() {
initAutoCompleteWidget(this.element.id);
}
}

window.wagtail.app.register('autocomplete-widget', AutoCompleteWidgetController);
7 changes: 6 additions & 1 deletion wagtailautocomplete/tests/test_edit_handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from django.contrib.auth.models import AnonymousUser
from django.core.exceptions import ImproperlyConfigured
from django.test import RequestFactory, TestCase
from wagtail import VERSION as WAGTAIL_VERSION
from wagtail.admin.panels import ObjectList

from wagtailautocomplete.edit_handlers import AutocompletePanel
Expand Down Expand Up @@ -50,7 +51,11 @@ def test_form_field_media(self):

def test_render_js_init(self):
result = self.autocomplete_panel.render_html()
self.assertIn('initAutoCompleteWidget("id_owner");', result)

if WAGTAIL_VERSION >= (6, 0): # type: ignore
self.assertIn('data-autocomplete-input-id="id_owner"', result)
else:
self.assertIn('initAutoCompleteWidget("id_owner");', result)

def test_render_as_field(self):
result = self.autocomplete_panel.render_html()
Expand Down
171 changes: 117 additions & 54 deletions wagtailautocomplete/widgets.py
Original file line number Diff line number Diff line change
@@ -1,64 +1,127 @@
import json

from django import forms
from wagtail import VERSION as WAGTAIL_VERSION
from wagtail.admin.staticfiles import versioned_static
from wagtail.utils.widgets import WidgetWithScript

from .views import render_page

if WAGTAIL_VERSION >= (6, 0): # type: ignore

class Autocomplete(WidgetWithScript):
template_name = 'wagtailautocomplete/autocomplete.html'

def __init__(self, target_model, can_create=False, is_single=True, attrs=None):
super().__init__(attrs)

self.target_model = target_model
self.can_create = can_create
self.is_single = is_single

def get_context(self, name, value, attrs):
context = super().get_context(name, value, attrs)
context['widget']['target_model'] = self.target_model._meta.label
context['widget']['can_create'] = self.can_create
context['widget']['is_single'] = self.is_single
return context

def format_value(self, value):
if not value:
return 'null'
if isinstance(value, list):
return json.dumps([
render_page(page)
for page in self.target_model.objects.filter(pk__in=value)
])
else:
return json.dumps(render_page(self.target_model.objects.get(pk=value)))

def value_from_datadict(self, data, files, name):
# treat empty value as None to prevent deserialization error
original_value = super().value_from_datadict(data, files, name)
if not original_value:
from django.forms.widgets import Widget


class Autocomplete(Widget): # type: ignore
template_name = 'wagtailautocomplete/autocomplete.html'

def __init__(self, target_model, can_create=False, is_single=True, attrs=None):
super().__init__(attrs)

self.target_model = target_model
self.can_create = can_create
self.is_single = is_single

def get_context(self, name, value, attrs):
context = super().get_context(name, value, attrs)
context['widget']['target_model'] = self.target_model._meta.label
context['widget']['can_create'] = self.can_create
context['widget']['is_single'] = self.is_single
return context

def format_value(self, value):
if not value:
return 'null'
if isinstance(value, list):
return json.dumps([
render_page(page)
for page in self.target_model.objects.filter(pk__in=value)
])
else:
return json.dumps(render_page(self.target_model.objects.get(pk=value)))

def value_from_datadict(self, data, files, name):
# treat empty value as None to prevent deserialization error
original_value = super().value_from_datadict(data, files, name)
if not original_value:
return None

value = json.loads(original_value)

if isinstance(value, list):
return [obj['pk'] for obj in value if 'pk' in obj]
if isinstance(value, dict):
return value.get('pk', None)
return None

def build_attrs(self, *args, **kwargs):
attrs = super().build_attrs(*args, **kwargs)
attrs["data-controller"] = "autocomplete-widget"
return attrs

@property
def media(self):
return forms.Media(
css={
'all': [versioned_static('wagtailautocomplete/dist.css')],
},
js=[versioned_static('wagtailautocomplete/dist.js'), versioned_static('wagtailautocomplete/autocomplete-widget-controller.js')],
)
else:
from wagtail.utils.widgets import WidgetWithScript


class Autocomplete(WidgetWithScript):
template_name = 'wagtailautocomplete/autocomplete.html'

def __init__(self, target_model, can_create=False, is_single=True, attrs=None):
super().__init__(attrs)

self.target_model = target_model
self.can_create = can_create
self.is_single = is_single

def get_context(self, name, value, attrs):
context = super().get_context(name, value, attrs)
context['widget']['target_model'] = self.target_model._meta.label
context['widget']['can_create'] = self.can_create
context['widget']['is_single'] = self.is_single
return context

def format_value(self, value):
if not value:
return 'null'
if isinstance(value, list):
return json.dumps([
render_page(page)
for page in self.target_model.objects.filter(pk__in=value)
])
else:
return json.dumps(render_page(self.target_model.objects.get(pk=value)))

def value_from_datadict(self, data, files, name):
# treat empty value as None to prevent deserialization error
original_value = super().value_from_datadict(data, files, name)
if not original_value:
return None

value = json.loads(original_value)

if isinstance(value, list):
return [obj['pk'] for obj in value if 'pk' in obj]
if isinstance(value, dict):
return value.get('pk', None)
return None

value = json.loads(original_value)

if isinstance(value, list):
return [obj['pk'] for obj in value if 'pk' in obj]
if isinstance(value, dict):
return value.get('pk', None)
return None

def render_js_init(self, id_, name, value):
return "initAutoCompleteWidget({id});".format(
id=json.dumps(id_),
)

@property
def media(self):
return forms.Media(
css={
'all': [versioned_static('wagtailautocomplete/dist.css')],
},
js=[versioned_static('wagtailautocomplete/dist.js')],
)
def render_js_init(self, id_, name, value):
return "initAutoCompleteWidget({id});".format(
id=json.dumps(id_),
)

@property
def media(self):
return forms.Media(
css={
'all': [versioned_static('wagtailautocomplete/dist.css')],
},
js=[versioned_static('wagtailautocomplete/dist.js')],
)

0 comments on commit aa5e3d7

Please sign in to comment.