[CMF-checkins] SVN: CMF/trunk/C - added 'email_charset' property
and 'makeEmail' function
Yvo Schubbe
y.2006_ at wcm-solutions.de
Sun Oct 15 10:49:23 EDT 2006
Log message for revision 70654:
- added 'email_charset' property and 'makeEmail' function
- fixed encoding issues in registered_email and password_email
Changed:
U CMF/trunk/CHANGES.txt
U CMF/trunk/CMFDefault/profiles/default/properties.xml
U CMF/trunk/CMFDefault/skins/zpt_control/portal_config_control.py
U CMF/trunk/CMFDefault/skins/zpt_generic/password_email.py
U CMF/trunk/CMFDefault/skins/zpt_generic/password_email_template.pt
U CMF/trunk/CMFDefault/skins/zpt_generic/reconfig_form.py
U CMF/trunk/CMFDefault/skins/zpt_generic/reconfig_template.pt
U CMF/trunk/CMFDefault/skins/zpt_generic/registered_email.py
U CMF/trunk/CMFDefault/skins/zpt_generic/registered_email_template.pt
U CMF/trunk/CMFDefault/tests/RegistrationTool.txt
U CMF/trunk/CMFDefault/tests/test_RegistrationTool.py
U CMF/trunk/CMFDefault/utils.py
-=-
Modified: CMF/trunk/CHANGES.txt
===================================================================
--- CMF/trunk/CHANGES.txt 2006-10-15 14:43:52 UTC (rev 70653)
+++ CMF/trunk/CHANGES.txt 2006-10-15 14:49:21 UTC (rev 70654)
@@ -2,12 +2,22 @@
New Features
+ - Portal: Added 'email_charset' property.
+
+ - CMFDefault utils: Added 'makeEmail' function.
+
- CMFDefault.Image and CMFDefault.File: Overridden index_html methods
add Cache Policy Manager-awareness and thus bring these implementations
in line with CMFCore.FSFile and CMFCore.FSImage
(http://www.zope.org/Collectors/CMF/454)
+ Bug Fixes
+ - CMFDefault skins: Fixed encoding issues in welcome and reminder emails.
+ 'password_email' and 'registered_email' now encode their return value
+ correctly, using 'email_charset' and the new 'makeEmail' function.
+
+
CMF 2.1.0-alpha (2006/10/09)
New Features
Modified: CMF/trunk/CMFDefault/profiles/default/properties.xml
===================================================================
--- CMF/trunk/CMFDefault/profiles/default/properties.xml 2006-10-15 14:43:52 UTC (rev 70653)
+++ CMF/trunk/CMFDefault/profiles/default/properties.xml 2006-10-15 14:49:21 UTC (rev 70654)
@@ -8,5 +8,6 @@
type="string">Portal Administrator</property>
<property name="validate_email" type="boolean">False</property>
<property name="default_charset" type="string">utf-8</property>
+ <property name="email_charset" type="string">iso-8859-1</property>
<property name="enable_permalink" type="boolean">False</property>
</site>
Modified: CMF/trunk/CMFDefault/skins/zpt_control/portal_config_control.py
===================================================================
--- CMF/trunk/CMFDefault/skins/zpt_control/portal_config_control.py 2006-10-15 14:43:52 UTC (rev 70653)
+++ CMF/trunk/CMFDefault/skins/zpt_control/portal_config_control.py 2006-10-15 14:49:21 UTC (rev 70654)
@@ -7,6 +7,8 @@
if not ptool.hasProperty('default_charset'):
ptool.manage_addProperty('default_charset', '', 'string')
+if not ptool.hasProperty('email_charset'):
+ ptool.manage_addProperty('email_charset', '', 'string')
ptool.editProperties(kw)
return context.setStatus(True, _(u'CMF Settings changed.'))
Modified: CMF/trunk/CMFDefault/skins/zpt_generic/password_email.py
===================================================================
--- CMF/trunk/CMFDefault/skins/zpt_generic/password_email.py 2006-10-15 14:43:52 UTC (rev 70653)
+++ CMF/trunk/CMFDefault/skins/zpt_generic/password_email.py 2006-10-15 14:49:21 UTC (rev 70654)
@@ -1,29 +1,25 @@
-##parameters=member=None, password='baz'
+##parameters=member=None, password='secret'
##
from Products.CMFCore.utils import getToolByName
from Products.CMFDefault.utils import decode
+from Products.CMFDefault.utils import makeEmail
+from Products.CMFDefault.utils import Message as _
atool = getToolByName(script, 'portal_actions')
ptool = getToolByName(script, 'portal_properties')
utool = getToolByName(script, 'portal_url')
-default_charset = ptool.getProperty('default_charset')
portal_url = utool()
options = {}
-
-email_from_name = ptool.getProperty('email_from_name')
-email_from_address = ptool.getProperty('email_from_address')
-options['portal_address'] = '%s <%s>' % (email_from_name, email_from_address)
-member_address = member and member.email or 'foo at example.org'
-options['member_address'] = '<%s>' % member_address
-options['content_type'] = 'text/plain; charset=%s' % default_charset
-
-options['portal_title'] = ptool.title()
options['password'] = password
-rendered = context.password_email_template(**decode(options, script))
-if isinstance(rendered, unicode):
- return rendered.encode(default_charset)
-else:
- return rendered
+headers = {}
+headers['Subject'] = _(u'${portal_title}: Membership reminder',
+ mapping={'portal_title': decode(ptool.title(), script)})
+headers['From'] = '%s <%s>' % (ptool.getProperty('email_from_name'),
+ ptool.getProperty('email_from_address'))
+headers['To'] = '<%s>' % (member and member.email or 'foo at example.org')
+
+mtext = context.password_email_template(**decode(options, script))
+return makeEmail(mtext, script, headers)
Modified: CMF/trunk/CMFDefault/skins/zpt_generic/password_email_template.pt
===================================================================
--- CMF/trunk/CMFDefault/skins/zpt_generic/password_email_template.pt 2006-10-15 14:43:52 UTC (rev 70653)
+++ CMF/trunk/CMFDefault/skins/zpt_generic/password_email_template.pt 2006-10-15 14:49:21 UTC (rev 70654)
@@ -1,11 +1,5 @@
<tal:page i18n:domain="cmf_default"
->Subject: <tal:span i18n:translate=""><tal:span i18n:name="portal_title"
- tal:content="options/portal_title" />: Membership reminder</tal:span>
-From: <tal:span tal:replace="structure options/portal_address" />
-To: <tal:span tal:replace="structure options/member_address" />
-Content-Type: <tal:span tal:replace="structure options/content_type" />
-
-<tal:span i18n:translate="">Your password: <tal:span i18n:name="password"
+><tal:span i18n:translate="">Your password: <tal:span i18n:name="password"
tal:content="options/password | default">baz</tal:span></tal:span>
<tal:span i18n:translate="">Request made by IP <tal:span i18n:name="ip"
Modified: CMF/trunk/CMFDefault/skins/zpt_generic/reconfig_form.py
===================================================================
--- CMF/trunk/CMFDefault/skins/zpt_generic/reconfig_form.py 2006-10-15 14:43:52 UTC (rev 70653)
+++ CMF/trunk/CMFDefault/skins/zpt_generic/reconfig_form.py 2006-10-15 14:49:21 UTC (rev 70654)
@@ -30,6 +30,7 @@
'validate_email': ptool.getProperty('validate_email'),
'default_charset':
ptool.getProperty('default_charset', ''),
+ 'email_charset': ptool.getProperty('email_charset', ''),
'listButtonInfos': tuple(buttons) }
return context.reconfig_template(**decode(options, script))
Modified: CMF/trunk/CMFDefault/skins/zpt_generic/reconfig_template.pt
===================================================================
--- CMF/trunk/CMFDefault/skins/zpt_generic/reconfig_template.pt 2006-10-15 14:43:52 UTC (rev 70653)
+++ CMF/trunk/CMFDefault/skins/zpt_generic/reconfig_template.pt 2006-10-15 14:49:21 UTC (rev 70654)
@@ -94,6 +94,17 @@
</td>
</tr>
<tr>
+ <th i18n:translate="">Portal email encoding</th>
+ <td>
+ <input name="email_charset" value=""
+ tal:attributes="value form/email_charset" />
+ <dl class="FieldHelp">
+ <dd i18n:translate="">Charset used to encode emails send by the portal.
+ If empty, 'utf-8' is used if necessary.</dd>
+ </dl>
+ </td>
+ </tr>
+ <tr>
<td> </td>
<td>
<metal:macro metal:use-macro="context/form_widgets/macros/buttons" />
Modified: CMF/trunk/CMFDefault/skins/zpt_generic/registered_email.py
===================================================================
--- CMF/trunk/CMFDefault/skins/zpt_generic/registered_email.py 2006-10-15 14:43:52 UTC (rev 70653)
+++ CMF/trunk/CMFDefault/skins/zpt_generic/registered_email.py 2006-10-15 14:49:21 UTC (rev 70654)
@@ -1,23 +1,18 @@
-##parameters=member=None, password='baz', email='foo at example.org'
+##parameters=member=None, password='secret', email='foo at example.org'
##
from Products.CMFCore.utils import getToolByName
from Products.CMFDefault.utils import decode
+from Products.CMFDefault.utils import makeEmail
+from Products.CMFDefault.utils import Message as _
atool = getToolByName(script, 'portal_actions')
ptool = getToolByName(script, 'portal_properties')
utool = getToolByName(script, 'portal_url')
-default_charset = ptool.getProperty('default_charset')
portal_url = utool()
options = {}
-email_from_name = ptool.getProperty('email_from_name')
-email_from_address = ptool.getProperty('email_from_address')
-options['portal_address'] = '%s <%s>' % (email_from_name, email_from_address)
-options['member_address'] = '<%s>' % email
-options['content_type'] = 'text/plain; charset=%s' % default_charset
-
options['portal_title'] = ptool.title()
options['portal_description'] = ptool.getProperty('description')
options['portal_url'] = portal_url
@@ -28,10 +23,16 @@
target = atool.getActionInfo('user/login')['url']
options['login_url'] = '%s' % target
+
+email_from_name = ptool.getProperty('email_from_name')
options['signature'] = email_from_name
-rendered = context.registered_email_template(**decode(options, script))
-if isinstance(rendered, unicode):
- return rendered.encode(default_charset)
-else:
- return rendered
+headers = {}
+headers['Subject'] = _(u'${portal_title}: Your Membership Information',
+ mapping={'portal_title': decode(ptool.title(), script)})
+headers['From'] = '%s <%s>' % (email_from_name,
+ ptool.getProperty('email_from_address'))
+headers['To'] = '<%s>' % email
+
+mtext = context.registered_email_template(**decode(options, script))
+return makeEmail(mtext, script, headers)
Modified: CMF/trunk/CMFDefault/skins/zpt_generic/registered_email_template.pt
===================================================================
--- CMF/trunk/CMFDefault/skins/zpt_generic/registered_email_template.pt 2006-10-15 14:43:52 UTC (rev 70653)
+++ CMF/trunk/CMFDefault/skins/zpt_generic/registered_email_template.pt 2006-10-15 14:49:21 UTC (rev 70654)
@@ -1,12 +1,5 @@
<tal:page i18n:domain="cmf_default"
->Subject: <tal:span i18n:translate=""><tal:span i18n:name="portal_title"
- tal:content="options/portal_title"
- />: Your Membership Information</tal:span>
-From: <tal:span tal:replace="structure options/portal_address" />
-To: <tal:span tal:replace="structure options/member_address" />
-Content-Type: <tal:span tal:replace="structure options/content_type" />
-
-<tal:span i18n:translate=""
+><tal:span i18n:translate=""
>You have been registered as a member of "<tal:span i18n:name="portal_title"
tal:content="options/portal_title" />", which
allows you to personalize your view of the website and participate in the
Modified: CMF/trunk/CMFDefault/tests/RegistrationTool.txt
===================================================================
--- CMF/trunk/CMFDefault/tests/RegistrationTool.txt 2006-10-15 14:43:52 UTC (rev 70653)
+++ CMF/trunk/CMFDefault/tests/RegistrationTool.txt 2006-10-15 14:49:21 UTC (rev 70654)
@@ -1,4 +1,5 @@
RegistrationTool
+----------------
First we need some dummy code::
@@ -46,3 +47,164 @@
>>> rtool.MailHost.lastMessage
'Welcome: foo, secret, foo at example.org'
+
+password_email and registered_email
+-----------------------------------
+
+ First we need some dummy code::
+
+ >>> from os.path import join
+ >>> from OFS.Folder import Folder
+ >>> from Products.PageTemplates.ZopePageTemplate import ZopePageTemplate
+ >>> from Products.PythonScripts.PythonScript import PythonScript
+ >>> class DummySite(Folder):
+ ... def getPhysicalRoot(self): return self
+ ... def getPhysicalPath(self): return ('root',)
+ ... def addResource(self, dir, f_name):
+ ... r_name, f_type = f_name.split('.')
+ ... if f_type == 'pt': r = ZopePageTemplate(r_name)
+ ... if f_type == 'py': r = PythonScript(r_name)
+ ... f = file(join(dir, f_name), 'r')
+ ... r.write(f.read())
+ ... f.close()
+ ... setattr(self, r_name, r)
+
+ >>> class DummyTool(Folder):
+ ... def getActionInfo(self, action_chain): return self.login_action
+ ... def getProperty(self, id, d=None): return getattr(self, id, d)
+ ... def title(self): return self.Title
+ ... def __call__(self): return self.url
+
+ >>> from zope import interface
+ >>> from zope.i18n.interfaces import INegotiator
+ >>> class Negotiator:
+ ... interface.implements(INegotiator)
+ ... def getLanguage(*ignored): return 'test'
+
+ >>> from zope.i18n.testmessagecatalog import TestMessageFallbackDomain
+ >>> class DummyFallbackTranslationService:
+ ... def translate(self, domain, msgid, mapping, context,
+ ... target_language, default):
+ ... util = TestMessageFallbackDomain(domain)
+ ... return util.translate(msgid, mapping, context,
+ ... target_language, default)
+
+ And have to set up security and dummy translations::
+
+ >>> from AccessControl.SecurityManagement import newSecurityManager
+ >>> from AccessControl.User import UnrestrictedUser
+ >>> newSecurityManager(None, UnrestrictedUser('god', '', ['Manager'], ''))
+
+ >>> from zope import component
+ >>> component.provideUtility(Negotiator())
+ >>> from Products.Five import i18n
+ >>> old_fallback_translation_service = i18n._fallback_translation_service
+ >>> i18n._fallback_translation_service = DummyFallbackTranslationService()
+
+ Now we can set up password_email and registered_email with dummy context::
+
+ >>> from Testing.makerequest import makerequest
+ >>> s = makerequest(DummySite())
+ >>> s.REQUEST.environ['HTTP_X_FORWARDED_FOR'] = 'NNN.NNN.NNN.NNN'
+ >>> s.portal_actions = s.portal_properties = s.portal_url = DummyTool()
+ >>> s.ZopeTime = 'NNNN/NN/NN'
+ >>> s.description = 'THE SITE DESCRIPTION.'
+ >>> s.default_charset = 'utf-8'
+ >>> s.email_from_name = u'WEBMASTER \xc4\xd6\xdc'.encode('utf-8')
+ >>> s.email_from_address = 'WEBMASTER at EXAMPLE.ORG'
+ >>> s.Title = 'WWW.EXAMPLE.ORG'
+ >>> s.url = 'PORTAL_URL'
+ >>> s.login_action = {'url': 'LOGIN_URL'}
+
+ >>> from os.path import dirname
+ >>> from Products import CMFDefault
+ >>> dir = join(dirname(CMFDefault.__file__), 'skins', 'zpt_generic')
+ >>> s.addResource(dir, 'password_email.py')
+ >>> s.addResource(dir, 'password_email_template.pt')
+ >>> s.addResource(dir, 'registered_email.py')
+ >>> s.addResource(dir, 'registered_email_template.pt')
+
+ password_email creates a complete reminder email::
+
+ >>> s.email_charset = 'iso-8859-1'
+ >>> print s.password_email()
+ Content-Type: text/plain; charset="us-ascii"
+ MIME-Version: 1.0
+ Content-Transfer-Encoding: 7bit
+ To: <foo at example.org>
+ From: WEBMASTER =?iso-8859-1?q?=C4=D6=DC?= <WEBMASTER at EXAMPLE.ORG>
+ Subject: [[cmf_default][WWW.EXAMPLE.ORG: Membership reminder]]
+ <BLANKLINE>
+ [[cmf_default][Your password: secret]]
+ <BLANKLINE>
+ [[cmf_default][Request made by IP NNN.NNN.NNN.NNN at NNNN/NN/NN]]
+ <BLANKLINE>
+ <BLANKLINE>
+
+ >>> s.email_charset = ''
+ >>> print s.password_email()
+ Content-Type: text/plain; charset="us-ascii"
+ MIME-Version: 1.0
+ Content-Transfer-Encoding: 7bit
+ To: <foo at example.org>
+ From: WEBMASTER =?utf-8?b?w4TDlsOc?= <WEBMASTER at EXAMPLE.ORG>
+ Subject: [[cmf_default][WWW.EXAMPLE.ORG: Membership reminder]]
+ <BLANKLINE>
+ [[cmf_default][Your password: secret]]
+ <BLANKLINE>
+ [[cmf_default][Request made by IP NNN.NNN.NNN.NNN at NNNN/NN/NN]]
+ <BLANKLINE>
+ <BLANKLINE>
+
+ registered_email creates a complete welcome email::
+
+ >>> s.email_charset = 'iso-8859-1'
+ >>> print s.registered_email()
+ Content-Type: text/plain; charset="iso-8859-1"
+ MIME-Version: 1.0
+ Content-Transfer-Encoding: quoted-printable
+ To: <foo at example.org>
+ From: WEBMASTER =?iso-8859-1?q?=C4=D6=DC?= <WEBMASTER at EXAMPLE.ORG>
+ Subject: [[cmf_default][WWW.EXAMPLE.ORG: Your Membership Information]]
+ <BLANKLINE>
+ [[cmf_default][You have been ... (You have been ...)]]
+ <BLANKLINE>
+ [[cmf_default][This describes the purpose of the website:]]
+ <BLANKLINE>
+ THE SITE DESCRIPTION.
+ <BLANKLINE>
+ [[cmf_default][Visit us at PORTAL_URL]]
+ <BLANKLINE>
+ [[cmf_default][Here is your login data (mind upper and lower case):]]
+ <BLANKLINE>
+ [[cmf_default][Member ID]]: foo
+ [[cmf_default][Password]]: secret
+ <BLANKLINE>
+ [[cmf_default][You can use this URL to log in:]]
+ <BLANKLINE>
+ LOGIN_URL
+ <BLANKLINE>
+ <BLANKLINE>
+ WEBMASTER =C4=D6=DC
+ <BLANKLINE>
+ <BLANKLINE>
+
+ >>> s.email_charset = ''
+ >>> print s.registered_email()
+ Content-Type: text/plain; charset="utf-8"
+ MIME-Version: 1.0
+ Content-Transfer-Encoding: base64
+ To: <foo at example.org>
+ From: WEBMASTER =?utf-8?b?w4TDlsOc?= <WEBMASTER at EXAMPLE.ORG>
+ Subject: [[cmf_default][WWW.EXAMPLE.ORG: Your Membership Information]]
+ <BLANKLINE>
+ W1tj...
+ <BLANKLINE>
+
+ Finally we have to clean up::
+
+ >>> i18n._fallback_translation_service = old_fallback_translation_service
+ >>> from AccessControl.SecurityManagement import noSecurityManager
+ >>> noSecurityManager()
+ >>> from zope.testing.cleanup import cleanUp
+ >>> cleanUp()
Modified: CMF/trunk/CMFDefault/tests/test_RegistrationTool.py
===================================================================
--- CMF/trunk/CMFDefault/tests/test_RegistrationTool.py 2006-10-15 14:43:52 UTC (rev 70653)
+++ CMF/trunk/CMFDefault/tests/test_RegistrationTool.py 2006-10-15 14:49:21 UTC (rev 70654)
@@ -140,7 +140,7 @@
return unittest.TestSuite((
unittest.makeSuite(RegistrationToolTests),
doctest.DocFileSuite('RegistrationTool.txt',
- optionflags=doctest.NORMALIZE_WHITESPACE),
+ optionflags=(doctest.ELLIPSIS | doctest.NORMALIZE_WHITESPACE)),
))
if __name__ == '__main__':
Modified: CMF/trunk/CMFDefault/utils.py
===================================================================
--- CMF/trunk/CMFDefault/utils.py 2006-10-15 14:43:52 UTC (rev 70653)
+++ CMF/trunk/CMFDefault/utils.py 2006-10-15 14:49:21 UTC (rev 70654)
@@ -19,6 +19,8 @@
import re
import StringIO
import rfc822
+from email.Header import make_header
+from email.MIMEText import MIMEText
from sgmllib import SGMLParser
from AccessControl import ModuleSecurityInfo
@@ -26,6 +28,7 @@
from Products.PageTemplates.GlobalTranslationService \
import getGlobalTranslationService
from ZTUtils.Zope import complex_marshal
+from zope import i18n
from zope.i18n.interfaces import IUserPreferredCharsets
from zope.i18nmessageid import MessageFactory
@@ -466,5 +469,24 @@
charsets = envadapter.getPreferredCharsets() or ['utf-8']
return charsets[0]
+security.declarePublic('makeEmail')
+def makeEmail(mtext, context, headers={}):
+ """ Make email message.
+ """
+ ptool = getToolByName(context, 'portal_properties')
+ email_charset = ptool.getProperty('email_charset', None) or 'utf-8'
+ try:
+ msg = MIMEText(mtext.encode(), 'plain')
+ except UnicodeEncodeError:
+ msg = MIMEText(mtext.encode(email_charset), 'plain', email_charset)
+ for k, val in headers.items():
+ if isinstance(val, str):
+ val = decode(val, context)
+ if isinstance(val, i18n.Message):
+ val = translate(val, context)
+ header = make_header([ (w, email_charset) for w in val.split(' ') ])
+ msg[k] = str(header)
+ return msg.as_string()
+
security.declarePublic('Message')
Message = _ = MessageFactory('cmf_default')
More information about the CMF-checkins
mailing list