On this question I asked about what the structure of the email should be. This question is about how to use cfmail (and cfmailpart, cfmailparam, etc.) to produce the correct structure.
The desired structure is:
multipart/mixed
multipart/alternative
text/plain
text/html
image/jpeg
application/pdf
The code I've got currently:
<cfmail from='"No Reply" <noreply#example.com>' subject="Test 123" to="my_outlook_address#example.com,my_gmail_address#gmail.com">
<!--- Some code to get a query of attachment content here... --->
<cfloop query="qAttachments">
<!---
Some code to get the attachment file data here and put it in a variable named `myfile`...
myfile structure:
{
fileName: <string>,
fileContent: <base64 encoded file content>,
mimeType: <image/jpeg for the one, application/pdf for the other>
}
--->
<cfmailparam disposition="attachment" contentID="#myfile.fileName#" content="#myfile.fileContent#" file="#myfile.fileName#" type="#myfile.mimeType#" />
</cfloop>
<cfmailpart type="plain">
My plain text
</cfmailpart>
<cfmailpart type="html">
<strong>My fancypants text</strong>
</cfmailpart>
</cfmail>
However, this produces this structure:
multipart/mixed
multipart/alternative
text/plain
multipart/related
text/html
image/jpeg
application/pdf
I've tried code like this:
<cfmail from='"No Reply" <noreply#example.com>' subject="Test 123" to="my_outlook_address#example.com,my_gmail_address#gmail.com">
<!--- Some code to get a query of attachment content here... --->
<cfloop query="qAttachments">
<cfmailparam disposition="attachment" contentID="#myfile.fileName#" content="#myfile.fileContent#" file="#myfile.fileName#" type="#myfile.mimeType#" />
</cfloop>
<cfmailpart type="multipart/alternative">
<cfmailpart type="plain">
My plain text
</cfmailpart>
<cfmailpart type="html">
<strong>My fancypants text</strong>
</cfmailpart>
</cfmailpart>
</cfmail>
But then it just goes to the undelivered email list in cfadmin.
With both versions of the code I tried for values of the type attribute on the cfmail tag itself:
plain
html
multipart/mixed
multipart/alternative
to no avail.
How do I achieve the desired MIME structure in ColdFusion?
My approach I landed on may not be ideal, but it works in all 3 mail clients I was targeting. What I ended up doing was this:
for any image attachments, I would include a contentID attribute on the cfmailparam tag and include an <img src="cid:..."> with the contentID value
for all other attachments, I omit the contentID attribute on the cfmailparam tag
This has the end result that all images are presented inline in the message body, and all other files are displayed as regular file attachments.
Based on the discussion by who I assume is a developer on the CF team here https://tracker.adobe.com/#/view/CF-4166939 I'm under the impression that the MIME header structure is controlled by ColdFusion and isn't directly manageable by ColdFusion developers. Unfortunate, but at least I have something of a workaround. Hopefully this will help someone.
Related
UPDATED WITH WORKING SOLUTION
I'm trying to integrate the DocuSign API using ColdFusion and keep running into an issue. I am able to authenticate successfully and retrieve the base URL. However, when trying to create the envelope with a single recipient and document, I get an error stating that the "boundary terminator" is not found in the request (when it clearly is).
I know this questions is similar to another post here but that was from a while ago and was never completely answered.
I first read in the PDF document:
<cffile action="READBINARY" file="Agreement.pdf" variable="docData">
<cfset docData = BinaryEncode(docData,"Base64")>
I then create the envelope definition in XML using the element to include the encoded binary PDF data:
<cfset envelope = "<envelopeDefinition xmlns=""http://www.docusign.com/restapi"">
<status>Sent</status>
<emailSubject>eSignature request</emailSubject>
<emailBlurb>Please sign the document</emailBlurb>
<recipients>
<signers>
<signer>
<recipientId>1</recipientId>
<name>eCS Buyer</name>
<email>XXX#XXXX.com</email>
<tabs>
<signHereTabs>
<signHere>
<documentId>1</documentId>
<pageNumber>3</pageNumber>
<xPosition>10</xPosition>
<yPosition>100</yPosition>
</signHere>
</signHereTabs>
</tabs>
</signer>
</signers>
</recipients>
<documents>
<document>
<name>Agreement.pdf</name>
<documentId>1</documentId>
<fileExtension>pdf</fileExtension>
<documentBase64>#docData#</documentBase64>
</document>
</documents>
</envelopeDefinition>">
Lastly, I POST the information:
<cfhttp url="#baseURL#/envelopes" method="POST" resolveurl="Yes" throwonerror="No">
<cfhttpparam name="X-DocuSign-Authentication" type="HEADER" value="<DocuSignCredentials><Username>#userName#</Username><Password>#password#</Password><IntegratorKey>#integratorKey#</IntegratorKey></DocuSignCredentials>">
<cfhttpparam name="Content-Type" type="HEADER" value="application/xml">
<cfhttpparam name="Accept" type="HEADER" value="application/xml">
<cfhttpparam name="Content-Length" type="HEADER" value="#Len(envelope)#">
<cfhttpparam name="request_body" type="BODY" value="#envelope#">
</cfhttp>
I tried changing the TYPE attribute for the request body to XML and FORMFIELD but is still doesn't work. I even tried changing the envelope to JSON format to no avail.
The error that is generated is:
<errorCode>INVALID_REQUEST_BODY</errorCode><message>The request body is missing or improperly formatted. The XML request does not match the expected format. </message></errorDetails>
I have been struggling with this for weeks. Any guidance would be GREATLY appreciated.
Most likely an issue with extra CRLF (i.e., extra line breaks). See the answer in this other forum post for an example of what the full request structure should look like: Docusign : Unable to create envelope from document in restapi v2.
UPDATE #1
Looks like your XML request body is missing information about the Document. Try adding a documents element as a child element of envelopeDefinition (i.e., as a peer element to recipients):
<envelopeDefinition xmlns=""http://www.docusign.com/restapi"">
...
<documents>
<document>
<name>Agreement.pdf</name>
<documentId>1</documentId>
</document>
</documents>
...
</envelopeDefinition>
Also, make sure the document bytes that your sending in the subsequent part of the request are not encoded.
UPDATE #2
I don't know much about ColdFusion, but this line of your code makes it look like you're (base64) encoding the byte stream that you're including in the Request:
<cfset docData = BinaryEncode(docData,"Base64")>
This could be causing your latest issue, as I don't believe DocuSign will accept an encoded byte stream when it's included in the manner you're currently utilizing.
If you must base64-encode the byte stream, you could add a documentBase64 property under the document element to contain the base64-encoded byte stream. (See page 104 of the DocuSign REST API Guide -- http://www.docusign.com/sites/default/files/REST_API_Guide_v2.pdf.) If you utilized this approach, your request would no longer need to be a 'multipart' request, since the document bytes would be included in the XML portion of the Request Body (not in a subsequent/separate part). Here's an example of what the Request would look like, if the base64-encoded byte stream was included within the documentBase64 element:
POST /restapi/v2/accounts/ACCOUNT_NUMBER/envelopes HTTP/1.1
Host: demo.docusign.net
X-DocuSign-Authentication: {"Username":"SENDER_EMAIL_ADDRESS","Password":"PASSWORD","IntegratorKey":"INT_KEY"}
Content-Type: application/xml
<envelopeDefinition xmlns="http://www.docusign.com/restapi">
<accountId>ACCOUNT_ID</accountId>
<status>sent</status>
<emailSubject>eSignature request</emailSubject>
<emailBlurb>Please sign the document</emailBlurb>
<recipients>
<signers>
<signer>
<email>johnsEmail#outlook.com</email>
<name>John Doe</name>
<recipientId>1</recipientId>
<routingOrder>1</routingOrder>
<tabs>
<signHereTabs>
<signHere>
<documentId>1</documentId>
<pageNumber>1</pageNumber>
<xPosition>10</xPosition>
<yPosition>100</yPosition>
</signHere>
</signHereTabs>
</tabs>
</signer>
</signers>
</recipients>
<documents>
<document>
<name>Agreement.pdf</name>
<documentId>1</documentId>
<fileExtension>pdf</fileExtension>
<documentBase64>BASE64-ENCODED-BYTE-STREAM</documentBase64>
</document>
</documents>
</envelopeDefinition>
Since the document bytes are included within the XML portion of the request, the request no longer needs to be multi-part -- simply pass the XML Request body as I've shown above, and that's it.
The error is most likely due to the extra CRLF you have after the document bytes and before your closing boundary --MYBOUNDARY--. You have this:
#docData#
--MYBOUNDARY--
Try changing that to this without that extra CRLF:
#docData#
--MYBOUNDARY--
For reference you can check out the DocuSign API Walkthrough for sending a signature request on a document. Each of the different language versions shows you how the outgoing request body should be formatted.
I'm creating a company directory using our existing Active Directory information. I'm able to pull all the data I need, but I wanted to use Active Directory for the photo as well.
I found this snippet of code on this blog: http://plus10.blogspot.com/2008/02/coldfusion-cfldap-display-images-stored.html
<!--- imageFile.cfm --->
<cfsilent>
<cfldap action="QUERY"
name="ldap"
attributes="jpegPhoto"
start="dc=[yourdc],dc=com"
filter="sAMAccountName=[loginname]"
server="[yourserver]"
username="[username]"
password="[password]">
<cfscript>
ldapPhoto = toString(ldap.jpegPhoto);
ldapPhoto = binaryDecode(ldapPhoto,"base64");
</cfscript>
</cfsilent><cfcontent type="image/jpeg" variable="#ldapPhoto#">
<!--- to display the image on a page --->
<img src="imageFile.cfm" width="100" height="125" alt="">
I plugged in all my server data and I get the error
The image "....imagefile.cfm" cannot be displayed because it contains errors
Why isn't the image displaying? and how can I correct the code?
I did a <cfdump> on the query and it just shows as "jpegPhoto" not binary data.
I can't post the actual page because it is on an internal network only.
Note, the poster answered her own question. The cfcontent and img tags should be replaced with a cfimage tag using the source attribute and action="writeToBrowser"
crediting the author: src: https://plus10.blogspot.com/2008/02/coldfusion-cfldap-display-images-stored.html
February 12, 2008
ColdFusion CFLDAP - Display images stored in Microsoft Active Directory
Not too hard once I found out how MS stores the data in the photoJpeg field of Active Directory.
<!--- imageFile.cfm --->
<cfsilent>
<cfldap action="QUERY"
name="ldap"
attributes="jpegPhoto"
start="dc=[yourdc],dc=com"
filter="sAMAccountName=[loginname]"
server="[yourserver]"
username="[username]"
password="[password]">
<cfscript>
ldapPhoto = toString(ldap.jpegPhoto);
ldapPhoto = binaryDecode(ldapPhoto,"base64");
</cfscript>
</cfsilent>
<cfcontent type="image/jpeg" variable="#ldapPhoto#">
<!--- to display the image on a page --->
<img src="imageFile.cfm" width="100" height="125" alt="">
I have a form that I would like to submit to a component for processing (CRUD behaviors), the problem is it seems passing multipart/form-data to a component somehow looses the file location. When it gets to the part of the component that should be uploading the file I get the infamous form field did not contain a file error.
I am not 100% sure why this happening but if I submit the form directly to a .cfm page that performs the cffile action everything works as expected, but if the .cfm page does something like:
<cfobject name="process_form" component="processor" />
<cfset result = process_form.upload( form ) />
and the component "processor" tries to do the upload, I get the form field did not contain a file.
My processor looks like:
<cfcomponent name="processor">
<cffunction name="upload" returntype="string">
<cfargument name="form_data" type="struct" />
<cffile action="upload" filefield="#arguments.form_data.file_1#" ...>
[ ... ]
</cffunction>
</cfcomponent>
One thing to note, is if I try use the variable arguments.form_data.file_1 without the # signs around it, I get the error:
The form field arguments.form_data.file_1 did not contain a file.
If I put the # signs around the variable I get:
The form field C:\JRun4\servers\cfusion\SERVER-INF\temp\cfusion-war-tmp\neotmp7350969777287007477.tmp did not contain a file.
Any idea on how to fix this issue? I would rather have all my processing actions inside a component, but right now I can't seem to make that work.
Thanks!
Will
You shouldn't need to use the full variable name when using a cffile tag--you just need the form field name, so something like:
<cffile action="upload" filefield="file_1" ...>
should suffice. The FORM struct field holds the location of the temporary file, but the cffile tag doesn't need that (I'd image that id directly accesses the FORM struct on the backend based on the fieldname you've provided).
I'm trying to post an XML file to a multipart HTML form, it's not working, and I can't figure out what the problem is. It appears that ColdFusion is just not transmitting the file. I've tried posting to the actual form I need to post to as well as a test page that dumps requests, and all that comes through are the form fields.
Here's the relevant part of my code:
<cfhttp url="#endPoint#" method="post" multipart="yes">
<cfhttpparam type="formField" name="file_name" value="test.xml">
<cfhttpparam type="formField" name="user_name" value="test">
<cfhttpparam type="formField" name="password" value="test">
<cfhttpparam type="file" name="test.xml" file="#localfile#">
</cfhttp>
I can confirm that endPoint points to a valid URL, the formField names/values are valid, and that test.xml does indeed exist at the location specified by localfile.
Suggestions? Any input would be appreciated!
Add'l info: I am on CF MX 6.1 if that makes a difference.
EDIT: After reviewing the input below, I did some more testing.
I can confirm that localfile is an absolute path (C:\path_to_my_files\test.xml).
I'm not sure what the target form is running on. I don't know for sure, but I don't think it's ColdFusion.
I built my own HTTP test page using GetHTTPRequestData() as mentioned below, and I think that works ok, though it looks a litle strange to me. Writing GetHTTPRequestData().content to a file lets me see what I'm trying to send. However, the final form still reports that I'm not including a file. Posttestserver.com also reports no file. I've included the result from both my page and the POST test server below.
My test page result:
-------------------------------7d0d117230764
Content-Disposition: form-data; name="file_name"
Content-Type: text/plain; charset=UTF-8
test.xml
-------------------------------7d0d117230764
Content-Disposition: form-data; name="user_name"
Content-Type: text/plain; charset=UTF-8
test
-------------------------------7d0d117230764
Content-Disposition: form-data; name="password"
Content-Type: text/plain; charset=UTF-8
test
-------------------------------7d0d117230764
Content-Disposition: form-data; name="test.xml"; filename="C:\my_files\test.xml"
Content-Type: text/xml
%3C%3Fxml%20version%3D%221%2E0%22%20encoding%3D%22UTF%2D8%22%20%3F%3E%3CjobFeed%3E%3Cjob%3E%3CjobId%3E1234%3C%2FjobId%3E%3CjobTitle%3ETest%20Job%3C%2FjobTitle%3E%3CjobCity%3ETest%20City%3C%2FjobCity%3E%3CjobState%3ETest%20State%3C%2FjobState%3E%3CjobDescription%3ETest%20Description%3C%2FjobDescription%3E%3CjobZip%3E12345%3C%2FjobZip%3E%3CjobUrl%3Ehttp%3A%2F%2Fwww%2Etest%2Ecom%3C%2FjobUrl%3E%3CJobType%3ETEC%3C%2FJobType%3E%3C%2Fjob%3E%3C%2FjobFeed%3E
-------------------------------7d0d117230764--
I've got no idea what this "-------------------------------7d0d117230764" business is.
Here's what I got from the test site:
Headers (Some may be inserted by server)
UNIQUE_ID = TlZTra3sqvkAAECsSBsAAAAL
HTTP_HOST = www.posttestserver.com
HTTP_CONNECTION = close
HTTP_USER_AGENT = ColdFusion
HTTP_ACCEPT_ENCODING = deflate, gzip, x-gzip, compress, x-compress
CONTENT_TYPE = multipart/form-data; boundary=-----------------------------7d0d117230764
CONTENT_LENGTH = 1159
GATEWAY_INTERFACE = CGI/1.1
REQUEST_METHOD = POST
QUERY_STRING =
REQUEST_URI = /post.php
REQUEST_TIME = 1314280365
Post Params:
key: 'file_name' value: 'test.xml'
key: 'user_name' value: 'test'
key: 'password' value: 'test'
== Begin post body ==
== End post body ==
Here it appears I've transmitted no file.
I'm still looking at it, but I'm not seeing the problem. Ideas?
When you specify a type of file, then the file data is sent in the post body, not in a form field name that you can reference. If you check the LiveDocs on CFHTTPPARAM it states for the type="file" attribute:
The absolute path to the file that is sent in the request body.
So as Leigh stated, on your receiving page, you need to use GetHttpRequestData, something like this:
<cfset objRequest = GetHttpRequestData() />
<cfset object = objRequest.Content() />
If the endPoint is a CF page, then I am wondering if something else may be going on. The data is sent in the request body, yes. But with CF pages, it should still parse the information and create a form field for that file. Same as with a regular form upload. In this case the field name would be form["test.xml"]. Could that be part of the issue?
CFDUMP results under MX6.1 and CF9
FIELDNAMES FILE_NAME,USER_NAME,PASSWORD,TEXT.XML
FILE_NAME test.xml
PASSWORD test
TEXT.XML C:\CFusionMX\...\temp\wwwroot-tmp\neotmp6275345679234991.tmp
USER_NAME test
Note: One difference under CF9 was that getHttpRequestData().content is empty. Whereas under MX6 it is still populated. Seems like 6.1 preserves a copy of the data (after processing it) but CF9 does not. Not sure why.
I've created a utf8 encoded RSS feed which presents news data drawn from a database. I've set all aspects of my database to utf8 and also saved the text which i have put into the database as utf8 by pasting it into notepad and saving as utf8. So everything should be encoded in utf8 when the RSS feed is presented to the browser, however I am still getting the weird question mark characters for pound signs :(
Here is my RSS feed code (CFML):
<cfsilent>
<!--- Get News --->
<cfinvoke component="com.news" method="getAll" dsn="#Request.App.dsn#" returnvariable="news" />
</cfsilent>
<!--- If we have news items --->
cfif news.RecordCount GT 0>
<!--- Serve RSS content-type --->
<cfcontent type="application/rss+xml">
<!--- Output feed --->
<cfcontent reset="true"><?xml version="1.0" encoding="utf-8"?>
<cfoutput>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>News RSS Feed</title>
<link>#Application.siteRoot#</link>
<description>Welcome to the News RSS Feed</description>
<lastBuildDate>Wed, 19 Nov 2008 09:05:00 GMT</lastBuildDate>
<language>en-uk</language>
<atom:link href="#Application.siteRoot#news/rss/index.cfm" rel="self" type="application/rss+xml" />
<cfloop query="news">
<!--- Make data xml compliant --->
<cfscript>
news.headline = replace(news.headline, "<", "<", "ALL");
news.body = replace(news.body, "<", "<", "ALL");
news.date = dateformat(news.date, "ddd, dd mmm yyyy");
news.time = timeformat(news.time, "HH:mm:ss") & " GMT";
</cfscript>
<item>
<title>#news.headline#</title>
<link>#Application.siteRoot#news/index.cfm?id=#news.id#</link>
<guid>#Application.siteRoot#news/index.cfm?id=#news.id#</guid>
<pubDate>#news.date# #news.time#</pubDate>
<description>#news.body#</description>
</item>
</cfloop>
</channel>
</rss>
</cfoutput>
<cfelse>
<!--- If we have no news items, relocate to news page --->
<cflocation url="../news/index.cfm" addtoken="no">
</cfif>
Has anyone any suggestions? I've done loads of research but can't find any answers :(
Thanks in advance,
Chromis
Get rid of your escaping code and use XMLFormat instead:
<item>
<title>#XMLFormat(news.headline)#</title>
<link>#Application.siteRoot#news/index.cfm?id=#XMLFormat(news.id)#</link>
<guid>#Application.siteRoot#news/index.cfm?id=#XMLFormat(news.id)#</guid>
<pubDate>#XMLFormat(news.date)# #XMLFormat(news.time)#</pubDate>
<description>#XMLFormat(news.body)#</description>
</item>
View XMLFormat livedoc page.
This worked for me, simply combine into one cfcontent tag and append charset=utf-8.
<cfcontent type="text/xml; charset=utf-8" reset="yes" />
Your escaping function is too simple. You need to change & to & first.
If you use named entities (i.e. £) that is cause of the error.
Sanitize every input when it is entered in the database, that way should simplify the display of such data afterwards.
If you are on Adobe ColdFusion 9 or above, consider using CFFEED with the "escapeChars" attribute to create your RSS (CF8 also supports CFFEED, but not that attribute).
http://help.adobe.com/en_US/ColdFusion/9.0/CFMLRef/WSc3ff6d0ea77859461172e0811cbec22c24-7675.html