Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for ListBlock #510

Merged
merged 2 commits into from
Feb 3, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions wagtail_localize/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
get_serializable_data_for_fields,
model_from_serializable_data,
)
from wagtail import VERSION as WAGTAIL_VERSION
from wagtail.core import blocks
from wagtail.core.fields import StreamField
from wagtail.core.models import (
Expand Down Expand Up @@ -1514,6 +1515,10 @@ def get_field_path_from_stream_block(stream_value, path_components):
] + get_field_path_from_stream_block(
block.value, path_components[1:]
)
elif isinstance(
block_def, blocks.ListBlock
) and WAGTAIL_VERSION >= (2, 16):
return [block.block_type, path_components[1]]

else:
return [block.block_type]
Expand Down
32 changes: 25 additions & 7 deletions wagtail_localize/segments/extract.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from django.core.exceptions import ImproperlyConfigured
from django.db import models
from modelcluster.fields import ParentalKey
from wagtail import VERSION as WAGTAIL_VERSION
from wagtail.core import blocks
from wagtail.core.fields import RichTextField, StreamField
from wagtail.core.models import Page, TranslatableMixin
Expand Down Expand Up @@ -40,7 +41,7 @@ def __init__(self, field, include_overridables=False):
self.field = field
self.include_overridables = include_overridables

def handle_block(self, block_type, block_value):
def handle_block(self, block_type, block_value, raw_value=None):
# Need to check if the app is installed before importing EmbedBlock
# See: https://github.com/wagtail/wagtail-localize/issues/309
if apps.is_installed("wagtail.embeds"):
Expand Down Expand Up @@ -96,7 +97,7 @@ def handle_block(self, block_type, block_value):
return self.handle_struct_block(block_value)

elif isinstance(block_type, blocks.ListBlock):
return self.handle_list_block(block_value)
return self.handle_list_block(block_value, raw_value)

elif isinstance(block_type, blocks.StreamBlock):
return self.handle_stream_block(block_value)
Expand Down Expand Up @@ -132,17 +133,34 @@ def handle_struct_block(self, struct_block):

return segments

def handle_list_block(self, list_block):
# TODO
return []
def handle_list_block(self, list_block, raw_value):
segments = []
if WAGTAIL_VERSION >= (2, 16):
zerolab marked this conversation as resolved.
Show resolved Hide resolved
# Wagtail 2.16 changes ListBlock values to be ListValue objects (i.e. {'value': '', 'id': ''})
# and will automatically convert from the simple list format used before. However that requires
# the block to be saved. bound_blocks will return ListValue objects, so we need to check that the
# stored value is the new format before extracting segments, othewise the block ids will continue
# to change.
has_block_format = list_block.list_block._item_is_in_block_format(
raw_value["value"][0]
)
if has_block_format:
for block in list_block.bound_blocks:
segments.extend(
segment.wrap(block.id)
for segment in self.handle_block(block.block, block.value)
)
return segments

def handle_stream_block(self, stream_block):
segments = []

for block in stream_block:
for index, block in enumerate(stream_block):
segments.extend(
segment.wrap(block.id)
for segment in self.handle_block(block.block, block.value)
for segment in self.handle_block(
block.block, block.value, raw_value=stream_block.raw_data[index]
)
)

return segments
Expand Down
17 changes: 15 additions & 2 deletions wagtail_localize/segments/ingest.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from django.apps import apps
from django.db import models
from wagtail import VERSION as WAGTAIL_VERSION
from wagtail.core import blocks
from wagtail.core.fields import RichTextField, StreamField
from wagtail.core.rich_text import RichText
Expand Down Expand Up @@ -202,8 +203,20 @@ def handle_struct_block(self, struct_block, segments):
return struct_block

def handle_list_block(self, list_block, segments):
# TODO
pass
if WAGTAIL_VERSION >= (2, 16):
segments_by_block = defaultdict(list)

for segment in segments:
block_uuid, segment = segment.unwrap()
segments_by_block[block_uuid].append(segment)

for block_index, block in enumerate(list_block.bound_blocks):
block_segments = segments_by_block[block.id]
list_block.bound_blocks[block_index].value = self.handle_block(
block.block, block.value, block_segments
)

return list_block

def get_stream_block_child_data(self, stream_block, block_uuid):
for stream_child in stream_block:
Expand Down
50 changes: 39 additions & 11 deletions wagtail_localize/segments/tests/test_segment_extraction.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

