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-d8c106675794plone.app.standardtiles.contentlisting/210ab0406fda4dab93458c040115c5acplone.app.standardtiles.contentlisting/3342cfd463aa469aa00f257db8bf34deplone.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?