[gen] Pod field: send pod results by emails.

This commit is contained in:
Gaetan Delannay 2014-09-17 18:09:30 +02:00
parent b565f38d97
commit 4947e2956c
13 changed files with 176 additions and 32 deletions

View file

@ -124,8 +124,15 @@ class Pod(Field):
<x if="hasMailings" var2="sendLabel=_('email_send')"> <x if="hasMailings" var2="sendLabel=_('email_send')">
<tr for="mailing in mailings[fmt]" valign="top" <tr for="mailing in mailings[fmt]" valign="top"
var2="mailingName=field.getMailingName(obj, mailing)"> var2="mailingName=field.getMailingName(obj, mailing)">
<td width="85px"><span title=":sendLabel">:mailingName</span></td> <td colspan="2">
<td width="15px"><img src=":url('email')"/></td> <a var="js='generatePod(%s,%s,%s,%s,%s,null,null,%s)' % \
(q(uid), q(name), q(info.template), q(fmt), \
q(ztool.getQueryInfo()), q(mailing))"
onclick=":'askConfirm(%s,%s)' % (q('script'), q(js, False))"
title=":sendLabel">
<img src=":url('email')" align="left" style="margin-right: 2px"/>
<x>:mailingName</x></a>
</td>
</tr> </tr>
</x> </x>
</table> </table>
@ -404,7 +411,58 @@ class Pod(Field):
# Deduce a nice name from p_mailing # Deduce a nice name from p_mailing
res = gutils.produceNiceMessage(mailing) res = gutils.produceNiceMessage(mailing)
return res return res
def getMailingInfo(self, obj, template, mailing):
'''Gets the necessary information for sending an email to
p_mailing list.'''
res = self.mailingInfo(obj, mailing)
subject = res.subject
if not subject:
# Give a predefined subject
mapping = {'site': obj.tool.o.getSiteUrl(),
'title': obj.o.getShownValue('title'),
'template': self.getTemplateName(obj, template)}
subject = obj.translate('podmail_subject', mapping=mapping)
body = res.body
if not body:
# Give a predefined body
mapping = {'site': obj.tool.o.getSiteUrl()}
body = obj.translate('podmail_body', mapping=mapping)
return res.logins, subject, body
def sendMailing(self, obj, template, mailing, attachment):
'''Sends the emails for m_mailing.'''
logins, subject, body = self.getMailingInfo(obj, template, mailing)
if not logins:
obj.log('mailing %s contains no recipient.' % mailing)
return 'action_ko'
tool = obj.tool
# Collect logins corresponding to inexistent users and recipients
missing = []
recipients = []
for login in logins:
user = tool.search1('User', noSecurity=True, login=login)
if not user:
missing.append(login)
continue
else:
recipient = user.getMailRecipient()
if not recipient:
missing.append(login)
else:
recipients.append(recipient)
if missing:
obj.log('mailing %s: inexistent user or no email for %s.' % \
(mailing, str(missing)))
if not recipients:
obj.log('mailing %s contains no recipient (after removing wrong ' \
'entries, see above).' % mailing)
msg = 'action_ko'
else:
tool.sendMail(recipients, subject, body, [attachment])
msg = 'action_done'
return msg
def getValue(self, obj, template=None, format=None, result=None, def getValue(self, obj, template=None, format=None, result=None,
queryData=None, customContext=None, noSecurity=False): queryData=None, customContext=None, noSecurity=False):
'''For a pod field, getting its value means computing a pod document or '''For a pod field, getting its value means computing a pod document or
@ -669,8 +727,17 @@ class Pod(Field):
obj.say(res) obj.say(res)
return tool.goto(rq.get('HTTP_REFERER')) return tool.goto(rq.get('HTTP_REFERER'))
# res contains a FileInfo instance. # res contains a FileInfo instance.
res.writeResponse(rq.RESPONSE) # Must we return the res to the ui or send a mail with the res as
return # attachment?
mailing = rq.get('mailing')
if not mailing:
res.writeResponse(rq.RESPONSE)
return
else:
# Send the email(s).
msg = self.sendMailing(obj, template, mailing, res)
obj.say(obj.translate(msg))
return tool.goto(rq.get('HTTP_REFERER'))
# Performing any other action requires write access to p_obj. # Performing any other action requires write access to p_obj.
obj.o.mayEdit(self.writePermission, raiseError=True) obj.o.mayEdit(self.writePermission, raiseError=True)
msg = 'action_done' msg = 'action_done'

