How do I send a cfm file as the body of an e-mail using ColdFusion? - email

I have a legacy application where an email.cfm file is used with a cfmail tag to send e-mail:
<cfmail from="abc#123.com" to="def#456.com" subject="New e-mail!">
// lots of HTML
</cfmail>
Now I'd like to update it for ColdFusion Model Glue 3. I want to send it using a mail object in the controller, and include in the body a CFM page:
var mail = new mail();
mail.setFrom("abc#123.com");
mail.setTo("def#456.com");
mail.setSubject("New e-mail!");
mail.setBody( ** SOME CFM FILE ** );
mail.send();
Does anybody have any idea how I can do this?

You can render the content you want to email in a cfsavecontent block and then use that in the email, like:
<cfsavecontent variable="myemail">
...add some HTML, include another file, whatever...
</cfsavecontent>
<cfscript>
mail.setBody( myemail );
</cfscript>
See http://help.adobe.com/en_US/ColdFusion/9.0/CFMLRef/WSc3ff6d0ea77859461172e0811cbec22c24-7d57.html

Call the CFC assigning it to a variable, like cfset request.emaiBody = cfc.function(). Then just put it in your setBody tag.

OP was convinced to use CFML, but to answer the question as it was initially asked:
var mail = new Mail();
mail.setFrom("abc#123.com");
mail.setTo("def#456.com");
mail.setSubject("New e-mail!");
mail.setType("html");
savecontent variable="mailBody" {
include "email.cfm";
}
mail.setBody(mailBody);
mail.send();

I ended up following Henry's advice in the comments and created a CFML-based CFC:
<cfcomponent>
<cffunction name="SendMail">
<cfargument name="from"/>
<cfargument name="to"/>
<cfargument name="subject"/>
<cfmail from="#from#" to="#to#" subject="#subject#">
<!--- HTML for e-mail body here --->
</cfmail>
</cffunction>
</cfcomponent>
Dave Long's suggestion is also good, which is to create components using <cfcomponent>, then wrapping the code in <cfscript> tags. This gives you the ability to fall back to CFML in case the there is no cfscript equivalent or it's easier to do with CFML:
<cfcomponent>
<cfscript>
void function GetData()
{
RunDbQuery();
}
</cfscript>
<cffunction name="RunDbQuery">
<cfquery name="data">
SELECT * FROM ABC;
</cfquery>
<cfreturn data>
</cffunction>
</cfcomponent>

Related

Using Coldfusion to create a REST request

Using Coldfusion I would like to create a REST service
I would like to post it to: https://www.test.co.za/ using the username "person1" and password "password1".
The data type would be: application/json
And I would like to pass the following parameters:
callbackURL (string)
amount (bigdecimal)
reference (string)
description (string)
This will then return:
reference (string)
redirectURL (string)
status (string)
This is what I have got so far:
index.cfm
<cfset restInitApplication("C:\ColdFusion10\cfusion\wwwroot\restApp","IIT")>
<cfhttp url="http://127.0.0.1:8500/rest/IIT/rest" method="get">
<cfoutput>#cfhttp.filecontent#</cfoutput>
rest.cfc
<cfcomponent rest="true" restpath="restService">
<cffunction name="sayHello" access="remote" returntype="String" httpmethod="GET">
<cfset callbackURL = "https://www.google.com">
<cfset amount = "100,50">
<cfset reference = "123456789">
<cfset description = "This is a description">
<cfreturn rest>
</cffunction>
</cfcomponent>
I would like to know if I am on the right track and how I would post my values to https://www.test.co.za/ with a username and password
I am not sure if it will look something like this:
<cfhttp
url="https://www.test.co.za/"
method="post"
port="8500"
result="res">
</cfhttp>
Thanks in advance

Invalid Signature with telesign call REST API

