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

Slots #1063

Open
wants to merge 118 commits into
base: main
Choose a base branch
from
Open

Slots #1063

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
118 commits
Select commit Hold shift + click to select a range
24b3649
WIP
tiberiuichim Feb 4, 2021
8aeec3c
Add slot_block_ids index
tiberiuichim Feb 4, 2021
0302833
Improve patch service
tiberiuichim Feb 4, 2021
85b9fa7
WIP on deserializers
tiberiuichim Feb 4, 2021
3bf47f6
WIP on deserializers
tiberiuichim Feb 4, 2021
f17d4d1
WIP
tiberiuichim Feb 5, 2021
98e009d
Add slots engine unittest
tiberiuichim Feb 6, 2021
34bc973
Improve test formatting
tiberiuichim Feb 6, 2021
8401e0c
Merge remote-tracking branch 'origin/master' into slots
tiberiuichim Feb 6, 2021
02223e0
More tests on slot engine crutch
tiberiuichim Feb 6, 2021
68d534c
Add VolatileSmartField
tiberiuichim Feb 6, 2021
901e3a0
WIP
tiberiuichim Feb 6, 2021
09630a2
Add another way of treating reordered slot fills
tiberiuichim Feb 7, 2021
48f3865
Add save_data_to_slot to slot engine
tiberiuichim Feb 7, 2021
ef71439
Move SlotsEngine to .slots, tweak names
tiberiuichim Feb 7, 2021
3af5692
Rename ISlotsStorage -> ISlotStorage
tiberiuichim Feb 7, 2021
4a479be
Move zca registrations to slots.zcml; more tests
tiberiuichim Feb 8, 2021
44747ad
More tests
tiberiuichim Feb 8, 2021
3b1540c
Add discovery of slots, add slot storage serializer
tiberiuichim Feb 8, 2021
6fd2006
Add initial test of slot service
tiberiuichim Feb 8, 2021
9b087c5
Fix slot serialization service
tiberiuichim Feb 8, 2021
6ee8e76
Fix slot serializer
tiberiuichim Feb 8, 2021
870881d
Add configlet and control panel for content slots
tiberiuichim Feb 8, 2021
62676e4
Shuffle events code
tiberiuichim Feb 9, 2021
65f02ff
Add get_editable_slots implementation
tiberiuichim Feb 9, 2021
a8a0d98
Pass editable status in @slots endpoints
tiberiuichim Feb 9, 2021
53953af
Fix tests
tiberiuichim Feb 9, 2021
58ccfbc
Fix tests
tiberiuichim Feb 9, 2021
ffa86ba
Add security tests
tiberiuichim Feb 9, 2021
84b5779
Add security tests
tiberiuichim Feb 9, 2021
f56b43a
WIP on deserializer
tiberiuichim Feb 10, 2021
6e27022
Rename slot_blocks -> blocks, slot_blocks_layout -> blocks_layout, as…
tiberiuichim Feb 10, 2021
895f5ad
Visual formatting
tiberiuichim Feb 10, 2021
15305b3
Visual formatting
tiberiuichim Feb 10, 2021
cdc54c3
Move interfaces to .slots package
tiberiuichim Feb 10, 2021
b4693a3
Separate deserializer test
tiberiuichim Feb 10, 2021
15809aa
Fix deserializer
tiberiuichim Feb 10, 2021
3505931
Fix deserializer
tiberiuichim Feb 10, 2021
b70875e
Fix deserializer
tiberiuichim Feb 10, 2021
e23c68d
Add another test
tiberiuichim Feb 10, 2021
afae726
Fix indexing, test remove of block from child layout
tiberiuichim Feb 10, 2021
42f007d
Fix indexing, test remove of block from child layout
tiberiuichim Feb 10, 2021
6344b64
WIP on deserializers
tiberiuichim Feb 10, 2021
ee327f3
Fix deserializer + tests
tiberiuichim Feb 11, 2021
fb0e637
Fix tests; don't use an index to keep block sanity, instead resolve i…
tiberiuichim Feb 11, 2021
03c1962
Remove block removed events and associated catalog index, they're not…
tiberiuichim Feb 11, 2021
cd90fba
Run black
tiberiuichim Feb 11, 2021
a28ea4e
Fix slot
tiberiuichim Feb 12, 2021
790d571
Simplify test content creation
tiberiuichim Feb 12, 2021
bea4d76
Add skip csrf protection
tiberiuichim Feb 14, 2021
1922a9d
Use edit status in serializer
tiberiuichim Feb 14, 2021
dd20be7
Use edit status in serializer
tiberiuichim Feb 14, 2021
063f4d3
Fix deserializer, don't trigger ObjectModifiedEvent, as it needs its …
tiberiuichim Feb 15, 2021
24be48d
Fix block transformer for slots
tiberiuichim Feb 16, 2021
14fbd04
Merge remote-tracking branch 'origin/master' into slots
tiberiuichim Mar 8, 2021
216f867
Merge remote-tracking branch 'origin/master' into slots
tiberiuichim Mar 19, 2021
d420b87
Run black
tiberiuichim Mar 19, 2021
8cfc7e2
Merge remote-tracking branch 'origin/master' into slots
tiberiuichim Mar 29, 2021
c3ae3db
Add can_manage_slots info to response
tiberiuichim Mar 29, 2021
923f7ad
Fix a bug in deleting slot fills
tiberiuichim Mar 29, 2021
495380e
Fix another delete
tiberiuichim Mar 30, 2021
08cd548
Merge master
tiberiuichim May 25, 2021
b54a298
Merge remote-tracking branch 'origin/master' into slots
tiberiuichim Jun 7, 2021
49fce60
Merge remote-tracking branch 'origin/master' into slots
tiberiuichim Jul 2, 2021
2ee8de7
Add transaction doom
tiberiuichim Jul 2, 2021
fc8d000
Add full parameter
tiberiuichim Jul 2, 2021
28cab5d
Fix deserializer
tiberiuichim Jul 2, 2021
e00ed37
Add block parent
tiberiuichim Jul 2, 2021
401a7ea
Fix parameters
tiberiuichim Jul 2, 2021
d6583c0
Improve block_parent implementation
tiberiuichim Jul 2, 2021
e6bd47b
Pass blocks when full is true
tiberiuichim Jul 2, 2021
cfae76c
Add info about attributes
tiberiuichim Jul 5, 2021
f2cdd56
Fix _v_ handling; send a _v_original to the frontend
tiberiuichim Jul 6, 2021
df11630
WIP
tiberiuichim Jul 6, 2021
8ea93d8
Add test for block hiding
tiberiuichim Jul 8, 2021
30c2f99
Fix sameAs handling
tiberiuichim Jul 8, 2021
527c621
Fix some tests
tiberiuichim Jul 8, 2021
00f5159
More tests, enable the _v_original as it's needed in frontend when 'u…
tiberiuichim Jul 8, 2021
4061471
Fix some tests, run black
tiberiuichim Jul 8, 2021
33a0d8f
Fix deserializer test
tiberiuichim Jul 8, 2021
4ac03ea
Merge remote-tracking branch 'origin/master' into slots
tiberiuichim Jul 8, 2021
3c6cfa4
Fix serializer test
tiberiuichim Jul 8, 2021
4964317
Fix one test in test_addons
tiberiuichim Jul 8, 2021
f729ae0
Add upgrade handler
tiberiuichim Jul 8, 2021
8fb791d
Remove unused import
tiberiuichim Jul 8, 2021
4ce5fde
Fix _v_ handling
tiberiuichim Jul 10, 2021
8aea2f9
Fix deserializing when showing a hidden parent
tiberiuichim Jul 10, 2021
fa3c80f
Fix parent if None
tiberiuichim Jul 10, 2021
222043b
Run black, localize test urls
tiberiuichim Jul 10, 2021
ecf0788
Add some test descriptions
tiberiuichim Jul 11, 2021
d8eaaae
Add a test for third-hand inherited slots
tiberiuichim Jul 11, 2021
211420e
Make the test more clear
tiberiuichim Jul 11, 2021
1bfe28f
WIP on test
tiberiuichim Jul 11, 2021
79eb618
Fix deep inheritance test
tiberiuichim Jul 12, 2021
9eb4403
Don't include _v_inherit everywhere; don't fail on deleted inherited …
tiberiuichim Jul 12, 2021
66e7687
WIP on new traversal for slots
tiberiuichim Jul 28, 2021
493f345
Add missing file
tiberiuichim Jul 28, 2021
c5723f6
WIP on new traversal for slots
tiberiuichim Jul 28, 2021
59c3644
Adjust tests for new slots serialized ids
tiberiuichim Jul 29, 2021
f77f5a4
Run zpretty
tiberiuichim Jul 29, 2021
b484298
Run black
tiberiuichim Jul 29, 2021
72aa841
Remove print statement
tiberiuichim Jul 29, 2021
1d524f3
Merge pull request #1187 from plone/slots_traversing
tiberiuichim Jul 29, 2021
6eabb69
Merge remote-tracking branch 'origin/master' into slots
tiberiuichim Jul 29, 2021
16839b3
Pass expanders in @slots endpoint
tiberiuichim Jul 29, 2021
29a5a63
Run black
tiberiuichim Jul 31, 2021
7800daa
Fix tests
tiberiuichim Jul 31, 2021
e4aa1e3
Merge master
tiberiuichim Oct 17, 2021
378e5a1
Pass readOnly
tiberiuichim Oct 26, 2021
0e34a87
Merge remote-tracking branch 'origin/master' into slots
tiberiuichim Nov 3, 2021
7695215
Merge remote-tracking branch 'origin/master' into slots
tiberiuichim Nov 11, 2021
850cf31
Merge remote-tracking branch 'origin/master' into slots
tiberiuichim Nov 27, 2021
544f614
Merge remote-tracking branch 'origin/master' into slots
tiberiuichim Mar 5, 2022
79f72da
Merge remote-tracking branch 'origin/master' into slots
tiberiuichim May 17, 2022
9b93908
Merge branch 'master' into slots
tiberiuichim Jul 23, 2022
3f6cf2a
Merge remote-tracking branch 'origin/master' into slots
tiberiuichim Aug 2, 2022
be413c1
Merge from 'master'
laszlocseh Jul 27, 2023
af48f1f
Merge remote-tracking branch 'origin/main' into slots
tiberiuichim Oct 11, 2023
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
94 changes: 94 additions & 0 deletions docs/source/endpoints/tiles.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
---
myst:
html_meta:
"description": "A tile in Plone is an HTML snippet that can contain arbitrary content, such as text, images, or videos. The tiles endpoint is deprecated and will be removed in plone.restapi 9."
"property=og:description": "A tile in Plone is an HTML snippet that can contain arbitrary content, such as text, images, or videos. The tiles endpoint is deprecated and will be removed in plone.restapi 9."
"property=og:title": "Tiles"
"keywords": "Plone, plone.restapi, REST, API, Tiles"
---

