wagtail 5.2.7__py3-none-any.whl → 5.2.8__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
wagtail/__init__.py CHANGED
@@ -6,7 +6,7 @@ from wagtail.utils.version import get_semver_version, get_version
6
6
 
7
7
  # major.minor.patch.release.number
8
8
  # release must be one of alpha, beta, rc, or final
9
- VERSION = (5, 2, 7, "final", 1)
9
+ VERSION = (5, 2, 8, "final", 1)
10
10
 
11
11
  __version__ = get_version(VERSION)
12
12
 
@@ -110,6 +110,8 @@ class MigrateStreamData(RunPython):
110
110
 
111
111
  updated_model_instances_buffer = []
112
112
  for instance in model_queryset.iterator(chunk_size=self.chunk_size):
113
+ if instance.raw_content is None:
114
+ continue
113
115
 
114
116
  revision_query_maker.append_instance_data_for_revision_query(instance)
115
117
 
@@ -153,29 +153,29 @@ class StreamChildrenToListBlockOperation(BaseBlockOperation):
153
153
  super().__init__()
154
154
  self.block_name = block_name
155
155
  self.list_block_name = list_block_name
156
- self.temp_blocks = []
157
156
 
158
157
  def apply(self, block_value):
158
+ candidate_blocks = []
159
159
  mapped_block_value = []
160
160
  for child_block in block_value:
161
161
  if child_block["type"] == self.block_name:
162
- self.temp_blocks.append(child_block)
162
+ candidate_blocks.append(child_block)
163
163
  else:
164
164
  mapped_block_value.append(child_block)
165
165
 
166
- self.map_temp_blocks_to_list_items()
166
+ list_items = self.map_temp_blocks_to_list_items(candidate_blocks)
167
167
 
168
- if self.temp_blocks:
169
- new_list_block = {"type": self.list_block_name, "value": self.temp_blocks}
168
+ if list_items:
169
+ new_list_block = {"type": self.list_block_name, "value": list_items}
170
170
  mapped_block_value.append(new_list_block)
171
171
 
172
172
  return mapped_block_value
173
173
 
174
- def map_temp_blocks_to_list_items(self):
175
- new_temp_blocks = []
176
- for block in self.temp_blocks:
177
- new_temp_blocks.append({**block, "type": "item"})
178
- self.temp_blocks = new_temp_blocks
174
+ def map_temp_blocks_to_list_items(self, blocks):
175
+ list_items = []
176
+ for block in blocks:
177
+ list_items.append({**block, "type": "item"})
178
+ return list_items
179
179
 
180
180
  @property
181
181
  def operation_name_fragment(self):
@@ -653,7 +653,7 @@ class StreamValue(MutableSequence):
653
653
  raw_values = OrderedDict(
654
654
  (i, raw_item["value"])
655
655
  for i, raw_item in enumerate(self._raw_data)
656
- if raw_item["type"] == type_name and self._bound_blocks[i] is None
656
+ if self._bound_blocks[i] is None and raw_item["type"] == type_name
657
657
  )
658
658
  # pass the raw block values to bulk_to_python as a list
659
659
  converted_values = child_block.bulk_to_python(raw_values.values())
@@ -611,11 +611,13 @@ class TestFormPageWithCustomFormBuilder(WagtailTestUtils, TestCase):
611
611
  html=True,
612
612
  )
613
613
  # check ip address field has rendered
614
- self.assertContains(
615
- response,
616
- '<input type="text" name="device_ip_address" required id="id_device_ip_address" />',
617
- html=True,
618
- )
614
+ # (not comparing HTML directly because https://docs.djangoproject.com/en/5.1/releases/5.1.5/
615
+ # added a maxlength attribute)
616
+ soup = self.get_soup(response.content)
617
+ input = soup.find("input", {"name": "device_ip_address"})
618
+ self.assertEqual(input["type"], "text")
619
+ self.assertEqual(input["required"], "")
620
+ self.assertEqual(input["id"], "id_device_ip_address")
619
621
 
620
622
  def test_post_invalid_form(self):
621
623
  response = self.client.post(
@@ -1,3 +1,4 @@
1
+ import re
1
2
  import warnings
2
3
  from collections import OrderedDict
3
4
 
@@ -336,9 +337,12 @@ class MySQLSearchQueryCompiler(BaseSearchQueryCompiler):
336
337
 
337
338
  def build_search_query_content(self, query, invert=False):
338
339
  if isinstance(query, PlainText):
339
- terms = query.query_string.split()
340
+ # For Boolean full text search queries in MySQL,
341
+ # non-alphanumeric characters act as separators
342
+ terms = [term for term in re.split(r"\W+", query.query_string) if term]
343
+
340
344
  if not terms:
341
- return None
345
+ return SearchQuery("")
342
346
 
343
347
  last_term = terms.pop()
344
348
 
@@ -68,6 +68,81 @@ class TestMySQLSearchBackend(BackendTests, TransactionTestCase):
68
68
  all_other_titles | {"JavaScript: The Definitive Guide"},
69
69
  )
70
70
 