View file

@ -9,8 +9,15 @@ from appy.shared.utils import sequenceTypes
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
def sendMail(tool, to, subject, body, attachments=None): def sendMail(tool, to, subject, body, attachments=None):
'''Sends a mail, via p_tool.mailHost, to p_to (a single email address or a '''Sends a mail, via p_tool.mailHost, to p_to (a single email recipient or
list of email addresses).''' a list of recipients). Every (string) recipient can be an email address
or a string of the form "[name] <[email]>".
p_attachment must be a list or tuple whose elements can have 2 forms:
1. a tuple (fileName, fileContent): "fileName" is the name of the file
as a string; "fileContent" is the file content, also as a string;
2. a appy.fields.file.FileInfo instance.
'''
# Just log things if mail is disabled # Just log things if mail is disabled
fromAddress = tool.mailFrom fromAddress = tool.mailFrom
if not tool.mailEnabled or not tool.mailHost: if not tool.mailEnabled or not tool.mailHost:
@ -18,20 +25,20 @@ def sendMail(tool, to, subject, body, attachments=None):
msg = ' (no mailhost defined)' msg = ' (no mailhost defined)'
else: else:
msg = '' msg = ''
tool.log('Mail disabled%s: should send mail from %s to %s.' % \ tool.log('mail disabled%s: should send mail from %s to %s.' % \
(msg, fromAddress, str(to))) (msg, fromAddress, str(to)))
tool.log('Subject: %s' % subject) tool.log('subject: %s' % subject)
tool.log('Body: %s' % body) tool.log('body: %s' % body)
if attachments: if attachments:
tool.log('%d attachment(s).' % len(attachments)) tool.log('%d attachment(s).' % len(attachments))
return return
tool.log('Sending mail from %s to %s (subject: %s).' % \ tool.log('sending mail from %s to %s (subject: %s).' % \
(fromAddress, str(to), subject)) (fromAddress, str(to), subject))
# Create the base MIME message # Create the base MIME message
body = MIMEText(body, 'plain', 'utf-8') body = MIMEText(body, 'plain', 'utf-8')
if attachments: if attachments:
msg = MIMEMultipart() msg = MIMEMultipart()
msg.attach( body ) msg.attach(body)
else: else:
msg = body msg = body
# Add the header values # Add the header values
@ -48,23 +55,18 @@ def sendMail(tool, to, subject, body, attachments=None):
to = fromAddress to = fromAddress
# Add attachments # Add attachments
if attachments: if attachments:
for fileName, fileContent in attachments: for attachment in attachments:
part = MIMEBase('application', 'octet-stream') # 2 possible forms for an attachment
if fileContent.__class__.__name__ == 'FileWrapper': if isinstance(attachment, tuple) or isinstance(attachment, list):
fileContent = fileContent._zopeFile fileName, fileContent = attachment
if hasattr(fileContent, 'data'):
# It is a File instance coming from the database
data = fileContent.data
if isinstance(data, basestring):
payLoad = data
else:
payLoad = ''
while data is not None:
payLoad += data.data
data = data.next
else: else:
payLoad = fileContent # a FileInfo instance
part.set_payload(payLoad) fileName = attachment.uploadName
f = file(attachment.fsPath, 'rb')
fileContent = f.read()
f.close()
part = MIMEBase('application', 'octet-stream')
part.set_payload(fileContent)
Encoders.encode_base64(part) Encoders.encode_base64(part)
part.add_header('Content-Disposition', part.add_header('Content-Disposition',
'attachment; filename="%s"' % fileName) 'attachment; filename="%s"' % fileName)
@ -85,12 +87,12 @@ def sendMail(tool, to, subject, body, attachments=None):
res = smtpServer.sendmail(fromAddress, [to], msg.as_string()) res = smtpServer.sendmail(fromAddress, [to], msg.as_string())
smtpServer.quit() smtpServer.quit()
if res: if res:
tool.log('Could not send mail to some recipients. %s' % str(res), tool.log('could not send mail to some recipients. %s' % str(res),
type='warning') type='warning')
except smtplib.SMTPException, e: except smtplib.SMTPException, e:
tool.log('Mail sending failed: %s' % str(e), type='error') tool.log('mail sending failed: %s' % str(e), type='error')
except socket.error, se: except socket.error, se:
tool.log('Mail sending failed: %s' % str(se), type='error') tool.log('mail sending failed: %s' % str(se), type='error')
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
def sendNotification(obj, transition, transitionName, workflow): def sendNotification(obj, transition, transitionName, workflow):