# Tiles

```{warning}
The tiles endpoint is deprecated and will be removed in `plone.restapi` 9.
```

```{note}
The tiles endpoint currently matches only partially, specifically the `GET` endpoints, the default Plone implementation.
```

A tile in Plone is an HTML snippet that can contain arbitrary content, such as text, images, or videos.


## Listing available tiles

```{note}
This endpoint currently does not return any data.
The functionality needs to be implemented.
```

List all available tiles types by sending a `GET` request to the `@tiles` endpoint on the portal root:

```
GET /plone/@tiles HTTP/1.1
Accept: application/json
Authorization: Basic YWRtaW46c2VjcmV0
```

The server responds with a {term}`200 OK` status and lists all available tiles:

```
HTTP/1.1 200 OK
Content-Type: application/json
[
{
"@id": "http://localhost:55001/plone/@tiles/title",
"title": "Title tile",
"description": "A field tile that will show the title of the content object",
},
{
"@id": "http://localhost:55001/plone/@tiles/description",
"title": "Description tile",
"description": "A field tile that will show the description of the content object",
},
]
```


## Retrieve JSON schema of an individual tile

```{note}
This endpoint currently does not return any data.
The functionality needs to be implemented.
```

Retrieve the JSON schema of a specific tile by calling the `@tiles` endpoint with the ID of the tile:

