Attach a PDF to an email

I am trying to figure out the best way to do this. I have an interception point that will basically send an email. The email is great except for one thing. I need to run a query get the records associated with this order and then create a pdf on the fly and attach it to the email. I have the pdf template working in a .cfm just how I want it. Just a little confused on where that cfdocument / pdf generation takes place. The query can be a service I inject here but just a little bit confused on how/where to generate my pdf and then attach it. Any suggestions?

public void function onLeadsImported(event,interceptData) {
var order = arguments.interceptData.order;
var user = arguments.interceptData.user;
var subject = “YOLO Marketing - You have new leads”;
var bodyTokens = {
firstName = user.getFirstName(),
lastname = user.getLastName()
};

var mail = mailservice.newMail(to=user.getEmail(),
from=variables.outgoingemail,
subject=subject,
bodyTokens=bodyTokens);

// generate content for email from template
mail.setBody( renderer.renderView(view=“email_templates/order_newleadsimport”) );
mail.setType(“HTML”);
mail.setUseSSL( getUseSSL() );
mail.setUseSSL( getUseTLS() );
mailService.send( mail );

// log the notification
}

One problem I see that there is no content argument in the Mail object.


	<!--- addMailParam --->
	<cffunction name="addMailParam" access="public" returntype="any" output="false" hint="Add mail params to this payload">
		<cfargument name="contentID" 	required="false" type="string" hint="Initial value for the contentID property." />
		<cfargument name="disposition" 	required="false" type="string" hint="Initial value for the dispositio nproperty." />
		<cfargument name="file" 		required="false" type="string" hint="Initial value for the file property." />
		<cfargument name="type" 		required="false" type="string" hint="Initial value for the type property." />
		<cfargument name="name" 		required="false" type="string" hint="Initial value for the name property." />
		<cfargument name="value" 		required="false" type="string" hint="Initial value for the value property." />
		<cfargument name="remove" 		required="false" type="boolean" hint="If true, ColdFusion removes attachment files (if any) after the mail is successfully delivered.">
		<cfscript>
			// Add new mail Param
			var mailparams = structnew();
			var key = 0;

			for( key in arguments ){
				if( structKeyExists(arguments, key) ){ mailparams[key] = arguments[key]; }
			}

			arrayAppend(getMailParams(), mailparams);

			return this;
		</cfscript>
	</cffunction>

Where in my quick test the cfdocument name is leads

Hello Dan,

OK so I figured it out, sorta. I put my pdf content in a view, grabbed the leads in my interceptor method and passed them to the render view method using the args argument. This almost works except the pdf attached to the document has an error (see attachment). Am I on the right course? If so what am I missing here?

public void function onLeadsImported(event,interceptData) {
var order = arguments.interceptData.order;
var user = arguments.interceptData.user;
var leads = leadService.getLeadsByOrderAndDate(order.getOrderID(),dateFormat(now(),‘yyyy-mm-dd’));
var subject = “YOLO Marketing - You have new leads”;
var bodyTokens = {
firstName = user.getFirstName(),
lastname = user.getLastName()
};

var mail = mailservice.newMail(to=user.getEmail(),
from=variables.outgoingemail,
cc="sales@yolo-marketing.com",
subject=subject,
bodyTokens=bodyTokens);

// generate content for email from template
mail.setBody( renderer.renderView(view=“email_templates/order_newleadsimport”) );
mail.setType(“HTML”);
mail.setUseSSL( getUseSSL() );
mail.setUseSSL( getUseTLS() );
mail.addMailParam(
file=“Leads-#dateFormat(now(),‘mm-dd-yyyy’)#.pdf”,type=“application/pdf”,
content=renderer.renderView(view=“email_templates/leadpdf”,
args={leads=leads})
);
mailService.send( mail );

// log the notification
}

pdf.png

Dan,

I think file and content are mutually exclusive.

signature0.jpg

Luis F. Majano
CEO
Ortus Solutions, Corp
www.ortussolutions.com

ColdBox Platform: http://www.coldbox.org
Linked In: http://www.linkedin.com/pub/3/731/483
Blog: http://www.luismajano.com
IECFUG Manager: http://www.iecfug.com

Social: twitter.com/lmajano facebook.com/lmajano

In my test outside of coldbox they are not. File is the name of the attachement and content is the actual file content.

Hello Dan,

I thought disposition was used

signature0.jpg

Luis F. Majano
CEO
Ortus Solutions, Corp
www.ortussolutions.com

ColdBox Platform: http://www.coldbox.org
Linked In: http://www.linkedin.com/pub/3/731/483
Blog: http://www.luismajano.com
IECFUG Manager: http://www.iecfug.com

Social: twitter.com/lmajano facebook.com/lmajano

Disposition is how the attached file will be handled (inline or attachement) and its attachment by default.

Really all you need is file and content.

I think the main problem here is that renderer.renderView doesn’t actually return anything.

