How can I send an email with a layout - grails

I am using grails mail plugin. When I submit my form, it will send an email from aaa#example.com to textField name="email successfully but how can I send an email with a layout...not blank like this picture http://www.4shared.com/photo/uT2YUCfo/Capture__2_.html or maybe some CSS..
FORM
<g:form action="send">
<table style="width:500px">
<tbody>
<tr>
<td>Your Email Address </td>
<td><g:textField style="width:250px" name = "email"/></td>
</tr>
<tr>
<td>Your Name</td>
<td><g:textField style="width:250px" name = "user"/></td>
</tr>
<tr>
<td><input type="submit"/></td>
</tr>
</tbody>
</table>
</g:form>
MAIL CLOSURE
def send = {
sendMail {
to params.email
from "aaa#yahoo.com"
subject "Test Reset Password"
body(view:"/user/layoutmail",model:[name:params.user])
}
render "Email Terkirim"
}

Well you could actually use a layout for emails, similarly how you would use layouts for view pages. What you would want to do is create a new layout and a view file for your email body content.
Layout: eg. ../views/layouts/emailLayout.gsp
<%# page contentType="text/html" %>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<style>
a {color: #348eda;}
</style>
</head>
<body>
<g:layoutBody/>
</body>
</html>
View eg. ../views/emails/welcomeEmail.gsp
<%# page contentType="text/html" %>
<g:applyLayout name="emailLayout">
<html>
<body
Your Welcome ${welcome.username}
</body>
</html>
</g:applyLayout>
And to send the mail heres an example
def sendWelcomeMail(User user, String url){
def rtn = [success:false]
if(user) {
def fooBar = [
username: user.username,
email: user.email,
url: url
]
sendMail {
async true
to fooBar.email.trim()
subject "Welcome Email"
body(view: '/emails/welcomeEmail', model: [welcome: fooBar])
}
rtn.success = true
}
return rtn
}

It isn't going to pick up a grails layout. And you don't really want it to. You should construct your email in a fashion that it could be a stand alone web page with no other dependencies. All the static resources used should be accessible via a public URL.

Related

Microsoft graph: The PATCH target $value specified and page content related to the specified PATCH target cannot be located

I ran into this error when I updating OneNote page content. But let me explain my input HTML for OneNote first before I show you the issue.
Here is my input HTML template:
<!DOCTYPE html>
<html>
<head>
<title>Title</title>
</head>
<body>
<object data-id="markdown-file" data-attachment="markdown.md" data="name:markdown" type="text/markdown" />
<div data-id="content">{{ content goes here }}</div>
</body>
</html>
And I sent the following patch command to update content if div[data-id="content"] exists:
{
'target': generated id of div[data-id="content"],
'action': 'replace',
'content': '<div data-id="content">{{ actual content }}</div>'
}
otherwise, I use another command:
{
'target': 'body',
'action': 'append',
'content': '<div data-id="content">{{ actual content }}</div>'
}
Most of time, it works fine. But sometimes not. Suppose we have the following output html:
HTML
<html lang="en-US">
<head>
<title>2</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body data-absolute-enabled="true" style="font-family:Calibri;font-size:11pt">
<div id="div:{bbed3bc8-6ec5-4900-b1ee-a11259b4d796}{2}" data-id="_default" style="position:absolute;left:48px;top:120px;width:624px">
<object data-attachment="markdown.md" type="text/markdown" data="https://graph.microsoft.com/v1.0/users('195d63c8-4d1e-4073-b535-5d8a32b6f6ce')/onenote/resources/1-5ae390556d1e4351b358b6e1a667a226!1-051437c2-f608-445d-b537-e68aea2dfcd9/$value" data-id="markdown-file" />
<div data-id="content" id="div:{ce16905b-a76b-4e35-86de-1a46b5f8a62f}{69}:{ce16905b-a76b-4e35-86de-1a46b5f8a62f}{81}">
<table id="table:{ce16905b-a76b-4e35-86de-1a46b5f8a62f}{69}" style="border:1px solid;border-collapse:collapse">
<tr id="tr:{ce16905b-a76b-4e35-86de-1a46b5f8a62f}{70}">
<td id="td:{ce16905b-a76b-4e35-86de-1a46b5f8a62f}{72}" style="background-color:white;border:1px solid;text-align:center"><span style="font-family:BlinkMacSystemFont;color:#363636;font-weight:bold">Head</span></td>
</tr>
<tr id="tr:{ce16905b-a76b-4e35-86de-1a46b5f8a62f}{71}">
<td id="td:{ce16905b-a76b-4e35-86de-1a46b5f8a62f}{75}" style="background-color:white;border:1px solid"><span style="font-family:BlinkMacSystemFont;color:#363636">Column</span></td>
</tr>
</table>
</div>
</div>
</body>
</html>
Notice that div[data-id="content"] only contains a table. If I try to replace div[data-id="content"], it shows the error The PATCH target $value specified and page content related to the specified PATCH target cannot be located. The error message is not quite clear, so I cannot know which target is missing.
But if the output HTML contains not only tables, but also other elements, it can be replaced successfully. My code works with the following output HTML:
<html lang="en-US">
<head>
<title>3</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body data-absolute-enabled="true" style="font-family:Calibri;font-size:11pt">
<div id="div:{a61d7d65-215f-4936-a8c3-4dda9a805827}{249}" data-id="_default" style="position:absolute;left:48px;top:120px;width:624px">
<object data-attachment="markdown.md" type="text/markdown" data="https://graph.microsoft.com/v1.0/users('195d63c8-4d1e-4073-b535-5d8a32b6f6ce')/onenote/resources/1-c52b2dd5a8d74a89a0e038373b52b3f1!1-051437c2-f608-445d-b537-e68aea2dfcd9/$value" data-id="markdown-file" />
<div data-id="content" id="div:{25489f27-57fa-4798-b4ef-d229a5c5841f}{171}:{25489f27-57fa-4798-b4ef-d229a5c5841f}{187}">
<table id="table:{25489f27-57fa-4798-b4ef-d229a5c5841f}{171}" style="border:1px solid;border-collapse:collapse">
<tr id="tr:{25489f27-57fa-4798-b4ef-d229a5c5841f}{172}">
<td id="td:{25489f27-57fa-4798-b4ef-d229a5c5841f}{174}" style="background-color:white;border:1px solid;text-align:center"><span style="font-family:BlinkMacSystemFont;color:#363636;font-weight:bold">Head</span></td>
</tr>
<tr id="tr:{25489f27-57fa-4798-b4ef-d229a5c5841f}{173}">
<td id="td:{25489f27-57fa-4798-b4ef-d229a5c5841f}{177}" style="background-color:white;border:1px solid"><span style="font-family:BlinkMacSystemFont;color:#363636">Column</span></td>
</tr>
</table>
<p id="p:{25489f27-57fa-4798-b4ef-d229a5c5841f}{187}" style="margin-top:5.5pt;margin-bottom:5.5pt"><span style="font-family:BlinkMacSystemFont;color:#4a4a4a">hello</span></p>
</div>
</div>
</body>
</html>
The only difference of two output html is the second one has a p tag. This issue seems weird.
Here is my code to update page content:
def update_page(id):
original_content = _get_page_content(id)
original_document = PyQuery(original_content)
content_div = original_document('div[data-id="content"]')
page = request.json
new_document = PyQuery(page['content'])
commands = [
{
'target': 'title',
'action': 'replace',
'content': page['title']
},
{
'target': '#markdown-file',
'action': 'replace',
'content': MARKDOWN_FILE_OBJECT_HTML
}
]
content = '<div data-id="content">{0}</div>'.format(
OneNoteHtmlMapper(new_document).get_html()) # OneNoteHtmlMapper is not implemented, it simply calls new_document.outer_html()
if content_div:
commands.append({
'target': content_div.attr('id'),
'action': 'replace',
'content': content
})
else:
commands.append({
'target': 'body',
'action': 'append',
'content': content
})
files = {
'Commands': ('', io.StringIO(json.dumps(commands)),
'application/json'),
'markdown': ('markdown.md', io.StringIO(page['markdown']),
'text/markdown')
}
oauth_client = oauth.microsoft_graph
response = oauth_client.request(
'PATCH', 'me/onenote/pages/{0}/content'.format(id), files=files)
return response.content, response.status_code
Thanks in advance!
A temporary fix: append a 1px * 1px white image to div if it only contains tables.

Grails- groovy sql each row to be displayed in gsp view

I want to display the result of sql each row from the service code to my gsp view.
My Service code is:
def health()
{
def schemaList = [:]
groovy.sql.Sql sql = new groovy.sql.Sql(dataSource);
sql.eachRow("SELECT SOURCE, count(1) as COUNT from fact group by SOURCE");
ArrayList returnResults = []
sqlStatement.eachRow(sqlString)
{
returnResults<<it.toRowResults()
}
sqlStatement.close()
return[returnMap:returnResults]
}
My Controller Code is:
def stats = {
def health = AccessLogService.heath()
render (template:'healthview', model:[health:health])
}
My gsp view is as follows:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="layout" content="admin" />
<title>Health</title>
</head>
<body>
<SCRIPT language="JavaScript">
</SCRIPT>
<br />
<br />
<font style='font-size:14px;font-weight:bold;'>Health Condition</font>
<div id='overall'>
<g:if test="${health.size() > 0}">
<table border="1">
<thead>
<tr>
<th>Source</th>
<th>Count</th>
</tr>
</thead>
<tbody>
<g:each in="${health}" status="i" var="thisRecord">
<tr>
<td>${thisRecord.SOURCE}</td>
<td>${thisRecord.COUNT}</td>
</tr>
</g:each>
</tbody>
</table>
</g:if>
</div>
</body>
</html>
I am not able to see the results of my query in gsp view? Where I am going wrong.
you are trying to get the wrong key of your model.
you service returns a hash [returnMap:returnResults] so your controller renders the model: [health:health] -> [health:[returnMap:returnResults]].
thus in your gsp you should refer to health.returnMap to see the list:
<g:if test="${health.returnMap}">
...
<g:each in="${health.returnMap}" status="i" var="thisRecord">
<tr>
<td>${thisRecord.SOURCE}</td>
<td>${thisRecord.COUNT}</td>
</tr>
</g:each>
...
</g:if>
UPDATE:
the code looks strange... this is how it should be:
ArrayList returnResults = []
sql.eachRow("SELECT SOURCE, count(1) as COUNT from fact group by SOURCE"){
returnResults << it.toRowResults()
}
Where is the sqlStatement variable declared in your service? I think that is the error.
And an advice you need to debug your program. for example, Test if the service returns result by:
running your app debug mode
using log.debug
using println
or if you are doing these and have seen any erorrs on your console post that here.

gsp mail plugin autosending

When I navigate to the page, why is the send() function being called automatically?
I want to be able to view the gsp page, fill in a few text fields, and THEN call the submit with an action of "send"
This is my gsp file
<%# page contentType="text/html;charset=UTF-8" %>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"/>
<meta name="layout" content="main"/>
<title>Contact Form</title>
</head>
<body>
<g:form name="contactForm" action = "send">
<g:render template = "contactFormFields"/>
<g:actionSubmit value = "submit" action = "send"/>
</g:form>
</body>
</html>
This is the contactFormFields template
<g:select name = 'subject' from = '${EmailService.options}' noSelection='Topic'/>
Contact Name: <g:textField name = "contact"/>
Contact Number: <g:textField name = "phone"/>
Contact Email: <g:textField name = "email"/>
Aditional Information:
<g:textArea name = "information" rows="5" cols="40"/>
EmailServiceController
class EmailServiceController {
def defaultAction = "contactService"
def send() {
sendMail(){
to "mygroovytest#gmail.com"
from params.email
subject params.subject
body params.information
}
}
}
domain class
class EmailService {
static constraints = {
def options = new ArrayList()
options.push("Qestions about service")
options.push("Feedback on performed service")
options.push("Other")
options.push("Why am I doing this")
}
}
gsp that calls the service
<div class="banner">
<h1>My HVAC company</h1>
Contact me today!
Services
Have Me Contact You!
</div>
You don't have a contactService action in your EmailServiceController so it's probably treating send() as the default action when you link to the controller with no action name. Try adding an empty contactService action
def contactService() { }

Display of image in show page

I have option of photo upload in my create.gsp page. I'm able to see the uploaded photo in my create page and able to upload my photo in database, but cannot see that photo in my show page.
This is create.gsp page
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="layout" content="billing-without-grid" />
<g:set var="entityName"
value="${message(code: 'skeletonBill.label', default: 'SkeletonBill')}" />
<title><g:message code="default.create.label"
args="[entityName]" /></title>
<script>
function showPhoto(imageFile) {
var fileReader = new FileReader();
var image = document.getElementById("uploadPhotoFile");
fileReader.onload = function(e) {
image.src = e.target.result;
}
fileReader.readAsDataURL(imageFile.files[0]);
}
</script>
</head>
<body>
<div class="body">
<div class="container-fluid">
<div class="row">
<div class="span6">
<img src="" name="uploadPhotoFile" id="uploadPhotoFile"
height="200" width="160" />
<table style="width: 25%">
<tbody>
<tr class="prop">
<td valign="top" class="name"><label for="uploadPhoto"><g:message code="uploadInformation.uploadPhoto.label" default="Upload Photo" /></label></td>
<td valign="top" class="value ${hasErrors(bean: uploadInformationInstance, field: 'uploadPhoto', 'errors')}">
<input type="file" id="uploadPhoto" name="uploadPhoto" onchange="showPhoto(this)" />
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</body>
</html> '
Domain class that I created
class UploadInformation {
static constraints = {
uploadPhoto(nullable:true, maxSize: 16384 /* 16K */)
}
byte[] uploadPhoto
static transients = ["uploadPhoto"]
static mapping = {
uploadPhoto column:"uploadPhoto",sqlType: "blob"
}
}
Anuj.
The problem I see after quick look at your code is:
static transients = ["uploadPhoto"]
UploadPhoto will not be saved to database because it's declarated as transient.
See more details transient properties

Render Errors From A Service

I call a service that creates a parent and a child record. If an error happens, the service throws a RuntimeException. The RuntimeExceptionis is caught by the controller and then there there is a redirect back to the gsp. But the error is not being rendered.
In this case, I guess the controller and thus the gsp doesn't really no anything about the objects, since everything is done in the service. So how do I render the errors?
Simple Data Entry GSP
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Sample title</title>
</head>
<body>
<h1>Add A Record</h1>
<g:hasErrors bean="${parent}">
<div class="errors">
<g:renderErrors bean="${parent}" as="list" />
</div>
</g:hasErrors>
<g:hasErrors bean="${child}">
<div class="errors">
<g:renderErrors bean="${child}" as="list" />
</div>
</g:hasErrors>
<g:form action="add" name="doAdd">
<table>
<tr>
<td>
Parent Name
</td>
<td>
Child Name
</td>
</tr>
<tr>
<td>
<g:textField name="parentName" />
</td>
<td>
<g:textField name="childName" />
</td>
</tr>
<tr><td><g:submitButton name="update" value="Update" /></td></tr>
</table>
</g:form>
</body>
</html>
Controller
class AddrecordController {
def addRecordsService
def index = {
redirect action:"show", params:params
}
def add = {
println "do add"
try {
addRecordsService.addAll(params)
} catch (java.lang.RuntimeException re){
println re.message
flash.message = re.message
}
redirect action:"show", params:params
}
def show = {}
}
Service
class AddRecordsService {
static transactional = true
def addAll(params) {
def Parent theParent = addParent(params.parentName)
def Child theChild = addChild(params.childName,theParent)
}
def addParent(pName) {
def theParent = new Parent(name:pName)
if(!theParent.save()){
throw new RuntimeException('unable to save parent')
}
return theParent
}
def addChild(cName,Parent theParent) {
def theChild = new Child(name:cName,parent:theParent)
if(!theChild.save()){
throw new RuntimeException('unable to save child')
}
return theChild
}
}
You need to get a reference to the invalid object somehow and pass it to the view via the model so I would extend RuntimeException and add fields to contain the objects with validation errors e.g.
}catch(MyCustomException m){
render view:'show', model:[parent:m.getParent(), child:m.getChild()]
}
This whole exercise might be easier using Parent.withTransaction instead of the automatic rollback via RuntimeExceptions. Then you could manually rollback the transaction if there's validation errors and just return the objects instead of having to contain them in the exception.

Resources