Paired Custom Tags
All of the custom tags you've seen here so far are called empty custom tags because they do not span a region of content or code. Paired custom tags do span, or "wrap," a region of content or code, and act upon that region in some way. You deal with this concept every day; for example:
Th218 Bold element is a typical paired tag that acts on the span of content between its opening and closing tags. This may seem like baby steps to you, but the exact same process is applied to ColdFusion paired custom tags.Essentially, a paired tag of any kind always executes twice once for the opening tag (Start mode) and once for the closing tag (End mode)and performs a distinct operation in both Start mode and End mode. Th218 Bold tag, for example, in the opening tag establishes the beginning of the content to be modified; the closing tag establishes the end of that content as well as instructs the browser to boldface the content. As you continue reading, keep in mind this concept of a paired tag executing twice and doing something different in each execution.
<b>I am the very model of a modern major-general.</b>
The Execution Cycle
A paired custom tag goes through three modes of execution, in order, when it is called:
1. | Start mode, which executes immediately after the opening tag, and triggers the first execution of the paired custom tag. |
2. | Inactive mode, which is the idle period between the opening and closing tags. |
3. | End mode, which executes immediately after the closing tag, and triggers the second execution of the paired custom tag. |
In a ColdFusion paired custom tag, the opening tag triggers the first execution, during which ThisTag.ExecutionMode is "Start". ThisTag is a special scope that exists within custom tags and refers to specific properties of the custom tag, such as its current execution mode, whether the custom tag has an end tag (as a paired tag would), and more, as you'll see later in this chapter.After the first execution of the paired custom tag, the tag goes into Inactive mode during which time the contents in the body of the tag are accumulated into ThisTag.GeneratedContent. The closing tag then triggers the second execution of the paired custom tag, during which ThisTag.ExecutionMode is "End".You separate Start mode logic from End mode logic using a simple If construct:
We only test for ThisTag.ExecutionMode EQ "Start" and assume that the <cfelse> clause refers to the End mode, even though there are two other execution modes besides Start. This is because Inactive mode isn't actually executed; it's an "idle" mode during which the content is accumulated into ThisTag.GeneratedContent.Like any other type of custom tag, a paired custom tag may take attributes that can be passed to the logic in the custom tag to modify its behavior. These attributes become part of the Attributes scope just as they do in other types of custom tags. Because attributes are always passed in an opening tag, those attributes are "seen" by and can therefore be used by logic in the Start mode of the paired custom tag. Once an execution mode gains access to something, any other execution modes that follow will also have access; therefore, because the Start mode can access the attributes passed into the tag, so will the Inactive and End modes.NOTEThe Inactive mode doesn't do any processing of its own, but you'll learn later that it passes process flow to any child tags nested in the body (between the opening and closing tags) of the paired tag. Those child tags can then gain access to the objects seen by their parent tag's Start mode.The Start mode may execute logic or output content, but it can neither see nor modify any content or code in the body of the paired tag. The Inactive mode is the first point at which the content in the body of the paired tag can be seen, but the only thing the Inactive mode can do with this content is accumulate it into a special variable. The End mode, which can see, access, and modify everything that happened in the execution modes before it, is therefore the most powerful execution mode, and as such it's often where the majority of paired custom tag logic is executed.Figure 18.1 sums up the execution modes that a paired tag goes through and what typically happens in each mode.
<cfif ThisTag.ExecutionMode EQ "Start">
<cfelse>
</cfif>
Figure 18.1. The execution cycle of a paired custom tag.

