Ask Ben: Adding A Query String Pair Value To Existing HTML Using ColdFusion (Alternate Version 2)

<html>
<head>
	<title>Alter URL Demo</title>
</head>
<body>
 
<p>
Hey man, if you are looking for some good images, you should probably try out the search page on <a href="http://www.searchgalleries.com?source=bennadel.com" target="_blank">Search Galleries</a>. It's pretty darn comprehensive and seems to keep track of all the free galleries that you will ever need. If you want to mess with the URL, its easy; just add a "q" query string value to the search url. The general site search URL is <a href="http://www.searchgalleries.com/search/" target="_blank">http://www.searchgalleries.com/search/</a>. So, then, to add a query value to it, such as "mature", you would simply add the query string "q=mature" to the url: <a href="http://www.searchgalleries.com/search/?q=mature#links" target="_blank">http://www.searchgalleries.com/search/?q=mature</a>. You can even search for more than one value at a given time. So, for instance, if you want to search for mature brunette women, you would put go to the URL:
<a href="http://www.searchgalleries.com/search/?q=mature+brunette#links" target="_blank">http://www.searchgalleries.com/search/?q=mature+brunette</a>. Notice that "mature" and "brunette" are separated by a "+" sign. This is the URL encoded form of a space.
</p>
 
</body>
</html>
 
<!--- Get the page context. --->
<cfset objPageContext = GetPageContext() />
 
<!--- Get the page buffer. --->
<cfset objBuffer = objPageContext.GetOut().GetBuffer() />
 
<!---
	Get the content buffer string. This will give us everything
	that has NOT yet been flushed to the browser. This is just
	how I am doing it for this demo and is NOT the only way to
	perform this task. Since this page is small, (and is being
	tested), we can safely assume that the content has not yet
	been flushed to the client.
--->
<cfset strContent = objBuffer.ToString() />
 
<!---
	When examing the links, there a couple of case scenarios
	that we have to consider. Some URLs might have an existing
	query string. Other might not. Both might have a HASH value
	(page anchor) and URLs with an existing query string may
	already have the name-value pair that we are trying to
	insert.
 
	My original attempt used some fairly small regular
	expressions. I fear that to try and handle this entirely in
	regular expressions would become unreadable. Instead, I am
	going to go the Pattern / Matcher route. This way, we can
	examine each URL as it comes in.
 
	Let's create a pattern that matches any URL within an HREF.
	This pattern will require at least one none-quote character
	in its URL. It will also require quoated URLs.
--->
<cfset objPattern = CreateObject(
	"java",
	"java.util.regex.Pattern"
	).Compile(
		"(?<=href="")([^""]+)(?="")"
		) />
 
<!---
	Get a pattern matcher based on the content that we have
	pulled out of our page buffer.
--->
<cfset objMatcher = objPattern.Matcher( strContent ) />
 
<!---
	Create a string buffer into which we will store our update
	HTML content with updated URLs.
--->
<cfset objBuffer = CreateObject(
	"java",
	"java.lang.StringBuffer"
	).Init() />
 
 
<!--- Loop over all the matched links. --->
<cfloop condition="objMatcher.Find()">
 
	<!--- Get the matched URL. --->
	<cfset strURL = objMatcher.Group() />
 
	<!---
		First, we want to make sure that we are not duplicating
		our efforts. Check to see if the URL already contains
		our name/value pair.
	--->
	<cfif NOT FindNoCase( "source=bennadel.com", strURL )>
 
		<!---
			Split the URL on the hash sign. Even if there is no
			hash sign, this should result in an array with at
			least ONE index (the pre-hash value).
		--->
		<cfset arrUrlParts = strUrl.Split( "##" ) />
 
		<!---
			Save the first part (possibly the only part) back
			into the URL value. Then we can deal with that on
			its own and add any hash value back in later.
		--->
		<cfset strURL = arrUrlParts[ 1 ] />
 
		<!---
			Check to see if the URL contains an existing
			query string.
		--->
		<cfif Find( "?", strURL )>
 
			<!---
				Since there is already a query string, we can
				append ours to the query sting values.
			--->
			<cfset strURL = (strURL & "&source=bennadel.com") />
 
		<cfelse>
 
			<!---
				Since there is no query string yet, we can
				create one with our name-value pair as its
				only value.
			--->
			<cfset strURL = (strURL & "?source=bennadel.com") />
 
		</cfif>
 
 
		<!---
			Now that we have altered are base URL in the most
			appropriate way, let's see if we had a hash value
			to add back in. This will only be the case if we
			had a second parts index that has a value.
		--->
		<cfif (ArrayLen( arrUrlParts ) GT 1)>
 
			<!---
				Append the hash value to our new URL. When
				doing this, be sure to add the Hash sign back
				in. This was stripped out during our Split()
				method call.
			--->
			<cfset strURL = (
				strURL &
				"##" &
				arrUrlParts[ 2 ]
				) />
 
		</cfif>
 
	</cfif>
 
 
	<!---
		ASSERT: At this point, we have updated the strURL value
		or we have left it alone. Either way, we are ready to
		add it back into the string buffer. When doing this, be
		sure to escape any group references and character
		escapes that might exist in the string.
	--->
	<cfset objMatcher.AppendReplacement(
		objBuffer,
		strURL.ReplaceAll(
			"([\\\$]{1})",
			"\\$1"
			)
		) />
 
</cfloop>
 
 
<!---
	Now that we have matched all the URLs, add what ever
	content remains back into the results buffer.
--->
<cfset objMatcher.AppendTail( objBuffer ) />
 
<!--- Clear the existing content buffer. --->
<cfset objPageContext.GetOut().ClearBuffer() />
 
<!---
	Output the updated HTML. When doing this, we have to
	take our buffer and compile it down to a string.
--->
<cfset WriteOutput( objBuffer.ToString() ) />

For Cut-and-Paste