4
This section demonstrates the high-level template classes. All page
5
template classes in ``z3c.pt`` use path-expressions by default.
10
>>> from z3c.pt.pagetemplate import PageTemplate
11
>>> from z3c.pt.pagetemplate import PageTemplateFile
13
The ``PageTemplate`` class is initialized with a string.
15
>>> template = PageTemplate("""\
16
... <div xmlns="http://www.w3.org/1999/xhtml">
21
<div xmlns="http://www.w3.org/1999/xhtml">
25
The ``PageTemplateFile`` class is initialized with an absolute
26
path to a template file on disk.
28
>>> template_file = PageTemplateFile('tests/helloworld.pt')
29
>>> print template_file()
30
<div xmlns="http://www.w3.org/1999/xhtml">
35
>>> template_file.filename.startswith(os.sep)
38
If a ``content_type`` is not informed and one is not present in the
39
request, it will be set to 'text/html'.
41
>>> class Response(object):
42
... def __init__(self):
44
... self.getHeader = self.headers.get
45
... self.setHeader = self.headers.__setitem__
47
>>> class Request(object):
48
... def __init__(self):
49
... self.response = Response()
51
>>> template_file = PageTemplateFile('tests/helloworld.pt')
52
>>> request = Request()
53
>>> print request.response.getHeader('Content-Type')
56
>>> template = template_file.bind(None, request=request)
58
<div xmlns="http://www.w3.org/1999/xhtml">
62
>>> print request.response.getHeader('Content-Type')
65
If a ``content_type`` is present in the request, then we don't override it.
67
>>> request = Request()
68
>>> request.response.setHeader('Content-Type', 'text/xml')
69
>>> print request.response.getHeader('Content-Type')
72
>>> template = template_file.bind(None, request=request)
74
<div xmlns="http://www.w3.org/1999/xhtml">
78
>>> print request.response.getHeader('Content-Type')
81
A ``content_type`` can be also set at instantiation time, and it will
84
>>> template_file = PageTemplateFile('tests/helloworld.pt',
85
... content_type='application/rdf+xml')
87
>>> request = Request()
88
>>> print request.response.getHeader('Content-Type')
91
>>> template = template_file.bind(None, request=request)
93
<div xmlns="http://www.w3.org/1999/xhtml">
97
>>> print request.response.getHeader('Content-Type')
100
Both may be used as class attributes (properties).
102
>>> class MyClass(object):
103
... template = PageTemplate("""\
104
... <div xmlns="http://www.w3.org/1999/xhtml">
108
... template_file = PageTemplateFile('tests/helloworld.pt')
110
>>> instance = MyClass()
111
>>> print instance.template()
112
<div xmlns="http://www.w3.org/1999/xhtml">
116
>>> print instance.template_file()
117
<div xmlns="http://www.w3.org/1999/xhtml">
124
>>> from z3c.pt.pagetemplate import ViewPageTemplate
125
>>> from z3c.pt.pagetemplate import ViewPageTemplateFile
127
>>> class View(object):
128
... request = u'request'
129
... context = u'context'
131
... def __repr__(self):
136
As before, we can initialize view page templates with a string (here
137
incidentally loaded from disk).
139
>>> from z3c.pt import tests
140
>>> path = tests.__path__[0]
141
>>> template = ViewPageTemplate(
142
... open(path + '/view.pt').read())
144
To render the template in the context of a view, we bind the template
145
passing the view as an argument (view page templates derive from the
146
``property``-class and are usually defined as an attribute on a view
149
>>> print template.bind(view)(test=u'test')
150
<div xmlns="http://www.w3.org/1999/xhtml">
158
The exercise is similar for the file-based variant.
160
>>> template = ViewPageTemplateFile('tests/view.pt')
161
>>> print template.bind(view)(test=u'test')
162
<div xmlns="http://www.w3.org/1999/xhtml">
170
For compatibility reasons, view templates may be called with an
171
alternative context and request.
173
>>> print template(view, u"alt_context", "alt_request", test=u'test')
174
<div xmlns="http://www.w3.org/1999/xhtml">
176
<span>alt_context</span>
177
<span>alt_request</span>
183
Non-keyword arguments
184
---------------------
186
These are passed in as ``options/args``, when using the ``__call__`` method.
188
>>> print PageTemplate("""\
189
... <div xmlns="http://www.w3.org/1999/xhtml">
190
... <div tal:repeat="arg options/args">
191
... <span tal:content="arg" />
193
... </div>""").__call__(1, 2, 3)
194
<div xmlns="http://www.w3.org/1999/xhtml">
207
Global 'path' Function
208
----------------------
210
Just like ``zope.pagetemplate``, it is possible to use a globally
211
defined ``path()`` function in a ``python:`` expression in ``z3c.pt``:
213
>>> template = PageTemplate("""\
214
... <div xmlns="http://www.w3.org/1999/xhtml">
215
... <span tal:content="options/test" />
216
... <span tal:content="python: path('options/test')" />
219
>>> print template(test='test')
220
<div xmlns="http://www.w3.org/1999/xhtml">
225
Global 'exists' Function
226
------------------------
228
The same applies to the ``exists()`` function:
230
>>> template = PageTemplate("""\
231
... <div xmlns="http://www.w3.org/1999/xhtml">
232
... <span tal:content="python: exists('options/test') and 'Yes' or 'No'" />
235
>>> print template(test='test')
236
<div xmlns="http://www.w3.org/1999/xhtml">
240
'default' and path expressions
241
------------------------------
243
Another feature from standard ZPT: using 'default' means whatever the
244
the literal HTML contains will be output if the condition is not met.
246
This works for attributes:
248
>>> template = PageTemplate("""\
249
... <div xmlns="http://www.w3.org/1999/xhtml">
250
... <span tal:attributes="class options/not-existing | default"
251
... class="blue">i'm blue</span>
255
<div xmlns="http://www.w3.org/1999/xhtml">
256
<span class="blue">i'm blue</span>
259
And also for contents:
261
>>> template = PageTemplate("""\
262
... <div xmlns="http://www.w3.org/1999/xhtml">
263
... <span tal:content="options/not-existing | default">default content</span>
267
<div xmlns="http://www.w3.org/1999/xhtml">
268
<span>default content</span>
271
'exists'-type expression
272
------------------------
274
Using 'exists()' function on non-global name and global name:
276
>>> template = PageTemplate("""\
277
... <div xmlns="http://www.w3.org/1999/xhtml">
278
... <span tal:content="python: exists('options/nope') and 'Yes' or 'No'">do I exist?</span>
279
... <span tal:content="python: exists('nope') and 'Yes' or 'No'">do I exist?</span>
283
<div xmlns="http://www.w3.org/1999/xhtml">
288
Using 'exists:' expression on non-global name and global name
290
>>> template = PageTemplate("""\
291
... <div xmlns="http://www.w3.org/1999/xhtml">
292
... <span tal:define="yup exists:options/nope"
293
... tal:content="python: yup and 'Yes' or 'No'">do I exist?</span>
294
... <span tal:define="yup exists:nope"
295
... tal:content="python: yup and 'Yes' or 'No'">do I exist?</span>
299
<div xmlns="http://www.w3.org/1999/xhtml">
304
Using 'exists:' in conjunction with a negation:
306
>>> print PageTemplate("""\
307
... <div xmlns="http://www.w3.org/1999/xhtml">
308
... <span tal:condition="not:exists:options/nope">I don't exist?</span>
310
<div xmlns="http://www.w3.org/1999/xhtml">
311
<span>I don't exist?</span>
314
path expression with dictionaries
315
---------------------------------
317
Path expressions give preference to dictionary items instead of
318
dictionary attributes.
320
>>> print PageTemplate("""\
321
... <div xmlns="http://www.w3.org/1999/xhtml"
322
... tal:define="links python:{'copy':'XXX', 'delete':'YYY'}">
323
... <span tal:content="links/copy">ZZZ</span>
325
<div xmlns="http://www.w3.org/1999/xhtml">
330
Variable from one tag never leak into another
331
---------------------------------------------
334
... <div xmlns="http://www.w3.org/1999/xhtml"
335
... xmlns:tal="http://xml.zope.org/namespaces/tal"
336
... xmlns:metal="http://xml.zope.org/namespaces/metal">
337
... <div class="macro" metal:define-macro="greeting"
338
... tal:define="greeting greeting|string:'Hey'">
339
... <span tal:replace="greeting" />
341
... <div tal:define="greeting string:'Hello'">
342
... <metal:block metal:use-macro="python:template.macros['greeting']" />
345
... <metal:block metal:use-macro="python:template.macros['greeting']" />
348
>>> print PageTemplate(body)()
349
<div xmlns="http://www.w3.org/1999/xhtml">
369
TALES Function Namespaces
370
-------------------------
372
As described on http://wiki.zope.org/zope3/talesns.html, it is
373
possible to implement custom TALES Namespace Adapters. We also support
374
low-level TALES Function Namespaces (which the TALES Namespace
375
Adapters build upon).
378
>>> import zope.interface
379
>>> import zope.component
380
>>> from zope.traversing.interfaces import ITraversable
381
>>> from zope.traversing.interfaces import IPathAdapter
382
>>> from zope.tales.interfaces import ITALESFunctionNamespace
383
>>> from z3c.pt.namespaces import function_namespaces
385
>>> class ns1(object):
386
... zope.interface.implements(ITALESFunctionNamespace)
387
... def __init__(self, context):
388
... self.context = context
389
... def parent(self):
390
... return self.context.parent
392
>>> function_namespaces.registerFunctionNamespace('ns1', ns1)
394
>>> class ns2(object):
395
... def __init__(self, context):
396
... self.context = context
398
... return self.context.upper()
400
>>> zope.component.getGlobalSiteManager().registerAdapter(
401
... ns2, [zope.interface.Interface], IPathAdapter, 'ns2')
403
>>> class ns3(object):
404
... def __init__(self, context):
405
... self.context = context
406
... def fullDateTime(self):
407
... return self.context.strftime('%Y-%m-%d %H:%M:%S')
409
>>> zope.component.getGlobalSiteManager().registerAdapter(
410
... ns3, [zope.interface.Interface], IPathAdapter, 'ns3')
413
A really corner-ish case from a legacy application: the TALES
414
Namespace Adapter doesn't have a callable function but traverses the
415
remaining path instead::
417
>>> from zope.traversing.interfaces import TraversalError
419
>>> class ns4(object):
420
... zope.interface.implements(ITraversable)
422
... def __init__(self, context):
423
... self.context = context
425
... def traverse(self, name, furtherPath):
426
... if name == 'page':
427
... if len(furtherPath) == 1:
428
... pagetype = furtherPath.pop()
429
... elif not furtherPath:
430
... pagetype = 'default'
432
... raise TraversalError("Max 1 path segment after ns4:page")
433
... return self._page(pagetype)
434
... if len(furtherPath) == 1:
435
... name = '%s/%s' % (name, furtherPath.pop())
436
... return 'traversed: ' + name
438
... def _page(self, pagetype):
439
... return 'called page: ' + pagetype
441
>>> zope.component.getGlobalSiteManager().registerAdapter(
442
... ns4, [zope.interface.Interface], IPathAdapter, 'ns4')
444
>>> class ns5(object):
445
... zope.interface.implements(ITraversable)
447
... def __init__(self, context):
448
... self.context = context
450
... def traverse(self, name, furtherPath):
451
... name = '/'.join([name] + furtherPath[::-1])
452
... del furtherPath[:]
453
... return 'traversed: ' + name
455
>>> zope.component.getGlobalSiteManager().registerAdapter(
456
... ns5, [zope.interface.Interface], IPathAdapter, 'ns5')
458
>>> class Ob(object):
459
... def __init__(self, title, date, parent=None, child=None):
460
... self.title = title
462
... self.parent = parent
463
... self.child = child
465
>>> child = Ob('child', datetime.datetime(2008, 12, 30, 13, 48, 0, 0))
466
>>> father = Ob('father', datetime.datetime(1978, 12, 30, 13, 48, 0, 0))
467
>>> grandpa = Ob('grandpa', datetime.datetime(1948, 12, 30, 13, 48, 0, 0))
469
>>> child.parent = father
470
>>> father.child = child
471
>>> father.parent = grandpa
472
>>> grandpa.child = father
474
>>> class View(object):
475
... request = u'request'
478
... def __repr__(self):
482
>>> template = ViewPageTemplateFile('tests/function_namespaces.pt')
483
>>> print template.bind(view)()
484
<div xmlns="http://www.w3.org/1999/xhtml">
486
<span>2008-12-30 13:48:00</span>
487
<span>traversed: link:main</span>
488
<span>called page: default</span>
489
<span>called page: another</span>
491
<span>traversed: zope.Public</span>
492
<span>traversed: text-to-html</span>
493
<span>traversed: page/yet/even/another</span>