[View full width]<cf_TruncateQuote numberOfCharacters="125">If you'd like to try this out, type the above call to <cf_TruncateQuote> in a file named Test.cfm.Listing 18.1 shows cf_TruncateQuote's code, and Figure 18.2 displays the result of calling it. As long as you save this custom tag file within the same directory as the page that calls it (Test.cfm), you can run the calling page and see the results.
Four score and seven years ago our fathers brought forth on this continent a new nation,conceived in liberty and dedicated to the proposition that all men are created equal. Now
we are engaged in a great civil war, testing whether that nation or any nation so
conceived and so dedicated can long endure. We are met on a great battlefield of that war.
We have come to dedicate a portion of that field as a final resting-place for those who
here gave their lives that that nation might live. It is altogether fitting and proper
that we should do this.
</cf_TruncateQuote>
Listing 18.1. truncateQuote.cfmImplementing a Paired Tag
[View full width]
<!--- Author: Adam Phillip Churvis -- ProductivityEnhancement.com --->
<!--- Truncates and formats a body of text --->
<cfparam name="Attributes.numberOfCharacters" type="numeric" default="300">
<cfif ThisTag.ExecutionMode EQ "Start">
<div style="border: 1px solid Black; width:300px;">
<cfelse>
...
</div>
<cfset ThisTag.GeneratedContent = Left(ThisTag.GeneratedContent, Attributes.numberOfCharacters)>
</cfif>
Figure 18.2. The result of calling cf_TruncateQuote on The Gettysburg Address.

The Concept of GeneratedContent
As mentioned earlier, during Inactive mode a paired custom tag accumulates the content that spans the range between the opening and closing tags into a special variable named ThisTag.GeneratedContent. Since a paired custom tag has only two opportunities to run (once in Start mode and once in End mode), and since you can't yet see or modify ThisTag.GeneratedContent in the Start mode, the only place you can modify ThisTag.GeneratedContent is in the End mode. In the same way that the closin220 Bold tag modifies the contents between it and the opening tag, so does ColdFusion by manipulating ThisTag.GeneratedContent in the End mode.But why does the ellipsis appear at the end? It's because of the way ColdFusion prepares and outputs the results of running a paired custom tagsort of like preparing a sandwich in reverse (from the top down) and then serving it, as Figure 18.3 shows.
Figure 18.3. The "sandwich" model of how paired custom tags generate output.

