Articles Archive
Articles Search
Director Wiki
 

Integrating with Cold Fusion

May 4, 1999
by Zac Belado

Shockwave is, perhaps, one of the most misunderstood technologies on the Web. This is primarily because most people's experience of Shockwave has been the games and eyecandy that get featured on Shockwave enabled sites and the Shockwave Site of the Day. People see games and they assume, incorrectly, that Shockwave isn't capable of creating "mature" web applications.

One of Shockwave's often overlooked strengths is the ability to provide interfaces to server side database applications. Shockwave is ideal for creating interfaces to database applications because of the unique advantages that it has.

Consider the following example.

Company X wants to provide a web based display of their products. Visitors to their web-site will be able to view a list of product categories, select a product category to view the individual products and then select a specific product in order to view its details.

Typically this would mean that the user would be presented with 3 different web pages (which would also mean three different browser refreshes). The web and application server would be required to generate and format three different sets of data and the user would have to continually jump between pages if they wanted to browse different categories or products.

A much simpler solution is to package all this functionality into a single Shockwave application. To show why this is more effective, let's create a small Shockwave application that will interact with the databases that drive this website.

The Plan

Our Shockwave app will do the following

Additionally it will keep each level of data persistent on the screen so that the user can easily navigate through the data without requiring additional database queries.

The man behind the curtain

Director Online uses Allaire's Cold Fusion to generate the dynamic content on the site. Cold Fusion is a server side application that uses a set of custom tags (called CFML which is similar to HTML) to create database queries and then return the data that the server gets from the database.

A very simple example is

<CFQUERY NAME="getAuthor" DATASOURCE="aDataBaseName">
SELECT DISTINCT authorName
FROM thisDatbase
</CFQUERY>
<cfoutput query="getAuthor">
#authorName#
</cfoutput>

This code queries a database for all the distinct author names and then displays them all. (A good introduction to SQL the language that drives Cold Fusion and many other database systems can be found at http://www.geocities.com/ResearchTriangle/Node/9672/sqltut.html). A version of this page that will return a list of the authors who have written for DOUG is here. Take a second to examine the format in which the data gets returned in the browser window and also to view the page's source to see how the Cold Fusion server returns the data. The data as viewed in the browser window is a mess but the source code simply has each author's name on a separate line.

Unless you provide HTML code to format the data the Cold Fusion server will simply return the values stored in the database. This is somewhat useless if you want to create attractive web pages, but is great if you just want the data. All we need to know, in order to program our Shockwave app, is the format that the data is going to be returned.

Talking to the Cold Fusion server

In order to get the data from the Cold Fusion server our Shockwave app is going to make getNetText calls to CFML pages (Cold Fusion Markup Language) that will request the data from the server. There are, in simple terms, two ways to communicate with a Cold Fusion server. Either by requesting a page that is self-contained (it needs no other data to process its calls to the database) or by passing data to the server in the URL of the page we are requesting which the page will then use to create a unique database query. An example of the former is

test/getAuthor.cfm

and an example of the latter is


test/generateAuthorDetails.cfm?name=Zac%20Belado

In the first example the user will be presented with whatever data the page is coded to prepare. In the second case the URL provides the server with additional information, in this case the name of the author, which it then uses to modify its database query. Cold Fusion uses standard URL encoding schemes, so you add additional parameters to the URL by separating them with an ampersand. You can also send data to a Cold Fusion server using web page form elements (as you would with a traditional Perl CGI) but that format isn't applicable to our Shockwave application.

In our specification we have indicated that we will need three distinct Cold Fusion pages: a self contained page that will just return a distinct list of authors, a page to return all the articles written by a particular author and a final page to return either a description or short blurb for a distinct article. All that we need to write our Shockwave app is the format of the URLs and the format in which the data is returned by the Cold Fusion server.

URLs

The three URLs are

getAuthor.cfm [sample]
This page returns a list of the authors

generateAuthorDetails.cfm?name=<author name> [sample]
This page requires a name to be provided and then returns the data for each article written by that author.

getBlurb.cfm?id=<article id>&type=<type of blurb> [sample]
This final page requires an article ID number (which is supplied by generateAuthorDetails.cfm) and an optional type to differentiate between full and small article blurbs.

All the URLs have to be properly encoded (all the special characters have to be hex encoded) or the server will generate an error.

Data format

Each page will return data in the following formats

getAuthor.cfm
<author name>

generateAuthorDetails.cfm
<title>\<article ID>

getBlurb.cfm
<description>

Note: An older version of this article used Director's default text delimitier (the comma) to separate the data in the generateAuthorDetails.cfm page. This was a very silly thing to do and the author has rewritten his code to avoid this ghastly oversite.

Writing code

So now that we know what pages to call and what the data is going to look like, it's time to start writing some code. The first thing we are going to need is an autonomous object that will make the getNetText calls, wait for the Cold Fusion server to return the data and then send the results off to be processed. You could handle this process by using repeat loops or exitFrame checks to see if the netID was done, but it is much simpler to create an object, task it to wait for the netID, and then forget it.

The object will need to have 4 properties: the netID of the getNetText call it makes, the name of the field that will be the target for the text that gets returned from the Cold Fusion server, the URL it needs to call and a boolean value to determine whether the data should be either placed in the field or if it needs to be recorded into an internal results list (more on this later).

property pNetId, pFieldName, pURL, pRecordData

on new me, aURL, fieldName, aBoolean
  
  -- by default assume that we don';t want to record the data
  if voidP(aBoolean) then aBoolean = FALSE
  
  -- store the data we were passed
  pURL= aURL
  precordData = aBoolean
  pFieldName = fieldName
  
  -- clear out teh results field just to be sure
  member (pFieldName).text = ""
  
  -- send off for the data
  getCFMData me
  
  -- pop the object into the actorlist
  add (the actorlist, me)

end

Notice that the object doesn't have a "return me" statement. Since the object is to be autonomous (it doesn't need to have any other handler or method in the movie refer to it) we don't need to record a reference to the object anywhere. Instead, the object enters a reference to itself to the actorList. This way the code gets called every frame without having to have any external references to the object.

