Commit ddb8cdbc authored by Tres Seaver's avatar Tres Seaver

Collector #1927: Don't save rendered HTML / warnings as persistent attributes.

parent 861287b6
...@@ -26,6 +26,10 @@ Zope Changes ...@@ -26,6 +26,10 @@ Zope Changes
Bugs Fixed Bugs Fixed
- Collector #1927: Modified ZReST not to store rendered / warnings
as persistent attributes, using volatile attributes instead as
a cache.
- Collector #1926: fixed a typo in _doAddUser when password - Collector #1926: fixed a typo in _doAddUser when password
encryption is enabled. encryption is enabled.
......
...@@ -47,6 +47,7 @@ class ZReST(Item, PropertyManager, Historical, Implicit, Persistent): ...@@ -47,6 +47,7 @@ class ZReST(Item, PropertyManager, Historical, Implicit, Persistent):
''' '''
meta_type = 'ReStructuredText Document' meta_type = 'ReStructuredText Document'
security = ClassSecurityInfo() security = ClassSecurityInfo()
_v_formatted = _v_warnings = None
def __init__(self, id,output_encoding=None, def __init__(self, id,output_encoding=None,
input_encoding=None): input_encoding=None):
...@@ -54,7 +55,7 @@ class ZReST(Item, PropertyManager, Historical, Implicit, Persistent): ...@@ -54,7 +55,7 @@ class ZReST(Item, PropertyManager, Historical, Implicit, Persistent):
self.title = id self.title = id
self.stylesheet = 'default.css' self.stylesheet = 'default.css'
self.report_level = '2' self.report_level = '2'
self.source = self.formatted = '' self.source = ''
from reStructuredText import default_output_encoding, \ from reStructuredText import default_output_encoding, \
default_input_encoding default_input_encoding
...@@ -89,7 +90,7 @@ class ZReST(Item, PropertyManager, Historical, Implicit, Persistent): ...@@ -89,7 +90,7 @@ class ZReST(Item, PropertyManager, Historical, Implicit, Persistent):
''' '''
if REQUEST is not None: if REQUEST is not None:
REQUEST.RESPONSE.setHeader('content-type', 'text/html; charset=%s' % self.output_encoding) REQUEST.RESPONSE.setHeader('content-type', 'text/html; charset=%s' % self.output_encoding)
return self.formatted return self.render()
security.declareProtected('View', 'source_txt') security.declareProtected('View', 'source_txt')
def source_txt(self, REQUEST=None): def source_txt(self, REQUEST=None):
...@@ -113,7 +114,7 @@ class ZReST(Item, PropertyManager, Historical, Implicit, Persistent): ...@@ -113,7 +114,7 @@ class ZReST(Item, PropertyManager, Historical, Implicit, Persistent):
return self._er(data, SUBMIT, dtpref_cols, dtpref_rows, REQUEST) return self._er(data, SUBMIT, dtpref_cols, dtpref_rows, REQUEST)
if data != self.source: if data != self.source:
self.source = data self.source = data
self.render() self._clear_cache()
if REQUEST is not None: if REQUEST is not None:
message="Saved changes." message="Saved changes."
...@@ -142,6 +143,7 @@ class ZReST(Item, PropertyManager, Historical, Implicit, Persistent): ...@@ -142,6 +143,7 @@ class ZReST(Item, PropertyManager, Historical, Implicit, Persistent):
setCookie("dtpref_cols", cols, path='/', expires=e) setCookie("dtpref_cols", cols, path='/', expires=e)
REQUEST.other.update({"dtpref_cols":cols, "dtpref_rows":rows}) REQUEST.other.update({"dtpref_cols":cols, "dtpref_rows":rows})
return self.manage_main(self, REQUEST, __str__=self.quotedHTML(data)) return self.manage_main(self, REQUEST, __str__=self.quotedHTML(data))
security.declarePrivate('quotedHTML') security.declarePrivate('quotedHTML')
def quotedHTML(self, def quotedHTML(self,
text=None, text=None,
...@@ -155,6 +157,18 @@ class ZReST(Item, PropertyManager, Historical, Implicit, Persistent): ...@@ -155,6 +157,18 @@ class ZReST(Item, PropertyManager, Historical, Implicit, Persistent):
if text.find(re) >= 0: text=name.join(text.split(re)) if text.find(re) >= 0: text=name.join(text.split(re))
return text return text
security.declarePrivate('_clear_cache')
def _clear_cache(self):
""" Forget results of rendering.
"""
try:
del self._v_formatted
except AttributeError:
pass
try:
del self._v_warnings
except AttributeError:
pass
# handle uploads too # handle uploads too
security.declareProtected('Edit ReStructuredText', 'manage_upload') security.declareProtected('Edit ReStructuredText', 'manage_upload')
...@@ -165,7 +179,7 @@ class ZReST(Item, PropertyManager, Historical, Implicit, Persistent): ...@@ -165,7 +179,7 @@ class ZReST(Item, PropertyManager, Historical, Implicit, Persistent):
self.source = file self.source = file
else: else:
self.source = file.read() self.source = file.read()
self.render() self._clear_cache()
if REQUEST is not None: if REQUEST is not None:
message="Saved changes." message="Saved changes."
...@@ -175,55 +189,58 @@ class ZReST(Item, PropertyManager, Historical, Implicit, Persistent): ...@@ -175,55 +189,58 @@ class ZReST(Item, PropertyManager, Historical, Implicit, Persistent):
def render(self): def render(self):
''' Render the source to HTML ''' Render the source to HTML
''' '''
# format with strings if self._v_formatted is None:
pub = docutils.core.Publisher() # format with strings
pub.set_reader('standalone', None, 'restructuredtext') pub = docutils.core.Publisher()
pub.set_writer('html') pub.set_reader('standalone', None, 'restructuredtext')
pub.set_writer('html')
# go with the defaults
pub.get_settings()
# go with the defaults # this is needed, but doesn't seem to do anything
pub.get_settings() pub.settings._destination = ''
# this is needed, but doesn't seem to do anything # use the stylesheet chosen by the user
pub.settings._destination = '' pub.settings.stylesheet = self.stylesheet
# use the stylesheet chosen by the user # set the reporting level to something sane
pub.settings.stylesheet = self.stylesheet pub.settings.report_level = int(self.report_level)
# set the reporting level to something sane # disallow use of the .. include directive for security reasons
pub.settings.report_level = int(self.report_level) pub.settings.file_insertion_enabled = 0
# disallow use of the .. include directive for security reasons # don't break if we get errors
pub.settings.file_insertion_enabled = 0 pub.settings.halt_level = 6
# don't break if we get errors # remember warnings
pub.settings.halt_level = 6 pub.settings.warning_stream = Warnings()
# remember warnings pub.source = docutils.io.StringInput(
pub.settings.warning_stream = Warnings() source=self.source, encoding=self.input_encoding)
pub.source = docutils.io.StringInput( # output - not that it's needed
source=self.source, encoding=self.input_encoding) pub.settings.output_encoding = self.output_encoding
pub.destination = docutils.io.StringOutput(
encoding=self.output_encoding)
# output - not that it's needed # parse!
pub.settings.output_encoding = self.output_encoding document = pub.reader.read(pub.source, pub.parser, pub.settings)
pub.destination = docutils.io.StringOutput(
encoding=self.output_encoding)
# parse! # transform
document = pub.reader.read(pub.source, pub.parser, pub.settings) pub.apply_transforms(document)
# transform self._v_warnings = ''.join(pub.settings.warning_stream.messages)
pub.apply_transforms(document)
self.warnings = ''.join(pub.settings.warning_stream.messages) if document.children:
item = document.children[0]
if item.tagname == 'title':
self.title = item.children[0].astext()
if document.children: # do the format
item = document.children[0] self._v_formatted = pub.writer.write(document, pub.destination)
if item.tagname == 'title':
self.title = item.children[0].astext()
# do the format return self._v_formatted
self.formatted = pub.writer.write(document, pub.destination)
security.declareProtected('Edit ReStructuredText', 'PUT', 'manage_FTPput') security.declareProtected('Edit ReStructuredText', 'PUT', 'manage_FTPput')
...@@ -247,7 +264,6 @@ class ZReST(Item, PropertyManager, Historical, Implicit, Persistent): ...@@ -247,7 +264,6 @@ class ZReST(Item, PropertyManager, Historical, Implicit, Persistent):
pass # ignore pass # ignore
data = '\n'.join(new) + '\n'.join(data[i:]) data = '\n'.join(new) + '\n'.join(data[i:])
self.source = data self.source = data
self.render()
RESPONSE.setStatus(204) RESPONSE.setStatus(204)
return RESPONSE return RESPONSE
...@@ -263,9 +279,9 @@ class ZReST(Item, PropertyManager, Historical, Implicit, Persistent): ...@@ -263,9 +279,9 @@ class ZReST(Item, PropertyManager, Historical, Implicit, Persistent):
'.. stylesheet='+self.stylesheet, '.. stylesheet='+self.stylesheet,
'.. report_level='+self.report_level '.. report_level='+self.report_level
] ]
if self.warnings: if self._v_warnings:
s.append('.. ') s.append('.. ')
s.append('.. ' + '\n.. '.join(self.warnings.splitlines())) s.append('.. ' + '\n.. '.join(self._v_warnings.splitlines()))
s.append('.. ') s.append('.. ')
return '\n'.join(s) + '\n' + self.source return '\n'.join(s) + '\n' + self.source
...@@ -290,7 +306,7 @@ class ZReST(Item, PropertyManager, Historical, Implicit, Persistent): ...@@ -290,7 +306,7 @@ class ZReST(Item, PropertyManager, Historical, Implicit, Persistent):
def manage_editProperties(self, REQUEST): def manage_editProperties(self, REQUEST):
""" re-render the page after changing the properties (encodings!!!) """ """ re-render the page after changing the properties (encodings!!!) """
result = PropertyManager.manage_editProperties(self, REQUEST) result = PropertyManager.manage_editProperties(self, REQUEST)
self.render() self._clear_cache()
return result return result
......
""" Unit tests for ZReST product.
$Id$
"""
""" Unit tests for ZReST objects
$Id$
"""
import unittest
class TestZReST(unittest.TestCase):
def _getTargetClass(self):
from Products.ZReST.ZReST import ZReST
return ZReST
def _makeOne(self, id='test', *args, **kw):
return self._getTargetClass()(id=id, *args, **kw)
def test_empty(self):
empty = self._makeOne()
# New instances should not have non-volatile cache attributes
self.assertRaises(AttributeError, lambda: empty.formatted)
self.assertRaises(AttributeError, lambda: empty.warnings)
self.assertEqual(empty._v_formatted, None)
self.assertEqual(empty._v_formatted, None)
def test_formatted_ignored(self):
resty = self._makeOne()
resty.formatted = 'IGNORE ME'
self.failIf('IGNORE ME' in resty.index_html())
def test_suite():
suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(TestZReST))
return suite
if __name__ == '__main__':
unittest.main(defaultSuite='test_suite')
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