Commit 67ec9bb7 authored by Andreas Jung's avatar Andreas Jung

This version of PageTemplateFile seems to work fine although

it requires further testing.

The implementation uses its own Engine and traverse since browser 
pages configured through ZCML are looked up by a bobo_traverse()
hack. However the standdard simpleTraverse() method of zope.tales.
expressions only performs the traversal using __getitem__(). So we
define our own traverser that also tries a traversal using 
restrictedTraverse().

The code is still ugly, needs some more cleanup and renaming.
parent 044eb948
...@@ -10,34 +10,96 @@ ...@@ -10,34 +10,96 @@
# FOR A PARTICULAR PURPOSE # FOR A PARTICULAR PURPOSE
# #
############################################################################## ##############################################################################
"""Filesystem Page Template module
Zope object encapsulating a Page Template from the filesystem. import os
"""
__version__ = '$Revision: 1.30 $'[11:-2] from Globals import package_home, InitializeClass
from App.config import getConfiguration
from ZopePageTemplate import ZopePageTemplate
from zope.app.content_types import guess_content_type
import AccessControl
import os, AccessControl
from logging import getLogger
from Globals import package_home, DevelopmentMode
from Shared.DC.Scripts.Script import Script
from Shared.DC.Scripts.Signature import FuncCode
from AccessControl import getSecurityManager
from OFS.Traversable import Traversable
from PageTemplate import PageTemplate
from Expressions import SecureModuleImporter
from ComputedAttribute import ComputedAttribute from ComputedAttribute import ComputedAttribute
from Acquisition import aq_parent, aq_inner from OFS.SimpleItem import SimpleItem
from App.config import getConfiguration from Expressions import SecureModuleImporter
from OFS.Traversable import Traversable
from zope.pagetemplate.pagetemplatefile import PageTemplateFile as PTF
from zope.pagetemplate.pagetemplate import PageTemplate as PT
from Shared.DC.Scripts.Script import Script
from OFS.SimpleItem import Item_w__name__ from OFS.SimpleItem import Item_w__name__
from Shared.DC.Scripts.Signature import FuncCode
from zope.tales.tales import ExpressionEngine
from zope.tales.expressions import PathExpr, StringExpr, NotExpr, DeferExpr, SubPathExpr
from zope.tales.tales import _valid_name, _parse_expr, NAME_RE, Undefined
from zope.tales.expressions import SimpleModuleImporter
from zope.tales.pythonexpr import PythonExpr
from zope.tales.expressions import PathExpr
_marker = object()
def extendedSimpleTraverse(object, path_items, econtext):
"""Traverses a sequence of names, first trying attributes then items.
"""
for name in path_items:
next = getattr(object, name, _marker)
if next is not _marker:
object = next
elif hasattr(object, '__getitem__'):
try:
object = object[name]
except:
# FIX bare try..except
object = object.restrictedTraverse(name)
else:
# Allow AttributeError to propagate
object = getattr(object, name)
return object
class MyPathExpr(PathExpr):
def __init__(self, name, expr, engine, traverser=extendedSimpleTraverse):
self._s = expr
self._name = name
paths = expr.split('|')
self._subexprs = []
add = self._subexprs.append
for i in range(len(paths)):
path = paths[i].lstrip()
if _parse_expr(path):
# This part is the start of another expression type,
# so glue it back together and compile it.
add(engine.compile('|'.join(paths[i:]).lstrip()))
break
add(SubPathExpr(path, traverser, engine)._eval)
LOG = getLogger('PageTemplateFile') def Engine():
e = ExpressionEngine()
reg = e.registerType
for pt in MyPathExpr._default_type_names:
reg(pt, MyPathExpr)
reg('string', StringExpr)
reg('python', PythonExpr)
reg('not', NotExpr)
reg('defer', DeferExpr)
e.registerBaseName('modules', SimpleModuleImporter())
return e
class PageTemplateFile(Item_w__name__, Script, PageTemplate, Traversable): Engine = Engine()
"Zope wrapper for filesystem Page Template using TAL, TALES, and METAL"
class PageTemplateFile(SimpleItem, Script, PT, Traversable):
meta_type = 'Page Template (File)'
func_defaults = None func_defaults = None
func_code = FuncCode((), 0) func_code = FuncCode((), 0)
...@@ -53,32 +115,44 @@ class PageTemplateFile(Item_w__name__, Script, PageTemplate, Traversable): ...@@ -53,32 +115,44 @@ class PageTemplateFile(Item_w__name__, Script, PageTemplate, Traversable):
security.declareProtected('View management screens', security.declareProtected('View management screens',
'read', 'document_src') 'read', 'document_src')
_default_bindings = {'name_subpath': 'traverse_subpath'}
def __init__(self, filename, _prefix=None, **kw): def __init__(self, filename, _prefix=None, **kw):
self.ZBindings_edit(self._default_bindings)
if _prefix is None: name = None
_prefix = getConfiguration().softwarehome if kw.has_key('__name__'):
elif not isinstance(_prefix, str): name = kw['__name__']
_prefix = package_home(_prefix) del kw['__name__']
name = kw.get('__name__')
basepath, ext = os.path.splitext(filename) basepath, ext = os.path.splitext(filename)
if name: if name:
self._need__name__ = 0 self.id = self.__name__ = name
self.__name__ = name
else: else:
self.__name__ = os.path.basename(basepath) self.id = self.__name__ = os.path.basename(basepath)
if _prefix:
if isinstance(_prefix, str):
filename = os.path.join(_prefix, filename)
else:
filename = os.path.join(package_home(_prefix), filename)
if not ext: if not ext:
# XXX This is pretty bogus, but can't be removed since
# it's been released this way.
filename = filename + '.zpt' filename = filename + '.zpt'
self.filename = os.path.join(_prefix, filename)
def getId(self): self.filename = filename
"""return the ID of this object"""
return self.__name__ content = open(filename).read()
from ZopePageTemplate import guess_type
self.pt_edit( content, guess_type(filename, content))
def pt_getContext(self): def pt_getContext(self):
root = self.getPhysicalRoot() root = self.getPhysicalRoot()
context = self._getContext() context = self._getContext()
from DateTime.DateTime import DateTime
c = {'template': self, c = {'template': self,
'here': context, 'here': context,
'context': context, 'context': context,
...@@ -86,6 +160,7 @@ class PageTemplateFile(Item_w__name__, Script, PageTemplate, Traversable): ...@@ -86,6 +160,7 @@ class PageTemplateFile(Item_w__name__, Script, PageTemplate, Traversable):
'nothing': None, 'nothing': None,
'options': {}, 'options': {},
'root': root, 'root': root,
'DateTime' : DateTime,
'request': getattr(root, 'REQUEST', None), 'request': getattr(root, 'REQUEST', None),
'modules': SecureModuleImporter, 'modules': SecureModuleImporter,
} }
...@@ -106,11 +181,14 @@ class PageTemplateFile(Item_w__name__, Script, PageTemplate, Traversable): ...@@ -106,11 +181,14 @@ class PageTemplateFile(Item_w__name__, Script, PageTemplate, Traversable):
pass pass
# Execute the template in a new security context. # Execute the template in a new security context.
security = getSecurityManager() security = AccessControl.getSecurityManager()
bound_names['user'] = security.getUser() bound_names['user'] = security.getUser()
security.addContext(self) security.addContext(self)
try: try:
return self.pt_render(extra_context=bound_names) context = self.pt_getContext()
context.update(bound_names)
return self.pt_render(context)
finally: finally:
security.removeContext(self) security.removeContext(self)
...@@ -118,41 +196,6 @@ class PageTemplateFile(Item_w__name__, Script, PageTemplate, Traversable): ...@@ -118,41 +196,6 @@ class PageTemplateFile(Item_w__name__, Script, PageTemplate, Traversable):
self._cook_check() self._cook_check()
return PageTemplate.pt_macros(self) return PageTemplate.pt_macros(self)
def pt_source_file(self):
"""Returns a file name to be compiled into the TAL code."""
return self.__name__ # Don't reveal filesystem paths
def _cook_check(self):
if self._v_last_read and not DevelopmentMode:
return
__traceback_info__ = self.filename
try:
mtime = os.path.getmtime(self.filename)
except OSError:
mtime = 0
if self._v_program is not None and mtime == self._v_last_read:
return
f = open(self.filename, "rb")
try:
text = f.read(XML_PREFIX_MAX_LENGTH)
except:
f.close()
raise
t = sniff_type(text)
if t != "text/xml":
# For HTML, we really want the file read in text mode:
f.close()
f = open(self.filename)
text = ''
text += f.read()
f.close()
self.pt_edit(text, t)
self._cook()
if self._v_errors:
LOG.error('Error in template %s' % '\n'.join(self._v_errors))
return
self._v_last_read = mtime
def document_src(self, REQUEST=None, RESPONSE=None): def document_src(self, REQUEST=None, RESPONSE=None):
"""Return expanded document source.""" """Return expanded document source."""
...@@ -182,25 +225,12 @@ class PageTemplateFile(Item_w__name__, Script, PageTemplate, Traversable): ...@@ -182,25 +225,12 @@ class PageTemplateFile(Item_w__name__, Script, PageTemplate, Traversable):
""" """
return None return None
def pt_getEngine(self):
return Engine
def __getstate__(self): def __getstate__(self):
from ZODB.POSException import StorageError from ZODB.POSException import StorageError
raise StorageError, ("Instance of AntiPersistent class %s " raise StorageError, ("Instance of AntiPersistent class %s "
"cannot be stored." % self.__class__.__name__) "cannot be stored." % self.__class__.__name__)
InitializeClass(PageTemplateFile)
XML_PREFIXES = [
"<?xml", # ascii, utf-8
"\xef\xbb\xbf<?xml", # utf-8 w/ byte order mark
"\0<\0?\0x\0m\0l", # utf-16 big endian
"<\0?\0x\0m\0l\0", # utf-16 little endian
"\xfe\xff\0<\0?\0x\0m\0l", # utf-16 big endian w/ byte order mark
"\xff\xfe<\0?\0x\0m\0l\0", # utf-16 little endian w/ byte order mark
]
XML_PREFIX_MAX_LENGTH = max(map(len, XML_PREFIXES))
def sniff_type(text):
for prefix in XML_PREFIXES:
if text.startswith(prefix):
return "text/xml"
return None
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment