Generic Form Processing / Emailing In ColdFusion

<!---
	The email address to which the email will be sent. If you
	don't want to put email addresses in the public form, you
	can always put it in the Default attribute of this tag.
--->
<cfparam
	name="FORM.to_email"
	type="string"
	/>
 
<!---
	The email address from which the email will be sent. If
	this field is not included, the to_email address will
	automatically be used for both the to and from field.
--->
<cfparam
	name="FORM.from_email"
	type="string"
	default="#FORM.to_email#"
	/>
 
<!--- The subject text of the outgoing email. --->
<cfparam
	name="FORM.subject"
	type="string"
	/>
 
<!---
	The url to which we should forward the page control once
	the email has been sent out. If this is not included, the
	page will just close out when the processing is done.
	NOTE: This URL must either be an absolute url or one that
	is relative to THIS PROCESSING TEMPLATE.
--->
<cfparam
	name="FORM.success_url"
	type="string"
	default=""
	/>
 
<!---
	The full path directory in which uploaded files will be
	stored so that they can be attached to the outgoing email.
	If you are going to use this, remember to set your FORM
	encryption type to: enctype="multipart/form-data";
	otherwise, the files will not submit as uploaded files.
 
	If you don't want to put the temp file path in the form,
	you can put it in the default value here. That way, no
	public user would see anything about your server's
	directory structure.
--->
<cfparam
	name="FORM.temp_directory"
	type="string"
	default=""
	/>
 
<!---
	This is the comma-delimited list of fields to include in
	the outgoing email. If you do not include this value, then
	all fields will be included in an arbitrary order.
--->
<cfparam
	name="FORM.field_list"
	type="string"
	default=""
	/>
 
 
<!---
	ASSERT: At this point, we have paramed the data in the
	FORM scope. Any data that was required but did not exist
	will have thrown an error.
--->
 
 
<!---
	Copy the FORM data into the another structure so that we
	can remove the special Email-related data keys without
	echoing them in the email body.
--->
<cfset REQUEST.EmailData = Duplicate( FORM ) />
 
<!--- Delete the email meta data. --->
<cfset StructDelete( REQUEST.EmailData, "fieldnames" ) />
<cfset StructDelete( REQUEST.EmailData, "to_email" ) />
<cfset StructDelete( REQUEST.EmailData, "from_email" ) />
<cfset StructDelete( REQUEST.EmailData, "subject" ) />
<cfset StructDelete( REQUEST.EmailData, "success_url" ) />
<cfset StructDelete( REQUEST.EmailData, "temp_directory" ) />
<cfset StructDelete( REQUEST.EmailData, "field_list" ) />
 
 
<!---
	Build an array to hold the form fields that might have
	uploaded files. This will be translated into future
	CFMailParam tags.
--->
<cfset REQUEST.Files = ArrayNew( 1 ) />
 
<!---
	Now that we have a clean EmailData structure, we want to
	see if the user has any files that they are uploading.
	To do this, we are going to see if any of the form values
	translates to a temporary file (which is how ColdFusion
	displays uploaded files - the form field value is not the
	uploaded file, but rather the temp file on the server).
--->
<cfif Len( FORM.temp_directory )>
 
	<!--- Loop over all the form fields. --->
	<cfloop
		item="REQUEST.Key"
		collection="#REQUEST.EmailData#">
 
		<!---
			Check to see if this form field has a temp file.
			This will be a value that starts with a drive letter
			such as D: and ends with a .tmp file extension.
		--->
		<cfif REFind( "^\w:.+?\.tmp$", REQUEST.EmailData[ REQUEST.Key ] )>
 
			<!---
				This form field holds the temporary path of an
				uploaded file. We don't want this to be part of
				the primary form fields so, remove this key from
				the form data.
			--->
			<cfset StructDelete( REQUEST.EmailData, REQUEST.Key ) />
 
			<!---
				Upload the file to the user-defined temp
				directory. This is really just moving it
				from ColdFusion's temp directory to our
				temp directory and renaming it.
			--->
			<cffile
				action="upload"
				destination="#FORM.temp_directory#"
				filefield="#REQUEST.Key#"
				nameconflict="makeunique"
				/>
 
			<!---
				Now that the file has been uploaded, add the
				target file path to the array of files that will
				be attached to the outgoing email.
			--->
			<cfset ArrayAppend(
				REQUEST.Files,
				"#CFFILE.ServerDirectory#/#CFFILE.ServerFile#"
				) />
 
		</cfif>
 
	</cfloop>
 
</cfif>
 
 
<!---
	Check to see if the user had defined the list of fields to
	use. If not, then just get the list of fields from the data
	struct (which will be returned in some arbitrary order).