```
GET /plone/@tiles/title HTTP/1.1
Accept: application/json
Authorization: Basic YWRtaW46c2VjcmV0
```

The server responds with a JSON schema definition for that particular tile:

```
HTTP/1.1 200 OK
Content-Type: application/json+schema

{
"properties": {
"title": {
"description": "",
"title": "Title",
"type": "string"
},
...
},
"required": [
"title",
],
"title": "Title Tile",
"type": "object"
}
```
1 change: 1 addition & 0 deletions src/plone/restapi/configure.zcml
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@
<include package=".deserializer" />
<include package=".types" />
<include package=".search" />
<include package=".slots" />
<include package=".cache" />

<include package=".upgrades" />
Expand Down
20 changes: 20 additions & 0 deletions src/plone/restapi/deserializer/blocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,26 @@ def __call__(self, block):
return block


@adapter(IBlocks, IBrowserRequest)
@implementer(IBlockFieldDeserializationTransformer)
class VolatileSmartField(object):
"""When deserializing block values, delete all block fields that start with `_v_`"""

order = float("inf")
block_type = None

def __init__(self, context, request):
self.context = context
self.request = request

def __call__(self, block):
keys = [k for k in block.keys() if k.startswith("_v_")]
for k in keys:
del block[k]

return block


@adapter(IBlocks, IBrowserRequest)
@implementer(IBlockFieldDeserializationTransformer)
class ResolveUIDDeserializer(ResolveUIDDeserializerBase):
Expand Down
8 changes: 8 additions & 0 deletions src/plone/restapi/deserializer/configure.zcml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,14 @@
<adapter factory=".dxfields.DecimalFieldDeserializer" />
<adapter factory=".blocks.BlocksJSONFieldDeserializer" />

