diff --git a/fields/pod.py b/fields/pod.py index 587061b..846b03b 100644 --- a/fields/pod.py +++ b/fields/pod.py @@ -124,8 +124,15 @@ class Pod(Field): - :mailingName - + + + + :mailingName + @@ -404,7 +411,58 @@ class Pod(Field): # Deduce a nice name from p_mailing res = gutils.produceNiceMessage(mailing) 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, queryData=None, customContext=None, noSecurity=False): '''For a pod field, getting its value means computing a pod document or @@ -669,8 +727,17 @@ class Pod(Field): obj.say(res) return tool.goto(rq.get('HTTP_REFERER')) # res contains a FileInfo instance. - res.writeResponse(rq.RESPONSE) - return + # Must we return the res to the ui or send a mail with the res as + # 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. obj.o.mayEdit(self.writePermission, raiseError=True) msg = 'action_done' diff --git a/gen/mail.py b/gen/mail.py index ed75b10..d286dae 100644 --- a/gen/mail.py +++ b/gen/mail.py @@ -9,8 +9,15 @@ from appy.shared.utils import sequenceTypes # ------------------------------------------------------------------------------ def sendMail(tool, to, subject, body, attachments=None): - '''Sends a mail, via p_tool.mailHost, to p_to (a single email address or a - list of email addresses).''' + '''Sends a mail, via p_tool.mailHost, to p_to (a single email recipient or + 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 fromAddress = tool.mailFrom if not tool.mailEnabled or not tool.mailHost: @@ -18,20 +25,20 @@ def sendMail(tool, to, subject, body, attachments=None): msg = ' (no mailhost defined)' else: 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))) - tool.log('Subject: %s' % subject) - tool.log('Body: %s' % body) + tool.log('subject: %s' % subject) + tool.log('body: %s' % body) if attachments: tool.log('%d attachment(s).' % len(attachments)) 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)) # Create the base MIME message body = MIMEText(body, 'plain', 'utf-8') if attachments: msg = MIMEMultipart() - msg.attach( body ) + msg.attach(body) else: msg = body # Add the header values @@ -48,23 +55,18 @@ def sendMail(tool, to, subject, body, attachments=None): to = fromAddress # Add attachments if attachments: - for fileName, fileContent in attachments: - part = MIMEBase('application', 'octet-stream') - if fileContent.__class__.__name__ == 'FileWrapper': - fileContent = fileContent._zopeFile - 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 + for attachment in attachments: + # 2 possible forms for an attachment + if isinstance(attachment, tuple) or isinstance(attachment, list): + fileName, fileContent = attachment else: - payLoad = fileContent - part.set_payload(payLoad) + # a FileInfo instance + 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) part.add_header('Content-Disposition', 'attachment; filename="%s"' % fileName) @@ -85,12 +87,12 @@ def sendMail(tool, to, subject, body, attachments=None): res = smtpServer.sendmail(fromAddress, [to], msg.as_string()) smtpServer.quit() 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') 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: - 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): diff --git a/gen/tr/Appy.pot b/gen/tr/Appy.pot index 08bc2f3..fb44412 100644 --- a/gen/tr/Appy.pot +++ b/gen/tr/Appy.pot @@ -722,3 +722,11 @@ msgstr "" #. Default: "Send by email" msgid "email_send" 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 "" diff --git a/gen/tr/ar.po b/gen/tr/ar.po index 9192172..dc24e50 100644 --- a/gen/tr/ar.po +++ b/gen/tr/ar.po @@ -722,3 +722,11 @@ msgstr "" #. Default: "Send by email" msgid "email_send" 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 "" diff --git a/gen/tr/de.po b/gen/tr/de.po index 07d443b..fac8bff 100644 --- a/gen/tr/de.po +++ b/gen/tr/de.po @@ -722,3 +722,11 @@ msgstr "" #. Default: "Send by email" msgid "email_send" 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 "" diff --git a/gen/tr/en.po b/gen/tr/en.po index 2d00439..7f0470f 100644 --- a/gen/tr/en.po +++ b/gen/tr/en.po @@ -723,3 +723,11 @@ msgstr "Microsoft Internet Explorer ${version} is not supported. Please upgrade #. Default: "Send by email" msgid "email_send" 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)." diff --git a/gen/tr/es.po b/gen/tr/es.po index 19dfde3..e9f272f 100644 --- a/gen/tr/es.po +++ b/gen/tr/es.po @@ -722,3 +722,11 @@ msgstr "" #. Default: "Send by email" msgid "email_send" 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 "" diff --git a/gen/tr/fr.po b/gen/tr/fr.po index 304deb2..354226a 100644 --- a/gen/tr/fr.po +++ b/gen/tr/fr.po @@ -723,3 +723,11 @@ msgstr "Microsoft Internet Explorer ${version} n'est pas supporté. Veuillez met #. Default: "Send by email" msgid "email_send" 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)." diff --git a/gen/tr/it.po b/gen/tr/it.po index 43b74c9..adc9086 100644 --- a/gen/tr/it.po +++ b/gen/tr/it.po @@ -722,3 +722,11 @@ msgstr "" #. Default: "Send by email" msgid "email_send" 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 "" diff --git a/gen/tr/nl.po b/gen/tr/nl.po index f5c491f..4c04e9b 100644 --- a/gen/tr/nl.po +++ b/gen/tr/nl.po @@ -722,3 +722,11 @@ msgstr "" #. Default: "Send by email" msgid "email_send" 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 "" diff --git a/gen/ui/appy.js b/gen/ui/appy.js index cdedcf8..43a6540 100644 --- a/gen/ui/appy.js +++ b/gen/ui/appy.js @@ -686,7 +686,7 @@ function toggleCookie(cookieId) { // Function that allows to generate a document from a pod template. function generatePod(uid, fieldName, template, podFormat, queryData, - customParams, getChecked) { + customParams, getChecked, mailing) { var f = document.getElementById('podForm'); f.objectUid.value = uid; f.fieldName.value = fieldName; @@ -695,6 +695,7 @@ function generatePod(uid, fieldName, template, podFormat, queryData, f.queryData.value = queryData; if (customParams) { f.customParams.value = customParams; } else { f.customParams.value = ''; } + if (mailing) f.mailing.value = mailing; f.action.value = 'generate'; f.checkedUids.value = ''; f.checkedSem.value = ''; diff --git a/gen/wrappers/ToolWrapper.py b/gen/wrappers/ToolWrapper.py index 4df33b9..5ace912 100644 --- a/gen/wrappers/ToolWrapper.py +++ b/gen/wrappers/ToolWrapper.py @@ -146,6 +146,7 @@ class ToolWrapper(AbstractWrapper): + ''') diff --git a/gen/wrappers/UserWrapper.py b/gen/wrappers/UserWrapper.py index 94b823e..9dce449 100644 --- a/gen/wrappers/UserWrapper.py +++ b/gen/wrappers/UserWrapper.py @@ -1,4 +1,5 @@ # ------------------------------------------------------------------------------ +from appy.fields.string import String from appy.gen import WorkflowOwner from appy.gen.layout import summaryPageLayouts from appy.gen.wrappers import AbstractWrapper @@ -107,6 +108,14 @@ class UserWrapper(AbstractWrapper): from AccessControl.AuthEncoding import pw_validate 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): '''Changes the login of this user from p_oldLogin to p_newLogin.''' self.login = newLogin