plone.tiles 3.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- plone/tiles/__init__.py +7 -0
- plone/tiles/absoluteurl.py +101 -0
- plone/tiles/configure.zcml +184 -0
- plone/tiles/data.py +422 -0
- plone/tiles/data.rst +179 -0
- plone/tiles/directives.py +13 -0
- plone/tiles/directives.rst +221 -0
- plone/tiles/esi.py +214 -0
- plone/tiles/esi.rst +325 -0
- plone/tiles/fieldtypeconverters.py +39 -0
- plone/tiles/interfaces.py +172 -0
- plone/tiles/meta.py +162 -0
- plone/tiles/meta.zcml +16 -0
- plone/tiles/test.pt +1 -0
- plone/tiles/testing.py +59 -0
- plone/tiles/tests/__init__.py +0 -0
- plone/tiles/tests/test_data.py +83 -0
- plone/tiles/tests/test_doctests.py +30 -0
- plone/tiles/tile.py +186 -0
- plone/tiles/tiles.rst +783 -0
- plone/tiles/type.py +39 -0
- plone.tiles-3.0.0-py3.13-nspkg.pth +1 -0
- plone_tiles-3.0.0.dist-info/LICENSE.GPL +339 -0
- plone_tiles-3.0.0.dist-info/LICENSE.txt +16 -0
- plone_tiles-3.0.0.dist-info/METADATA +1990 -0
- plone_tiles-3.0.0.dist-info/RECORD +29 -0
- plone_tiles-3.0.0.dist-info/WHEEL +5 -0
- plone_tiles-3.0.0.dist-info/namespace_packages.txt +1 -0
- plone_tiles-3.0.0.dist-info/top_level.txt +1 -0
plone/tiles/esi.rst
ADDED
@@ -0,0 +1,325 @@
|
|
1
|
+
ESI support
|
2
|
+
===========
|
3
|
+
|
4
|
+
Some sites may choose to render tiles in a delayed fashion using Edge Side Includes or some similar mechanism.
|
5
|
+
``plone.tiles`` includes some support to help render ESI placeholders.
|
6
|
+
This is used in ``plone.app.blocks`` to facilitate ESI rendering.
|
7
|
+
Since ESI normally involves a "dumb" replacement operation,
|
8
|
+
``plone.tiles`` also provides a means of accessing just the head and/or just the body of a tile.
|
9
|
+
|
10
|
+
To use the package, you should first load its ZCML configuration.
|
11
|
+
|
12
|
+
.. code-block:: python
|
13
|
+
|
14
|
+
>>> configuration = """\
|
15
|
+
... <configure
|
16
|
+
... xmlns="http://namespaces.zope.org/zope"
|
17
|
+
... xmlns:plone="http://namespaces.plone.org/plone"
|
18
|
+
... i18n_domain="plone.tiles.tests">
|
19
|
+
...
|
20
|
+
... <include package="zope.component" file="meta.zcml" />
|
21
|
+
... <include package="zope.browserpage" file="meta.zcml" />
|
22
|
+
...
|
23
|
+
... <include package="plone.tiles" file="meta.zcml" />
|
24
|
+
... <include package="plone.tiles" />
|
25
|
+
...
|
26
|
+
... </configure>
|
27
|
+
... """
|
28
|
+
|
29
|
+
>>> from io import StringIO
|
30
|
+
>>> from zope.configuration import xmlconfig
|
31
|
+
>>> xmlconfig.xmlconfig(StringIO(configuration))
|
32
|
+
|
33
|
+
Marking a tile as ESI-rendered
|
34
|
+
------------------------------
|
35
|
+
|
36
|
+
For ESI rendering to be available, the tile must be marked with the ``IESIRendered`` marker interface.
|
37
|
+
We can create a dummy tile with this interface like so:
|
38
|
+
|
39
|
+
.. code-block:: python
|
40
|
+
|
41
|
+
>>> from zope.interface import implementer
|
42
|
+
>>> from plone.tiles.interfaces import IESIRendered
|
43
|
+
>>> from plone.tiles import Tile
|
44
|
+
|
45
|
+
>>> @implementer(IESIRendered)
|
46
|
+
... class SampleTile(Tile):
|
47
|
+
...
|
48
|
+
... __name__ = 'sample.tile' # would normally be set by ZCML handler
|
49
|
+
...
|
50
|
+
... def __call__(self):
|
51
|
+
... return '<html><head><title>Title</title></head><body><b>My tile</b></body></html>'
|
52
|
+
|
53
|
+
Above, we have created a simple HTML string.
|
54
|
+
This would normally be rendered using a page template.
|
55
|
+
|
56
|
+
We'll register this tile manually here.
|
57
|
+
Ordinarily, of course, it would be registered via ZCML.
|
58
|
+
|
59
|
+
.. code-block:: python
|
60
|
+
|
61
|
+
>>> from plone.tiles.type import TileType
|
62
|
+
>>> from zope.security.permission import Permission
|
63
|
+
>>> permission = Permission('dummy.Permission')
|
64
|
+
>>> sampleTileType = TileType(
|
65
|
+
... name=u'sample.tile',
|
66
|
+
... title=u'Sample tile',
|
67
|
+
... description=u'A tile used for testing',
|
68
|
+
... add_permission='dummy.Permission',
|
69
|
+
... view_permission='dummy.Permission',
|
70
|
+
... schema=None)
|
71
|
+
|
72
|
+
>>> from zope.component import provideAdapter, provideUtility
|
73
|
+
>>> from zope.interface import Interface
|
74
|
+
>>> from plone.tiles.interfaces import IBasicTile
|
75
|
+
|
76
|
+
>>> provideUtility(permission, name=u'dummy.Permission')
|
77
|
+
>>> provideUtility(sampleTileType, name=u'sample.tile')
|
78
|
+
>>> provideAdapter(SampleTile, (Interface, Interface), IBasicTile, name=u'sample.tile')
|
79
|
+
|
80
|
+
ESI lookup
|
81
|
+
----------
|
82
|
+
|
83
|
+
When a page is rendered
|
84
|
+
(for example by a system like ``plone.app.blocks``, but see below),
|
85
|
+
a tile placeholder may be replaced by a link such as:
|
86
|
+
|
87
|
+
.. code-block:: xml
|
88
|
+
|
89
|
+
<esi:include src="/path/to/context/@@sample.tile/tile1/@@esi-body" />
|
90
|
+
|
91
|
+
When this is resolved, it will return the body part of the tile.
|
92
|
+
Equally, a tile in the head can be replaced by:
|
93
|
+
|
94
|
+
.. code-block:: xml
|
95
|
+
|
96
|
+
<esi:include src="/path/to/context/@@sample.tile/tile1/@@esi-head" />
|
97
|
+
|
98
|
+
To illustrate how this works,
|
99
|
+
let's create a sample context,
|
100
|
+
look up the view as it would be during traversal,
|
101
|
+
and instantiate the tile,
|
102
|
+
before looking up the ESI views and rendering them.
|
103
|
+
|
104
|
+
.. code-block:: python
|
105
|
+
|
106
|
+
>>> from zope.interface import implementer
|
107
|
+
>>> from zope.publisher.browser import TestRequest
|
108
|
+
|
109
|
+
>>> class IContext(Interface):
|
110
|
+
... pass
|
111
|
+
|
112
|
+
>>> @implementer(IContext)
|
113
|
+
... class Context(object):
|
114
|
+
... pass
|
115
|
+
|
116
|
+
>>> class IntegratedTestRequest(TestRequest):
|
117
|
+
... @property
|
118
|
+
... def environ(self):
|
119
|
+
... return self._environ
|
120
|
+
|
121
|
+
>>> context = Context()
|
122
|
+
>>> request = IntegratedTestRequest()
|
123
|
+
|
124
|
+
The following simulates traversal to ``context/@@sample.tile/tile1``
|
125
|
+
|
126
|
+
.. code-block:: python
|
127
|
+
|
128
|
+
>>> from zope.interface import Interface
|
129
|
+
>>> from zope.component import getMultiAdapter
|
130
|
+
>>> tile = getMultiAdapter((context, request), name=u'sample.tile')
|
131
|
+
>>> tile = tile['tile1'] # simulates sub-path traversal
|
132
|
+
|
133
|
+
This tile should be ESI rendered:
|
134
|
+
|
135
|
+
.. code-block:: python
|
136
|
+
|
137
|
+
>>> IESIRendered.providedBy(tile)
|
138
|
+
True
|
139
|
+
|
140
|
+
At this point, we can look up the ESI views:
|
141
|
+
|
142
|
+
.. code-block:: python
|
143
|
+
|
144
|
+
>>> head = getMultiAdapter((tile, request), name='esi-head')
|
145
|
+
>>> head()
|
146
|
+
Traceback (most recent call last):
|
147
|
+
...
|
148
|
+
zExceptions.unauthorized.Unauthorized: Unauthorized()
|
149
|
+
|
150
|
+
But we can only render them when we have the required permissions:
|
151
|
+
|
152
|
+
>>> from AccessControl.SecurityManagement import newSecurityManager
|
153
|
+
>>> from AccessControl.User import Super
|
154
|
+
>>> newSecurityManager(None, Super('manager', '', ['Manager'], []))
|
155
|
+
>>> print(head())
|
156
|
+
<title>Title</title>
|
157
|
+
|
158
|
+
>>> body = getMultiAdapter((tile, request), name='esi-body')
|
159
|
+
>>> print(body())
|
160
|
+
<b>My tile</b>
|
161
|
+
|
162
|
+
Tiles without heads or bodies
|
163
|
+
-----------------------------
|
164
|
+
|
165
|
+
In general, tiles are supposed to return full HTML documents.
|
166
|
+
The ``esi-head`` and ``esi-body`` views are tolerant of tiles that do not.
|
167
|
+
If they cannot find a ``<head />`` or ``<body />`` element, respectively, they will return the underlying tile output unaltered.
|
168
|
+
|
169
|
+
For example:
|
170
|
+
|
171
|
+
.. code-block:: python
|
172
|
+
|
173
|
+
>>> from plone.tiles.esi import ESITile
|
174
|
+
>>> class LazyTile(ESITile):
|
175
|
+
... __name__ = 'sample.esi1' # would normally be set by ZCML handler
|
176
|
+
... def __call__(self):
|
177
|
+
... return '<title>Page title</title>'
|
178
|
+
|
179
|
+
We won't bother to register this for this test, instead just instantiating it directly:
|
180
|
+
|
181
|
+
.. code-block:: python
|
182
|
+
|
183
|
+
>>> tile = LazyTile(context, request)['tile1']
|
184
|
+
|
185
|
+
>>> IESIRendered.providedBy(tile)
|
186
|
+
True
|
187
|
+
|
188
|
+
>>> head = getMultiAdapter((tile, request), name='esi-head')
|
189
|
+
>>> print(head())
|
190
|
+
<title>Page title</title>
|
191
|
+
|
192
|
+
Of course, the ESI body renderer would return the same thing,
|
193
|
+
since it can't extract a specific body either:
|
194
|
+
|
195
|
+
.. code-block:: python
|
196
|
+
|
197
|
+
>>> body = getMultiAdapter((tile, request), name='esi-body')
|
198
|
+
>>> print(body())
|
199
|
+
<title>Page title</title>
|
200
|
+
|
201
|
+
In this case, we would likely end up with invalid HTML,
|
202
|
+
since the ``<title />`` tag is not allowed in the body.
|
203
|
+
Whether and how to resolve this is left up to the ESI interpolation implementation.
|
204
|
+
|
205
|
+
Convenience classes and placeholder rendering
|
206
|
+
---------------------------------------------
|
207
|
+
|
208
|
+
Two convenience base classes can be found in the ``plone.tiles.esi`` module.
|
209
|
+
These extend the standard ``Tile`` and ``PersistentTile`` classes to provide the ``IESIRendered`` interface.
|
210
|
+
|
211
|
+
* ``plone.tiles.esi.ESITile``, a transient, ESI-rendered tile
|
212
|
+
* ``plone.tiles.esi.ESIPersistentTile``, a persistent, ESI-rendered tile
|
213
|
+
|
214
|
+
These are particularly useful if you are creating a template-only tile and want ESI rendering.
|
215
|
+
For example:
|
216
|
+
|
217
|
+
.. code-block:: xml
|
218
|
+
|
219
|
+
<plone:tile
|
220
|
+
name="sample.esitile"
|
221
|
+
title="An ESI-rendered tile"
|
222
|
+
add_permission="plone.tiles.tests.DummyAdd"
|
223
|
+
template="esitile.pt"
|
224
|
+
class="plone.tiles.esi.ESITile"
|
225
|
+
for="*"
|
226
|
+
permission="zope.View"
|
227
|
+
/>
|
228
|
+
|
229
|
+
Additionally,
|
230
|
+
these base classes implement a ``__call__()`` method that will render a tile placeholder,
|
231
|
+
if the request contains an ``X-ESI-Enabled`` header set to the literal 'true'.
|
232
|
+
|
233
|
+
The placeholder is a simple HTML ``<a />`` tag,
|
234
|
+
which can be transformed into an ``<esi:include />`` tag using the helper function ``substituteESILinks()``.
|
235
|
+
The reason for this indirection is that the ``esi`` namespace is not allowed in HTML documents,
|
236
|
+
and are liable to be stripped out by transforms using the ``libxml2`` / ``lxml`` HTML parser.
|
237
|
+
|
238
|
+
Let us now create a simple ESI tile. To benefit from the default rendering,
|
239
|
+
we should implement the ``render()`` method instead of ``__call__()``. Setting
|
240
|
+
a page template as the ``index`` class variable or using the ``template``
|
241
|
+
attribute to the ZCML directive will work also.
|
242
|
+
|
243
|
+
.. code-block:: python
|
244
|
+
|
245
|
+
>>> from plone.tiles.esi import ESITile
|
246
|
+
|
247
|
+
>>> class SampleESITile(ESITile):
|
248
|
+
... __name__ = 'sample.esitile' # would normally be set by ZCML handler
|
249
|
+
...
|
250
|
+
... def render(self):
|
251
|
+
... return '<html><head><title>Title</title></head><body><b>My ESI tile</b></body></html>'
|
252
|
+
|
253
|
+
>>> sampleESITileType = TileType(
|
254
|
+
... name=u'sample.esitile',
|
255
|
+
... title=u'Sample ESI tile',
|
256
|
+
... description=u'A tile used for testing ESI',
|
257
|
+
... add_permission='dummy.Permission',
|
258
|
+
... view_permission='dummy.Permission',
|
259
|
+
... schema=None)
|
260
|
+
|
261
|
+
>>> provideUtility(sampleESITileType, name=u'sample.esitile')
|
262
|
+
>>> provideAdapter(SampleESITile, (Interface, Interface), IBasicTile, name=u'sample.esitile')
|
263
|
+
|
264
|
+
The following simulates traversal to ``context/@@sample.esitile/tile1``
|
265
|
+
|
266
|
+
.. code-block:: python
|
267
|
+
|
268
|
+
>>> tile = getMultiAdapter((context, request), name=u'sample.esitile')
|
269
|
+
>>> tile = tile['tile1'] # simulates sub-path traversal
|
270
|
+
|
271
|
+
By default, the tile renders as normal:
|
272
|
+
|
273
|
+
.. code-block:: python
|
274
|
+
|
275
|
+
>>> print(tile())
|
276
|
+
<html><head><title>Title</title></head><body><b>My ESI tile</b></body></html>
|
277
|
+
|
278
|
+
However, if we opt into ESI rendering via a request header, we get a different view:
|
279
|
+
|
280
|
+
.. code-block:: python
|
281
|
+
|
282
|
+
>>> from plone.tiles.interfaces import ESI_HEADER_KEY
|
283
|
+
>>> request.environ[ESI_HEADER_KEY] = 'true'
|
284
|
+
>>> print(tile()) # doctest: +NORMALIZE_WHITESPACE
|
285
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
286
|
+
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
287
|
+
<html xmlns="http://www.w3.org/1999/xhtml">
|
288
|
+
<body>
|
289
|
+
<a class="_esi_placeholder"
|
290
|
+
rel="esi"
|
291
|
+
href="http://127.0.0.1/@@esi-body?"></a>
|
292
|
+
</body>
|
293
|
+
</html>
|
294
|
+
|
295
|
+
This can be transformed into a proper ESI tag with ``substituteESILinks()``:
|
296
|
+
|
297
|
+
.. code-block:: python
|
298
|
+
|
299
|
+
>>> from plone.tiles.esi import substituteESILinks
|
300
|
+
>>> print(substituteESILinks(tile())) # doctest: +NORMALIZE_WHITESPACE
|
301
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
302
|
+
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
303
|
+
<html xmlns:esi="http://www.edge-delivery.org/esi/1.0" xmlns="http://www.w3.org/1999/xhtml">
|
304
|
+
<body>
|
305
|
+
<esi:include src="http://127.0.0.1/@@esi-body?" />
|
306
|
+
</body>
|
307
|
+
</html>
|
308
|
+
|
309
|
+
It is also possible to render the ESI tile for the head.
|
310
|
+
This is done with a class variable 'head'
|
311
|
+
(which would of course normally be set within the class):
|
312
|
+
|
313
|
+
.. code-block:: python
|
314
|
+
|
315
|
+
>>> SampleESITile.head = True
|
316
|
+
>>> print(tile()) # doctest: +NORMALIZE_WHITESPACE
|
317
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
318
|
+
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
319
|
+
<html xmlns="http://www.w3.org/1999/xhtml">
|
320
|
+
<body>
|
321
|
+
<a class="_esi_placeholder"
|
322
|
+
rel="esi"
|
323
|
+
href="http://127.0.0.1/@@esi-head?"></a>
|
324
|
+
</body>
|
325
|
+
</html>
|
@@ -0,0 +1,39 @@
|
|
1
|
+
from plone.tiles.interfaces import IFieldTypeConverter
|
2
|
+
from zope.interface import implementer
|
3
|
+
|
4
|
+
|
5
|
+
@implementer(IFieldTypeConverter)
|
6
|
+
class NoConverter:
|
7
|
+
|
8
|
+
def __init__(self, field):
|
9
|
+
self.field = field
|
10
|
+
|
11
|
+
token = None
|
12
|
+
|
13
|
+
|
14
|
+
class TextConverter(NoConverter):
|
15
|
+
token = "text"
|
16
|
+
|
17
|
+
|
18
|
+
class LongConverter(NoConverter):
|
19
|
+
token = "long"
|
20
|
+
|
21
|
+
|
22
|
+
class FloatConverter(NoConverter):
|
23
|
+
token = "float"
|
24
|
+
|
25
|
+
|
26
|
+
class BoolConverter(NoConverter):
|
27
|
+
token = "boolean"
|
28
|
+
|
29
|
+
|
30
|
+
class TupleConverter(NoConverter):
|
31
|
+
token = "tuple"
|
32
|
+
|
33
|
+
|
34
|
+
class ListConverter(NoConverter):
|
35
|
+
token = "list"
|
36
|
+
|
37
|
+
|
38
|
+
class DictConverter(NoConverter):
|
39
|
+
token = "record"
|
@@ -0,0 +1,172 @@
|
|
1
|
+
from zope.interface import Interface
|
2
|
+
from zope.interface.common.mapping import IMapping
|
3
|
+
from zope.interface.interfaces import IInterface
|
4
|
+
from zope.publisher.interfaces.browser import IBrowserView
|
5
|
+
|
6
|
+
import zope.schema
|
7
|
+
|
8
|
+
|
9
|
+
ESI_HEADER = "X-ESI-Enabled"
|
10
|
+
ESI_HEADER_KEY = "HTTP_" + ESI_HEADER.replace("-", "_").upper()
|
11
|
+
|
12
|
+
|
13
|
+
class ITileType(Interface):
|
14
|
+
"""A utility that describes a type of tile"""
|
15
|
+
|
16
|
+
__name__ = zope.schema.DottedName(title="Tile name (same as utility name)")
|
17
|
+
|
18
|
+
title = zope.schema.TextLine(title="Title")
|
19
|
+
|
20
|
+
description = zope.schema.Text(title="Description", required=False)
|
21
|
+
|
22
|
+
icon = zope.schema.Text(title="Icon", required=False)
|
23
|
+
|
24
|
+
add_permission = zope.schema.Id(title="Zope 3 IPermission utility name")
|
25
|
+
|
26
|
+
schema = zope.schema.Object(
|
27
|
+
title="Tile schema",
|
28
|
+
description="Describes configurable data for this tile and allows a "
|
29
|
+
"form to be rendered to edit it. Set to None if the tile "
|
30
|
+
"has no configurable schema",
|
31
|
+
schema=IInterface,
|
32
|
+
required=False,
|
33
|
+
)
|
34
|
+
|
35
|
+
|
36
|
+
class IBasicTile(IBrowserView):
|
37
|
+
"""A tile is a publishable resource that can be inserted into a site or
|
38
|
+
page layout.
|
39
|
+
|
40
|
+
The tile should be a named multi adapter on (<context>, <layer>)
|
41
|
+
providing IBasicTile.
|
42
|
+
|
43
|
+
It will normally be traversed to like this::
|
44
|
+
|
45
|
+
http://localhost:8080/plone-site/object/@@my.tile/tile1
|
46
|
+
|
47
|
+
In this case:
|
48
|
+
|
49
|
+
* The tile context is the content object at /plone-site/object.
|
50
|
+
* The ``__name__`` of the tile instance is 'my.tile'
|
51
|
+
* The ``id`` of the tile instance is 'tile1'
|
52
|
+
* The ``url`` of the tile instance is the URL as above
|
53
|
+
"""
|
54
|
+
|
55
|
+
__name__ = zope.schema.DottedName(
|
56
|
+
title="The name of the type of this tile",
|
57
|
+
description="This should be a dotted name prefixed with the "
|
58
|
+
"package that defined the tile",
|
59
|
+
)
|
60
|
+
|
61
|
+
id = zope.schema.DottedName(
|
62
|
+
title="Tile instance id",
|
63
|
+
description="The id is normally set using sub-path traversal"
|
64
|
+
"A given tile type may be used multiple times on "
|
65
|
+
"the same page, each with a unique id. The id must "
|
66
|
+
"be unique even across multiple layouts for the "
|
67
|
+
"same context.",
|
68
|
+
)
|
69
|
+
|
70
|
+
|
71
|
+
class ITile(IBasicTile):
|
72
|
+
"""A tile with some data (probably from a query string)."""
|
73
|
+
|
74
|
+
data = zope.schema.Dict(
|
75
|
+
title="The tile's configuration data",
|
76
|
+
description="This attribute cannot be set, but the dictionary may "
|
77
|
+
"be updated",
|
78
|
+
key_type=zope.schema.Id(title="The data element name"),
|
79
|
+
value_type=zope.schema.Field(title="The value"),
|
80
|
+
required=True,
|
81
|
+
readonly=True,
|
82
|
+
default={},
|
83
|
+
)
|
84
|
+
|
85
|
+
url = zope.schema.URI(
|
86
|
+
title="Tile URL",
|
87
|
+
description="This is the canonical URL for the tile. In the "
|
88
|
+
"case of transient tiles with data, this may "
|
89
|
+
"include a query string with parameters. Provided "
|
90
|
+
"that the `id` attribute is set, it will also "
|
91
|
+
"include a sub-path with this in it.",
|
92
|
+
)
|
93
|
+
|
94
|
+
|
95
|
+
class IPersistentTile(ITile):
|
96
|
+
"""A tile with full-blown persistent data (stored in annotations)."""
|
97
|
+
|
98
|
+
|
99
|
+
class IESIRendered(Interface):
|
100
|
+
"""Marker interface for tiles which are to be rendered via ESI.
|
101
|
+
|
102
|
+
Two corresponding views, @@esi-body and @@esi-head, will be made available
|
103
|
+
on the tile itself. This will return the children of the <head /> or
|
104
|
+
<body /> of the tile, respectively.
|
105
|
+
|
106
|
+
Thus, a tile marked with this interface may be replaced with an ESI
|
107
|
+
instruction like::
|
108
|
+
|
109
|
+
<esi:include src="./@@my.tile/tile-id/@@esi-body" />
|
110
|
+
|
111
|
+
When fetched, this placeholder will be replaced by the body of the tile.
|
112
|
+
"""
|
113
|
+
|
114
|
+
|
115
|
+
class ITileDataManager(Interface):
|
116
|
+
"""Support for getting and setting tile data dicts.
|
117
|
+
|
118
|
+
This is an adapter on a tile. The tile's id must be set.
|
119
|
+
"""
|
120
|
+
|
121
|
+
def get():
|
122
|
+
"""Get a dictionary with tile data for this tile. The dictionary is
|
123
|
+
disconnected from the underlying storage.
|
124
|
+
"""
|
125
|
+
|
126
|
+
def set(data):
|
127
|
+
"""Persist the given data dict."""
|
128
|
+
|
129
|
+
def delete():
|
130
|
+
"""Delete the data record for this tile."""
|
131
|
+
|
132
|
+
|
133
|
+
class ITileDataContext(Interface):
|
134
|
+
"""Indirection to help determine where persistent tiles store their data.
|
135
|
+
|
136
|
+
This is a multi-adapter on ``(context, request, tile)``. The context and
|
137
|
+
request are the same as ``tile.context`` and ``tile.request``, but these
|
138
|
+
discriminators allow the data context to be customised depending on
|
139
|
+
the context or request.
|
140
|
+
|
141
|
+
The default implementation simply returns ``tile.context``. That must
|
142
|
+
be annotatable for the default tile data storage adapter and
|
143
|
+
persistent tile ``ITileDataManager`` to work.
|
144
|
+
"""
|
145
|
+
|
146
|
+
|
147
|
+
class ITileDataStorage(IMapping):
|
148
|
+
"""Indirection to help determine how persistent tiles store their data.
|
149
|
+
|
150
|
+
This is a multi-adapter on ``(context, request, tile)``. The context and
|
151
|
+
request are the same as ``tile.context`` and ``tile.request``, but these
|
152
|
+
discriminators allow the data context to be customised depending on
|
153
|
+
the context or request.
|
154
|
+
|
155
|
+
The default implementation simply returns the configured zope.annotation
|
156
|
+
storage for the given context.
|
157
|
+
|
158
|
+
The adapter is expected to provide IMapping interface and be accessed
|
159
|
+
by tile data managers similarly to zope.annotation storage.
|
160
|
+
"""
|
161
|
+
|
162
|
+
|
163
|
+
class IFieldTypeConverter(Interface):
|
164
|
+
"""Field type converter for querystring parameters for Zope."""
|
165
|
+
|
166
|
+
token = zope.schema.TextLine(
|
167
|
+
title="Token",
|
168
|
+
description="""
|
169
|
+
String parameter appended to the field id
|
170
|
+
for the Zope Publisher to cast it.
|
171
|
+
""",
|
172
|
+
)
|
plone/tiles/meta.py
ADDED
@@ -0,0 +1,162 @@
|
|
1
|
+
from plone.tiles.interfaces import ITileType
|
2
|
+
from plone.tiles.tile import Tile
|
3
|
+
from plone.tiles.type import TileType
|
4
|
+
from Products.Five.browser.metaconfigure import page
|
5
|
+
from zope import schema
|
6
|
+
from zope.component.zcml import utility
|
7
|
+
from zope.configuration.exceptions import ConfigurationError
|
8
|
+
from zope.configuration.fields import GlobalInterface
|
9
|
+
from zope.configuration.fields import GlobalObject
|
10
|
+
from zope.configuration.fields import MessageID
|
11
|
+
from zope.configuration.fields import Path
|
12
|
+
from zope.interface import Interface
|
13
|
+
from zope.publisher.interfaces.browser import IDefaultBrowserLayer
|
14
|
+
from zope.security.zcml import Permission
|
15
|
+
|
16
|
+
|
17
|
+
class ITileDirective(Interface):
|
18
|
+
"""Directive which registers a new type of tile"""
|
19
|
+
|
20
|
+
name = schema.DottedName(
|
21
|
+
title="Name",
|
22
|
+
description="A unique, dotted name for the tile",
|
23
|
+
required=True,
|
24
|
+
)
|
25
|
+
|
26
|
+
title = MessageID(
|
27
|
+
title="Title",
|
28
|
+
description="A user friendly title, used when configuring the tile",
|
29
|
+
required=False,
|
30
|
+
)
|
31
|
+
|
32
|
+
description = MessageID(
|
33
|
+
title="Description",
|
34
|
+
description="A longer summary of the tile's purpose and function",
|
35
|
+
required=False,
|
36
|
+
)
|
37
|
+
|
38
|
+
icon = MessageID(
|
39
|
+
title="Icon",
|
40
|
+
description="Image that represents tile purpose and function",
|
41
|
+
required=False,
|
42
|
+
)
|
43
|
+
|
44
|
+
add_permission = Permission(
|
45
|
+
title="Add permission",
|
46
|
+
description="Name of the permission required to instantiate " "this tile",
|
47
|
+
required=False,
|
48
|
+
)
|
49
|
+
|
50
|
+
edit_permission = Permission(
|
51
|
+
title="Edit permission",
|
52
|
+
description="Name of the permission required to edit this tile",
|
53
|
+
required=False,
|
54
|
+
)
|
55
|
+
|
56
|
+
delete_permission = Permission(
|
57
|
+
title="Delete permission",
|
58
|
+
description="Name of the permission required to delete this tile",
|
59
|
+
required=False,
|
60
|
+
)
|
61
|
+
|
62
|
+
schema = GlobalInterface(
|
63
|
+
title="Configuration schema for the tile",
|
64
|
+
description="This is used to create standard add/edit forms",
|
65
|
+
required=False,
|
66
|
+
)
|
67
|
+
|
68
|
+
for_ = GlobalObject(
|
69
|
+
title="The interface or class this tile is available for",
|
70
|
+
required=False,
|
71
|
+
)
|
72
|
+
|
73
|
+
layer = GlobalInterface(title="The layer the tile is available for", required=False)
|
74
|
+
|
75
|
+
class_ = GlobalObject(
|
76
|
+
title="Class", description="Class implementing this tile", required=False
|
77
|
+
)
|
78
|
+
|
79
|
+
template = Path(
|
80
|
+
title="The name of a template that renders this tile",
|
81
|
+
description="Refers to a file containing a page template",
|
82
|
+
required=False,
|
83
|
+
)
|
84
|
+
|
85
|
+
permission = Permission(
|
86
|
+
title="View permission",
|
87
|
+
description="Name of the permission required to view this item",
|
88
|
+
required=True,
|
89
|
+
)
|
90
|
+
|
91
|
+
|
92
|
+
def tile(
|
93
|
+
_context,
|
94
|
+
name,
|
95
|
+
title=None,
|
96
|
+
description=None,
|
97
|
+
icon=None,
|
98
|
+
add_permission=None,
|
99
|
+
edit_permission=None,
|
100
|
+
delete_permission=None,
|
101
|
+
schema=None,
|
102
|
+
for_=None,
|
103
|
+
layer=None,
|
104
|
+
class_=None,
|
105
|
+
template=None,
|
106
|
+
permission=None,
|
107
|
+
):
|
108
|
+
"""Implements the <plone:tile /> directive"""
|
109
|
+
if (
|
110
|
+
title is not None
|
111
|
+
or description is not None
|
112
|
+
or icon is not None
|
113
|
+
or add_permission is not None
|
114
|
+
or schema is not None
|
115
|
+
):
|
116
|
+
if title is None or add_permission is None:
|
117
|
+
raise ConfigurationError(
|
118
|
+
"When configuring a new type of tile, 'title' and "
|
119
|
+
"'add_permission' are required"
|
120
|
+
)
|
121
|
+
type_ = TileType(
|
122
|
+
name,
|
123
|
+
title,
|
124
|
+
add_permission,
|
125
|
+
permission,
|
126
|
+
edit_permission=edit_permission,
|
127
|
+
delete_permission=delete_permission,
|
128
|
+
description=description,
|
129
|
+
icon=icon,
|
130
|
+
schema=schema,
|
131
|
+
)
|
132
|
+
|
133
|
+
utility(_context, provides=ITileType, component=type_, name=name)
|
134
|
+
|
135
|
+
if (
|
136
|
+
for_ is not None
|
137
|
+
or layer is not None
|
138
|
+
or class_ is not None
|
139
|
+
or template is not None
|
140
|
+
):
|
141
|
+
if class_ is None and template is None:
|
142
|
+
raise ConfigurationError(
|
143
|
+
"'class' or 'template' must be given when configuring a tile."
|
144
|
+
)
|
145
|
+
|
146
|
+
if for_ is None:
|
147
|
+
for_ = Interface
|
148
|
+
if layer is None:
|
149
|
+
layer = IDefaultBrowserLayer
|
150
|
+
|
151
|
+
if class_ is None:
|
152
|
+
class_ = Tile
|
153
|
+
|
154
|
+
page(
|
155
|
+
_context,
|
156
|
+
name=name,
|
157
|
+
permission=permission,
|
158
|
+
for_=for_,
|
159
|
+
layer=layer,
|
160
|
+
template=template,
|
161
|
+
class_=class_,
|
162
|
+
)
|