The API sends a verification code via a phone call that the user then enters into a website... basically verifying that there phone number is valid.
But I'm having trouble signing the request. No matter what I try it returns "Invalid Signature"
The API documentation:
http://docs.telesign.com/rest/content/verify-call.html
The authentication documentation:
http://docs.telesign.com/rest/content/rest-auth.html
The authentication examples:
http://docs.telesign.com/rest/content/auth-examples.html
The code:
<cffunction name="encryptHmacSHA1" returntype="binary" access="public" output="false">
<cfargument name="base64Key" type="string" required="true">
<cfargument name="signMessage" type="string" required="true">
<cfargument name="encoding" type="string" default="UTF-8">
<cfset var messageBytes = JavaCast("string",arguments.signMessage).getBytes(arguments.encoding)>
<cfset var keyBytes = binaryDecode(arguments.base64Key, "base64")>
<cfset var key = createObject("java","javax.crypto.spec.SecretKeySpec")>
<cfset var mac = createObject("java","javax.crypto.Mac")>
<cfset key = key.init(keyBytes,"HmacSHA1")>
<cfset mac = mac.getInstance(key.getAlgorithm())>
<cfset mac.init(key)>
<cfset mac.update(messageBytes)>
<cfreturn mac.doFinal()>
</cffunction>
<cfscript>
// PHONE NUMBER TO CALL
phoneNumberToCall = "15554565555";
// KEYS
keys = structNew();
keys.customerID = "D561FCF4-BA8D-4DFC-86D1-1A46DF47A308";
keys.apiKey = "mDzGHsMOc2g/ivkuINEFVh6fn/v4kdjvlTvtgFVOShu7hVWXS0eV2nLSw1FXgEzDSuOjhlKLXvneiq+YFG1/Vg==";
// DATES
dates = structNew();
dates.timeZoneInfo = GetTimeZoneInfo();
dates.dateToUse = DateAdd("h",dates.timeZoneInfo.utcHourOffset,now());
dates.signingDate = DateFormat(dates.dateToUse,"ddd, dd mmm yyyy") & " " & TimeFormat(dates.dateToUse,"HH:mm:ss") & " +0000";
// HEADERS
headers = [
"POST",
"application/x-www-form-urlencoded",
"#dates.signingDate#",
"phone_number=#phoneNumberToCall#&ucid=OTHR",
"/v1/verify/call"
];
headerText = arrayToList(headers, chr(10)) & chr(10);
// CREATE SIGNATURE
stringToSign = binaryEncode( encryptHmacSHA1(keys.apiKey, headerText), "base64");
// AUTHORIZE HEADER
Authorization = "TSA" & " " & keys.customerID & ":" & stringToSign;
</cfscript>
<cfhttp method="POST" url="https://rest.telesign.com/v1/verify/call" port="443" charset="UTF-8" result="verifyPhoneCall">
<cfhttpparam type="header" name="authorization" value="#Authorization#">
<cfhttpparam type="header" name="content-type" value="application/x-www-form-urlencoded">
<cfhttpparam type="header" name="date" value="#dates.signingDate#">
<cfhttpparam name="phone_number" value="#phoneNumberToCall#" type="formfield">
<cfhttpparam name="ucid" value="OTHR" type="formfield">
</cfhttp>
<cfdump var="#verifyPhoneCall#">
The headers need to be included in the signature with "new lines". And the docs also say that they need to be in the same order that the http tag is sending them in. I don't think I have the order correct... or even how I'm supposed to set an order in the cfhttp call.
Any help is appreciated. And yes the keys are real. I'll generate new ones soon.
Thanks,
Brian
Looking over their documentation on Constructing the CanonicalizedPOSTVariables Element it mentions that the POST body must match the string used when constructing the signature (emphasis mine):
... when you construct the signature string, you must use the body of
the POST request exactly as it is delivered to the service.
By default, cfhttpparam url encodes any formField values. Since you are not encoding those values when you construct the signature, the content submitted by cfhttp does not match. Hence the error.
Either disable automatic encoding for all of the form fields in the signature:
<cfhttpparam name="phone_number"
value="#phoneNumberToCall#"
type="formfield"
encoded="false">
... or use type="body" instead. Then you have complete control over the post content:
<cfhttpparam type="body"
value="phone_number=15554565555&ucid=OTHR">
Update:
Also, get rid of the final new line ie chr(10) in the "headerText". Otherwise your cfhttp content still will not match the signature. ie Use this:
headerText = arrayToList(headers, chr(10));
.. instead of:
headerText = arrayToList(headers, chr(10)) & chr(10);

Storing form values from a form in a session in coldfusion

