Skip to main content
Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.

Fixing Protocols In My ColdFusion Custom Tag DSL For HTML Emails

By Ben Nadel on
Tags: ColdFusion

Last week, I looked at the fact that [Yahoo! Mail won't render href attributes with encoded protocols]. In that post, I created a ColdFusion user defined function (UDF) to un-encoded the https:// portion of the href attribute. After letting that approach bake in production at InVison for a while without an issues, I've decided to move the fix into my ColdFusion custom tag DSL for HTML emails. With this new implementation, I've pushed the logic down into the <html:a> and <html:src> custom tag implementations so that the developer can continue to use these tags without any changes to their code.

View this code in my ColdFusion Custom Tag Emails project on GitHub.

To see this in action, I've created a new example that include both an anchor tag and an image tag that each use the encodeForHtmlAttribute() when rendering their href and src tags, respectively:

<!--- Import custom tag libraries. --->
<cfimport prefix="core" taglib="./core/" />
<cfimport prefix="html" taglib="./core/html/" />

<!--- // ------------------------------------------------------------------------- // --->
<!--- // ------------------------------------------------------------------------- // --->

<core:Email
	subject="Testing encoded HREF and SRC attributes."
	teaser="Because Yahoo! Mail">
	<core:Body>

		<html:h1>
			Testing encoded HREF and SRC attributes
		</html:h1>

		<html:p>
			It seems that some email clients
			<html:strong>*cough*</html:strong> Yahoo! Mail <html:strong>*cough*</html:strong>
			will strip-out <html:code>href</html:code> attributes if they contain an
			encoded protocol. As such, I've updated the <html:code>&lt;A&gt;</html:code>
			and <html:code>&lt;IMG&gt;</html:code> tags to use some additional logic in
			their rendering to <html:em>un-escape</html:em> both <html:code>href</html:code>
			and <html:code>src</html:code> attributes.
		</html:p>

		<html:h2>
			Some test tags:
		</html:h2>

		<html:p>
			<html:a href="#encodeForHtmlAttribute( 'www.bennadel.com/people/who-rock-my-world.htm' )#">A test anchor</html:a>
		</html:p>

		<html:p>
			<html:img
				src="#encodeForHtmlAttribute( 'https://bennadel-cdn.com/images/header/photos/jessica_eisner.jpg' )#"
				width="500"
				height="254"
			/>
		</html:p>

	</core:Body>
</core:Email>

As you can see, there's nothing special in this mark-up logic. The developer is just passing-in encoded attribute values the way they would normally do in order to prevent malicious values from breaking the HTML tree structure. And, if we render this HTML email body in the browser and look at the rendered page source, we'll see that the anchor tag href attribute is:

href="https://www .bennadel.com&#x2f;people&#x2f;who-rock-my-world.htm"

... and the image tag src attribute is:

src="https://bennadel-cdn.com&#x2f;images&#x2f;header&#x2f;photos&#x2f;jessica_eisner.jpg"

Notice that in both cases, the https:// value is fully un-encoded while the rest of the "special characters" within the attribute values remain fully encoded.

To see how this is implemented, here's the current source code for the <html:a> anchor tag - you'll see that the href attribute value is being passed-through a new function, fixProtocol():

<!---
	CAUTION: For INLINE ELEMENTS, we have to be fanatical in the way that we manage the
	output of the ColdFusion custom tag since we don't want to inadvertently add any
	leading or trailing whitespace around the tag content. As such, we're going to wrap
	the output in a custom tag that will trim the output.
---><cfmodule template="../TrimOutput.cfm">

<!--- Define custom tag attributes. --->
<cfparam name="attributes.class" type="string" default="" />
<cfparam name="attributes.href" type="string" />
<cfparam name="attributes.decoration" type="boolean" default="true" />
<cfparam name="attributes.style" type="string" default="" />
<cfparam name="attributes.target" type="string" default="_blank" />

<!--- // ------------------------------------------------------------------------- // --->
<!--- // ------------------------------------------------------------------------- // --->

<cfswitch expression="#thistag.executionMode#">
	<cfcase value="end">
		<cfoutput>

			<cfmodule
				template="../Styles.cfm"
				variable="inlineStyle"
				entityName="a"
				entityClass="#attributes.class#"
				entityStyle="#attributes.style#">
				<cfif ! attributes.decoration>
					text-decoration: none ;
				</cfif>
			</cfmodule>

			<a
				href="#fixProtocol( attributes.href )#"
				target="#attributes.target#"
				class="#trim( 'html-entity-a #attributes.class#' )#"
				style="#inlineStyle#"
				>#thistag.generatedContent#</a>

			<!--- Reset the generated content since we're overriding the output. --->
			<cfset thistag.generatedContent = "" />

		</cfoutput>
	</cfcase>
</cfswitch>

<!--- End of fanatical whitespace management. --->
</cfmodule><cfexit method="exitTemplate" />

<!--- // ------------------------------------------------------------------------- // --->
<!--- // ------------------------------------------------------------------------- // --->

<cfscript>

	/**
	* I unencode the protocol contained within the given attribute. Some email clients,
	* like Yahoo! Mail, will strip-out the HREF attribute if it contains an encoded
	* protocol.
	* 
	* @encodedAttribute I am the HREF value being "fixed".
	*/
	public string function fixProtocol( required string encodedAttribute ) {

		var unencodedSuffix = "://";
		var encodedSuffix = encodeForHtmlAttribute( unencodedSuffix );

		return(
			reReplaceNoCase(
				arguments.encodedAttribute,
				"^([a-z0-9]+)#encodedSuffix#",
				"\1#unencodedSuffix#",
				"one"
			)
		);

	}

</cfscript>

And now, my ColdFusion custom tag DSL (Domain Specific Language) for HTML emails should render anchor tags properly in Yahoo! Mail. And, do so in a way that is completely transparent to the developer.



Reader Comments

What has two thumbs and hopes you leave a comment? This Guy! (Ben Nadel).

Post A Comment

You — Get Out Of My Dreams, Get Into My Blog
Live in the Now
Oops!
Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.