71
+ def test_empty_search(self):
72
+ results = self.backend.search("", models.Book.objects.all())
73
+ self.assertSetEqual(
74
+ {r.title for r in results},
75
+ set(),
76
+ )
77
+
78
+ results = self.backend.search(" ", models.Book.objects.all())
79
+ self.assertSetEqual(
80
+ {r.title for r in results},
81
+ set(),
82
+ )
83
+
84
+ results = self.backend.search("*", models.Book.objects.all())
85
+ self.assertSetEqual(
86
+ {r.title for r in results},
87
+ set(),
88
+ )
89
+
90
+ def test_empty_autocomplete(self):
91
+ results = self.backend.autocomplete("", models.Book.objects.all())
92
+ self.assertSetEqual(
93
+ {r.title for r in results},
94
+ set(),
95
+ )
96
+
97
+ results = self.backend.autocomplete(" ", models.Book.objects.all())
98
+ self.assertSetEqual(
99
+ {r.title for r in results},
100
+ set(),
101
+ )
102
+
103
+ results = self.backend.autocomplete("*", models.Book.objects.all())
104
+ self.assertSetEqual(
105
+ {r.title for r in results},
106
+ set(),
107
+ )
108
+
109
+ def test_symbols_in_search_term(self):
110
+ # symbols as their own tokens should be ignored
111
+ results = self.backend.search("javascript @ parts", models.Book.objects.all())
112
+ self.assertSetEqual(
113
+ {r.title for r in results},
114
+ {"JavaScript: The good parts"},
115
+ )
116
+
117
+ results = self.backend.search("javascript parts @", models.Book.objects.all())
118
+ self.assertSetEqual(
119
+ {r.title for r in results},
120
+ {"JavaScript: The good parts"},
121
+ )
122
+
123
+ results = self.backend.search("@ javascript parts", models.Book.objects.all())
124
+ self.assertSetEqual(
125
+ {r.title for r in results},
126
+ {"JavaScript: The good parts"},
127
+ )
128
+
129
+ # tokens containing both symbols and alphanumerics should not be discarded
130
+ # or treated as equivalent to the same token without symbols
131
+ results = self.backend.search("java@script parts", models.Book.objects.all())
132
+ self.assertSetEqual(
133
+ {r.title for r in results},
134
+ set(),
135
+ )
136
+
137
+ def test_autocomplete_with_symbols(self):
138
+ # the * is not part of the autocomplete mechanism, but if someone includes it
139
+ # we want it to be gracefully ignored
140
+ results = self.backend.autocomplete("parts javasc*", models.Book.objects.all())
141
+ self.assertSetEqual(
142
+ {r.title for r in results},
143
+ {"JavaScript: The good parts"},
144
+ )
145
+
71
146
  @skip(
72
147
  "The MySQL backend doesn't support choosing individual fields for the search, only (body, title) or (autocomplete) fields may be searched."
73
148
  )
@@ -7,3 +7,13 @@ class WagtailSnippetsTestsAppConfig(AppConfig):
7
7
  name = "wagtail.test.snippets"
8
8
  label = "snippetstests"
9
9
  verbose_name = _("Wagtail snippets tests")
10
+
11
+ def ready(self):
12
+ # Test registration of permission order within the group permissions view,
13
+ # as per https://docs.wagtail.org/en/stable/extending/customizing_group_views.html#customizing-the-group-editor-permissions-ordering
14
+ # Invoking `register` from `ready` confirms that it does not perform any database queries -
15
+ # if it did, it would fail (on a standard test run without --keepdb at least) because the
16
+ # test database hasn't been migrated yet.
17
+ from wagtail.users.permission_order import register
18
+
19
+ register("snippetstests.fancysnippet", order=999)
@@ -10,7 +10,7 @@ class MigrationTestMixin:
10
10
  default_operation_and_block_path = []
11
11
  app_name = None
12
12
 
13
- def init_migration(self, revisions_from=None, operations_and_block_path=None):
13
+ def init_migration(self, revisions_from=None, operations_and_block_paths=None):
14
14
  migration = Migration(
15
15
  "test_migration", "wagtail_streamfield_migration_toolkit_test"
16
16
  )
@@ -18,7 +18,7 @@ class MigrationTestMixin:
18
18
  app_name=self.app_name,
19
19
  model_name=self.model.__name__,
20
20
  field_name="content",
21
- operations_and_block_paths=operations_and_block_path
21
+ operations_and_block_paths=operations_and_block_paths
22
22
  or self.default_operation_and_block_path,
23
23
  revisions_from=revisions_from,
24
24
  )
@@ -29,11 +29,11 @@ class MigrationTestMixin:
29
29
  def apply_migration(
30
30
  self,
31
31
  revisions_from=None,
32
- operations_and_block_path=None,
32
+ operations_and_block_paths=None,
33
33
  ):
34
34
  migration = self.init_migration(
35
35
  revisions_from=revisions_from,
36
- operations_and_block_path=operations_and_block_path,
36
+ operations_and_block_paths=operations_and_block_paths,
37
37
  )
38
38
 
39
39
  loader = MigrationLoader(connection=connection)
@@ -13,35 +13,35 @@ class MigrationNameTest(TestCase, MigrationTestMixin):
13
13
  app_name = "wagtail_streamfield_migration_toolkit_test"
14
14
 
15
15
  def test_rename(self):