<adapter factory=".slots.SlotDeserializer" />
<adapter factory=".slots.SlotDeserializerRoot" />
<adapter factory=".slots.SlotsDeserializer" />
<adapter
factory=".slots.SlotsDeserializerRoot"
provides="plone.restapi.interfaces.IDeserializeFromJson"
/>

<subscriber
factory=".blocks.TextBlockDeserializer"
provides="plone.restapi.interfaces.IBlockFieldDeserializationTransformer"
Expand Down
142 changes: 142 additions & 0 deletions src/plone/restapi/deserializer/slots.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
# -*- coding: utf-8 -*-

""" Slots deserializers """

from plone.restapi.deserializer import json_body
from plone.restapi.interfaces import IBlockFieldDeserializationTransformer
from plone.restapi.interfaces import IDeserializeFromJson
from plone.restapi.slots import Slot
from plone.restapi.slots.interfaces import ISlot
from plone.restapi.slots.interfaces import ISlots
from plone.restapi.slots.interfaces import ISlotStorage
from Products.CMFCore.interfaces import IContentish
from Products.CMFPlone.interfaces import IPloneSiteRoot
from zope.component import adapter
from zope.component import getMultiAdapter
from zope.component import subscribers
from zope.interface import implementer
from zope.location.interfaces import ILocation
from zope.publisher.interfaces.browser import IBrowserRequest

import copy


@adapter(IContentish, ISlot, IBrowserRequest)
@implementer(IDeserializeFromJson)
class SlotDeserializer(object):
"""Deserializer of one slot for contentish objects"""

def __init__(self, context, slot, request):
self.context = context
self.slot = slot
self.request = request

def __call__(self, data=None):
if data is None:
data = json_body(self.request)

if not data:
return

incoming_blocks = copy.deepcopy(data["blocks"])

parent_block_ids = []
parent = ILocation(self.context).__parent__
if parent is not None:
engine = ISlots(parent)
parent_block_ids = list(
engine.get_data(self.slot.__name__, full=True)["blocks"].keys()
)

# don't keep blocks that are not in incoming data
for k in list(self.slot.blocks.keys()):
if not ((k in parent_block_ids) or (k in incoming_blocks.keys())):
del self.slot.blocks[k]

inherited = []
# don't store blocks that are inherited, keep only those that really exist
for k, v in list(incoming_blocks.items()):
if v.get("_v_inherit"):
del incoming_blocks[k]
if k in parent_block_ids:
inherited.append(k)

slot = self.slot.__of__(self.context)

for id, block_value in incoming_blocks.items():
block_type = block_value.get("@type", "")

handlers = []
for h in subscribers(
(slot, self.request),
IBlockFieldDeserializationTransformer,
):
if h.block_type == block_type or h.block_type is None:
handlers.append(h)

for handler in sorted(handlers, key=lambda h: h.order):
if not getattr(handler, "disabled", False):
block_value = handler(block_value)

incoming_blocks[id] = block_value

self.slot.blocks = incoming_blocks

# don't keep block ids in layout if they're nowhere in the inheritance tree
all_ids = parent_block_ids + list(self.slot.blocks.keys()) + inherited
layout = [b for b in data["blocks_layout"]["items"] if b in all_ids]
data["blocks_layout"]["items"] = layout
self.slot.blocks_layout = data["blocks_layout"]

