Skip to content

Commit e272e89

Browse files
authored
Merge branch 'main' into plip-1997-poc
2 parents d43b0f8 + 7a7f048 commit e272e89

19 files changed

Lines changed: 937 additions & 100 deletions

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,11 @@ local.cfg
4444
/instance/
4545
/.make-sentinels/
4646
/*-mxdev.txt
47+
/.mxdev_cache
4748
/reports/
4849
/sources/
4950
/venv/
51+
/.venv/
5052
.installed.txt
5153
.mxdev_cache/
5254

.pre-commit-config.yaml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,6 @@ repos:
5757
# _your own configuration lines_
5858
# """
5959
##
60-
- repo: https://github.com/mgedmin/check-manifest
61-
rev: "0.51"
62-
hooks:
63-
- id: check-manifest
6460
- repo: https://github.com/regebro/pyroma
6561
rev: "5.0.1"
6662
hooks:

CHANGES.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,23 @@
99

1010
<!-- towncrier release notes start -->
1111

12+
## 10.0.0 (2026-05-18)
13+
14+
No significant changes.
15+
16+
17+
## 10.0.0rc3 (2026-04-13)
18+
19+
20+
### New features
21+
22+
- Add support for plate block from `@kitconcept/volto-plate` (text indexer, resolveuid transforms, link integrity). @davisagli #1998
23+
24+
25+
### Bug fixes
26+
27+
- Fix deserialization of content that has a non-context-aware defaultFactory. @davisagli #2013
28+
1229
## 10.0.0rc2 (2026-03-27)
1330

1431

news/2013.bugfix

Lines changed: 0 additions & 1 deletion
This file was deleted.

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import pathlib
44
import sys
55

6-
version = "10.0.0rc3.dev0"
6+
version = "10.0.1.dev0"
77

88
if getattr(sys.version_info, "major") == 2:
99
raise ValueError(

src/plone/restapi/blocks.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from collections import deque
12
from plone.restapi.interfaces import IBlockVisitor
23
from zope.component import adapter
34
from zope.component import subscribers
@@ -72,3 +73,15 @@ def __call__(self, block_value):
7273
yield from block_value["data"]["blocks"].values()
7374
if "blocks" in block_value:
7475
yield from block_value["blocks"].values()
76+
if block_value.get("@type") == "__somersault__":
77+
yield from self.visit_plate_value(block_value.get("value", []))
78+
79+
def visit_plate_value(self, value):
80+
queue = deque(value)
81+
while queue:
82+
child = queue.pop()
83+
if isinstance(child, dict):
84+
if "@type" in child:
85+
yield child
86+
elif child.get("children", []):
87+
queue.extend(child["children"] or [])

src/plone/restapi/blocks_linkintegrity.py

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,7 @@ def __call__(self, block):
5454
return links
5555

5656

57-
@adapter(IBlocks, IBrowserRequest)
58-
@implementer(IBlockFieldLinkIntegrityRetriever)
59-
class SlateBlockLinksRetriever:
60-
order = 100
61-
block_type = "slate"
57+
class BaseSlateOrPlateBlockLinksRetriever:
6258
field = "value"
6359

6460
def __init__(self, context, request):
@@ -77,9 +73,16 @@ def __call__(self, block):
7773
value = handler(child)
7874
if value:
7975
self.links.append(value)
80-
8176
return self.links
8277

78+
79+
@adapter(IBlocks, IBrowserRequest)
80+
@implementer(IBlockFieldLinkIntegrityRetriever)
81+
class SlateBlockLinksRetriever(BaseSlateOrPlateBlockLinksRetriever):
82+
order = 100
83+
block_type = "slate"
84+
field = "value"
85+
8386
def handle_a(self, child):
8487
data = child.get("data", {})
8588
if data.get("link", {}).get("internal", {}).get("internal_link"):
@@ -92,6 +95,17 @@ def handle_link(self, child):
9295
return child["data"]["url"]
9396

9497

98+
@adapter(IBlocks, IBrowserRequest)
99+
@implementer(IBlockFieldLinkIntegrityRetriever)
100+
class PlateBlockLinksRetriever(BaseSlateOrPlateBlockLinksRetriever):
101+
order = 100
102+
block_type = "__somersault__"
103+
field = "value"
104+
105+
def handle_a(self, child):
106+
return child.get("url")
107+
108+
95109
@adapter(IBlocks, IBrowserRequest)
96110
@implementer(IBlockFieldLinkIntegrityRetriever)
97111
class GenericBlockLinksRetriever:

src/plone/restapi/configure.zcml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,5 +152,9 @@
152152
factory=".blocks_linkintegrity.SlateBlockLinksRetriever"
153153
provides="plone.restapi.interfaces.IBlockFieldLinkIntegrityRetriever"
154154
/>
155+
<subscriber
156+
factory=".blocks_linkintegrity.PlateBlockLinksRetriever"
157+
provides="plone.restapi.interfaces.IBlockFieldLinkIntegrityRetriever"
158+
/>
155159

156160
</configure>

src/plone/restapi/indexers.py

Lines changed: 42 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22
from plone.indexer.decorator import indexer
33
from plone.restapi import HAS_PLONE_6
44
from plone.restapi.behaviors import IBlocks
5+
from plone.restapi.blocks import visit_blocks
56
from plone.restapi.blocks import visit_subblocks
67
from plone.restapi.interfaces import IBlockSearchableText
8+
from typing import List
79
from zope.component import adapter
810
from zope.component import queryMultiAdapter
911
from zope.globalrequest import getRequest
@@ -65,6 +67,40 @@ def __call__(self, block):
6567
return block.get("plaintext", "")
6668

6769

70+
@implementer(IBlockSearchableText)
71+
@adapter(IBlocks, IBrowserRequest)
72+
class PlateTextIndexer:
73+
"""Searchable Text indexer for plate blocks."""
74+
75+
def __init__(self, context, request):
76+
self.context = context
77+
self.request = request
78+
79+
def __call__(self, block) -> str:
80+
texts = [self.extract_plate_text(block["value"])]
81+
for subblock in visit_subblocks(self.context, block):
82+
texts.append(extract_text(subblock, self.context, self.request))
83+
result = text_strip(texts)
84+
print(result)
85+
return result
86+
87+
def extract_plate_text(self, value) -> str:
88+
if isinstance(value, list):
89+
return " ".join(self.extract_plate_text(item) for item in value)
90+
elif isinstance(value, dict):
91+
if "@type" in value:
92+
# sub-block, will be processed via visit_blocks
93+
return ""
94+
texts = []
95+
for key in ("text", "children"):
96+
if key in value:
97+
texts.append(self.extract_plate_text(value[key]))
98+
return " ".join(texts)
99+
elif isinstance(value, str):
100+
return value.strip()
101+
return ""
102+
103+
68104
def extract_text(block, obj, request):
69105
"""Extract text information from a block.
70106
@@ -93,23 +129,16 @@ def extract_text(block, obj, request):
93129
# Use server side adapters to extract the text data
94130
adapter = queryMultiAdapter((obj, request), IBlockSearchableText, name=block_type)
95131
result = adapter(block) if adapter is not None else ""
96-
if not result:
97-
for subblock in visit_subblocks(obj, block):
98-
tmp_result = extract_text(subblock, obj, request)
99-
result = f"{result}\n{tmp_result}"
100132
return result
101133

102134

103-
def get_blocks_text(obj):
135+
def get_blocks_text(obj) -> List[str]:
104136
"""Extract text to be used by the SearchableText index in the Catalog."""
105137
request = getRequest()
106-
blocks = obj.blocks
107-
blocks_layout = obj.blocks_layout
108-
blocks_text = []
109-
for block_id in blocks_layout.get("items", []):
110-
block = blocks.get(block_id, {})
111-
blocks_text.append(extract_text(block, obj, request))
112-
return blocks_text
138+
texts = []
139+
for block in visit_blocks(obj, obj.blocks):
140+
texts.append(extract_text(block, obj, request))
141+
return texts
113142

114143

115144
def text_strip(text_list):
@@ -139,5 +168,4 @@ def SearchableText_blocks(obj):
139168
blocks_text = get_blocks_text(obj)
140169
# Extract text using the base plone.app.contenttypes indexer
141170
std_text = SearchableText(obj)
142-
blocks_text.append(std_text)
143-
return text_strip(blocks_text)
171+
return text_strip([std_text] + blocks_text)

src/plone/restapi/indexers.zcml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,5 +26,9 @@
2626
factory=".indexers.TableBlockSearchableText"
2727
name="table"
2828
/>
29+
<adapter
30+
factory=".indexers.PlateTextIndexer"
31+
name="__somersault__"
32+
/>
2933

3034
</configure>

0 commit comments

Comments
 (0)