on getCFMData me
  
  -- turn the load loop animation on
  toggleLoadLoop True
  
  -- send off for the data
  getNetText pURL
  
  -- record the id of this process for further testing
  set pNetId = getLatestNetId()
  
end

The object takes the URL it was provided and uses it in a getNetText call and then records the netID of that process for later reference.

on stepFrame me

  -- if the process isn't finished then just skip the rest of the code
  if not netDone(pNetId) then return
  
  -- if we're done then hand off the results to process them
  
  -- check for an error first
  if netError (pNetId) = "" OR netError (pNetId) = "OK" then

    processResults netTextResult (pNetId), pFieldName, pRecordData
    
  else
    alert netError (pNetId)
  end if
  
  -- when done, delete myself from the actorlist
  deleteOne( the actorList, me)
  
end

Then every frame, the object will check to see if the getNextText call has returned any results, check for an error and then pass the resulting data off to be processed if there wasn't an error. As a final act it deletes itself from the actorList.

Now we have a large amount of text and need to process it.

What do I do with all this text?

Unlike a browser, Shockwave will not simply ignore that white space around the data that the Cold Fusion server produces. So if the browser shows

Al Hospers Alan Levine Andrew White

The source for the page will look like



Al Hospers
Alan Levine
Andrew White

Notice the white space that preceeeds the data.

The Cold Fusion server will take the CFML code and, for security reasons, delete it from the page that it creates and returns to the browser. So for every line of CFML tags you have you will get a resulting blank line in the page the server returns. You can get the Cold Fusion server to suppress these extraneous line by using the <CFSILENT> tag pair in Cold Fusion 4.5 or the ENABLECFOUTPUTONLY paramater of the <CFSETTING> tag in Cold Fusion 4.0.

As well, depending on the page you will also get stray spaces and/or tab characters that have been inserted by your HTML editor. Cold Fusion Studio, the application that most developers use to create Cold Fusion pages, uses either tabs or spaces to indent lines and these characters sometimes appear on lines that appear to be blank.

Spaces and extraneous characters can sometimes be added accidentally to the database during data entry and also due to a small bug in the Access database engine. All of these extra characters need to be removed from the data before we add it to a field for the user to view. This process can be aided by using the Trim tag in Cold Fusion to strip any extra characters from the data returned by the database.

<cfoutput query="getBlurb">
#Trim(displaytext)#
</cfoutput>

But this still won't take care of all the "garbage" characters that can appear in a CFML file so we will also need to clean up the data that we are going to display before we present it to the user in our Shockwave application.

