Magic templates in Plone 5
Due to the new rendering-engine chameleon it is fun again to write templates
Plone 5 uses Chameleon a its rendering engine. Did you know that because of that you can put a pdb
in a template? If you saw the keynote by Eric Steele on Plone 5 you probably do.
But did you also know that the variable econtext holds all current variables up to the moment the pdb
is thrown?
Let's put a pdb in newsitem.pt
:
(...)
<metal:content-core fill-slot="content-core">
<metal:block define-macro="content-core"
tal:define="templateId template/getId;
scale_func context/@@images;
scaled_image python: getattr(context.aq_explicit, 'image', False) and scale_func.scale('image', scale='mini')">
<?python import pdb; pdb.set_trace() ?>
<figure class="newsImageContainer"
tal:condition="python: scaled_image">
<a href="#"
tal:define="here_url context/@@plone_context_state/object_url;
large_image python: scale_func.scale('image', scale='large');"
tal:attributes="href large_image/url">
<img tal:replace="structure python: scaled_image.tag(css_class='newsImage')" />
(...)
When rendering a News Item the variable scaled_image
is accessible as econtext['scaled_image']
:
> /Users/philip/workspace/test/f56e9585b89e34318d1171acc17f531a7a428a1f.py(132)render_content_core()
(Pdb) econtext['scaled_image']
<plone.namedfile.scaling.ImageScale object at 0x110073b90>
(Pdb) econtext['scaled_image'].width
200
You can inspect the whole econtext
:
(Pdb) from pprint import pprint as pp
(Pdb) pp econtext
{'__convert': <function translate at 0x10fb4d7d0>,
'__decode': <function decode at 0x10fb4d578>,
'__slot_content_core': deque([]),
'__slot_javascript_head_slot': deque([]),
'__translate': <function translate at 0x10fb4d7d0>,
'ajax_include_head': None,
'ajax_load': False,
'args': (),
'body_class': 'template-newsitem_view portaltype-news-item site-Plone section-super-news userrole-manager userrole-authenticated userrole-owner plone-toolbar-left-default',
'checkPermission': <bound method MembershipTool.checkPermission of <MembershipTool at /Plone/portal_membership used for /Plone/super-news>>,
'container': <NewsItem at /Plone/super-news>,
'context': <NewsItem at /Plone/super-news>,
'context_state': <Products.Five.metaclass.ContextState object at 0x10db00910>,
'default': <object object at 0x100291bf0>,
'dummy': None,
'here': <NewsItem at /Plone/super-news>,
'isRTL': False,
'lang': 'de',
'loop': {},
'modules': <Products.PageTemplates.ZRPythonExpr._SecureModuleImporter instance at 0x102e31b48>,
'nothing': None,
'options': {},
'plone_layout': <Products.Five.metaclass.LayoutPolicy object at 0x10db00310>,
'plone_view': <Products.Five.metaclass.Plone object at 0x10db00dd0>,
'portal_state': <Products.Five.metaclass.PortalState object at 0x10fbe8d50>,
'portal_url': 'http://localhost:8080/Plone',
'repeat': {},
'request': <HTTPRequest, URL=http://localhost:8080/Plone/super-news/newsitem_view>,
'root': <Application at >,
'scale_func': <Products.Five.metaclass.ImageScaling object at 0x10c2bf390>,
'scaled_image': <plone.namedfile.scaling.ImageScale object at 0x110073b90>,
'site_properties': <SimpleItemWithProperties at /Plone/portal_properties/site_properties>,
'sl': False,
'sr': False,
'target_language': None,
'template': <Products.Five.browser.pagetemplatefile.ViewPageTemplateFile object at 0x10fff3ed0>,
'templateId': 'newsitem.pt',
'toolbar_class': 'pat-toolbar initialized plone-toolbar-left',
'translate': <function translate at 0x10fb4d7d0>,
'traverse_subpath': [],
'user': <PropertiedUser 'adminstarzel'>,
'view': <Products.Five.metaclass.SimpleViewClass from /Users/philip/workspace/test/src-mrd/plone.app.contenttypes/plone/app/contenttypes/browser/templates/newsitem.pt object at 0x10cd93910>,
'views': <Products.Five.browser.pagetemplatefile.ViewMapper object at 0x10f5cc190>,
'wrapped_repeat': <Products.PageTemplates.Expressions.SafeMapping object at 0x10ffba1b0>}
Using n
you can actually walk down the template and inspect new variables as they appear. After pressing n
about 11 times the variable large_image
appears as econtext['large_image']
.
(Pdb) econtext['large_image'].width
768
The pdb-session you are in is no restricted python but real python. This means you can do the following:
(Pdb) from plone import api
(Pdb) portal = api.portal.get_tool('portal_memberdata')
(Pdb) memberdata = api.portal.get_tool('portal_memberdata')
(Pdb) memberdata.getProperty('wysiwyg_editor')
'kupu'
Hey, what is kupu doing there? I found that in some of our sites that were migrated from Plone 3 this old setting prevented TinyMCE to work in static portlets. But that is a different story, let's just get rid of it.
(Pdb) memberdata.wysiwyg_editor = 'TinyMCE'
(Pdb) import transaction; transaction.commit()
This is a full-grown pdb and you can inspect and modify your complete site with it.
But there is more: You can actually have complete code-blocks into templates:
<?python
from plone import api
catalog = api.portal.get_tool('portal_catalog')
results = []
for brain in catalog(portal_type='Folder'):
results.append(brain.getURL())
?>
<ul>
<li tal:repeat="result results">
${result}
</li>
</ul>
Quick and dirty? Maybe dirty but really quick! It is still very true that having logic in templates is bad practice but I think in some use-cases it is ok:
- Debugging
- When you customize a existing template (with z3c.jbot or plone.app.themingplugins) and need some more logic
- When you quickly need to add some logic to a browser-view that only has a template but no class of it's own
Have fun templating with Plone 5! If you want to learn more about Plone 5 you can still register for the training "Mastering Plone 5 Development" in March 2.-6. (http://www.starzel.de/leistungen/training/)
Update:
As discussed here you can add the econtext to the the locals() by using the following stanza:
<?python locals().update(econtext); import pdb; pdb.set_trace() ?>