I needed to create a mosaic layout by hand, export it into my package so I can apply it at will.
I want to share with you how I did that to find out if I did something wrong or if I could have done that easier/better.
Goals:
- Create a frontpage with mosaic programatically with a upgrade-step.
- The layout must be editable.
- The layout consist of two rows each with some simple richtext tiles and some contentlisting tiles.
After I created the page and made the custom layout I inspected the object with pdb. I saw two attributes which seem to hold the required information:
customContentLayout
and content
. According to the behavior ILayoutAware
the first is 'Custom content and content layout of this page' and content
is 'Transient tile configurations and data for this page'.
So I stored these values:
MOSAIC_FRONTPAGE_CONTENT = """<!DOCTYPE html>
<html lang="en" data-layout="./@@page-site-layout">
<body data-panel="content">
<div data-tile="@@plone.app.standardtiles.html/8db0d793-eac3-4576-ba8b-d8c106675794">
<div>
<h2>Links</h2>
<p>
<a href="/Plone/resolveuid/7067534cf4c54a49b570cee6ff56af52" data-linktype="internal" data-val="7067534cf4c54a49b570cee6ff56af52" data-mce-href="../resolveuid/7067534cf4c54a49b570cee6ff56af52">Foo</a>
</p>
</div>
</div>
<div data-tile="@@plone.app.standardtiles.html/a558f9ab-6d4d-454e-9458-3b85c31bddfa">
<div>
<h2>FAQ</h2>
<p>Lorem ipsum dolor sit amet, consectetuer adipiscing elit.</p>
</div>
</div>
<div data-tile="@@plone.app.standardtiles.contentlisting/210ab0406fda4dab93458c040115c5ac" data-tiledata='{"title": null, "description": "", "query": [{"i": "portal_type", "o": "plone.app.querystring.operation.selection.any", "v": ["News Item"]}], "sort_on": "effective", "sort_reversed": true, "limit": 4, "view_template": "summary_view"}'>
</div>
<div data-tile="@@plone.app.standardtiles.contentlisting/3342cfd463aa469aa00f257db8bf34de" data-tiledata='{"title": "Termine", "description": "", "query": [{"i": "portal_type", "o": "plone.app.querystring.operation.selection.any", "v": ["Event"]}, {"i": "review_state", "o": "plone.app.querystring.operation.selection.any", "v": ["internally_published", "published"]}], "sort_on": "start", "sort_reversed": true, "limit": 3, "view_template": "summary_view"}'>
</div>
</body>
</html>"""
MOSAIC_FRONTPAGE_LAYOUT = """
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en" data-layout="./@@page-site-layout">
<body>
<div data-panel="content" data-max-columns="4">
<div class="mosaic-grid-row">
<div class="mosaic-grid-cell mosaic-width-full mosaic-position-leftmost">
<div class="movable removable mosaic-tile mosaic-IDublinCore-title-tile">
<div class="mosaic-tile-content">
<div data-tile="./@@plone.app.standardtiles.field?field=IDublinCore-title">
</div>
</div>
</div>
</div>
</div>
<div class="mosaic-grid-row">
<div class="mosaic-grid-cell mosaic-width-full mosaic-position-leftmost">
<div class="movable removable mosaic-tile mosaic-IDublinCore-description-tile">
<div class="mosaic-tile-content">
<div data-tile="./@@plone.app.standardtiles.field?field=IDublinCore-description">
</div>
</div>
</div>
</div>
</div>
<div class="mosaic-grid-row top-row">
<div class="mosaic-grid-cell mosaic-position-leftmost mosaic-width-quarter">
<div class="movable removable mosaic-tile mosaic-plone.app.standardtiles.html-tile">
<div class="mosaic-tile-content">
<div data-tile="./@@plone.app.standardtiles.html/8db0d793-eac3-4576-ba8b-d8c106675794">
</div>
</div>
</div>
</div>
<div class="mosaic-grid-cell mosaic-position-quarter mosaic-width-half">
<div class="movable removable mosaic-tile mosaic-plone.app.standardtiles.contentlisting-tile">
<div class="mosaic-tile-content">
<div data-tile="./@@plone.app.standardtiles.contentlisting/210ab0406fda4dab93458c040115c5ac">
</div>
</div>
</div>
</div>
<div class="mosaic-grid-cell mosaic-position-three-quarters mosaic-width-quarter">
<div class="movable removable mosaic-tile mosaic-plone.app.standardtiles.contentlisting-tile">
<div class="mosaic-tile-content">
<div data-tile="./@@plone.app.standardtiles.contentlisting/3342cfd463aa469aa00f257db8bf34de">
</div>
</div>
</div>
</div>
</div>
<div class="mosaic-grid-row bottom-row">
<div class="mosaic-grid-cell mosaic-width-full mosaic-position-leftmost">
<div class="movable removable mosaic-tile mosaic-plone.app.standardtiles.html-tile">
<div class="mosaic-tile-content">
<div data-tile="./@@plone.app.standardtiles.html/a558f9ab-6d4d-454e-9458-3b85c31bddfa">
</div>
</div>
</div>
</div>
</div>
</div>
</body>
</html>"""
This configures four tiles in a simple two-row-layout:
plone.app.standardtiles.html/8db0d793-eac3-4576-ba8b-d8c106675794
plone.app.standardtiles.contentlisting/210ab0406fda4dab93458c040115c5ac
plone.app.standardtiles.contentlisting/3342cfd463aa469aa00f257db8bf34de
plone.app.standardtiles.html/a558f9ab-6d4d-454e-9458-3b85c31bddfa
Note that the id of the tile in the references the id of a tile in the content.
I then wrote the upgrade-step. The following code then sets the content and then the layout:
frontpage.setLayout('layout_view')
ILayoutAware(frontpage).content = MOSAIC_FRONTPAGE_CONTENT
ILayoutAware(frontpage).customContentLayout = MOSAIC_FRONTPAGE_LAYOUT
Together they replicate the layout I configured by hand in the beginning. It seems that at least theoretically content and layout might be able to be stored together in the attribute customContentLayout
.
I wa able to store the html as url-encoded wit the query-string content
like this: <div data-tile="./@@plone.app.standardtiles.html/8db0d793-eac3-4576-ba8b-d8c106675794?content=%3Cp%3Efoo%3C%2Fp%3E"></div>
(which is <p>foo</p>
).
That does not seem to work with contentlisting though. I did not find any documentation about that so it might be either a nonexistent feature or a bug. The same is true when I store a manually created layout and reapply it wo new content: Richtext is kept but contentlisting configuration is lost.
The layout can be made reuseable by other content on the site when it is stored as a html-file in a folder layouts
and registerd in zcml:
<plone:static
directory="layouts"
name="mylayouts"
type="contentlayout"
/>
I can then set this layout with
ILayoutAware(frontpage).contentLayout = '++contentlayout++mylayouts/frontpage.html'
At this point I had everything I needed for my project but some questions remained in my mind:
-
How do I inspect the tile-configuration? I saw I can use
LayoutAwareTileDataStorage
:(Pdb++) from plone.app.blocks.layoutbehavior import LayoutAwareTileDataStorage (Pdb++) storage = LayoutAwareTileDataStorage(self.context, self.request) (Pdb++) pp storage['plone.app.standardtiles.contentlisting/210ab0406fda4dab93458c040115c5ac'] {'description': '', 'limit': 2, 'query': [{'i': 'portal_type', 'o': 'plone.app.querystring.operation.selection.any', 'v': ['News Item']}], 'sort_on': 'effective', 'sort_reversed': True, 'title': None, 'view_template': 'summary_view'}
But I can also traverse to the tiles:
(Pdb++) tile = self.context.restrictedTraverse('@@plone.app.standardtiles.contentlisting/210ab0406fda4dab93458c040115c5ac') (Pdb++) tile <Products.Five.browser.metaconfigure.ContentListingTile object at 0x11373b750> (Pdb++) tile.data {'title': None, 'description': '', 'query': [{'i': 'portal_type', 'o': 'plone.app.querystring.operation.selection.any', 'v': ['News Item']}], 'sort_on': 'effective', 'sort_reversed': True, 'limit': 2, 'view_template': 'summary_view'}
-
Which is the right way to modify existing tiles programatically so that the changes are actually stored?
-
How do I create and add new tiles in python to a content object?
-
Can I safely change the tile-id to something more readable?