from django.core.exceptions import ImproperlyConfigured
from django.test import TestCase
from wagtail import VERSION as WAGTAIL_VERSION
from wagtail.core.blocks import StreamValue
from wagtail.core.models import Page, Site

Expand All @@ -12,7 +13,10 @@
StringSegmentValue,
TemplateSegmentValue,
)
from wagtail_localize.segments.extract import extract_segments
from wagtail_localize.segments.extract import (
StreamFieldSegmentExtractor,
extract_segments,
)
from wagtail_localize.strings import StringValue
from wagtail_localize.test.models import (
TestChildObject,
Expand Down Expand Up @@ -336,24 +340,48 @@ def test_structblock(self):
],
)

@unittest.expectedFailure # Not supported (probably won't ever be due to lack of path stability)
@unittest.skipUnless(
WAGTAIL_VERSION >= (2, 16),
"ListBlocks are supported starting with Wagtail 2.16",
)
def test_listblock(self):
block_id = uuid.uuid4()
page = make_test_page_with_streamfield_block(
str(block_id), "test_listblock", ["Test content", "Some more test content"]
str(block_id),
"test_listblock",
[
{
"type": "item",
"value": "Test content",
"id": "11111111-1111-1111-1111-111111111111",
},
{
"type": "item",
"value": "Some more test content",
"id": "22222222-2222-2222-2222-222222222222",
},
],
)

expected_segments = [
StringSegmentValue(f"test_streamfield.{block_id}.{item.id}", item.value)
for item in page.test_streamfield[0].value.bound_blocks
]
segments = extract_segments(page)
self.assertEqual(segments, expected_segments)

self.assertEqual(
segments,
[
StringSegmentValue(f"test_streamfield.{block_id}", "Test content"),
StringSegmentValue(
f"test_streamfield.{block_id}", "Some more test content"
),
],
@unittest.skipUnless(
WAGTAIL_VERSION >= (2, 16),
"ListBlocks are supported starting with Wagtail 2.16",
)
def test_listblock_not_extracted_when_not_in_block_format(self):
page = make_test_page_with_streamfield_block(
uuid.uuid4(), "test_listblock", ["Test content", "Some more test content"]
)
segments = StreamFieldSegmentExtractor(
page.test_streamfield
).handle_stream_block(page.test_streamfield)
self.assertEqual(segments, [])

def test_nestedstreamblock(self):
block_id = uuid.uuid4()
Expand Down
44 changes: 29 additions & 15 deletions wagtail_localize/segments/tests/test_segment_ingestion.py
Original file line number Diff line number Diff line change
Expand Up @@ -567,28 +567,31 @@ def test_structblock(self):
],
)

@unittest.expectedFailure # Not supported (probably won't ever be due to lack of path stability)
@unittest.skipUnless(
WAGTAIL_VERSION >= (2, 16), "ListBlocks are supported starting Wagtail 2.16"
)
def test_listblock(self):
block_id = uuid.uuid4()
page = make_test_page_with_streamfield_block(
str(block_id), "test_listblock", ["Test content", "Some more test content"]
)

translated_page = page.copy_for_translation(self.locale)

translated_strings = ["Tester le contenu", "Encore du contenu de test"]
block_ids = [
item.id for item in translated_page.test_streamfield[0].value.bound_blocks
]
expected_segments = [
StringSegmentValue(
f"test_streamfield.{block_id}.{item_id}",
translated_strings[index],
order=index,
)
for index, item_id in enumerate(block_ids)
]

ingest_segments(
page,
translated_page,
self.src_locale,
self.locale,
[
StringSegmentValue(
f"test_streamfield.{block_id}", "Tester le contenu", order=0
),
StringSegmentValue(
f"test_streamfield.{block_id}", "Encore du contenu de test", order=1
),
],
page, translated_page, self.src_locale, self.locale, expected_segments
)

translated_page.save()
Expand All @@ -600,7 +603,18 @@ def test_listblock(self):
{
"id": str(block_id),
"type": "test_listblock",
"value": ["Tester le contenu", "Encore du contenu de test"],
"value": [
{
"type": "item",
"value": "Tester le contenu",
"id": block_ids[0],
},
{
"type": "item",
"value": "Encore du contenu de test",
"id": block_ids[1],
},
],
}
],
)
Expand Down