diff --git a/product/ERP5/Document/TextDocument.py b/product/ERP5/Document/TextDocument.py index 7a96f257ad2b6979a43c1d6c556484fe7016d265..f3a97fbc55e3e789fbfed255d072bfbf9cf315fb 100644 --- a/product/ERP5/Document/TextDocument.py +++ b/product/ERP5/Document/TextDocument.py @@ -26,6 +26,7 @@ # ############################################################################## +from AccessControl.ZopeGuards import guarded_getattr from AccessControl import ClassSecurityInfo from zLOG import LOG, WARNING from Products.ERP5Type.Base import WorkflowMethod @@ -37,6 +38,11 @@ from Products.ERP5Type.WebDAVSupport import TextContent from Products.CMFDefault.utils import isHTMLSafe import re +try: + from string import Template +except ImportError: + from Products.ERP5Type.patches.string import Template + DEFAULT_TEXT_FORMAT = 'text/html' class TextDocument(Document, TextContent): @@ -162,6 +168,26 @@ class TextDocument(Document, TextContent): # check if document has set text_content and convert if necessary text_content = self.getTextContent() if text_content is not None: + # If a method for string substitutions of the text content, perform it. + # Decode everything into unicode before the substitutions, in order to + # avoid encoding errors. + method_id = self.getTextContentSubstitutionMappingMethodId() + if method_id: + mapping = guarded_getattr(self, method_id)() + + if isinstance(text_content, str): + text_content = text_content.decode('utf-8') + + unicode_mapping = {} + for k, v in mapping.iteritems(): + if isinstance(v, str): + v = v.decode('utf-8') + elif not isinstance(v, unicode): + v = str(v).decode('utf-8') + unicode_mapping[k] = v + + text_content = Template(text_content).substitute(unicode_mapping) + portal_transforms = getToolByName(self, 'portal_transforms') result = portal_transforms.convertToData(mime_type, text_content, object=self, context=self, diff --git a/product/ERP5/PropertySheet/TextDocument.py b/product/ERP5/PropertySheet/TextDocument.py index 201f16ba54366ae92bd62749b5366d9ba57eb495..8bb4bc68f40f25fae142c9be7cd274877b92cadf 100644 --- a/product/ERP5/PropertySheet/TextDocument.py +++ b/product/ERP5/PropertySheet/TextDocument.py @@ -37,6 +37,11 @@ class TextDocument: 'type' : 'text', 'mode' : 'w' }, + { 'id' : 'text_content_substitution_mapping_method_id', + 'description' : 'The method ID which returns a mapping for substitutions of a text content', + 'type' : 'string', + 'mode' : 'w' + }, { 'id' : 'text_format', 'description' : 'The format of the text content of this document', 'type' : 'string', diff --git a/product/ERP5/tests/testERP5Web.py b/product/ERP5/tests/testERP5Web.py index b6a370f4baef0b579738f9d5a59acdb384896aea..3db9963f2bd3cade4fd54e56c4854a629e0f6a31 100644 --- a/product/ERP5/tests/testERP5Web.py +++ b/product/ERP5/tests/testERP5Web.py @@ -362,6 +362,44 @@ class TestERP5Web(ERP5TypeTestCase, ZopeTestCase.Functional): self.logout() self.assertRaises(Unauthorized, websection._getExtensibleContent, request, document_reference) + def test_07_WebPageTextContentSubstituions(self, quiet=quiet, run=run_all_test): + """ + Simple Case of showing the proper text content with and without a substitution + mapping method. + """ + if not run: + return + if not quiet: + message = '\ntest_07_WebPageTextContentSubstituions' + ZopeTestCase._print(message) + + content = '<a href="${toto}">$titi</a>' + substituted_content = '<a href="foo">bar</a>' + mapping = dict(toto='foo', titi='bar') + + portal = self.getPortal() + document = portal.web_page_module.newContent(portal_type='Web Page', + text_content=content) + + # No substitution should occur. + self.assertEquals(document.asStrippedHTML(), content) + + klass = document.__class__ + klass.getTestSubstitutionMapping = lambda self, **kw: mapping + document.setTextContentSubstitutionMappingMethodId('getTestSubstitutionMapping') + + # Substitutions should occur. + # XXX purge transformation cache. + if hasattr(document, '_v_transform_cache'): + delattr(document, '_v_transform_cache') + self.assertEquals(document.asStrippedHTML(), substituted_content) + + klass._getTestSubstitutionMapping = klass.getTestSubstitutionMapping + document.setTextContentSubstitutionMappingMethodId('_getTestSubstitutionMapping') + + # Even with the same callable object, a restricted method id should not be callable. + self.assertRaises(Unauthorized, document.asStrippedHTML) + def test_suite(): suite = unittest.TestSuite() suite.addTest(unittest.makeSuite(TestERP5Web))