View file

@ -722,3 +722,11 @@ msgstr ""
#. Default: "Send by email" #. Default: "Send by email"
msgid "email_send" msgid "email_send"
msgstr "" msgstr ""
#. Default: "${site} - ${title} - ${template}"
msgid "podmail_subject"
msgstr ""
#. Default: "Hello, this email that was sent to you via ${site}. Please consult the attached file(s)."
msgid "podmail_body"
msgstr ""

View file

@ -722,3 +722,11 @@ msgstr ""
#. Default: "Send by email" #. Default: "Send by email"
msgid "email_send" msgid "email_send"
msgstr "" msgstr ""
#. Default: "${site} - ${title} - ${template}"
msgid "podmail_subject"
msgstr ""
#. Default: "Hello, this email that was sent to you via ${site}. Please consult the attached file(s)."
msgid "podmail_body"
msgstr ""

View file

@ -722,3 +722,11 @@ msgstr ""
#. Default: "Send by email" #. Default: "Send by email"
msgid "email_send" msgid "email_send"
msgstr "" msgstr ""
#. Default: "${site} - ${title} - ${template}"
msgid "podmail_subject"
msgstr ""
#. Default: "Hello, this email that was sent to you via ${site}. Please consult the attached file(s)."
msgid "podmail_body"
msgstr ""

View file

@ -723,3 +723,11 @@ msgstr "Microsoft Internet Explorer ${version} is not supported. Please upgrade
#. Default: "Send by email" #. Default: "Send by email"
msgid "email_send" msgid "email_send"
msgstr "Send by email" msgstr "Send by email"
#. Default: "${site} - ${title} - ${template}"
msgid "podmail_subject"
msgstr "${site} - ${title} - ${template}"
#. Default: "Hello, this email that was sent to you via ${site}. Please consult the attached file(s)."
msgid "podmail_body"
msgstr "Hello, this email that was sent to you via ${site}. Please consult the attached file(s)."

View file

@ -722,3 +722,11 @@ msgstr ""
#. Default: "Send by email" #. Default: "Send by email"
msgid "email_send" msgid "email_send"
msgstr "" msgstr ""
#. Default: "${site} - ${title} - ${template}"
msgid "podmail_subject"
msgstr ""
#. Default: "Hello, this email that was sent to you via ${site}. Please consult the attached file(s)."
msgid "podmail_body"
msgstr ""

View file

@ -723,3 +723,11 @@ msgstr "Microsoft Internet Explorer ${version} n'est pas supporté. Veuillez met
#. Default: "Send by email" #. Default: "Send by email"
msgid "email_send" msgid "email_send"
msgstr "Envoyer par email" msgstr "Envoyer par email"
#. Default: "${site} - ${title} - ${template}"
msgid "podmail_subject"
msgstr "${site} - ${title} - ${template}"
#. Default: "Hello, this email that was sent to you via ${site}. Please consult the attached file(s)."
msgid "podmail_body"
msgstr "Bonjour, cet email vous est envoyé depuis ${site}. Veuillez consulter le(s) fichier(s) joint(s)."

