Last updated by
4 years ago
Page: Mail from Grails, Version:2
Proposal for Email Integration in Grails
Spring Mail
Default implementation for email integration will use Spring framework's JavaMailSender implementation. However, we should create test or development mail senders for environments that are not set to send email. A test mail sender will dump emails sent ( and headers ) into a text file or database for easy viewing.Configuration
Configuration should be done similar to data sources, with XxxMailSender.groovy files under the grails-app/conf directory.class ProductionMailSender {
String host="localhost"
String username="user"
String password="pass"
}class TestMailSender {
String mailSender="org.codehaus.groovy.grails.commons.TestMailSender"
String directory="/WEB-INF/emails/"
}Lifecycle
When application is loading mail sender will be placed in the Spring context under 'mailSender', where it will be available to the grails framework.MVC Architecture
Since email is really a view component, emails should be handled in an MVC way. We do not want to have code similar to the following in our controllers:class SomeController { def sendEmail = { …
def mailSender = appContext.getBean( "mailSender" )
def message = new SimpleMailMessage() // Set properties
message.to = 'user@domain.com'
message.subject = 'subject' def body = "Dear ${session.user},"
body = body + "rn You have a new message"
body = body + " to view this message go here."
body = body + "rnhttp://domain.com/send/email" message.body = body mailSender.send( message ) }}class SomeController { SomeMailer someMailer def sendEmail = { …
def message = someMailer.createMessage(user: session.user)
message.to = 'user@domain.com'
someMailer.sendMessageNotification( message ) }}// views directory - messageNotification.gsp
Dear ${session.user},
You have a new message to view this message go here.
http://domain.com/send/email/${session.user.id}- First the required mailer is injected into the controller.
- Then a message is created using the createMessage method of the mailer that takes the model
- We then invoke the send method on the someMailer. The send method uses the latter part of its name, in this case "MessageNotification" to map to delegate to the appropriate view that contains the e-mail message (in this case messageNotification.gsp)
Mailer(s) - The alternative approach.
_The difference is that we don't build an email in the controller or service. We delegate the building of the email to a closure within the Mailer class. This approach keeps your code very clean._A mailer prepares an email for sending. They set the recipients, attach any files, build or generate the body(s). To create a mailer you simply create a class whose name ends with "Mailer" and place it within the "grails-app/mailers" directory.Mailers are injectable into other classes such as controllers, services and jobs.Sending emails
An mailer can have multiple closure properties. Each of these properties maps to a different email you may wish to build:class OrderMailer {
def conformation {
to = ["smith@example.com":"Smith"]
subject = "Order Conformation" body(text:"Text Message" )
body(html:"<H1>HTML Message</H1>" ) attach(url:"/path/to/file.pdf")
}
}OrderMailer orderMailer
orderMailer.sendConformation()
class OrderMailer {
def conformation {order |
to = order.email:order.name
subject = "Order Comformation for ${order.name}" body """Dear ${order.name},
Your orders coming.
Thanks"""
}
}class OrderService {
OrderMailer orderMailer def void processOrder(Order order) { // Process the order.. // Send conformation email.
orderMailer.sendConformation(order)
}
}to = [test@example.com:"Test",webmaster@yahoo.com:"webmaster"] cc = "test@example.com" bcc = ["test@example.com":"Test"]
Email Properties
- to (optional) - The recipient(s). String, Map or List
- cc (optional) - The carbon copy recipient(s). String, Map or List
- bcc (optional) - The blind carbon copy recipient(s). String, Map or List
- subject (optional) - The email subject
- headers (optional) - Map of additional header information
- from (optional) - The from address. String or Map
- bounce (optional) - The bounce address
- replyTo (optional) - the reply to address. String or Map
Email Dynamic Methods
body
Description
A multi-purpose method for rendering the body of emails. This can be called multiple times to add bodies of different content type. LIke the render method on controllers it is best illustrated with a few examples!Parameters
- text (optional) - The text to render in the body
- html (optional) - The html to render in the body. Sets contentType = text/html automagicly
- builder (optional) - The builder to use when rendering markup
- view (optional) - The view to delegate the rendering to
- template (optional) - The template to render (default = name of calling closure)
- var (optional) - The name of the variable to be passed into a template, defaults to the groovy default argument 'it' if not specified
- bean (optional) - The beanto use in rendering
- model (optional) - The model to use in rendering
- collection (optional) - For rendering a template against each item in a collection
- contentType (optional) - The contentType of the body (default = text/plain)
- encoding (optional) - The encoding of the response
Examples
// renders text for the body of the email body "some text"// renders text for a specified content-type/encoding body(text:"<h1>Hello World</h1>",contentType:"text/html",encoding:"UTF-8")// render a template for the body for the specified model body(template:"book",model:[book:new Book(title:'The Shining',author:'Stephen King')])// render each item in the collection using the specified template body(template:"book",collection:[b1, b2, b3])// render a template to the response for the specified bean body(template:"book",bean:new Book(title:'The Shining',author:'Stephen King'))// render some markup to the body body { h1() { "some body text" } }// render some HTML markup to the body body(contentType:"text/html") { books { for(b in books) { book(title:b.title,author:b.author) } } }
attach
Description
A method for adding attachments to an email.Parameters
- name (optional) - Name of attachment
- url (optional) - Url to the file
- file (optional) - The actual file
- description (optional) Description of the attachment
- disposition (optional) Either attachment or inline (default = attachment)
Receive Emails
Our xxxMailer can be configured to receive incoming email and interact with the domain model of the application on the basis of the content. We handle these incoming emails by defining a method called receive() that takes and Email object (Yet to define what an email object actualy consists of).class xxxMailer { def receive(Email email) {
// Perform some action on domain object
def user = User.findByEmail(email.from)
} // Method for checking your email…
def checkMail() {
// checking code implemented by developer.
// for (message in xzyz.getMessages()) {
// receive(new Email(message));
// } }
}class MyJob { // Injected here by spring
def XxxMailer xxxMailer def cronExpression = "0 0 6 * * ?" def execute(){
xxxMailer.checkMail()
}
}Another variation/hybrid
My feeling is that we must use the MVC model for the messages. This means having controllers populate the model for the mail view, without requiring service injection, which in my view messes things up and requires controllers to know what is handling the mail of a certain type.The Web Controller
class SomeController { def sendEmail = { ... mail( 'usermessages/mail', user: session.user ) // This is a "magic" method like render()
mail( 'sysmessages/mail', [to: 'sysadmin@localhost', user: session.user] )
mail( 'otherfolder/customMailView', [to: 'sysadmin@localhost', user: session.user] )
mail( 'otherfolder/customMailView') {
encoding( 'ISO-8859-1')
to('sysadmin@localhost')
to(user.emailAddress)
subject(
from('webserver@mydomain.com')
attach( 'http://grails.org/Controllers' ) // attach contents of a URL, autodetect
attach( new File( '/etc/passwd') ) // attach file
} redirect(action:mailThankYou) // prevent refresh = double post
}}mail( view, model)
mail( view, object)
mail( model/object) // uses 'views/controllername/mail/actionID' as view
mail( model) { builder code }
mail( view, model) { builder code }