Custom Tags That May Be Called as Paired or Empty
There are times when you might need similar functionality that applies to an entire ColdFusion page or only a section of a page, but that behaves a little differently in each case. For example, you might want to create a custom tag that makes checking for authentication and authorization easy to apply. So if you wanted to ensure that users attempting to access a ColdFusion page were both authenticated and authorized with the ADMIN role, you could call the custom tag as an empty (nonpaired) tag at the top of the page like this:
And if you wanted to prevent only a portion of a page from being displayed unless the user is both authenticated and authorized with the ADMIN role, you could call the custom tag as a paired tag like this:
<cf_Auth roles="ADMIN">
<p>You won't see this page unless you're logged in and have been assigned the ADMIN role.</p>
[View full width]<cf_Auth roles="ADMIN">But you want the tag to react differently in each case. If it's called as an empty tag and the user isn't authorized, you want to redirect to a page that tells the user they have insufficient privileges; on the other hand, if the tag is called as a paired tag under the same conditions, then anything in the body of the tag is disregardedboth content and code. To determine whether a custom tag is called as an empty tag or a paired tag, you check the value of ThisTag.HasEndTag.
<p>You won't see this section unless you're logged in and have been assigned the ADMINrole.</p>
</cf_Auth>
<p>You'll see this section no matter who you are, and you don't have to be logged in,either.</p>
ThisTag.HasEndTag
As mentioned earlier, the ThisTag scope contains a variable named ThisTag.HasEndTag that describes whether or not a custom tag has a corresponding closing tag (i.e., it is a paired tag). Here's how you would use HasEndTag in our scenario:
Although this code tests only the authentication portion of the security check, it gives you a clear idea of how you can create a custom tag that can adapt its behavior based on how it is called, either as an empty tag or as a paired tag.
<!--- Check to see if the user is logged in --->
<cflogin>
<cfif ThisTag.HasEndTag>
<!--- If this tag is called as a paired tag and the user is not logged in,
exit the tag now to prevent execution of any code between the start and
end tags. --->
<cfexit method="ExitTag">
<cfelse>
<!--- If this tag is not called as a paired tag and the user is not logged
in, redirect to the login form. --->
<cflocation url="#Application.urlRoot#/login/LoginForm.cfm" addtoken="No">
</cfif>
</cflogin>
Using CFEXIT to Control Custom Tag Processing Flow
Paired custom tags aren't relegated to only modifying content; they can also prevent code in the body of the paired tag from being executed, or they can control the flow of logic in such code. Let's see how this works.Notice the <cfexit method="ExitTag"> in the preceding code. It tells the flow of logic to ignore the remainder of the custom tag and continue immediately after the end tag. So if the user hasn't logged in yet, the <cflogin> section executes and, if the custom tag was called as a paired tag, it ignores the rest of the custom tag, its execution cycles, its outputeverything. Any content wrapped with this custom tag will not display, and any wrapped code will not be executed.Keep this principle in mind as you review Listing 18.2, which is the completed <cf_Auth> custom tag.
Listing 18.2. Auth.cfmUsing CFEXIT to Control Custom Tag Processing Flow
<cfexit> has three possible methods: ExitTag, which exits the entire custom tag; ExitTemplate, which exits the current execution mode of the custom tag and continues with the next; and Loop, which exits the End mode and starts over again in the Inactive mode (most often used to repetitively execute nested child tags). As you'll see later in this chapter, if the body of the paired tag contains nested child tags, Inactive mode passes processing to the first such child tag.
<!--- Author: Adam Phillip Churvis -- ProductivityEnhancement.com --->
<!--- Ensures that user is authenticated and authorized --->
<cfparam name="Attributes.roles" type="string" default=">
<cfif ThisTag.ExecutionMode EQ "Start">
<!--- Check to see if the user is logged in --->
<cflogin>
<cfif ThisTag.HasEndTag>
<!--- If this tag is called as a paired tag and the user is not logged in,
exit the tag now to prevent execution of any code between the start and
end tags. --->
<cfexit method="ExitTag">
<cfelse>
<!--- If this tag is not called as a paired tag and the user is not logged
in, redirect to the login form. --->
<cflocation url="#Application.urlRoot#/login/LoginForm.cfm" addtoken="No">
</cfif>
</cflogin>
<!--- Check to see if the user must be a member of one or more roles --->
<cfif Len(Trim(Attributes.roles)) GT 0>
<!--- We must now see if at least one of the user's roles match those in
Attributes.roles --->
<!--- Loop over the list of required roles --->
<cfset inRole = FALSE>
<cfloop list="#Attributes.roles#" index="currentRole">
<cfif IsUserInRole(currentRole)>
<!--- If Attributes.memberOf is "AnyOfTheseRoles", and there is a match,
the user may access content. --->
<cfset inRole = TRUE>
<cfbreak>
</cfif>
</cfloop>
<!--- Check to see if the user may access content. --->
<cfif NOT inRole>
<cfif ThisTag.HasEndTag>
<!--- If this tag is called as a paired tag and the user cannot access
content, exit the tag now to prevent execution of any code between the
start and end tags. --->
<cfexit method="ExitTag">
<cfelse>
<!--- If this tag is not called as a paired tag and the user cannot
access content, redirect to the login form. --->
<cflocation url="#Application.urlRoot#/login/InsufficientPrivileges.cfm"
addtoken="No">
</cfif>
</cfif>
</cfif>
</cfif>
METHOD | LOCATION | BEHAVIOR |
---|---|---|
ExitTag | Calling page | Terminate processing |
Start mode | Continue after end tag | |
End mode | Continue after end tag | |
ExitTemplate | Calling page | Terminate processing |
Start mode | Jump down to Inactive mode | |
End mode | Continue after end tag | |
Loop | Calling page | (Error) |
Start mode | (Error) | |
End mode | Jump back into Inactive mode |