Skip to content

Commit

Permalink
Add support for ListBlock (#510)
Browse files Browse the repository at this point in the history
* Add support for `ListBlock`s
* Only extract `ListBlock` segments when they are in the new format
  • Loading branch information
zerolab committed Feb 3, 2022
1 parent 1cf48a3 commit 58d3231
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 35 deletions.
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):
# 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

0 comments on commit 58d3231

Please sign in to comment.