An added complication is that, in some cases, we want to display all the data, but in another (when we get the details of the author's articles) we want to store some data for later reference and just display a title. In order to facilitate this we will store a boolean value in our object and then use that to decide whether we want to simply process and display the data or process it, parse it and then display only a portion of it.

on processResults thisText, thisFieldName, recordData
  
  oldDelims = the itemDelimiter
  the itemDelimiter = "\"
  
  -- if not provided we do not record the data retreived
  -- from the CFM page
  if voidP (recordData) then recordData = FALSE
  
  -- clear the text fields
  member("tempText").text = ""
  member(thisFieldName).text = ""
  
  -- reset the global results list if we are recording data
  if recordData then
    set resultsList = []
  end if
  
  -- find out how many lines are in the results we got back
  thisLimit = thisText.line.count
  linesAdded = FALSE
  
  -- now process each line of the results of thge CFM call
  repeat with index = 1 to thisLimit
    
    -- get the nth line
    thisLine = thisText.line[index]
    
    -- don't add blank lines
    if thisLine <> "" then
      
      -- if we want to record the data then there will
      -- be subsequent items in the line
      if recordData then
        thisTitle = thisLine.item[1]
        thisID = integer(thisLine.item[2])
        
        -- put them in a list
        thisList = [thisTitle, thisID]
        
        -- append this list to the global resuls list
        append resultsList, thisList
      else
        -- if we aren't recording the data then just grab all of it
        thisTitle = thisLine
      end if
      
      -- trim the line
      thistitle = Trim(thisTitle)
      
      -- add the line to our offscreen text field as long as it
      -- isn't a line with only a carriage return
      if charToNum(thisTitle.char[1]) <> 13 AND thisTitle.chars.count <> 0 then
        linesAdded = TRUE
        member ("tempText").text = member ("tempText").text & thisTitle & RETURN
      end if
      
    end if
    
  end repeat
  
  -- test to see if the results were blank
  
  if linesAdded then
    -- now display the results
    member (thisFieldName).text = member("tempText").text
    
  else
    member (thisFieldName).text = "No data available"
  end if
  
  toggleLoadLoop False
  
  -- make sure that the field is at the top of the displayed text
  member(thisFieldName).scrollTop = 0
  
  the itemDelimiter = oldDelims
  
end

This handler takes the text (and display details) from the object and then steps through each line of text. It strips any preceding and trailing extra characters (the Trim function) and then checks to make sure that the line isn't blank or just a carriage return (ASCII value 13). If the line passes all these tests, then it is added to a temporary field. Once all the lines have been processed then the text is copied from the temporary field into our target field.

Putting it together

So all we need to do is string these disparate pieces of code together.

The process we want to follow has three steps

  1. Generate a URL via a user initiated event (clicking on a name or article) or during the startMovie handler
  2. Create an object to make a getNetText call using the URL and wait for the results
  3. Process the results and display them in a field

So to start things off, the startMovie handler will create an instance of the object that will get a list of all the authors who have written for DOUG.

  thisURL = "test/getAuthor.cfm"
  thisObj = new (script "cfmRetreive", thisURL, "authors")

The Cold Fusion page we want to call is self contained and doesn't need to have any extra data provided to it in so we can just pass the object the full URL to the page (with no parameters in the URL). The results of this call will be displayed in the "authors" field.

Once the data is in the author field the user can double click on a name and we will then call to a second page to get all the articles written by that author. Since an author's name is only ever entered in the database if they have written an article for DOUG there is no reason to do any error checking on the results of this second call. We know there will be at least one article. So we can attach a small script to the "author" field.

on mouseUp
  
  -- only load an author if the user double clicked
  if the doubleClick then
    
    -- get the author name
    thisAuthor = member("authors").text.line[the mouseLine]
    
    -- if the author isn't a blank line then proceeed
    if thisAuthor <> "" then
      
      -- now URL encode the spaces in the name
      thisAuthor = URLencode (thisAuthor)
      
      -- clear the article displays
      toggleArticleDisplay False
      
      -- generate the url and pass it off to an instance of
      -- the CFMRetreive object
      thisURL = "test/generateAuthordetails.cfm?name=" & thisAuthor
      thisObj = new (script "cfmRetreive", thisURL, "CGIResults", TRUE)
      
    end if
    
  end if
  
end

In this case the URL we generate has a parameter, the author's name, added to it. This parameter will be used by the Cold Fusion page to create a database query that will retrieve the details of all the articles written by that author.

<CFQUERY NAME="getArticles" DATASOURCE="aDataSource">
SELECT title, ID
FROM articleTable
WHERE author LIKE '%#Trim(URL.name)#%'
</CFQUERY>
<cfoutput query="getArticles">
#Trim(title)#\#ID#
</cfoutput>

The Cold Fusion code will take the author's name from the URL and substitute it in the SQL query. So if the user double clicked on Alan Levine's name the SQL query would look like

SELECT title. ID
FROM articleTable
WHERE aAuthor LIKE '%Alan Levine%'

And in Alan's case the Cold Fusion server would return

Education and clocks\3 
How to do storyboards in Director.\93 
Perl or How I learned to stop fearing and love CGIs\94 
A memory space of one's own\95 
Random numbers\96 
Hey Browser it's Me! Shockwave? Hello?\95 

This would then, in turn, be passed to the processResults function. Notice that, in this case, the call to the CFMRetreive object also includes the TRUE value for the pRecordData property. This means that when we call the processResults function it will record the values for each article in the global resultsList but only display the article names in the CGIResults field.

The final step is to allow the user to view further data about the article (either the small or full description) or view the article. This is a two-step process. The user has to select an article and then select an option to view either a small blurb, a full blurb or to open the article in a new browser window. So when the suer double clicks on an article title we will store the article ID we got from our previous CFML page in a global variable and display three buttons, one for each option, and allow the user to select one of them if they wish.

The details for each article were stored in the global list resultsList in the order in which they are displayed so its a simple matter to take the mouseLine value and use that to retreive the data we want from the global resultsList.

global resultsList, currentID

on mousedown
  
  if the doubleClick then
    
    thisLine = the mouseLine
    
    -- check to make sure that the user isn't clicking on blank lines
    if member("CGIResults").line[thisLine] <> "" then
      
      -- clear the display and turn the sprites for the buttons on
      toggleArticleDisplay
      
      -- display the title and store the article ID
      member ("title").text = resultsList[thisLine][1]
      currentID = resultsList[thisLine][2]
      
    end if
    
  end if
  
end

Once we have the article ID stored we can then call that value up if the user decides that they want more information about a particular article. We can facilitate this by adding a small piece of code to the "blurb" button.

global currentID

on mouseUp
  
  -- check to make sure that an article has been selected
  if currentID = 0 then
    
    alert "No article selected"
    
  else
    
    -- generate the URL for this article and pass it on to the object
    thisURL = "test/getBlurb.cfm?id=" & currentID &"&type=small"
    thisObj = new (script "cfmRetreive", thisURL, "blurb", FALSE)

  end if
  
end

This script uses the ID number we generated when the user double-clicked on an article title and then adds a "type" parameter to tell the CFML page what type of description we want to retrieve. In this case it is the small description. The code for the button that retrieves the full description is exactly the same except that it has a type value of "large". Not that it is really necessary. The Cold fusion code is written in such a way that it only actually checks if the type is "small". Since there are only two values we can safely do this. Either the user wants a small description or a full description. If it isn't small...

<cfparam name="URL.type" default="small">
<cfif URL.type is "small">
    <CFQUERY NAME="getBlurb" DATASOURCE="aDataSource">
    SELECT blurb as displayText
    FROM articles
    WHERE articleID = #URL.id#
    </CFQUERY>
<cfelse>
    <CFQUERY NAME="getBlurb" DATASOURCE=" aDataSource ">
    SELECT description as displayText
    FROM articles
    WHERE articleID = #URL.id#
    </CFQUERY>
</cfif>
<cfoutput query="getBlurb">
#Trim(displaytext)#
</cfoutput>

The Cold Fusion page will take the ID and type and return the appropriate text, which we will display for the user after cleaning it up. If the user wants to view the article then it's a simple matter of taking the article ID we stored previously and using it to create a URL to that article. We then make a new gotoNetPage call but target a new window.

global currentID

on mouseUp
  
  -- check to make sure that an article has been selected
  if currentID = 0 then
    alert "No article selected"
  else
    
    -- generate the URL to the article and use a gotoNetPage command to browse it
    thisURL = "buildArticle.php?id=" & currentID
    gotoNetPage thisURL, "_blank"
    
  end if
  
end

Summary

Shockwave provides a very convenient way to encapsulate a wide range of functionality into a single application. Not only does this allow you to provide a more attractive interface for the user but also a "deeper" interface. With a Shockwave front-end to your database applications you can keep data persistent with no extra effort. No need for cookies or complicated Java applets.

While this article dealt with Cold Fusion there is no reason why you couldn't use Shockwave as a front-end for ASP or PHP pages. In fact, Alan Levine wrote an article that details some of the issues involved with integrating Perl and Shockwave.

Check out the code in action...

And if you want you can download a sample movie (in Mac or PC format) to test the code yourself. This sample movie is in Director 7 format.

Zac Belado is a programmer, web developer and rehabilitated ex-designer based in Vancouver, British Columbia. He currently works as an Application Developer for a Vancouver software company. His primary focus is web applications built using ColdFusion. He has been involved in multimedia and web-based development, producing work for clients such as Levi Straus, Motorola and Adobe Systems. As well, he has written for the Macromedia Users Journal and been a featured speaker at the Macromedia Users Convention.

Copyright 1997-2024, Director Online. Article content copyright by respective authors.