From 50786247582211edcb70578734efedd85502f1e4 Mon Sep 17 00:00:00 2001 From: Dieter Maurer Date: Mon, 26 Feb 2024 13:16:59 +0100 Subject: [PATCH] Support the `Chameleon` expression type `structure` (#1199) --- CHANGES.rst | 3 +++ src/Products/PageTemplates/Expressions.py | 26 ++++++++++++++++++- src/Products/PageTemplates/expression.py | 4 ++- src/Products/PageTemplates/tests/structure.pt | 1 + .../PageTemplates/tests/test_pagetemplate.py | 9 +++++++ 5 files changed, 41 insertions(+), 2 deletions(-) create mode 100644 src/Products/PageTemplates/tests/structure.pt diff --git a/CHANGES.rst b/CHANGES.rst index 423c17c820..609fd1b031 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -26,6 +26,9 @@ https://github.com/zopefoundation/Zope/blob/4.x/CHANGES.rst - Fix ``Content-Disposition`` filename for clients without rfc6266 support. (`#1198 `_) +- Support ``Chameleon`` ``structure`` expression type. + Fixes `#1077 `_. + 5.9 (2023-11-24) ---------------- diff --git a/src/Products/PageTemplates/Expressions.py b/src/Products/PageTemplates/Expressions.py index 39102017c1..137db2d836 100644 --- a/src/Products/PageTemplates/Expressions.py +++ b/src/Products/PageTemplates/Expressions.py @@ -10,7 +10,7 @@ # FOR A PARTICULAR PURPOSE # ############################################################################## -"""Page Template Expression Engine +"""Page Template Expression Engine based on ``zope.tales``. Page Template-specific implementation of TALES, with handlers for Python expressions, string literals, and paths. @@ -19,6 +19,8 @@ import logging import warnings +from chameleon.tales import Markup + import OFS.interfaces from AccessControl import safe_builtins from AccessControl.SecurityManagement import getSecurityManager @@ -39,6 +41,7 @@ from zope.tales.expressions import StringExpr from zope.tales.expressions import SubPathExpr from zope.tales.expressions import Undefs +from zope.tales.interfaces import ITALESExpression from zope.tales.pythonexpr import PythonExpr from zope.tales.tales import Context from zope.tales.tales import ErrorInfo as BaseErrorInfo @@ -238,6 +241,26 @@ class TrustedZopePathExpr(ZopePathExpr): SUBEXPR_FACTORY = TrustedSubPathExpr +@implementer(ITALESExpression) +class StructureExpr: + """ + An expression that tells the template engine to + render the value as structure (i.e. with markup). + + Note: will only work with ``chameleon`` template engine. + """ + + def __init__(self, name, expr, engine): + self._s = expr = expr.lstrip() + self._c = engine.compile(expr) + + def __call__(self, econtext): + return Markup(econtext.evaluate(self._c)) + + def __repr__(self): + return '' % repr(self._s) + + class SafeMapping(MultiMapping): """Mapping with security declarations and limited method exposure. @@ -482,6 +505,7 @@ def createZopeEngine(zpe=ZopePathExpr, untrusted=True): e.registerType('defer', DeferExpr) e.registerType('lazy', LazyExpr) e.registerType('provider', TALESProviderExpression) + e.registerType('structure', StructureExpr) e.registerBaseName('modules', SecureModuleImporter) e.untrusted = untrusted return e diff --git a/src/Products/PageTemplates/expression.py b/src/Products/PageTemplates/expression.py index da01002c60..0f8f06dbe8 100644 --- a/src/Products/PageTemplates/expression.py +++ b/src/Products/PageTemplates/expression.py @@ -9,6 +9,7 @@ from chameleon.codegen import template from chameleon.tales import NotExpr from chameleon.tales import StringExpr +from chameleon.tales import StructureExpr from AccessControl.SecurityManagement import getSecurityManager from AccessControl.ZopeGuards import guarded_apply @@ -226,7 +227,8 @@ def compile(self, expression): exists=ExistsExpr, path=PathExpr, provider=expressions.ProviderExpr, - nocall=NocallExpr) + nocall=NocallExpr, + structure=StructureExpr) def createChameleonEngine(types=types, untrusted=True, **overrides): diff --git a/src/Products/PageTemplates/tests/structure.pt b/src/Products/PageTemplates/tests/structure.pt new file mode 100644 index 0000000000..b9b6def2a9 --- /dev/null +++ b/src/Products/PageTemplates/tests/structure.pt @@ -0,0 +1 @@ +${structure:options/param} diff --git a/src/Products/PageTemplates/tests/test_pagetemplate.py b/src/Products/PageTemplates/tests/test_pagetemplate.py index c834bb20e6..d3b8ad1bda 100644 --- a/src/Products/PageTemplates/tests/test_pagetemplate.py +++ b/src/Products/PageTemplates/tests/test_pagetemplate.py @@ -3,6 +3,8 @@ from Products.PageTemplates.PageTemplateFile import PageTemplateFile from Testing.ZopeTestCase import ZopeTestCase +from zope.component import provideAdapter +from zope.traversing.adapters import DefaultTraversable from .util import useChameleonEngine @@ -14,6 +16,7 @@ class TestPageTemplateFile(ZopeTestCase): def afterSetUp(self): useChameleonEngine() + provideAdapter(DefaultTraversable, (None,)) def _makeOne(self, name): return PageTemplateFile(os.path.join(path, name)).__of__(self.app) @@ -81,6 +84,12 @@ def test_secure(self): result = template(soup=soup) self.assertTrue('<foo></bar>' in result) + def test_structure(self): + template = self._makeOne("structure.pt") + param = "abc" + result = template(param=param) + self.assertTrue(param in result) + def test_suite(): return unittest.defaultTestLoader.loadTestsFromTestCase(