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/meta.zcml
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
<configure
|
2
|
+
xmlns="http://namespaces.zope.org/zope"
|
3
|
+
xmlns:meta="http://namespaces.zope.org/meta"
|
4
|
+
>
|
5
|
+
|
6
|
+
<meta:directives namespace="http://namespaces.plone.org/plone">
|
7
|
+
|
8
|
+
<meta:directive
|
9
|
+
name="tile"
|
10
|
+
handler=".meta.tile"
|
11
|
+
schema=".meta.ITileDirective"
|
12
|
+
/>
|
13
|
+
|
14
|
+
</meta:directives>
|
15
|
+
|
16
|
+
</configure>
|
plone/tiles/test.pt
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
<b i18n:ignore="">test!</b>
|
plone/tiles/testing.py
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
from plone.testing import Layer
|
2
|
+
from plone.testing import z2
|
3
|
+
from plone.testing import zca
|
4
|
+
from plone.tiles import PersistentTile
|
5
|
+
from plone.tiles import Tile
|
6
|
+
from zope import schema
|
7
|
+
from zope.configuration import xmlconfig
|
8
|
+
from zope.interface import Interface
|
9
|
+
|
10
|
+
|
11
|
+
# For directive tests
|
12
|
+
|
13
|
+
|
14
|
+
class IDummySchema(Interface):
|
15
|
+
foo = schema.TextLine(title="Foo")
|
16
|
+
|
17
|
+
|
18
|
+
class IDummyContext(Interface):
|
19
|
+
pass
|
20
|
+
|
21
|
+
|
22
|
+
class IDummyLayer(Interface):
|
23
|
+
pass
|
24
|
+
|
25
|
+
|
26
|
+
class DummyTile(Tile):
|
27
|
+
|
28
|
+
def __call__(self):
|
29
|
+
return "dummy"
|
30
|
+
|
31
|
+
|
32
|
+
class DummyTileWithTemplate(PersistentTile):
|
33
|
+
pass
|
34
|
+
|
35
|
+
|
36
|
+
class PloneTiles(Layer):
|
37
|
+
defaultBases = (z2.STARTUP,)
|
38
|
+
|
39
|
+
def setUp(self):
|
40
|
+
self["configurationContext"] = context = zca.stackConfigurationContext(
|
41
|
+
self.get("configurationContext")
|
42
|
+
)
|
43
|
+
import zope.annotation
|
44
|
+
|
45
|
+
xmlconfig.file("configure.zcml", zope.annotation, context=context)
|
46
|
+
import plone.tiles
|
47
|
+
|
48
|
+
xmlconfig.file("configure.zcml", plone.tiles, context=context)
|
49
|
+
|
50
|
+
def tearDown(self):
|
51
|
+
del self["configurationContext"]
|
52
|
+
|
53
|
+
|
54
|
+
PLONE_TILES_FIXTURE = PloneTiles()
|
55
|
+
|
56
|
+
|
57
|
+
PLONE_TILES_INTEGRATION_TESTING = z2.IntegrationTesting(
|
58
|
+
bases=(PLONE_TILES_FIXTURE,), name="PloneTiles:Functional"
|
59
|
+
)
|
File without changes
|
@@ -0,0 +1,83 @@
|
|
1
|
+
from plone.rfc822.interfaces import IPrimaryField
|
2
|
+
from plone.tiles.data import decode
|
3
|
+
from plone.tiles.data import encode
|
4
|
+
from plone.tiles.testing import PLONE_TILES_INTEGRATION_TESTING
|
5
|
+
from zope import schema
|
6
|
+
from zope.interface import alsoProvides
|
7
|
+
from zope.interface import Interface
|
8
|
+
|
9
|
+
import unittest
|
10
|
+
|
11
|
+
|
12
|
+
class IQuerySchema(Interface):
|
13
|
+
|
14
|
+
query = schema.List(
|
15
|
+
title="Search terms",
|
16
|
+
value_type=schema.Dict(value_type=schema.Field(), key_type=schema.TextLine()),
|
17
|
+
required=False,
|
18
|
+
)
|
19
|
+
|
20
|
+
lines = schema.List(title="Strings", value_type=schema.TextLine(), required=False)
|
21
|
+
|
22
|
+
title = schema.TextLine(title="Title")
|
23
|
+
|
24
|
+
|
25
|
+
class IWords(Interface):
|
26
|
+
|
27
|
+
words = schema.List(title="Words", value_type=schema.TextLine(), required=False)
|
28
|
+
|
29
|
+
|
30
|
+
class IPrimary(Interface):
|
31
|
+
|
32
|
+
words = schema.List(title="Words", value_type=schema.TextLine(), required=False)
|
33
|
+
|
34
|
+
|
35
|
+
alsoProvides(IPrimary["words"], IPrimaryField)
|
36
|
+
|
37
|
+
|
38
|
+
class TestEncode(unittest.TestCase):
|
39
|
+
|
40
|
+
layer = PLONE_TILES_INTEGRATION_TESTING
|
41
|
+
|
42
|
+
def test_encode_querystring_special(self):
|
43
|
+
data = {
|
44
|
+
"query": [
|
45
|
+
{
|
46
|
+
"i": "Subject",
|
47
|
+
"o": "plone.app.querystring.operation.selection.any",
|
48
|
+
"v": ["äüö"],
|
49
|
+
}
|
50
|
+
],
|
51
|
+
"title": "Hello World",
|
52
|
+
}
|
53
|
+
self.assertEqual(
|
54
|
+
encode(data, schema=IQuerySchema),
|
55
|
+
(
|
56
|
+
"query.i%3Arecords=Subject&query.o%3A"
|
57
|
+
"records=plone.app.querystring.operation.selection.any&"
|
58
|
+
"query.v%3Alist%3Arecords=%C3%A4%C3%BC%C3%B6&title=Hello+World"
|
59
|
+
),
|
60
|
+
)
|
61
|
+
|
62
|
+
def test_encode_unicode_lines(self):
|
63
|
+
data = {"words": ["ä", "ö"]}
|
64
|
+
self.assertEqual(
|
65
|
+
encode(data, schema=IWords), "words%3Alist=%C3%A4&words%3Alist=%C3%B6"
|
66
|
+
)
|
67
|
+
|
68
|
+
def test_skip_encoding_primary_fields(self):
|
69
|
+
data = {"words": ["ä", "ö"]}
|
70
|
+
self.assertEqual(encode(data, schema=IPrimary), "")
|
71
|
+
|
72
|
+
|
73
|
+
class TestDecode(unittest.TestCase):
|
74
|
+
|
75
|
+
layer = PLONE_TILES_INTEGRATION_TESTING
|
76
|
+
|
77
|
+
def test_decode_unicode_lines(self):
|
78
|
+
data = {"words": ["ä", "ö"]}
|
79
|
+
self.assertEqual(decode(data, schema=IWords), {"words": ["ä", "ö"]})
|
80
|
+
|
81
|
+
def test_skip_decoding_primary_fields(self):
|
82
|
+
data = {"words": ["ä", "ö"]}
|
83
|
+
self.assertEqual(decode(data, schema=IPrimary), {})
|
@@ -0,0 +1,30 @@
|
|
1
|
+
from plone.testing import layered
|
2
|
+
from plone.tiles.testing import PLONE_TILES_INTEGRATION_TESTING
|
3
|
+
|
4
|
+
import doctest
|
5
|
+
import re
|
6
|
+
import unittest
|
7
|
+
|
8
|
+
|
9
|
+
class Py23DocChecker(doctest.OutputChecker):
|
10
|
+
def check_output(self, want, got, optionflags):
|
11
|
+
want = re.sub("u'(.*?)'", "'\\1'", want)
|
12
|
+
return doctest.OutputChecker.check_output(self, want, got, optionflags)
|
13
|
+
|
14
|
+
|
15
|
+
def test_suite():
|
16
|
+
return unittest.TestSuite(
|
17
|
+
(
|
18
|
+
layered(
|
19
|
+
doctest.DocFileSuite(
|
20
|
+
"../tiles.rst",
|
21
|
+
"../directives.rst",
|
22
|
+
"../data.rst",
|
23
|
+
"../esi.rst",
|
24
|
+
optionflags=doctest.ELLIPSIS,
|
25
|
+
checker=Py23DocChecker(),
|
26
|
+
),
|
27
|
+
layer=PLONE_TILES_INTEGRATION_TESTING,
|
28
|
+
),
|
29
|
+
)
|
30
|
+
)
|
plone/tiles/tile.py
ADDED
@@ -0,0 +1,186 @@
|
|
1
|
+
from plone.tiles.interfaces import IPersistentTile
|
2
|
+
from plone.tiles.interfaces import ITile
|
3
|
+
from plone.tiles.interfaces import ITileDataManager
|
4
|
+
from Products.Five import BrowserView
|
5
|
+
from zExceptions import Forbidden
|
6
|
+
from zope.component import queryMultiAdapter
|
7
|
+
from zope.interface import implementer
|
8
|
+
from zope.traversing.browser.absoluteurl import absoluteURL
|
9
|
+
|
10
|
+
|
11
|
+
@implementer(ITile)
|
12
|
+
class Tile(BrowserView):
|
13
|
+
"""Basic implementation of a transient tile. Subclasses should override
|
14
|
+
__call__ or set an 'index' variable to point to a view page template file.
|
15
|
+
|
16
|
+
The tile is basically a browser view, with the following enhancements:
|
17
|
+
|
18
|
+
* The attribute `data` can be used to read the tile data, as returned by
|
19
|
+
`ITileDataManager(tile).get()`. This value is cached when it is first
|
20
|
+
read.
|
21
|
+
* The attribute `url` can be used to obtain the tile's URL, including the
|
22
|
+
id specifier and any data associated with a transient tile. Again, the
|
23
|
+
return value is cached after the first access.
|
24
|
+
* The class implements __getitem__() to set the tile id from the traversal
|
25
|
+
sub-path, as well as to allow views to be looked up. This is what allows
|
26
|
+
a URL like `http://.../@@example.tile/foo` to result in a tile with id
|
27
|
+
`foo`.
|
28
|
+
"""
|
29
|
+
|
30
|
+
__cachedData = None
|
31
|
+
__cachedURL = None
|
32
|
+
|
33
|
+
id = None
|
34
|
+
|
35
|
+
def __getitem__(self, name):
|
36
|
+
|
37
|
+
# If we haven't set the id yet, do that first
|
38
|
+
if self.id is None:
|
39
|
+
self.id = name
|
40
|
+
|
41
|
+
# This is pretty stupid, but it's required to keep the ZPublisher
|
42
|
+
# happy in Zope 2. It doesn't normally check for docstrings on
|
43
|
+
# views, but it does check for them on sub-objects obtained via
|
44
|
+
# __getitem__.
|
45
|
+
|
46
|
+
if self.__doc__ is None:
|
47
|
+
self.__doc__ = "For Zope 2, to keep the ZPublisher happy"
|
48
|
+
|
49
|
+
# Note: X-Tile-Url was added to make it easier for editor to know
|
50
|
+
# the URL of a new tile after receiving the redirected response
|
51
|
+
# from a tile form. That's why it's only set for customizable tiles
|
52
|
+
# (tiles with id).
|
53
|
+
if self.id is not None:
|
54
|
+
self.request.response.setHeader("X-Tile-Url", self.url)
|
55
|
+
|
56
|
+
return self
|
57
|
+
|
58
|
+
# Also allow views on tiles even without @@.
|
59
|
+
viewName = name
|
60
|
+
if viewName.startswith("@@"):
|
61
|
+
viewName = name[2:]
|
62
|
+
view = queryMultiAdapter((self, self.request), name=viewName)
|
63
|
+
if view is not None:
|
64
|
+
view.__parent__ = self
|
65
|
+
view.__name__ = viewName
|
66
|
+
return view
|
67
|
+
|
68
|
+
raise KeyError(name)
|
69
|
+
|
70
|
+
def browserDefault(self, request):
|
71
|
+
"""By default, tiles render themselves with no browser-default view"""
|
72
|
+
return self, ()
|
73
|
+
|
74
|
+
def publishTraverse(self, request, name):
|
75
|
+
"""Ensure that publish-traversal uses the same semantics as
|
76
|
+
__getitem__.
|
77
|
+
"""
|
78
|
+
return self[name]
|
79
|
+
|
80
|
+
def __call__(self, *args, **kwargs):
|
81
|
+
if getattr(self, "index", None) is None:
|
82
|
+
raise NotImplementedError(
|
83
|
+
'Override __call__ or set a class variable "index" to point '
|
84
|
+
"to a view page template file"
|
85
|
+
)
|
86
|
+
return self.index(*args, **kwargs)
|
87
|
+
|
88
|
+
@property
|
89
|
+
def data(self):
|
90
|
+
if self.__cachedData is None:
|
91
|
+
reader = ITileDataManager(self)
|
92
|
+
self.__cachedData = reader.get()
|
93
|
+
return self.__cachedData
|
94
|
+
|
95
|
+
@property
|
96
|
+
def url(self):
|
97
|
+
return absoluteURL(self, self.request)
|
98
|
+
|
99
|
+
|
100
|
+
@implementer(IPersistentTile)
|
101
|
+
class PersistentTile(Tile):
|
102
|
+
"""Base class for persistent tiles. Identical to `Tile`, except that the
|
103
|
+
data dict is never serialized with the URL.
|
104
|
+
"""
|
105
|
+
|
106
|
+
|
107
|
+
class TileThemingTransform:
|
108
|
+
"""Disable plone.app.theming for tile responses"""
|
109
|
+
|
110
|
+
order = 8800
|
111
|
+
|
112
|
+
def __init__(self, published, request):
|
113
|
+
self.published = published
|
114
|
+
self.request = request
|
115
|
+
|
116
|
+
def transform(self, result, encoding):
|
117
|
+
self.request.response.setHeader("X-Theme-Disabled", "1")
|
118
|
+
return None
|
119
|
+
|
120
|
+
def transformBytes(self, result, encoding):
|
121
|
+
return self.transform(result, encoding)
|
122
|
+
|
123
|
+
def transformUnicode(self, result, encoding):
|
124
|
+
return self.transform(result, encoding)
|
125
|
+
|
126
|
+
def transformIterable(self, result, encoding):
|
127
|
+
return self.transform(result, encoding)
|
128
|
+
|
129
|
+
|
130
|
+
class TileProtectTransform:
|
131
|
+
"""Replacement transform for plone.protect's ProtectTransform, to drop
|
132
|
+
X-Tile-Url-header from unauthorized responses and disable the default
|
133
|
+
ProtectTransform for authorized responses (to avoid causing issues
|
134
|
+
like extra protect.js-injections for tile editors)
|
135
|
+
"""
|
136
|
+
|
137
|
+
order = 9000
|
138
|
+
|
139
|
+
def __init__(self, published, request):
|
140
|
+
self.published = published
|
141
|
+
self.request = request
|
142
|
+
try:
|
143
|
+
from plone.protect.auto import ProtectTransform
|
144
|
+
|
145
|
+
self.protect = ProtectTransform(published, request)
|
146
|
+
except ImportError:
|
147
|
+
self.protect = None
|
148
|
+
|
149
|
+
def transform(self, result, encoding):
|
150
|
+
from plone.protect import CheckAuthenticator
|
151
|
+
|
152
|
+
CheckAuthenticator(self.request)
|
153
|
+
return None
|
154
|
+
|
155
|
+
def transformBytes(self, result, encoding):
|
156
|
+
try:
|
157
|
+
return self.transform(result, encoding)
|
158
|
+
except Forbidden:
|
159
|
+
if "x-tile-url" in self.request.response.headers:
|
160
|
+
del self.request.response.headers["x-tile-url"]
|
161
|
+
if self.protect is not None:
|
162
|
+
return self.protect.transformBytes(result, encoding)
|
163
|
+
else:
|
164
|
+
return None
|
165
|
+
|
166
|
+
def transformUnicode(self, result, encoding):
|
167
|
+
try:
|
168
|
+
return self.transform(result, encoding)
|
169
|
+
except Forbidden:
|
170
|
+
if "x-tile-url" in self.request.response.headers:
|
171
|
+
del self.request.response.headers["x-tile-url"]
|
172
|
+
if self.protect is not None:
|
173
|
+
return self.protect.transformUnicode(result, encoding)
|
174
|
+
else:
|
175
|
+
return None
|
176
|
+
|
177
|
+
def transformIterable(self, result, encoding):
|
178
|
+
try:
|
179
|
+
return self.transform(result, encoding)
|
180
|
+
except Forbidden:
|
181
|
+
if "x-tile-url" in self.request.response.headers:
|
182
|
+
del self.request.response.headers["x-tile-url"]
|
183
|
+
if self.protect is not None:
|
184
|
+
return self.protect.transformIterable(result, encoding)
|
185
|
+
else:
|
186
|
+
return None
|