I have these form values I'm trying capture in a session:
prop_zip
prop_st
address
city
fname
lname
email
pri_phone_1
pri_phone_2
pri_phone_3
Here is my session code:
<cfapplication name="qsErrorVals" clientmanagement="yes" sessionmanagement="yes" sessiontimeout="#createTimeSpan(0,0,360,0)#">
<cflock timeout="120" name="#session.sessionID#" type="exclusive">
<cfset session.prop_st="#prop_st#">
<cfset session.prop_zip="#prop_zip#">
<cfset session.address="#address#">
<cfset session.fname="#fname#">
<cfset session.lname="#lname#">
<cfset session.email="#email#">
<cfset session.pri_phone_1="#pri_phone_1#">
<cfset session.pri_phone_2="#pri_phone_2#">
<cfset session.pri_phone_3="#pri_phone_3#">
</cflock>
So far, I can only save two of the variables from the form:
prop_zip
prop_st
The rest I'm having trouble with saving the form values to the session. I've defined the variables I want to store in the session before creating the session and I've tried using this code:
<cfset session.email="#form.email#">
etc...
Still I can only save the two variables I mentioned. What am I doing wrong?
Eh, I usually take the easy way out. In addition to the recommendations about cfapplication, scoping and paraming form vars in Micah's answer, I'd copy the whole form struct into session.
session.theForm = structCopy(form);
It's difficult to answer specifically without looking at all of your code but hopefully this example will help.
The cfapplication tag should go in an Application.cfm or Application.cfc file.
The session code should most likely be placed in another file.
I'd recommend specifying a scope attribute of session in the cflock tag.
Application.cfm file:
<cfapplication name="qsErrorVals" clientmanagement="yes" sessionmanagement="yes" sessiontimeout="#createTimeSpan(0,0,360,0)#">
ErrorVals.cfm or whatever you want to call the file:
<cfparam name="form.prop_st" default="NY" > <!--- I've added default data for testing --->
<cfparam name="form.prop_zip" default="12345" >
<cfparam name="form.address" default="1st Main St." >
<cfparam name="form.fname" default="John" >
<cfparam name="form.lname" default="Doe" >
<cfparam name="form.email" default="me#domain.com" >
<cfparam name="form.pri_phone_1" default="123-123-1234" >
<cfparam name="form.pri_phone_2" default="123-123-1234" >
<cfparam name="form.pri_phone_3" default="123-123-1234" >
<cfscript>
prop_st = trim(form.prop_st); // validate, trim, etc.
prop_zip = trim(form.prop_zip);
address = trim(form.address );
fname = trim(form.fname );
lname = trim(form.lname );
email = trim(form.email);
pri_phone_1 = trim(form.pri_phone_1);
pri_phone_2 = trim(form.pri_phone_2);
pri_phone_3 = trim(form.pri_phone_3);
</cfscript>
<cflock scope="session" type="exclusive" timeout="10" >
<cfset session.prop_st = prop_st>
<cfset session.prop_zip = prop_zip>
<cfset session.address= address>
<cfset session.fname= fname>
<cfset session.lname= lname>
<cfset session.email= email>
<cfset session.pri_phone_1= pri_phone_1>
<cfset session.pri_phone_2= pri_phone_2>
<cfset session.pri_phone_3= pri_phone_3>
</cflock>
<cfdump var="#session#">

Magento: Email template crashes new template form

I have a module that I'm trying to use with an email template I created. I created the .phtml template directly (not through the new template form on the backend) into the locale > en_US > template > email folder. The template seems to work as the variables passed to it work and the email gets sent fine. My only problem is that now when I go into the management > Transactional Emails > New Template, the page crashes. The dropdown is empty and everything after it does't get rendered.
I think it might have something to do with the way I'm loading the template in the modules config.xml. When I remove the reference to the template the problem goes away. Put the reference back in and the form crashes..
Config.xml
<?xml version="1.0"?>
<config>
<modules>
<Optimise_Requestcallback>
<version>0.1.9</version>
</Optimise_Requestcallback>
</modules>
<frontend>
<routers>
<requestcallback>
<use>standard</use>
<args>
<module>Optimise_Requestcallback</module>
<frontName>request-callback</frontName>
</args>
</requestcallback>
</routers>
<layout>
<updates>
<requestcallback>
<file>optimise.xml</file>
</requestcallback>
</updates>
</layout>
</frontend>
<global>
<template>
<email>
<requestcallback_template translate="label" module="requestcallback">
<label>Optimise RequestCallback</label>
<file>requestcallback_template.html</file>
<type>html</type>
</requestcallback_template>
</email>
</template>
</global>
</config>
Here is how I send the email:
public function sendemailAction() {
$emailTemplate = Mage::getModel('core/email_template')
->loadDefault('requestcallback_template');
$emailTemplateVariables = array();
//Fetch submited params
$params = $this->getRequest()->getParams();
$subjectOfMail = "Request a Callback from the Puji Website<br /><br />Product = " . $params['product'] . "<br />Name = " . $params['name'] . "<br />Email = " . $params['email'] . "<br />Telephone = " . $params['telephone'] . "<br />Message = " . $params['comment'];
$emailTemplateVariables['body'] = $subjectOfMail;
$emailTemplate->setSenderName($params['name']);
$emailTemplate->setSenderEmail($params['email']);
try {
$emailTemplate->send('billy#optimiseweb.co.uk', 'Sales', $emailTemplateVariables);
Mage::getSingleton('core/session')->addSuccess('Thank you! We will contact you very soon.');
} catch (Exception $ex) {
$translate->setTranslateInline(true);
Mage::getSingleton('customer/session')->addError(Mage::helper('contacts')->__('Unable to submit your request. Please, try again later'));
$this->_redirect('*/*/');
return;
}
//Redirect back to index action of (this) activecodeline-simplecontact controller
$this->_redirect('request-callback/');
}
And the template itself probably couldn't be any simpler!
<!--#subject Request a Callback from the Puji Website #-->
{{var body}}
Can anyone see an issue here that would cause the New template form to crash?
There can be various reasons, but first check config.xml file for each custom module, one-by-one.
There must be one module, where you will find code like:
module="[some-module-name-here]"
Try removing this code one-by-one and reload the transactional e-mail template form again.
I am sure, it will solve the problem.

