Skip to content

Commit

Permalink
Merge efabf84 into c6f8b74
Browse files Browse the repository at this point in the history
  • Loading branch information
d-maurer committed Jul 8, 2020
2 parents c6f8b74 + efabf84 commit 1e5db85
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 4 deletions.
10 changes: 10 additions & 0 deletions CHANGES.rst
Expand Up @@ -10,6 +10,16 @@ https://zope.readthedocs.io/en/2.13/CHANGES.html
4.4.5 (unreleased)
------------------

- New interface ``Products.PageTemplates.interfaces.IZopeAwareEngine``.
It can be used as the "provides" of an adapter registration
to adapt a non ``Zope`` tales engine to an engine to be used
by ``Zope`` page templates
(`#864 <https://github.com/zopefoundation/Zope/issues/864>`_).
Currently, the adaptation is used only when the
template is rendered with ``chameleon``;
with ``zope.pagetemplate``, the engine is used
as is - this may change in the future.

- Allow (some) builtins as first element of a (TALES) path expression:
in an untrusted context, the builtins from
``AccessControl.safe_builtins`` are allowed;
Expand Down
12 changes: 10 additions & 2 deletions src/Products/PageTemplates/Expressions.py
Expand Up @@ -25,8 +25,6 @@
from AccessControl import safe_builtins
from Acquisition import aq_base
from MultiMapping import MultiMapping
from Products.PageTemplates import ZRPythonExpr
from Products.PageTemplates.interfaces import IUnicodeEncodingConflictResolver
from zExceptions import NotFound
from zExceptions import Unauthorized
from zope.component import queryUtility
Expand All @@ -49,6 +47,10 @@
from zope.traversing.adapters import traversePathElement
from zope.traversing.interfaces import ITraversable

from . import ZRPythonExpr
from .interfaces import IUnicodeEncodingConflictResolver
from .interfaces import IZopeAwareEngine


SecureModuleImporter = ZRPythonExpr._SecureModuleImporter()

Expand Down Expand Up @@ -325,6 +327,12 @@ class ErrorInfo(BaseErrorInfo):
__allow_access_to_unprotected_subobjects__ = True


# Whether an engine is Zope aware does not depend on the class
# but how it is configured - especially, that is uses a Zope aware
# `PathExpr` implementation.
# Nevertheless, we mark the class as "Zope aware" for simplicity
# assuming that users of the class use a proper `PathExpr`
@implementer(IZopeAwareEngine)
class ZopeEngine(Z3Engine):

_create_context = ZopeContext
Expand Down
3 changes: 2 additions & 1 deletion src/Products/PageTemplates/engine.py
Expand Up @@ -37,6 +37,7 @@

from .Expressions import PathIterator
from .Expressions import SecureModuleImporter
from .interfaces import IZopeAwareEngine


class _PseudoContext(object):
Expand Down Expand Up @@ -310,7 +311,7 @@ class Program(object):

def __init__(self, template, engine):
self.template = template
self.engine = engine
self.engine = IZopeAwareEngine(engine, engine)

def __call__(self, context, macros, tal=True, **options):
if tal is False:
Expand Down
11 changes: 10 additions & 1 deletion src/Products/PageTemplates/expression.py
Expand Up @@ -16,16 +16,19 @@
from AccessControl.ZopeGuards import guarded_iter
from AccessControl.ZopeGuards import protected_inplacevar
from OFS.interfaces import ITraversable
from Products.PageTemplates.Expressions import render
from RestrictedPython import RestrictingNodeTransformer
from RestrictedPython.Utilities import utility_builtins
from z3c.pt import expressions
from zExceptions import NotFound
from zExceptions import Unauthorized
from zope.interface import implementer
from zope.tales.tales import ExpressionEngine
from zope.traversing.adapters import traversePathElement
from zope.traversing.interfaces import TraversalError

from .Expressions import render
from .interfaces import IZopeAwareEngine


_marker = object()

Expand Down Expand Up @@ -167,6 +170,12 @@ def parse(self, string):
return node


# Whether an engine is Zope aware does not depend on the class
# but how it is configured - especially, that is uses a Zope aware
# `PathExpr` implementation.
# Nevertheless, we mark the class as "Zope aware" for simplicity
# assuming that users of the class use a proper `PathExpr`
@implementer(IZopeAwareEngine)
class ChameleonEngine(ExpressionEngine):
"""Expression engine for ``chameleon.tales``.
Expand Down
4 changes: 4 additions & 0 deletions src/Products/PageTemplates/interfaces.py
Expand Up @@ -27,3 +27,7 @@ def resolve(context, text, expression):
'expression' is the original expression (can be used for
logging purposes)
"""


class IZopeAwareEngine(Interface):
"""Interface to mark a TALES engine aware of Zope specifics."""
59 changes: 59 additions & 0 deletions src/Products/PageTemplates/tests/testZopeAwareEngine.py
@@ -0,0 +1,59 @@
##############################################################################
#
# Copyright (c) 2020 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################

from unittest import TestCase

from zope.component import provideAdapter
from zope.component.testing import PlacelessSetup
from zope.pagetemplate.engine import Engine as base_engine

from ..engine import Program
from ..expression import getEngine as getChameleonEngine
from ..Expressions import getEngine as getZopeEngine
from ..interfaces import IZopeAwareEngine


def adapt(engine):
return getZopeEngine()


class TestsWithoutAdapter(PlacelessSetup, TestCase):
def test_ch_engine(self):
self.check(getChameleonEngine())

def test_zope_engine(self):
self.check(getZopeEngine())

def test_base_engine(self):
self.check(base_engine, getZopeEngine())

def check(self, engine, zengine=None):
zengine = self.should(zengine)
if zengine is None:
zengine = engine
p = Program(None, engine)
self.assertIs(p.engine, zengine)

def should(self, zengine):
# without adapter, we must ignore *zengine*
return


class TestWithsAdapter(TestsWithoutAdapter):
def setUp(self):
super(TestWithsAdapter, self).setUp()
provideAdapter(adapt, (None,), IZopeAwareEngine)

def should(self, zengine):
# with adapter, *zengine* specifies the result
return zengine

0 comments on commit 1e5db85

Please sign in to comment.