--->
<cfif NOT Len( FORM.field_list )>
 
	<!--- Get key list for fields. --->
	<cfset FORM.field_list = StructKeyList( REQUEST.EmailData ) />
 
</cfif>
 
 
<!---
	Since this is a generic form handler, we don't have very
	much formatting. That gives us the ability to easily create
	both types of mail body - HTML and Plain Text.
--->
 
 
<!--- Build the HTML version of the email. --->
<cfsavecontent variable="REQUEST.HtmlBody">
	<cfoutput>
 
		<p>
			Email processed on
			#DateFormat( Now(), "mmm d, yyyy" )# at
			#TimeFormat( Now(), "hh:mm TT" )#
		</p>
 
		<h3>
			Form Data
		</h3>
 
		<dl>
			<!--- Loop over the form data. --->
			<cfloop
				index="REQUEST.Key"
				list="#FORM.field_list#"
				delimiters=",">
 
				<dt>
					#REQUEST.Key#
				</dt>
				<dd>
					#HtmlEditFormat(
						REQUEST.EmailData[ Trim( REQUEST.Key ) ]
						)#
				</dd>
 
			</cfloop>
		</dl>
 
 
		<!---
			Add some additional data about the user and where
			the form came from. This might help with debugging.
		--->
 
		<h3>
			User Data
		</h3>
 
		<dl>
			<dt>
				Referrer:
			</dt>
			<dd>
				#CGI.http_referer#
			</dd>
			<dt>
				IP:
			</dt>
			<dd>
				#CGI.remote_addr#
			</dd>
		</dl>
 
	</cfoutput>
</cfsavecontent>
 
 
<!---
	Build the Text version of the email. Don't worry about the
	leading white spaces in each line. We are going to trim
	that out afterwards (we are putting it in now so that our
	code page has nice flow to it).
--->
<cfsavecontent variable="REQUEST.TextBody">
	<cfoutput>
 
		Email processed on #DateFormat( Now(), "mmm d, yyyy" )# at #TimeFormat( Now(), "hh:mm TT" )#
 
		-- FORM DATA --
 
		<!--- Loop over the form data. --->
		<cfloop
			index="REQUEST.Key"
			list="#FORM.field_list#"
			delimiters=",">
			#Trim( REQUEST.Key )#
			#HtmlEditFormat( REQUEST.EmailData[ Trim( REQUEST.Key ) ] )#
		</cfloop>
 
 
		-- USER DATA --
 
		Referrer:
		#CGI.http_referer#
		IP:
		#CGI.remote_addr#
 
	</cfoutput>
</cfsavecontent>
 
<!---
	Because plain text emails keep all the white space, we
	have to remove the leading tabs from each line of the above
	content. This can be done with an easy multiline regex.
--->
<cfset REQUEST.TextBody = REReplace(
	REQUEST.TextBody,
	"(?mi)^\t+|\t+$",
	"",
	"all"
	) />
 
 
<!---
	Send out the email. When using both the plain text and the
	html mail parts, be sure to use the HTML version LAST. If
	you put it first, Google Mail will not render the email.
--->
<cfmail
	to="#FORM.to_email#"
	from="#FORM.from_email#"
	subject="#FORM.subject#">
 
	<!--- Include the Text version. --->
	<cfmailpart type="text/plain">#REQUEST.TextBody#</cfmailpart>
 
	<!--- Include the HTML version. --->
	<cfmailpart type="text/html">#REQUEST.HtmlBody#</cfmailpart>
 
	<!--- Loop over files that we need to attach. --->
	<cfloop
		index="REQUEST.FileIndex"
		from="1"
		to="#ArrayLen( REQUEST.Files )#"
		step="1">
 
		<!--- Attach file. --->
		<cfmailparam
			file="#REQUEST.Files[ REQUEST.FileIndex ]#"
			disposition="attachment"
			/>
 
	</cfloop>
</cfmail>
 
 
 
<!---
	Now that the email has been sent out, check to see if we
	have a success url to which me need to forward the user.
--->
<cfif Len( FORM.success_url )>
 
	<!--- Forward user. --->
	<cflocation
		url="#FORM.success_url#"
		addtoken="false"
		/>
 
</cfif>
 
 
<!---
	ASSERT: If we have made it this far, then the user did
	not provide a success URL. Therefore, we need to provide
	our own simple success page.
--->
 
 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
	<title>Your Form Has Been Processed</title>
</head>
<body>
 
	<h1>
		Your Form Has Been Processed
	</h1>
 
	<p>
		Thank you for taking the time to fill out our form.
		It has been processed and sent to the appropriate
		people.
	</p>
 
	<p>
		<cfoutput>
			<a href="#CGI.http_referer#">Go back</a>
		</cfoutput>
	</p>
 
</body>
</html>

For Cut-and-Paste