Commit d9b010c4 authored by Hanno Schlichting's avatar Hanno Schlichting

Revert "Remove ZCacheable logic and StandardCacheManagers dependency."

This reverts part of commit dbb476e6.
parent b43972fe
...@@ -27,6 +27,8 @@ Features Added ...@@ -27,6 +27,8 @@ Features Added
Restructuring Restructuring
+++++++++++++ +++++++++++++
- Add back ZCacheable support.
- Update to zope.testbrowser 5.0 and its WebTest based implementation. - Update to zope.testbrowser 5.0 and its WebTest based implementation.
- Use `@implementer` and `@adapter` class decorators. - Use `@implementer` and `@adapter` class decorators.
......
This diff is collapsed.
...@@ -60,6 +60,7 @@ class DTMLDocument(PropertyManager, DTMLMethod): ...@@ -60,6 +60,7 @@ class DTMLDocument(PropertyManager, DTMLMethod):
file = file.read() file = file.read()
self.munge(file) self.munge(file)
self.ZCacheable_invalidate()
if REQUEST: if REQUEST:
message = "Content uploaded." message = "Content uploaded."
return self.manage_main(self, REQUEST, manage_tabs_message=message) return self.manage_main(self, REQUEST, manage_tabs_message=message)
...@@ -69,6 +70,12 @@ class DTMLDocument(PropertyManager, DTMLMethod): ...@@ -69,6 +70,12 @@ class DTMLDocument(PropertyManager, DTMLMethod):
o If supplied, use REQUEST mapping, Response, and key word arguments. o If supplied, use REQUEST mapping, Response, and key word arguments.
""" """
if not self._cache_namespace_keys:
data = self.ZCacheable_get(default=_marker)
if data is not _marker:
# Return cached results.
return data
__traceback_supplement__ = (PathTracebackSupplement, self) __traceback_supplement__ = (PathTracebackSupplement, self)
kw['document_id'] = self.getId() kw['document_id'] = self.getId()
kw['document_title'] = self.title kw['document_title'] = self.title
...@@ -88,11 +95,15 @@ class DTMLDocument(PropertyManager, DTMLMethod): ...@@ -88,11 +95,15 @@ class DTMLDocument(PropertyManager, DTMLMethod):
result = r result = r
else: else:
result = decapitate(r, RESPONSE) result = decapitate(r, RESPONSE)
if not self._cache_namespace_keys:
self.ZCacheable_set(result)
return result return result
r = HTML.__call__(self, (client, bself), REQUEST, **kw) r = HTML.__call__(self, (client, bself), REQUEST, **kw)
if RESPONSE is None or not isinstance(r, str): if RESPONSE is None or not isinstance(r, str):
if not self._cache_namespace_keys:
self.ZCacheable_set(r)
return r return r
finally: finally:
...@@ -106,6 +117,8 @@ class DTMLDocument(PropertyManager, DTMLMethod): ...@@ -106,6 +117,8 @@ class DTMLDocument(PropertyManager, DTMLMethod):
c, e = guess_content_type(self.__name__, r) c, e = guess_content_type(self.__name__, r)
RESPONSE.setHeader('Content-Type', c) RESPONSE.setHeader('Content-Type', c)
result = decapitate(r, RESPONSE) result = decapitate(r, RESPONSE)
if not self._cache_namespace_keys:
self.ZCacheable_set(result)
return result return result
......
...@@ -37,6 +37,8 @@ from OFS import bbb ...@@ -37,6 +37,8 @@ from OFS import bbb
from OFS.Cache import Cacheable from OFS.Cache import Cacheable
from OFS.role import RoleManager from OFS.role import RoleManager
from OFS.SimpleItem import Item_w__name__ from OFS.SimpleItem import Item_w__name__
from ZPublisher.Iterators import IStreamIterator
if sys.version_info >= (3, ): if sys.version_info >= (3, ):
basestring = str basestring = str
...@@ -59,6 +61,7 @@ class DTMLMethod(RestrictedDTML, ...@@ -59,6 +61,7 @@ class DTMLMethod(RestrictedDTML,
""" """
meta_type = 'DTML Method' meta_type = 'DTML Method'
index_html = None # Prevent accidental acquisition index_html = None # Prevent accidental acquisition
_cache_namespace_keys = ()
security = ClassSecurityInfo() security = ClassSecurityInfo()
security.declareObjectProtected(View) security.declareObjectProtected(View)
...@@ -72,7 +75,8 @@ class DTMLMethod(RestrictedDTML, ...@@ -72,7 +75,8 @@ class DTMLMethod(RestrictedDTML,
{'label': 'Edit', 'action': 'manage_main'}, {'label': 'Edit', 'action': 'manage_main'},
) + ) +
RoleManager.manage_options + RoleManager.manage_options +
Item_w__name__.manage_options Item_w__name__.manage_options +
Cacheable.manage_options
) )
# More reasonable default for content-type for http HEAD requests. # More reasonable default for content-type for http HEAD requests.
...@@ -88,6 +92,27 @@ class DTMLMethod(RestrictedDTML, ...@@ -88,6 +92,27 @@ class DTMLMethod(RestrictedDTML,
o If supplied, use the REQUEST mapping, Response, and key word o If supplied, use the REQUEST mapping, Response, and key word
arguments. arguments.
""" """
if not self._cache_namespace_keys:
data = self.ZCacheable_get(default=_marker)
if data is not _marker:
if (IStreamIterator.isImplementedBy(data) and
RESPONSE is not None):
# This is a stream iterator and we need to set some
# headers now before giving it to medusa
headers_get = RESPONSE.headers.get
if headers_get('content-length', None) is None:
RESPONSE.setHeader('content-length', len(data))
if (headers_get('content-type', None) is None and
headers_get('Content-type', None) is None):
ct = (self.__dict__.get('content_type') or
self.default_content_type)
RESPONSE.setHeader('content-type', ct)
# Return cached results.
return data
__traceback_supplement__ = (PathTracebackSupplement, self) __traceback_supplement__ = (PathTracebackSupplement, self)
kw['document_id'] = self.getId() kw['document_id'] = self.getId()
kw['document_title'] = self.title kw['document_title'] = self.title
...@@ -108,10 +133,14 @@ class DTMLMethod(RestrictedDTML, ...@@ -108,10 +133,14 @@ class DTMLMethod(RestrictedDTML,
result = r result = r
else: else:
result = decapitate(r, RESPONSE) result = decapitate(r, RESPONSE)
if not self._cache_namespace_keys:
self.ZCacheable_set(result)
return result return result
r = HTML.__call__(self, client, REQUEST, **kw) r = HTML.__call__(self, client, REQUEST, **kw)
if RESPONSE is None or not isinstance(r, str): if RESPONSE is None or not isinstance(r, str):
if not self._cache_namespace_keys:
self.ZCacheable_set(r)
return r return r
finally: finally:
...@@ -127,24 +156,54 @@ class DTMLMethod(RestrictedDTML, ...@@ -127,24 +156,54 @@ class DTMLMethod(RestrictedDTML,
c, e = guess_content_type(self.getId(), r) c, e = guess_content_type(self.getId(), r)
RESPONSE.setHeader('Content-Type', c) RESPONSE.setHeader('Content-Type', c)
result = decapitate(r, RESPONSE) result = decapitate(r, RESPONSE)
if not self._cache_namespace_keys:
self.ZCacheable_set(result)
return result return result
def validate(self, inst, parent, name, value, md=None): def validate(self, inst, parent, name, value, md=None):
return getSecurityManager().validate(inst, parent, name, value) return getSecurityManager().validate(inst, parent, name, value)
def ZDocumentTemplate_beforeRender(self, md, default): def ZDocumentTemplate_beforeRender(self, md, default):
# Tries to get a cached value.
if self._cache_namespace_keys:
# Use the specified keys from the namespace to identify a
# cache entry.
kw = {}
for key in self._cache_namespace_keys:
try:
val = md[key]
except:
val = None
kw[key] = val
return self.ZCacheable_get(keywords=kw, default=default)
return default return default
def ZDocumentTemplate_afterRender(self, md, result): def ZDocumentTemplate_afterRender(self, md, result):
pass # Tries to set a cache value.
if self._cache_namespace_keys:
kw = {}
for key in self._cache_namespace_keys:
try:
val = md[key]
except:
val = None
kw[key] = val
self.ZCacheable_set(result, keywords=kw)
security.declareProtected(change_dtml_methods, 'getCacheNamespaceKeys') security.declareProtected(change_dtml_methods, 'getCacheNamespaceKeys')
def getCacheNamespaceKeys(self): def getCacheNamespaceKeys(self):
return () # Return the cacheNamespaceKeys.
return self._cache_namespace_keys
security.declareProtected(change_dtml_methods, 'setCacheNamespaceKeys') security.declareProtected(change_dtml_methods, 'setCacheNamespaceKeys')
def setCacheNamespaceKeys(self, keys, REQUEST=None): def setCacheNamespaceKeys(self, keys, REQUEST=None):
pass # Set the list of names looked up to provide a cache key.
ks = []
for key in keys:
key = str(key).strip()
if key:
ks.append(key)
self._cache_namespace_keys = tuple(ks)
security.declareProtected(View, 'get_size') security.declareProtected(View, 'get_size')
def get_size(self): def get_size(self):
...@@ -178,6 +237,7 @@ class DTMLMethod(RestrictedDTML, ...@@ -178,6 +237,7 @@ class DTMLMethod(RestrictedDTML,
if not isinstance(data, basestring): if not isinstance(data, basestring):
data = data.read() data = data.read()
self.munge(data) self.munge(data)
self.ZCacheable_invalidate()
if REQUEST: if REQUEST:
message = "Saved changes." message = "Saved changes."
return self.manage_main(self, REQUEST, manage_tabs_message=message) return self.manage_main(self, REQUEST, manage_tabs_message=message)
...@@ -195,6 +255,7 @@ class DTMLMethod(RestrictedDTML, ...@@ -195,6 +255,7 @@ class DTMLMethod(RestrictedDTML,
file = file.read() file = file.read()
self.munge(file) self.munge(file)
self.ZCacheable_invalidate()
if REQUEST: if REQUEST:
message = "Saved changes." message = "Saved changes."
return self.manage_main(self, REQUEST, manage_tabs_message=message) return self.manage_main(self, REQUEST, manage_tabs_message=message)
...@@ -220,6 +281,7 @@ class DTMLMethod(RestrictedDTML, ...@@ -220,6 +281,7 @@ class DTMLMethod(RestrictedDTML,
self.dav__simpleifhandler(REQUEST, RESPONSE, refresh=1) self.dav__simpleifhandler(REQUEST, RESPONSE, refresh=1)
body = REQUEST.get('BODY', '') body = REQUEST.get('BODY', '')
self.munge(body) self.munge(body)
self.ZCacheable_invalidate()
RESPONSE.setStatus(204) RESPONSE.setStatus(204)
return RESPONSE return RESPONSE
......
...@@ -112,7 +112,8 @@ class File(Persistent, Implicit, PropertyManager, ...@@ -112,7 +112,8 @@ class File(Persistent, Implicit, PropertyManager,
manage_options = ( manage_options = (
({'label': 'Edit', 'action': 'manage_main'}, ) + ({'label': 'Edit', 'action': 'manage_main'}, ) +
RoleManager.manage_options + RoleManager.manage_options +
Item_w__name__.manage_options Item_w__name__.manage_options +
Cacheable.manage_options
) )
_properties = ( _properties = (
...@@ -372,6 +373,13 @@ class File(Persistent, Implicit, PropertyManager, ...@@ -372,6 +373,13 @@ class File(Persistent, Implicit, PropertyManager,
""" """
if self._if_modified_since_request_handler(REQUEST, RESPONSE): if self._if_modified_since_request_handler(REQUEST, RESPONSE):
# we were able to handle this by returning a 304
# unfortunately, because the HTTP cache manager uses the cache
# API, and because 304 responses are required to carry the Expires
# header for HTTP/1.1, we need to call ZCacheable_set here.
# This is nonsensical for caches other than the HTTP cache manager
# unfortunately.
self.ZCacheable_set(None)
return '' return ''
if self.precondition and hasattr(self, str(self.precondition)): if self.precondition and hasattr(self, str(self.precondition)):
...@@ -393,6 +401,17 @@ class File(Persistent, Implicit, PropertyManager, ...@@ -393,6 +401,17 @@ class File(Persistent, Implicit, PropertyManager,
RESPONSE.setHeader('Content-Length', self.size) RESPONSE.setHeader('Content-Length', self.size)
RESPONSE.setHeader('Accept-Ranges', 'bytes') RESPONSE.setHeader('Accept-Ranges', 'bytes')
if self.ZCacheable_isCachingEnabled():
result = self.ZCacheable_get(default=None)
if result is not None:
# We will always get None from RAMCacheManager and HTTP
# Accelerated Cache Manager but we will get
# something implementing the IStreamIterator interface
# from a "FileCacheManager"
return result
self.ZCacheable_set(None)
data = self.data data = self.data
if isinstance(data, str): if isinstance(data, str):
RESPONSE.setBase(None) RESPONSE.setBase(None)
...@@ -430,6 +449,8 @@ class File(Persistent, Implicit, PropertyManager, ...@@ -430,6 +449,8 @@ class File(Persistent, Implicit, PropertyManager,
size = len(data) size = len(data)
self.size = size self.size = size
self.data = data self.data = data
self.ZCacheable_invalidate()
self.ZCacheable_set(None)
self.http__refreshEtag() self.http__refreshEtag()
security.declareProtected(change_images_and_files, 'manage_edit') security.declareProtected(change_images_and_files, 'manage_edit')
...@@ -449,6 +470,8 @@ class File(Persistent, Implicit, PropertyManager, ...@@ -449,6 +470,8 @@ class File(Persistent, Implicit, PropertyManager,
del self.precondition del self.precondition
if filedata is not None: if filedata is not None:
self.update_data(filedata, content_type, len(filedata)) self.update_data(filedata, content_type, len(filedata))
else:
self.ZCacheable_invalidate()
notify(ObjectModifiedEvent(self)) notify(ObjectModifiedEvent(self))
...@@ -609,6 +632,18 @@ class File(Persistent, Implicit, PropertyManager, ...@@ -609,6 +632,18 @@ class File(Persistent, Implicit, PropertyManager,
def manage_FTPget(self): def manage_FTPget(self):
"""Return body for ftp.""" """Return body for ftp."""
RESPONSE = self.REQUEST.RESPONSE RESPONSE = self.REQUEST.RESPONSE
if self.ZCacheable_isCachingEnabled():
result = self.ZCacheable_get(default=None)
if result is not None:
# We will always get None from RAMCacheManager but we will
# get something implementing the IStreamIterator interface
# from FileCacheManager.
# the content-length is required here by HTTPResponse,
# even though FTP doesn't use it.
RESPONSE.setHeader('Content-Length', self.size)
return result
data = self.data data = self.data
if isinstance(data, str): if isinstance(data, str):
RESPONSE.setBase(None) RESPONSE.setBase(None)
...@@ -765,7 +800,8 @@ class Image(File): ...@@ -765,7 +800,8 @@ class Image(File):
manage_options = ( manage_options = (
({'label': 'Edit', 'action': 'manage_main'}, ) + ({'label': 'Edit', 'action': 'manage_main'}, ) +
RoleManager.manage_options + RoleManager.manage_options +
Item_w__name__.manage_options Item_w__name__.manage_options +
Cacheable.manage_options
) )
manage_editForm = DTMLFile('dtml/imageEdit', globals(), manage_editForm = DTMLFile('dtml/imageEdit', globals(),
...@@ -803,6 +839,8 @@ class Image(File): ...@@ -803,6 +839,8 @@ class Image(File):
if content_type is not None: if content_type is not None:
self.content_type = content_type self.content_type = content_type
self.ZCacheable_invalidate()
self.ZCacheable_set(None)
self.http__refreshEtag() self.http__refreshEtag()
def __str__(self): def __str__(self):
......
...@@ -4,4 +4,7 @@ ...@@ -4,4 +4,7 @@
<five:deprecatedManageAddDelete <five:deprecatedManageAddDelete
class="OFS.userfolder.BasicUserFolder"/> class="OFS.userfolder.BasicUserFolder"/>
<five:deprecatedManageAddDelete
class="OFS.Cache.CacheManager"/>
</configure> </configure>
import unittest
from OFS.Cache import CacheManager
from OFS.Folder import Folder
from OFS.SimpleItem import SimpleItem
from OFS.metaconfigure import setDeprecatedManageAddDelete
class DummyCacheManager(CacheManager, SimpleItem):
def __init__(self, id, *args, **kw):
self.id = id
setDeprecatedManageAddDelete(DummyCacheManager)
class CacheTests(unittest.TestCase):
def test_managersExist(self):
from OFS.Cache import managersExist
from OFS.DTMLMethod import DTMLMethod
root = Folder('root')
root._setObject('root_cache', DummyCacheManager('root_cache'))
root._setObject('child', Folder('child'))
root.child._setObject('child_cache', DummyCacheManager('child_cache'))
root.child._setObject('child_content', DTMLMethod('child_content'))
# To begin with, cache managers will be found correctly
# using managersExist
self.assertTrue(managersExist(root.child.child_content))
# Now we delete the cache in the child folder
root.child.manage_delObjects(['child_cache'])
# The parent_cache should still trigger managersExist
self.assertTrue(managersExist(root.child.child_content))
...@@ -10,6 +10,8 @@ from io import BytesIO ...@@ -10,6 +10,8 @@ from io import BytesIO
from Acquisition import aq_base from Acquisition import aq_base
from OFS.Application import Application from OFS.Application import Application
from OFS.SimpleItem import SimpleItem
from OFS.Cache import ZCM_MANAGERS
from OFS.Image import Pdata from OFS.Image import Pdata
from ZPublisher.HTTPRequest import HTTPRequest from ZPublisher.HTTPRequest import HTTPRequest
from ZPublisher.HTTPResponse import HTTPResponse from ZPublisher.HTTPResponse import HTTPResponse
...@@ -50,6 +52,41 @@ def aputrequest(file, content_type): ...@@ -50,6 +52,41 @@ def aputrequest(file, content_type):
return req return req
class DummyCache(object):
def __init__(self):
self.clear()
def ZCache_set(self, ob, data, view_name='', keywords=None,
mtime_func=None):
self.set = (ob, data)
def ZCache_get(self, ob, data, view_name='', keywords=None,
mtime_func=None):
self.get = ob
if self.si:
return self.si
def ZCache_invalidate(self, ob):
self.invalidated = ob
def clear(self):
self.set = None
self.get = None
self.invalidated = None
self.si = None
def setStreamIterator(self, si):
self.si = si
ADummyCache = DummyCache()
class DummyCacheManager(SimpleItem):
def ZCacheManager_getCache(self):
return ADummyCache
class EventCatcher(object): class EventCatcher(object):
def __init__(self): def __init__(self):
...@@ -97,9 +134,13 @@ class FileTests(unittest.TestCase): ...@@ -97,9 +134,13 @@ class FileTests(unittest.TestCase):
self.root = a self.root = a
responseOut = self.responseOut = BytesIO() responseOut = self.responseOut = BytesIO()
self.app = makerequest(self.root, stdout=responseOut) self.app = makerequest(self.root, stdout=responseOut)
self.app.dcm = DummyCacheManager()
factory = getattr(self.app, self.factory) factory = getattr(self.app, self.factory)
factory('file', factory('file',
file=self.data, content_type=self.content_type) file=self.data, content_type=self.content_type)
self.app.file.ZCacheable_setManagerId('dcm')
self.app.file.ZCacheable_setEnabled(enabled=1)
setattr(self.app, ZCM_MANAGERS, ('dcm',))
# Hack, we need a _p_mtime for the file, so we make sure that it # Hack, we need a _p_mtime for the file, so we make sure that it
# has one. # has one.
transaction.commit() transaction.commit()
...@@ -128,6 +169,7 @@ class FileTests(unittest.TestCase): ...@@ -128,6 +169,7 @@ class FileTests(unittest.TestCase):
del self.responseOut del self.responseOut
del self.root del self.root
del self.connection del self.connection
ADummyCache.clear()
self.eventCatcher.tearDown() self.eventCatcher.tearDown()
def testViewImageOrFile(self): def testViewImageOrFile(self):
...@@ -137,6 +179,8 @@ class FileTests(unittest.TestCase): ...@@ -137,6 +179,8 @@ class FileTests(unittest.TestCase):
self.file.update_data('foo') self.file.update_data('foo')
self.assertEqual(self.file.size, 3) self.assertEqual(self.file.size, 3)
self.assertEqual(self.file.data, 'foo') self.assertEqual(self.file.data, 'foo')
self.assertTrue(ADummyCache.invalidated)
self.assertTrue(ADummyCache.set)
def testReadData(self): def testReadData(self):
s = "a" * (2 << 16) s = "a" * (2 << 16)
...@@ -161,6 +205,8 @@ class FileTests(unittest.TestCase): ...@@ -161,6 +205,8 @@ class FileTests(unittest.TestCase):
self.file.manage_edit('foobar', 'text/plain', filedata='ASD') self.file.manage_edit('foobar', 'text/plain', filedata='ASD')
self.assertEqual(self.file.title, 'foobar') self.assertEqual(self.file.title, 'foobar')
self.assertEqual(self.file.content_type, 'text/plain') self.assertEqual(self.file.content_type, 'text/plain')
self.assertTrue(ADummyCache.invalidated)
self.assertTrue(ADummyCache.set)
self.assertEqual(1, len(self.eventCatcher.modified)) self.assertEqual(1, len(self.eventCatcher.modified))
self.assertTrue(self.eventCatcher.modified[0].object is self.file) self.assertTrue(self.eventCatcher.modified[0].object is self.file)
...@@ -168,6 +214,7 @@ class FileTests(unittest.TestCase): ...@@ -168,6 +214,7 @@ class FileTests(unittest.TestCase):
self.file.manage_edit('foobar', 'text/plain') self.file.manage_edit('foobar', 'text/plain')
self.assertEqual(self.file.title, 'foobar') self.assertEqual(self.file.title, 'foobar')
self.assertEqual(self.file.content_type, 'text/plain') self.assertEqual(self.file.content_type, 'text/plain')
self.assertTrue(ADummyCache.invalidated)
self.assertEqual(1, len(self.eventCatcher.modified)) self.assertEqual(1, len(self.eventCatcher.modified))
self.assertTrue(self.eventCatcher.modified[0].object is self.file) self.assertTrue(self.eventCatcher.modified[0].object is self.file)
...@@ -256,6 +303,8 @@ class ImageTests(FileTests): ...@@ -256,6 +303,8 @@ class ImageTests(FileTests):
self.assertEqual(self.file.data, self.data) self.assertEqual(self.file.data, self.data)
self.assertEqual(self.file.width, 16) self.assertEqual(self.file.width, 16)
self.assertEqual(self.file.height, 16) self.assertEqual(self.file.height, 16)
self.assertTrue(ADummyCache.invalidated)
self.assertTrue(ADummyCache.set)
def testStr(self): def testStr(self):
self.assertEqual( self.assertEqual(
......
...@@ -88,7 +88,7 @@ class ZopePageTemplate(Script, PageTemplate, Cacheable, ...@@ -88,7 +88,7 @@ class ZopePageTemplate(Script, PageTemplate, Cacheable,
manage_options = ( manage_options = (
{'label': 'Edit', 'action': 'pt_editForm'}, {'label': 'Edit', 'action': 'pt_editForm'},
) + SimpleItem.manage_options ) + SimpleItem.manage_options + Cacheable.manage_options
_properties = ( _properties = (
{'id': 'title', 'type': 'ustring', 'mode': 'w'}, {'id': 'title', 'type': 'ustring', 'mode': 'w'},
...@@ -164,6 +164,7 @@ class ZopePageTemplate(Script, PageTemplate, Cacheable, ...@@ -164,6 +164,7 @@ class ZopePageTemplate(Script, PageTemplate, Cacheable,
if not is_unicode: if not is_unicode:
text = unicode(text, encoding) text = unicode(text, encoding)
self.ZCacheable_invalidate()
super(ZopePageTemplate, self).pt_edit(text, content_type) super(ZopePageTemplate, self).pt_edit(text, content_type)
pt_editForm = PageTemplateFile('www/ptEdit', globals(), pt_editForm = PageTemplateFile('www/ptEdit', globals(),
...@@ -203,6 +204,11 @@ class ZopePageTemplate(Script, PageTemplate, Cacheable, ...@@ -203,6 +204,11 @@ class ZopePageTemplate(Script, PageTemplate, Cacheable,
title = unicode(title, encoding) title = unicode(title, encoding)
self._setPropValue('title', title) self._setPropValue('title', title)
def _setPropValue(self, id, value):
""" set a property and invalidate the cache """
PropertyManager._setPropValue(self, id, value)
self.ZCacheable_invalidate()
security.declareProtected(change_page_templates, 'pt_upload') security.declareProtected(change_page_templates, 'pt_upload')
def pt_upload(self, REQUEST, file='', encoding='utf-8'): def pt_upload(self, REQUEST, file='', encoding='utf-8'):
"""Replace the document with the text in file.""" """Replace the document with the text in file."""
...@@ -248,6 +254,7 @@ class ZopePageTemplate(Script, PageTemplate, Cacheable, ...@@ -248,6 +254,7 @@ class ZopePageTemplate(Script, PageTemplate, Cacheable,
preferred_encodings) preferred_encodings)
self.output_encoding = encoding self.output_encoding = encoding
self.ZCacheable_invalidate()
ZopePageTemplate.inheritedAttribute('write')(self, text) ZopePageTemplate.inheritedAttribute('write')(self, text)
def _exec(self, bound_names, args, kw): def _exec(self, bound_names, args, kw):
...@@ -265,11 +272,25 @@ class ZopePageTemplate(Script, PageTemplate, Cacheable, ...@@ -265,11 +272,25 @@ class ZopePageTemplate(Script, PageTemplate, Cacheable,
security = getSecurityManager() security = getSecurityManager()
bound_names['user'] = security.getUser() bound_names['user'] = security.getUser()
# Retrieve the value from the cache.
keyset = None
if self.ZCacheable_isCachingEnabled():
# Prepare a cache key.
keyset = {'here': self._getContext(),
'bound_names': bound_names}
result = self.ZCacheable_get(keywords=keyset)
if result is not None:
# Got a cached value.
return result
# Execute the template in a new security context. # Execute the template in a new security context.
security.addContext(self) security.addContext(self)
try: try:
result = self.pt_render(extra_context=bound_names) result = self.pt_render(extra_context=bound_names)
if keyset is not None:
# Store the result in the cache.
self.ZCacheable_set(result, keywords=keyset)
return result return result
finally: finally:
security.removeContext(self) security.removeContext(self)
......
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