mail.addMailParam( file=“Leads-#dateFormat(now(),‘mm-dd-yyyy’)#.pdf”,type=“application/pdf”, content=renderer.renderView(view=“email_templates/leadpdf”, args={leads=leads}) );

If you were to actually dump out what gets returned then data is an empty string. Am I using the wrong method?

var data = renderer.renderView(view="email_templates/leadpdf

Dan,

With the code that you have below, can you confirm if the email_templates folder is indeed in the views folder.

Regards,
Andrew Scott
WebSite: http://www.andyscott.id.au/
Google+: http://plus.google.com/113032480415921517411

Yes, because if I take the param off the email works great, but yes the file is there.

mail.setBody( renderer.renderView(view=“email_templates/order_newleadsimport”) );
mail.setType(“HTML”);
mail.setUseSSL( getUseSSL() );
mail.setUseSSL( getUseTLS() );
mail.addMailParam(
file=“Leads-#dateFormat(now(),‘mm-dd-yyyy’)#.pdf”,type=“application/pdf”,
content=renderer.renderView(view=“email_templates/leadpdf”,
args={leads=leads})
);

Dan,

What if you try just RenderView() instead of renderer.renderView() are you using a custom renderer?

Regards,
Andrew Scott
WebSite: http://www.andyscott.id.au/
Google+: http://plus.google.com/113032480415921517411

Same problem.

My guess is that there is an error or something happening in the leadpdf.cfm have you tried just outputting normal non CFML in the view. Like just a hello this is a test, if that fails I think there is something else seriously wrong.

Regards,
Andrew Scott
WebSite: http://www.andyscott.id.au/
Google+: http://plus.google.com/113032480415921517411

Actually Dan, is there any reason your not using

event.setView()

Regards,
Andrew Scott
WebSite: http://www.andyscott.id.au/
Google+: http://plus.google.com/113032480415921517411

So I ran 2 other tests… Here in this method I just tried to see what was returned and again nothing gets returned.This is ultimately why I think the attachment is not working.

public void function testGetLeadsByOrderAndDate(event,rc,prc) {
var leads = leadService.getLeadsByOrderAndDate(‘402881843ce4677c013ce9a45f510008’,dateFormat(‘2013-02-19 22:46:48’,‘yyyy-mm-dd’));
var pdfContent = renderer.renderView(view=“email_templates/leadpdf”,args={leads=leads});

writeDump(pdfContent);
abort;

}

I also tried to call it from a test handler function

public void function testGetLeadsByOrderAndDate(event,rc,prc) {
var leads = leadService.getLeadsByOrderAndDate(‘402881843ce4677c013ce9a45f510008’,dateFormat(‘2013-02-19 22:46:48’,‘yyyy-mm-dd’));

renderView(view=“email_templates/leadpdf”,args={leads=leads});
abort;
}

lead.pdf

… html content

Hello Dan,

And that worked fine.

Got the email on that 2nd one with the filename and the pdf attachment was correct.

Well this works and its an ugly a$$ hack but until I figure out what’s going wrong It will have to do. leadsAttachement is the variable name of the cfdocument tag.

var mail = mailservice.newMail(to=user.getEmail(),
from=variables.outgoingemail,
cc="sales@yolo-marketing.com",
subject=subject,
bodyTokens=bodyTokens);

include “/views/email_templates/leadpdf.cfm”;

// generate content for email from template
mail.setBody( renderer.renderView(view=“email_templates/order_newleadsimport”) );
mail.setType(“HTML”);
mail.setUseSSL( getUseSSL() );
mail.setUseSSL( getUseTLS() );
mail.addMailParam(
file=“Leads-#dateFormat(now(),‘mm-dd-yyyy’)#.pdf”,type=“application/pdf”,
content=leadsAttachment
);
mailService.send( mail );

Dan,

Is there any reason you are not using event.setView() ?

Regards,
Andrew Scott
WebSite: http://www.andyscott.id.au/
Google+: http://plus.google.com/113032480415921517411

Actually Dan, you might be better of doing it like this.

var mail = mailservice.newMail( … );
mail.setBody( renderer.get().renderExternalView(view="/contentbox/email_templates/#template#") );
mailService.send( mail );

And to customize the template, you can even look at tokens here as well.

Regards,
Andrew Scott
WebSite: http://www.andyscott.id.au/
Google+: http://plus.google.com/113032480415921517411

I know why. The renderer is not supposed to be a singleton. Therefore it is loosing scope. In the docs I write that injecting the renderer is not a good idea. The solutions is to either do a provider of renderer or inject wirebox and request it on demand.

Here is an example

https://github.com/Ortus-Solutions/ContentBox/blob/development/modules/contentbox/model/system/NotificationService.cfc

Luis Majano
CEO
Ortus Solutions, Corp
www.ortussolutions.com

ColdBox Platform: http://www.coldbox.org
Linked In: http://www.linkedin.com/pub/3/731/483
Social: twitter.com/ortussolutions | twitter.com/coldbox | twitter.com/lmajano