Skip to content

Commit

Permalink
Support the Chameleon expression type structure
Browse files Browse the repository at this point in the history
  • Loading branch information
d-maurer committed Feb 26, 2024
1 parent 3b32e2e commit 15c1697
Show file tree
Hide file tree
Showing 5 changed files with 41 additions and 2 deletions.
3 changes: 3 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ https://github.com/zopefoundation/Zope/blob/4.x/CHANGES.rst
- Fix ``Content-Disposition`` filename for clients without rfc6266 support.
(`#1198 <https://github.com/zopefoundation/Zope/pull/1198>`_)

- Support ``Chameleon`` ``structure`` expression type.
Fixes `#1077 <https://github.com/zopefoundation/Zope/issues/1077>`_.


5.9 (2023-11-24)
----------------
Expand Down
26 changes: 25 additions & 1 deletion src/Products/PageTemplates/Expressions.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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 '<StructureExpr %s>' % repr(self._s)


class SafeMapping(MultiMapping):
"""Mapping with security declarations and limited method exposure.
Expand Down Expand Up @@ -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
Expand Down
4 changes: 3 additions & 1 deletion src/Products/PageTemplates/expression.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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):
Expand Down
1 change: 1 addition & 0 deletions src/Products/PageTemplates/tests/structure.pt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
${structure:options/param}
9 changes: 9 additions & 0 deletions src/Products/PageTemplates/tests/test_pagetemplate.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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)
Expand Down Expand Up @@ -81,6 +84,12 @@ def test_secure(self):
result = template(soup=soup)
self.assertTrue('&lt;foo&gt;&lt;/bar&gt;' in result)

def test_structure(self):
template = self._makeOne("structure.pt")
param = "<span>abc</span>"
result = template(param=param)
self.assertTrue(param in result)


def test_suite():
return unittest.defaultTestLoader.loadTestsFromTestCase(
Expand Down

0 comments on commit 15c1697

Please sign in to comment.