self.slot.block_parent = data.get("block_parent", False)
self.slot._p_changed = True


@adapter(IPloneSiteRoot, ISlot, IBrowserRequest)
@implementer(IDeserializeFromJson)
class SlotDeserializerRoot(SlotDeserializer):
"""Deserializer of one slot for site root"""


@adapter(IContentish, ISlotStorage, IBrowserRequest)
@implementer(IDeserializeFromJson)
class SlotsDeserializer(object):
"""Default deserializer of slots"""

def __init__(self, context, storage, request):
self.context = context
self.storage = storage
self.request = request

def __call__(self, data=None):
if data is None:
data = json_body(self.request)

for name, slot in self.storage.items():
incoming_data = data.get(name, None)

# remove the existing slot data if the slot doesn't exist in new data
if not incoming_data:
# notify(BlocksRemovedEvent(dict(
# context=self.context,
# blocks=slot.blocks
# )))

self.storage[name].blocks = {}
self.storage[name].blocks_layout = {"items": []}

for name, incoming_data in data.items():
if name not in self.storage:
# create new slots when found in incoming data
self.storage[name] = Slot()

deserializer = getMultiAdapter(
(self.context, self.storage[name], self.request), IDeserializeFromJson
)
deserializer(incoming_data)


@adapter(IPloneSiteRoot, ISlotStorage, IBrowserRequest)
@implementer(IDeserializeFromJson)
class SlotsDeserializerRoot(SlotsDeserializer):
"""Deserializer of slots for site root"""
9 changes: 9 additions & 0 deletions src/plone/restapi/interfaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from zope.interface import Attribute
from zope.interface import Interface
from zope.interface.interfaces import IObjectEvent
from zope.publisher.interfaces.browser import IDefaultBrowserLayer


Expand Down Expand Up @@ -216,6 +217,14 @@ def __call__(value):
"""Extract text from the block value. Returns text"""


class IBlocksRemovedEvent(IObjectEvent):
"""A bunch of blocks have been removed"""


class IBlockRemovedEvent(IObjectEvent):
"""A block has been removed"""


class IJSONSummarySerializerMetadata(Interface):
"""Configure JSONSummary serializer."""

Expand Down
2 changes: 2 additions & 0 deletions src/plone/restapi/permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@
# permissions. Granted to Anonymous (i.e. everyone) by default via rolemap.xml

UseRESTAPI = "plone.restapi: Use REST API"

ModifySlotsPermission = "plone.restapi: Modify slots information"
9 changes: 9 additions & 0 deletions src/plone/restapi/permissions.zcml
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,14 @@
id="plone.restapi.getusers"
title="plone.restapi: Access Plone user information"
/>
<permission
id="plone.restapi.GetSlots"
title="plone.restapi: Access slots information"
/>

<permission
id="plone.restapi.ModifySlots"
title="plone.restapi: Modify slots information"
/>

</configure>
20 changes: 20 additions & 0 deletions src/plone/restapi/profiles/default/controlpanel.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?xml version="1.0"?>
<object
name="portal_controlpanel"
xmlns:i18n="http://xml.zope.org/namespaces/i18n"
i18n:domain="plone.restapi">

<configlet
title="Slots"
action_id="slots_settings"
appId="plone.restapi.slots"
category="Plone"
condition_expr=""
url_expr="string:${portal_url}/@@slots-controlpanel"
icon_expr="string:${portal_url}/++resource++plone.app.theming.gif"
visible="True"
i18n:attributes="title">
<permission>Manage portal</permission>
</configlet>

</object>
2 changes: 1 addition & 1 deletion src/plone/restapi/profiles/default/metadata.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?xml version="1.0"?>
<metadata>
<version>0006</version>
<version>0007</version>
</metadata>
4 changes: 4 additions & 0 deletions src/plone/restapi/profiles/default/registry.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0"?>
<registry>
<records interface="plone.restapi.slots.interfaces.ISlotSettings" />
</registry>
3 changes: 3 additions & 0 deletions src/plone/restapi/serializer/configure.zcml
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,9 @@

<include package=".controlpanels" />

<adapter factory=".slots.SlotSerializer" />
<adapter factory=".slots.SlotsSerializer" />

<!-- Summary Serializer Metadata -->
<utility
factory=".summary.JSONSummarySerializerMetadata"
Expand Down
Loading
Loading