View file

@ -722,3 +722,11 @@ msgstr ""
#. Default: "Send by email" #. Default: "Send by email"
msgid "email_send" msgid "email_send"
msgstr "" msgstr ""
#. Default: "${site} - ${title} - ${template}"
msgid "podmail_subject"
msgstr ""
#. Default: "Hello, this email that was sent to you via ${site}. Please consult the attached file(s)."
msgid "podmail_body"
msgstr ""

View file

@ -722,3 +722,11 @@ msgstr ""
#. Default: "Send by email" #. Default: "Send by email"
msgid "email_send" msgid "email_send"
msgstr "" msgstr ""
#. Default: "${site} - ${title} - ${template}"
msgid "podmail_subject"
msgstr ""
#. Default: "Hello, this email that was sent to you via ${site}. Please consult the attached file(s)."
msgid "podmail_body"
msgstr ""

View file

@ -686,7 +686,7 @@ function toggleCookie(cookieId) {
// Function that allows to generate a document from a pod template. // Function that allows to generate a document from a pod template.
function generatePod(uid, fieldName, template, podFormat, queryData, function generatePod(uid, fieldName, template, podFormat, queryData,
customParams, getChecked) { customParams, getChecked, mailing) {
var f = document.getElementById('podForm'); var f = document.getElementById('podForm');
f.objectUid.value = uid; f.objectUid.value = uid;
f.fieldName.value = fieldName; f.fieldName.value = fieldName;
@ -695,6 +695,7 @@ function generatePod(uid, fieldName, template, podFormat, queryData,
f.queryData.value = queryData; f.queryData.value = queryData;
if (customParams) { f.customParams.value = customParams; } if (customParams) { f.customParams.value = customParams; }
else { f.customParams.value = ''; } else { f.customParams.value = ''; }
if (mailing) f.mailing.value = mailing;
f.action.value = 'generate'; f.action.value = 'generate';
f.checkedUids.value = ''; f.checkedUids.value = '';
f.checkedSem.value = ''; f.checkedSem.value = '';

View file

@ -146,6 +146,7 @@ class ToolWrapper(AbstractWrapper):
<input type="hidden" name="customParams"/> <input type="hidden" name="customParams"/>
<input type="hidden" name="checkedUids"/> <input type="hidden" name="checkedUids"/>
<input type="hidden" name="checkedSem"/> <input type="hidden" name="checkedSem"/>
<input type="hidden" name="mailing"/>
<input type="hidden" name="action" value="generate"/> <input type="hidden" name="action" value="generate"/>
</form>''') </form>''')

View file

@ -1,4 +1,5 @@
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
from appy.fields.string import String
from appy.gen import WorkflowOwner from appy.gen import WorkflowOwner
from appy.gen.layout import summaryPageLayouts from appy.gen.layout import summaryPageLayouts
from appy.gen.wrappers import AbstractWrapper from appy.gen.wrappers import AbstractWrapper
@ -107,6 +108,14 @@ class UserWrapper(AbstractWrapper):
from AccessControl.AuthEncoding import pw_validate from AccessControl.AuthEncoding import pw_validate
return pw_validate(encryptedPassword, clearPassword) return pw_validate(encryptedPassword, clearPassword)
def getMailRecipient(self):
'''Returns, for this user, the "recipient string" (first name, name,
email) as can be used for sending an email.'''
res = self.email or self.login
# Ensure this is really an email
if not String.EMAIL.match(res): return
return '%s <%s>' % (self.title, res)
def setLogin(self, oldLogin, newLogin): def setLogin(self, oldLogin, newLogin):
'''Changes the login of this user from p_oldLogin to p_newLogin.''' '''Changes the login of this user from p_oldLogin to p_newLogin.'''
self.login = newLogin self.login = newLogin