16
- operations_and_block_path = [
16
+ operations_and_block_paths = [
17
17
  (
18
18
  RenameStreamChildrenOperation(old_name="char1", new_name="renamed1"),
19
19
  "",
20
20
  )
21
21
  ]
22
22
  migration = self.init_migration(
23
- operations_and_block_path=operations_and_block_path
23
+ operations_and_block_paths=operations_and_block_paths
24
24
  )
25
25
 
26
26
  suggested_name = migration.suggest_name()
27
27
  self.assertEqual(suggested_name, "rename_char1_to_renamed1")
28
28
 
29
29
  def test_remove(self):
30
- operations_and_block_path = [
30
+ operations_and_block_paths = [
31
31
  (
32
32
  RemoveStreamChildrenOperation(name="char1"),
33
33
  "",
34
34
  )
35
35
  ]
36
36
  migration = self.init_migration(
37
- operations_and_block_path=operations_and_block_path
37
+ operations_and_block_paths=operations_and_block_paths
38
38
  )
39
39
 
40
40
  suggested_name = migration.suggest_name()
41
41
  self.assertEqual(suggested_name, "remove_char1")
42
42
 
43
43
  def test_multiple(self):
44
- operations_and_block_path = [
44
+ operations_and_block_paths = [
45
45
  (
46
46
  RenameStreamChildrenOperation(old_name="char1", new_name="renamed1"),
47
47
  "",
@@ -52,7 +52,7 @@ class MigrationNameTest(TestCase, MigrationTestMixin):
52
52
  ),
53
53
  ]
54
54
  migration = self.init_migration(
55
- operations_and_block_path=operations_and_block_path
55
+ operations_and_block_paths=operations_and_block_paths
56
56
  )
57
57
 
58
58
  suggested_name = migration.suggest_name()
@@ -1,12 +1,16 @@
1
1
  import datetime
2
2
  import json
3
3
 
4
- from django.db.models import F, JSONField
4
+ from django.db import connection
5
+ from django.db.models import F, JSONField, TextField
5
6
  from django.db.models.functions import Cast
6
7
  from django.test import TestCase
7
8
  from django.utils import timezone
8
9
 
9
- from wagtail.blocks.migrations.operations import RenameStreamChildrenOperation
10
+ from wagtail.blocks.migrations.operations import (
11
+ RenameStreamChildrenOperation,
12
+ StreamChildrenToListBlockOperation,
13
+ )
10
14
  from wagtail.test.streamfield_migrations import factories, models
11
15
  from wagtail.test.streamfield_migrations.testutils import MigrationTestMixin
12
16
 
@@ -24,8 +28,8 @@ class BaseMigrationTest(TestCase, MigrationTestMixin):
24
28
  ]
25
29
  app_name = None
26
30
 
27
- def setUp(self):
28
- instances = [
31
+ def _get_test_instances(self):
32
+ return [
29
33
  self.factory(
30
34
  content__0__char1="Test char 1",
31
35
  content__1__char1="Test char 2",
@@ -44,6 +48,9 @@ class BaseMigrationTest(TestCase, MigrationTestMixin):
44
48
  ),
45
49
  ]
46
50
 
51
+ def setUp(self):
52
+ instances = self._get_test_instances()
53
+
47
54
  self.original_raw_data = {}
48
55
  self.original_revisions = {}
49
56
 
@@ -102,9 +109,7 @@ class BaseMigrationTest(TestCase, MigrationTestMixin):
102
109
 
103
110
  self.apply_migration()
104
111
 
105
- instances = self.model.objects.all().annotate(
106
- raw_content=Cast(F("content"), JSONField())
107
- )
112
+ instances = self.model.objects.all()
108
113
 
109
114
  for instance in instances:
110
115
  old_revisions = self.original_revisions[instance.id]
@@ -128,9 +133,7 @@ class BaseMigrationTest(TestCase, MigrationTestMixin):
128
133
  revisions_from = timezone.now() + datetime.timedelta(days=2)
129
134
  self.apply_migration(revisions_from=revisions_from)
130
135
 
131
- instances = self.model.objects.all().annotate(
132
- raw_content=Cast(F("content"), JSONField())
133
- )
136
+ instances = self.model.objects.all()
134
137
 
135
138
  for instance in instances:
136
139
  old_revisions = self.original_revisions[instance.id]
@@ -159,9 +162,7 @@ class BaseMigrationTest(TestCase, MigrationTestMixin):
159
162
  revisions_from = timezone.now() - datetime.timedelta(days=2)
160
163
  self.apply_migration(revisions_from=revisions_from)
161
164
 
162
- instances = self.model.objects.all().annotate(
163
- raw_content=Cast(F("content"), JSONField())
164
- )
165
+ instances = self.model.objects.all()
165
166
 
166
167
  for instance in instances:
167
168
  old_revisions = self.original_revisions[instance.id]
@@ -209,3 +210,88 @@ class TestPage(BaseMigrationTest):
209
210
 
210
211
  def test_migrate_revisions_from_date(self):
211
212
  self._test_migrate_revisions_from_date()
213
+
214
+
215
+ class TestNullStreamField(BaseMigrationTest):
216
+ """
217
+ Migrations are processed if the underlying JSON is null.
218
+
219
+ This might occur if we're operating on a StreamField that was added to a model that
220
+ had existing records.
221
+ """
222
+
223
+ model = models.SamplePage
224
+ factory = factories.SamplePageFactory
225
+ has_revisions = True
226
+ app_name = "streamfield_migration_tests"
227
+
228
+ def _get_test_instances(self):
229
+ return self.factory.create_batch(1, content=None)
230
+
231
+ def setUp(self):
232
+ super().setUp()
233
+
234
+ # Bypass StreamField/StreamBlock processing that cast a None stream field value
235
+ # to the empty StreamValue, and set the underlying JSON to null.
236
+ with connection.cursor() as cursor:
237
+ cursor.execute(f"UPDATE {self.model._meta.db_table} SET content = 'null'")
238
+
239
+ def assert_null_content(self):
240
+ """
241
+ The raw JSON of all instances for this test is null.
242
+ """
243
+
244
+ instances = self.model.objects.all().annotate(
245
+ raw_content=Cast(F("content"), TextField())
246
+ )
247
+
248
+ for instance in instances:
249
+ with self.subTest(instance=instance):
250
+ self.assertEqual(instance.raw_content, "null")
251
+
252
+ def test_migrate_stream_data(self):
253
+ self.assert_null_content()
254
+ self.apply_migration()
255
+ self.assert_null_content()
256
+
257
+
258
+ class StreamChildrenToListBlockOperationTestCase(BaseMigrationTest):
259
+ model = models.SamplePage
260
+ factory = factories.SamplePageFactory
261
+ has_revisions = True
262
+ app_name = "streamfield_migration_tests"
263
+
264
+ def _get_test_instances(self):
265
+ return self.factory.create_batch(
266
+ size=3,
267
+ # Each content stream field has a single char block instance.
268
+ content__0__char1__value="Char Block 1",
269
+ )
270
+
271
+ def test_state_not_shared_across_instances(self):
272
+ """
273
+ StreamChildrenToListBlockOperation doesn't share state across model instances.
274
+
275
+ As a single operation instance is used to transform the data of multiple model
276
+ instances, we should not store model instance state on the operation instance.
277
+ See https://github.com/wagtail/wagtail/issues/12391.
278
+ """
279
+
280
+ self.apply_migration(
281
+ operations_and_block_paths=[
282
+ (
283
+ StreamChildrenToListBlockOperation(
284
+ block_name="char1", list_block_name="list1"
285
+ ),
286
+ "",
287
+ )
288
+ ]
289
+ )
290
+ for instance in self.model.objects.all().annotate(
291
+ raw_content=Cast(F("content"), JSONField())
292
+ ):
293
+ new_block = instance.raw_content[0]
294
+ self.assertEqual(new_block["type"], "list1")
295
+ self.assertEqual(len(new_block["value"]), 1)
296
+ self.assertEqual(new_block["value"][0]["type"], "item")
297
+ self.assertEqual(new_block["value"][0]["value"], "Char Block 1")
@@ -237,6 +237,24 @@ class TestStreamValueAccess(TestCase):
237
237
  self.assertEqual(fetched_body[1].block_type, "text")
238
238
  self.assertEqual(fetched_body[1].value, "bar")
239
239
 
240
+ def test_can_append_on_queried_instance(self):
241
+ # The test is analog to test_can_append(), but instead of working with the
242
+ # in-memory version from JSONStreamModel.objects.create(), we query a fresh
243
+ # instance from the db.
244
+ # It tests adding data to child blocks that
245
+ # have not yet been lazy loaded. This would previously crash.
246
+ self.json_body = JSONStreamModel.objects.get(pk=self.json_body.pk)
247
+ self.json_body.body.append(("text", "bar"))
248
+ self.json_body.save()
249
+
250
+ fetched_body = JSONStreamModel.objects.get(id=self.json_body.id).body
251
+ self.assertIsInstance(fetched_body, StreamValue)
252
+ self.assertEqual(len(fetched_body), 2)
253
+ self.assertEqual(fetched_body[0].block_type, "text")
254
+ self.assertEqual(fetched_body[0].value, "foo")
255
+ self.assertEqual(fetched_body[1].block_type, "text")
256
+ self.assertEqual(fetched_body[1].value, "bar")
257
+
240
258
 
241
259
  class TestStreamFieldRenderingBase(TestCase):
242
260
  model = JSONStreamModel
@@ -2,6 +2,7 @@ from django.contrib.contenttypes.models import ContentType
2
2
 
3
3
  from wagtail.coreutils import resolve_model_string
4
4
 
5
+ content_types_to_register = []
5
6
  CONTENT_TYPE_ORDER = {}
6
7
 
7
8
 
@@ -13,5 +14,18 @@ def register(model, **kwargs):
13
14
  """
14
15
  order = kwargs.pop("order", None)
15
16
  if order is not None:
16
- content_type = ContentType.objects.get_for_model(resolve_model_string(model))
17
- CONTENT_TYPE_ORDER[content_type.id] = order
17
+ # We typically call this at application startup, when the database may not be ready,
18
+ # and so we can't look up the content type yet. Instead we will queue up the
19
+ # (model, order) pair to be processed when the lookup is requested.
20
+ content_types_to_register.append((model, order))
21
+
22
+
23
+ def get_content_type_order_lookup():
24
+ if content_types_to_register:
25
+ for model, order in content_types_to_register:
26
+ content_type = ContentType.objects.get_for_model(
27
+ resolve_model_string(model)
28
+ )
29
+ CONTENT_TYPE_ORDER[content_type.id] = order
30
+ content_types_to_register.clear()
31
+ return CONTENT_TYPE_ORDER
@@ -4,7 +4,7 @@ import re
4
4
  from django import template
5
5
 
6
6
  from wagtail import hooks
7
- from wagtail.users.permission_order import CONTENT_TYPE_ORDER
7
+ from wagtail.users.permission_order import get_content_type_order_lookup
8
8
 
9
9
  register = template.Library()
10
10
 
@@ -42,9 +42,10 @@ def format_permissions(permission_bound_field):
42
42
  # get a distinct and ordered list of the content types that these permissions relate to.
43
43
  # relies on Permission model default ordering, dict.fromkeys() retaining that order
44
44
  # from the queryset, and the stability of sorted().
45
+ content_type_order = get_content_type_order_lookup()
45
46
  content_type_ids = sorted(
46
47
  dict.fromkeys(permissions.values_list("content_type_id", flat=True)),
47
- key=lambda ct: CONTENT_TYPE_ORDER.get(ct, float("inf")),
48
+ key=lambda ct: content_type_order.get(ct, float("inf")),
48
49
  )
49
50
 
50
51
  # iterate over permission_bound_field to build a lookup of individual renderable
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: wagtail
3
- Version: 5.2.7
3
+ Version: 5.2.8
4
4
  Summary: A Django content management system.
5
5
  Home-page: https://wagtail.org/
6
6
  Author: Wagtail core team + contributors
@@ -1,4 +1,4 @@
1
- wagtail/__init__.py,sha256=4St-DzE6DguGx8VaqXJompPQY0lqdDAZ8M1UzN9jMwM,724
1
+ wagtail/__init__.py,sha256=US8Zk6hZvFVh0vWFBCAhEpA3LrNuRjXc4KaLQ0Z8nLA,724
2
2
  wagtail/apps.py,sha256=38kXTdHoQzFnpUqDNxFpsqn2dut4V0u9rLOkhqCoYkc,713
3
3
  wagtail/compat.py,sha256=L41FhlX4xy5KgTdJ63smtM78mtKf1mxkPeOs8kyOwS0,538
4
4
  wagtail/coreutils.py,sha256=ZlmtoHC5YzXmhdKi13QkCSmBryAmS7k8s5jgQjahpEQ,20660
@@ -1018,11 +1018,11 @@ wagtail/blocks/base.py,sha256=10jhNwVVfDh8TCpCEbZTD1Kqywexn2Y5jHhpls5SRbA,26168
1018
1018
  wagtail/blocks/field_block.py,sha256=rYs7G2bHLzf4I7I47k0q_wbE7ExVCQ8WeD0eIEZpiOA,31376
1019
1019
  wagtail/blocks/list_block.py,sha256=WxZlGR7cgA_KvX7Z2222X5oADVDC5jKNjhM9my3UWY0,16375
1020
1020
  wagtail/blocks/static_block.py,sha256=e7f-zVewRCvdnmvsVtUn1fpoEbHfDIIA6G-yaXeD-vo,1627
1021
- wagtail/blocks/stream_block.py,sha256=JVoG7Kj2WJAPqFH8JY0p-2DLlKXinimDomVEDzpA4gs,30161
1021
+ wagtail/blocks/stream_block.py,sha256=woVuZXnEV-DklTgify0VskD7RkDmRd9VbDVZhwvkBas,30161
1022
1022
  wagtail/blocks/struct_block.py,sha256=Qu7Zo5Qk2VCw3zEyuWukWSVT_CSUnz6k3kLbe7ezGOQ,15122
1023
1023
  wagtail/blocks/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1024
- wagtail/blocks/migrations/migrate_operation.py,sha256=tO3usuHN2EdxvVAsiWy1oAPoivq2FoguJHJgiXpEyts,14218
1025
- wagtail/blocks/migrations/operations.py,sha256=lAWOqtG81xNkGgMr0LYVLY6FC2zEBvN9EvJIu1wLhD4,10594
1024
+ wagtail/blocks/migrations/migrate_operation.py,sha256=2yCTGHiQHoaaQEHpqpnHZ4ouPvstUgMlm8iixancOdg,14288
1025
+ wagtail/blocks/migrations/operations.py,sha256=rUmr3FNazwgDt7c5xEu9YvHLOLgstS5tedxLHmk3sNo,10582
1026
1026
  wagtail/blocks/migrations/utils.py,sha256=dk09vGBYRb-3sGPkFeWR5rxrboVgnlAlk6OGE4tqo2I,9544
1027
1027
  wagtail/contrib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1028
1028
  wagtail/contrib/forms/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -1161,7 +1161,7 @@ wagtail/contrib/forms/templates/wagtailforms/submissions_index.html,sha256=4rISA
1161
1161
  wagtail/contrib/forms/templates/wagtailforms/panels/form_responses_panel.html,sha256=iqKD050fLAHG2CZe-cK6ZsSlsHNirABtGtkfGvisc_4,331
1162
1162
  wagtail/contrib/forms/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1163
1163
  wagtail/contrib/forms/tests/test_forms.py,sha256=7tRUwJA2j9xzr28NpRC9hDYeOEfR1VTmBCPlny7UPVw,13634
1164
- wagtail/contrib/forms/tests/test_models.py,sha256=c1kPz-TWvIbqm16uUp5Ln8ndqhv6S2dFBS2IPZJdgXM,31728
1164
+ wagtail/contrib/forms/tests/test_models.py,sha256=7iecOyH2PyNcu0yUsmMJ-Flm1FktXjtHGR7BMkra2OA,31958
1165
1165
  wagtail/contrib/forms/tests/test_views.py,sha256=rXirGlom9s9veeodsYEU3mcBTCsRryT3D-pjErI05nM,73044
1166
1166
  wagtail/contrib/forms/tests/utils.py,sha256=OESefxdqGRgL1lDItVPSFNw_FJNB4X0PvozdvAhrpkc,6043
1167
1167
  wagtail/contrib/frontend_cache/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -3184,7 +3184,7 @@ wagtail/search/backends/elasticsearch8.py,sha256=n3y05d_atzbnkaM4F1BXp3OhayE-J6u
3184
3184
  wagtail/search/backends/database/__init__.py,sha256=u8dKGRONweefcHoIlCIJgxpU5DgWAARDz1sefSUq-Rw,1727
3185
3185
  wagtail/search/backends/database/fallback.py,sha256=fBa63S05v_deEqwPMTOU3qRjJ7U7Db6D0_OIZuwwrIs,7774
3186
3186
  wagtail/search/backends/database/mysql/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3187
- wagtail/search/backends/database/mysql/mysql.py,sha256=AaMzUywfztVpJicjCkVDNwhvKWeMIdgK63lKGpR7WZc,23325
3187
+ wagtail/search/backends/database/mysql/mysql.py,sha256=WwSrOSeUL6iZK6FfsL_6hRl_9GK6jOelzXcODBomktk,23505
3188
3188
  wagtail/search/backends/database/mysql/query.py,sha256=MnLF8OnH281YT48IwlHqq4WdUnDBEfYN2lU9AbJ-jAM,9684
3189
3189
  wagtail/search/backends/database/postgres/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3190
3190
  wagtail/search/backends/database/postgres/postgres.py,sha256=PJff9wSNF4HPWqfs5vtpYHh_K56Jh1BaLe7wSxQVoGY,26403
@@ -3316,7 +3316,7 @@ wagtail/search/tests/test_elasticsearch7_backend.py,sha256=NxjMGh1vUjgmaLn6MqULX
3316
3316
  wagtail/search/tests/test_elasticsearch8_backend.py,sha256=3EKGnVO8ldl8em5rE0xG4b_BXVFvh7QGk719FSXGbzE,482
3317
3317
  wagtail/search/tests/test_index_functions.py,sha256=jbkpVqO-jsbNLGl9E8vXvxebMa60Ea82qPWacPcv1Y0,7681
3318
3318
  wagtail/search/tests/test_indexed_class.py,sha256=WU_6y6iTxI02HX0xvoXfA21aml9W8i53HffepSgQGVY,5674
3319
- wagtail/search/tests/test_mysql_backend.py,sha256=BsAJk-0y0nKsSOo4qovY8yHIRLnL6SL6fHs57yKEfoQ,3749
3319
+ wagtail/search/tests/test_mysql_backend.py,sha256=WmID5KO2E7EQzjQUEARnyN5eDlt3-64QNqASUZHO0d4,6310
3320
3320
  wagtail/search/tests/test_page_search.py,sha256=1oPBcgyJQpJmAQaNAhDAWXdVe2xrZ6kf7clefPZ0JLY,2363
3321
3321
  wagtail/search/tests/test_postgres_backend.py,sha256=QAR78RKMAUCH9beNu3r1Qxuhb865K8NU1C9sWC8lVco,7406
3322
3322
  wagtail/search/tests/test_postgres_stemming.py,sha256=zUqIANnIu4FD8SQtDd0xtCKH6x5voe9mdgA7nK0g3Wk,1346
@@ -3696,7 +3696,7 @@ wagtail/test/search/migrations/0001_initial.py,sha256=_xWfml2RnP3RBGHWLnzZ8GrQ0Y
3696
3696
  wagtail/test/search/migrations/0002_bookunindexed.py,sha256=vC2kHmuGZinDB7rWe2NN1nqw5o09clHjUZb73CbqwJ4,1311
3697
3697
  wagtail/test/search/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3698
3698
  wagtail/test/snippets/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3699
- wagtail/test/snippets/apps.py,sha256=D9-IRRAwG0IUdU8mVAPHbUlehPmyMEGd3lOW2jvD1_E,303
3699
+ wagtail/test/snippets/apps.py,sha256=Oh5pcAGUgm8fKABmXLK8PrHieRvsuREXVJHQqWAsRMQ,916
3700
3700
  wagtail/test/snippets/forms.py,sha256=cWcsv-6rFsDemGUSjb-yRu77e9qrjDL14tMI6V_ZmRI,174
3701
3701
  wagtail/test/snippets/models.py,sha256=c0ranLPa4iHL_Lm-bO-0xIPxB914Us9eXMpiyHAbtg0,2836
3702
3702
  wagtail/test/snippets/migrations/0001_initial.py,sha256=AqtNn9DR9LwSP2t7TrkXWWZdxW7v_sMXY1AxaFJU6kg,2115
@@ -3714,7 +3714,7 @@ wagtail/test/snippets/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRk
3714
3714
  wagtail/test/streamfield_migrations/apps.py,sha256=4oPD49SeDtjk3E4--RSa5CSGhGAMlUTM2bHn7J_RLfw,286
3715
3715
  wagtail/test/streamfield_migrations/factories.py,sha256=8S2QrVBZ1-qf41YsStExePOflNa1EhqjvGK5i8Brzrc,2270
3716
3716
  wagtail/test/streamfield_migrations/models.py,sha256=d5A6C63xaH3eGbwUbZgNcukbLiZ8JS0Tk1w0Zexy4qs,1270
3717
- wagtail/test/streamfield_migrations/testutils.py,sha256=Uhp30gcyqnD64WYAYNZ4b0BGdlqXkRQdgqu5r0S6Xao,1464
3717
+ wagtail/test/streamfield_migrations/testutils.py,sha256=joVrQFjmgyCi62gcRnCIIbcDe_UYMlD_ChZj3oVW7Zc,1469
3718
3718
  wagtail/test/streamfield_migrations/migrations/0001_initial.py,sha256=LDTry71fjBnzIzDvpPZnwHXvuYqJIYPg9OwFTpkk5z4,16308
3719
3719
  wagtail/test/streamfield_migrations/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3720
3720
  wagtail/test/testapp/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -3862,7 +3862,7 @@ wagtail/tests/test_revision_model.py,sha256=-AQl_PWLAfP_5qpbAdC2l-CsT03L17qc7lpW
3862
3862
  wagtail/tests/test_rich_text.py,sha256=1AuzDkNAzl2Sr0zeVhdSMYESLyOt6_bdsIwBMlYmU9k,13391
3863
3863
  wagtail/tests/test_signals.py,sha256=A_VSQxD4CJ7_h5c9qGI-K-mQvuuaJYn0qiyFe_kP3OE,3671
3864
3864
  wagtail/tests/test_sites.py,sha256=syJCEPMHdKXfbxc_Vb7CH9ymuPVZiWDAae4JmJPmPx0,8488
3865
- wagtail/tests/test_streamfield.py,sha256=gG_fQ4kq7UJEyE1nkJcq5Ep4bwmRLYjU9r8WtCvZFS8,27069
3865
+ wagtail/tests/test_streamfield.py,sha256=1DWDtUAIfEWCKMZbdSmMbssz-K7qRACx5AL0ic_t2Wc,28014
3866
3866
  wagtail/tests/test_telepath.py,sha256=muiOryoRmkISEHVu9GPSrKJEVB_EvpOufhvuJF-HrTk,10374
3867
3867
  wagtail/tests/test_tests.py,sha256=Y801jv650IAENPg-1pGTiicrHc9uHijg-Ga2DIDh0yM,17074
3868
3868
  wagtail/tests/test_translatablemixin.py,sha256=zcVkhZhQKuEWxY392Ftdec5MUZFW4ctzcbC0efLEayc,7644
@@ -3875,8 +3875,8 @@ wagtail/tests/tests.py,sha256=Zxb4AmmyhQzj8D_zTpB8kY6jEKRq_tt6_ykFFqnpjMQ,28975
3875
3875
  wagtail/tests/utils.py,sha256=P3wf4sfFxcHO7qFU5Q1yDmByZ7DC4sAiyMPu4PEBDGI,389
3876
3876
  wagtail/tests/streamfield_migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3877
3877
  wagtail/tests/streamfield_migrations/test_bad_data.py,sha256=fK57skgrOCkFveHbyiZefNWqkwHSczmeFplPChwRoko,9436
3878
- wagtail/tests/streamfield_migrations/test_migration_names.py,sha256=JCRPcW05b3cqvfZ1aCyOdH1wkFkoIuyBpbM9_AKLPHE,1872
3879
- wagtail/tests/streamfield_migrations/test_migrations.py,sha256=2iZd5NqU2UhxqeBdtrmlOaxDT8G1D2ilAL8FE6I-P7M,8029
3878
+ wagtail/tests/streamfield_migrations/test_migration_names.py,sha256=BZQ5UnatWlxnFu8IK2Q-mEVW3cY041bixgtJ9CTuelk,1881
3879
+ wagtail/tests/streamfield_migrations/test_migrations.py,sha256=qunwTE0mquUUN0g_yD8OHWQ6WM_beuW59yIA5lyD56M,10908
3880
3880
  wagtail/tests/streamfield_migrations/test_nested_structures.py,sha256=cBD0ToxlYqNH_UfKzX43_tZb4CcKwglmz38S-Hh5sWM,32238
3881
3881
  wagtail/tests/streamfield_migrations/test_old_list.py,sha256=L_JMTvDn5qUHV_IhwidtWaw9X-636XD75fpBlWVeTgU,10196
3882
3882
  wagtail/tests/streamfield_migrations/test_simple_structures.py,sha256=-_dlN0BssdkUvLmL3sjtcmWMYzqcARJQfyDHzPiONOw,22123
@@ -3884,7 +3884,7 @@ wagtail/users/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3884
3884
  wagtail/users/apps.py,sha256=VZbo2PEZXatFIfR5ruJryrgLcuxelJws3BMrG6b5vcs,339
3885
3885
  wagtail/users/forms.py,sha256=NxbLd8wlsrxi1PCQ5hbcM5V53gekzUZGTfa9vQrjZaU,14983
3886
3886
  wagtail/users/models.py,sha256=sZhWBk732DjdZWxLyl_mdMJuPpgX5GY8audAxSrPxv4,2993
3887
- wagtail/users/permission_order.py,sha256=fG_zFoNCAaPo4itEGVXZVL67U-aFZRWVarO5uSXskww,550
3887
+ wagtail/users/permission_order.py,sha256=4HtB1k6qu9NPus8ni59PzYFOVvQCjrhLvWdqMqkNuLs,1130
3888
3888
  wagtail/users/utils.py,sha256=zBjp_tEKsRQ4qULkDukCmdu2qJZnPb5ivcwWWRwnulk,1631
3889
3889
  wagtail/users/wagtail_hooks.py,sha256=owznymtqg4NSrnNYHsHcGHbWIabPWzaMmlJWMMnJBYI,5566
3890
3890
  wagtail/users/widgets.py,sha256=bwhne7LDJRziGekBq4p3MrNgJAo4gqKV4idfwgB-fiI,267
@@ -4038,7 +4038,7 @@ wagtail/users/templates/wagtailusers/users/index.html,sha256=UqcJoWGU9Pfv8tE25GH
4038
4038
  wagtail/users/templates/wagtailusers/users/list.html,sha256=diXGYC4wWaWF_AOe_1FvPtWH5UOqQxy_O62ZnniOGK8,2974
4039
4039
  wagtail/users/templates/wagtailusers/users/results.html,sha256=ZIRsUgss4IFEXMcsEyfw8ZozHSYRnWV5cTrHfcBDzxM,1372
4040
4040
  wagtail/users/templatetags/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4041
- wagtail/users/templatetags/wagtailusers_tags.py,sha256=lo5TbX0YS6siSDlWj9gFtzHQv3gtaYpxmvbImUmJd5Q,4493
4041
+ wagtail/users/templatetags/wagtailusers_tags.py,sha256=7Fe_A55UaP5jMW2zBJeDWjpx9yR3thkxDakx7ZVTpHE,4561
4042
4042
  wagtail/users/tests/__init__.py,sha256=-_5MwigQVfaxvNytkEKUk-0sG90N-APaWajAxNmQgX0,192
4043
4043
  wagtail/users/tests/test_admin_views.py,sha256=5D-cRee7-MZukWSvPyml3NdF6FyR1_LMOy3OY5aMj2M,92833
4044
4044
  wagtail/users/tests/test_bulk_actions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -4071,9 +4071,9 @@ wagtail/utils/urlpatterns.py,sha256=RDhVScxdm-RV4HSMjWElyrbEoTPsXu841_SKMgoFKtY,
4071
4071
  wagtail/utils/utils.py,sha256=UALGn0KOyqi5ZoXOozWX99ZPUsVZP51ET8d7sZ1jlm0,430
4072
4072
  wagtail/utils/version.py,sha256=40WGMIy8nSPQJFF01p7c38L444SexH2Cb-bQmR8ztXg,1478
4073
4073
  wagtail/utils/widgets.py,sha256=oTpTMcmQ0Y4MugbSKp_Uigl97ftWdHvMfvhrrLyVmBs,1529
4074
- wagtail-5.2.7.dist-info/LICENSE,sha256=0aiL7_RJ2YkOjscmRI7opwmuURrY6h8MR0B24nrdRQU,1512
4075
- wagtail-5.2.7.dist-info/METADATA,sha256=JVYFVFZDxQZBq87QSzO0WuJfJNvTdNre3WKcRANAck0,3850
4076
- wagtail-5.2.7.dist-info/WHEEL,sha256=2wepM1nk4DS4eFpYrW1TTqPcoGNfHhhO_i5m4cOimbo,92
4077
- wagtail-5.2.7.dist-info/entry_points.txt,sha256=R14Z0xKoufNcDaku0EWDKM-K8J4ap0EImO8C-df8HVM,53
4078
- wagtail-5.2.7.dist-info/top_level.txt,sha256=zcKgvuRTi0gSgVzJ1qMoERCwhQ_i0n9bkyxza3oh9as,8
4079
- wagtail-5.2.7.dist-info/RECORD,,
4074
+ wagtail-5.2.8.dist-info/LICENSE,sha256=0aiL7_RJ2YkOjscmRI7opwmuURrY6h8MR0B24nrdRQU,1512
4075
+ wagtail-5.2.8.dist-info/METADATA,sha256=_msb-14XJUO-jZKV6qpBOjmb6nMBhNInOQNjQJZbhl0,3850
4076
+ wagtail-5.2.8.dist-info/WHEEL,sha256=2wepM1nk4DS4eFpYrW1TTqPcoGNfHhhO_i5m4cOimbo,92
4077
+ wagtail-5.2.8.dist-info/entry_points.txt,sha256=R14Z0xKoufNcDaku0EWDKM-K8J4ap0EImO8C-df8HVM,53
4078
+ wagtail-5.2.8.dist-info/top_level.txt,sha256=zcKgvuRTi0gSgVzJ1qMoERCwhQ_i0n9bkyxza3oh9as,8
4079
+ wagtail-5.2.8.dist-info/RECORD,,