How to send email to recipient with umlauts in domain name?

In my app I have to send email to recipient who has umlauts in domain name.
Example:
"test#äöü.test.com"
I'm using cfmail tag and I'm getting such error:
"invalid definition for attribute to at tag mail"
"Invalid E-Mail Address definition (test#äöü.test.com)"
Is there any way to send email to such recipients in coldfusion?
There is even a easier solution! Why not use Oracles built in class:
http://download.oracle.com/javase/6/docs/api/java/net/IDN.html#toUnicode(java.lang.String)
Then you only have to do this (example shows from punycode to Unicode):
<cfset strUrl = "xn--land-poa.se" />
<!--- Create a Java URL. --->
<cfset jUrl = CreateObject( "java", "java.net.IDN" ).toUnicode(strUrl) />
<cfoutput>
#jUrl#
You don´t have to download anything!
I'm no I18N expert but I was intrigued enough to investigate and come up with the following solution.
The problem is essentially how to send mail to Internationalised Domain Names (IDN), i.e. those which contain non-ASCII characters. IDNs are valid nowadays but not recognized by many systems including Java (and therefore ColdFusion, which uses the Java validation for CFMAIL address fields - hence the error you're seeing).
For a system to recognise an IDN it needs to be converted to an ASCII form called Punycode. For example müller.org needs to be converted to xn--mller-kva.org
LibIdn is an OS java library that will do this and the following code shows how you can hook it up to CF using Mark Mandel's JavaLoader.
<cffunction name="convertIdnToAscii" returntype="string" output="false">
<cfargument name="domain" type="string" required="true">
<cfscript>
var local = {};
// these paths assume the JavaLoader folder and the libidn-1.22.jar are in the same folder as the cfm template.
local.javaLoaderPath = "javaLoader.JavaLoader";
local.idnLibPath = ExpandPath( "libidn-1.22.jar" );
// convert the IDN lib path to an array which is what JavaLoader expects
local.libPathArray = [ local.idnLibPath ];
//load the IDN Lib
loader = CreateObject( "component",local.javaLoaderPath ).init( local.libPathArray );
// create an instance of the IDN lib
local.idn = loader.create( "gnu.inet.encoding.IDNA" ).init();
// convert the domain name
return local.idn.toASCII( arguments.domain );
</cfscript>
</cffunction>
<cffunction name="convertIdnAddress" returntype="string" output="false">
<cfargument name="address" type="string" required="true">
<cfscript>
var local = {};
local.domain = GetToken( arguments.address,2,"#" );
local.converted = convertIdnToAscii( local.domain );
return Replace( arguments.address,local.domain,local.converted );
</cfscript>
</cffunction>
<!--- Loop over a list of addresses and convert them if necessary --->
<cfset processedAddresses = []>
<cfloop list="test#äöü.test.com,test#example.com" index="address">
<cfif( NOT IsValid( "email",address ) )>
<cfset address = convertIdnAddress( address )>
</cfif>
<cfmail server="0.0.0.0" from="sender#mydomain.com" to="#address#" subject="test">Message</cfmail>
<cfset ArrayAppend( processedAddresses,address )>
</cfloop>
<cfdump var="#processedAddresses#">
This will send 2 emails (to a non-existent mailserver) and dump the converted addresses:
test#xn--4ca0bs.test.com
test#example.com
Notes:
To get the libidn jar file, download and extract the tar and look for it in the Java directory
The above assumes the libidn jar and JavaLoader package are located in the same folder as the template contain the CF code
The above should work on CF8 and above, although I've only tested it on CF9.
Be aware there's no error handling for addresses that might be invalid for reasons other than it containing an IDN.