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/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