Jared Rypka Hauer
Jared Rypka Hauer
Scott Stroz
Jeff Chastain

Created

20 December 2007

Requirements
User level
Beginning

 

Approximately two weeks before MAX 2007, Adobe contacted our company, Alagad, to help implement enhancements to the The United Way of America volunteerism website. Despite the short timeline (the finished application would be shown at the MAX keynote), we undertook developing enhancements. The features we helped to develop were in the following areas on the site:
 
  • Share Your Story: Creating a way for volunteers to share their stories
  • Ways to Help: Creating a way for users to see how they can volunteer
  • Volunteer Survey: Suggesting ways for users to volunteer based upon their responses to a survey by providing the user a bundle of customized PDF files for download
We had next to no time to make some significant changes to these sections of the site, yet the answer was simple: Use new features in ColdFusion 8, such as AJAX integration and PDF tools to enhance the end-user experience, and use Flex to create one of the new management tools for the site.
 
In this article, we describe the features we developed and the technical details behind developing them.
 
 
Developing the "Share Your Story" feature
The ‘Share Your Story' section of the site allows volunteers to share their experiences while volunteering and give them the opportunity to upload images.  While the site already had a process for users to author stories, the process for doing so was very labor-intensive for users.  Users had to write their story in a form and then add links to online image galleries.  The users could not  upload images directly, rather they could only submit a link to photo sharing sites like Flickr, and the information was not saved anywhere (it was sent via e-mail to a few people at The United Way of America). 
 
The United Way of America asked us to automate the workflow to simplify and enhance the authoring process for their volunteers. ColdFusion 8 provided image handling and AJAX integration that gave users a simplified, but rich experience for doing so. 
 
The original "Share your story" form
Figure 1. The original "Share your story" form
 
The revised "Share your story" form
Figure 2. The revised "Share your story" form
 
We started by updating the form so users could upload images, then we stored that data in a database, and improved the form field used for the stories themselves by using the ColdFusion 8 built-in rich text editor (RTE) with its "Basic" toolbar.  ColdFusion 8 makes it extremely easy to add a rich text editor to a form. Here is the code we used:
 
<cftextarea name="story" id="story" richtext="true"toolbar="Basic">#form.story#</cftextarea>
That's all the code that you need to get the RTE on the page.  There's no need to mess with JavaScript or complicated configuration files;  it's all simple, clean ColdFusion code—just how we like it.
 
Now that volunteers could submit stories and images, we needed a way for The United Way of America staff to select which stories and images would appear on the site. To accomplish this, we created a simple administrative interface with three different areas, two small areas to list approved and pending submissions, and the main content area with a form where administrators can approve items.  All three of these areas used the new ColdFusion cfdiv tag, which has AJAX functionality. The following is an example of the code used for the lists of submissions:
 
<cfdiv id="needApproval" bind="url:needApproval.cfm" style="height:200px;overflow:auto" /> <cfdiv id="isApproved" bind="url:approved.cfm" style="height:200px;overflow:auto" />
The addition of the cfdiv and the other AJAX functionality allows developers to create AJAX applications quickly and easily.  With the code above, ColdFusion automatically includes the necessary JavaScript files for handling the AJAX calls.  Also, when the page loads, the pages needApproval.cfm and approved.cfm load into the <div>s 'needApproval' and 'isApproved' respectively.
 
The last area to be implemented was a simple cfdiv tag with no binding or styling:
 
<cfdiv id="expFormDiv"></cfdiv>
This main area contains a form that The United Way of America staff uses to approve items.  It loads using AJAX whenever a staff member selects an item from the approved or pending list.  To load the form, we used the ColdFusion.navigate() JavaScript method, as follows:
 
<a href="javascript://" onclick="ColdFusion.navigate('expForm.cfm?experienceid=#q.experienceID#','expFormDiv')">#lastName#, #firstName#</a>
We used this code within a cfoutput block, looping over a query.  In essence, the JavaScript makes a request to the URL expForm.cfm?experienceid=#q.experienceID# and returns the response to the div tag with an ID of expFormDiv.
 
One gotcha that we encountered is that the RTE used on this form would not load correctly; instead it would load as a simple textarea.  This was due to the fact that normally when the RTE is loaded in a page, JavaScript is run to render the RTE. By default, this cannot be done with pages loaded via AJAX.  The solution to this was simple; we added the following code to the main page:
 
<cfaJAXimport tags="cfform,cftextarea" />
Basically, this bit of code directs ColdFusion to import the JavaScript files necessary for using AJAX with the items listed in the tags attribute of the cfform and cftextarea, in this instance.  Now when the form loads, the RTE renders correctly.
 
Once the staffer reviews the submission and approves the images, the form is submitted to the server.  Here, we used the ColdFusion.AJAX.SubmitForm() ColdFusion AJAX function to handle the form submission asynchronously. To do this we added the following JavaScript code to the page and execute it when the staffer clicks the save button:
 
function submitForm(){ ColdFusion.AJAX.submitForm('expform', 'expForm_submit.cfm', expCallback); }
In short, this code directs ColdFusion, or rather, the JavaScript included by ColdFusion, to take the form fields in the form with the ID of expform and send them to the page named expForm_submit.cfm.  When a response is returned form this page, the application runs the expCallback JavaScript function.
 
The form validation occurs on the server side. If any errors are encountered, they are returned from the AJAX call.  These errors are processed in the expCallback() function and displayed on the page.  If there are no errors, a message stating that the experience was updated appears and the lists of approved and pending items are updated using the following code:
 
ColdFusion.navigate('needApproval.cfm','needApproval'); ColdFusion.navigate('approved.cfm','isapproved');
If there are no errors in the form, the application performs some image manipulation on submitted images, using the <cfimage> tag set in ColdFusion 8.  For instance, we were able to create thumbnail images and resize the images so that they fit into the main content area.
 
<cfimage action="read" source="#expandpath("../img/ul/raw")&"/"&img#" name="image" /> <cfif imageGetWidth(image) GT 100> <cfset newHeight = ceiling(imageGetHeight(image)*(100/imageGetWidth(image))) /> <cfset imageResize(image,100,newHeight) /></cfif> <cfimage action="write" destination="#expandpath("../img/ul/small")&"/"&img#" source="#image#" />
This code above checks to see if the application has already created the thumbnail and re-sized the image. If it hasn't, the application reads the "raw" image.  If the image is wider than 100 pixels, it is resized for the thumbnail; if the image is wider than 285 pixels, it is resized it for the large image. Finally, the image is written to the appropriate directory, where it will be used on the "Share Your Story" page.
 
Finally, we created the "Share Your Story" page that displays the images and stories.  This page has 2 main areas, one to display the thumbnails we created, the other to display the stories.  Thumbnails are displayed on the side of the main content area, and are paginated.  The application uses the ColdlFusion.navigate() function to reload the thumbnail  <div> section, allowing users move forward or backward through the pagination.  When a user clicks a thumbnail, the application uses the ColdFusion.navigate() function again, only this time, the story related to the thumbnail is loaded.
 
 
Overhauling the survey
The United Way of America site also needed an overhaul of its survey.  The survey collected information from visitors regarding their volunteering preferences and history, stored the information in a database, and confirmed that the data was saved with the user with a "thank you" page.  Meanwhile,  The United Way of America site had a collection of single-page PDF documents that featured various information regarding volunteering, but there was no connection between this information and the survey form—until now.
 
The new survey accomplishes the following:
 
  • Captures survey answers from the visitor in a database
  • Stores the information for statistical purposes
  • Creates a customized packet of PDF documents
The custom PDF packet contains a cover sheet with a random "way to help" suggestion, along with the location and contact information of The United Way of America location closest to the visitor.  In addition, one or more of the single-page PDF documents (that were previously not being shared) are now included in the packet  based on the visitor's responses in the survey.  For example, if a visitor specified that she had kids, her packet would include a PDF document that listed ways to volunteer as a family.  Lastly, the packet includes a dynamically-generated PDF document that lists ways to help. The new PDF document support in ColdFusion 8 helped us to rapidly implement these features without much code.
 
For the custom-generated cover sheet, there were three requirements:
 
  • Select and display a random "way to help" suggestion
  • Display the visitor's local United Way location and contact information
  • Merge this information into a PDF document with The United Way of America header
We approached the first two requirements using the tag, which was introduced in ColdFusion MX 7 but was improved upon in ColdFusion 8 CFDocument.  We produced a single XHTML page layout with the customized information, and used CFDocument to convert it to a PDF document.  Adding the The United Way of America header information to the page required the new CFPDF tag introduced with ColdFusion 8.  One of the features of CFPDF is the ability to use a source PDF document and overlay it on another PDF document as a watermark.  Since the The United Way of America header was already in PDF format, this was a perfect fit and took a single line of code to implement.
 
Before ColdFusion 8, the ability to combine multiple PDF documents into a single document was beyond the capabilities of other scripting languages.  However, Adobe significantly stepped up the PDF capabilities of ColdFusion 8 with the addition of DDX, an XML document definition primarily used by the Adobe LiveCycle software.  Using DDX, you can describe the structure of a PDF document including one or more source PDF documents, headers, footers, table of contents, and more— then ColdFusion generates the document.
 
This fit the requirements of The United Way of America's feature request perfectly.  Using DDX, we programmatically added the preexisting single-page PDF documents together with the custom cover sheet and dynamic list of ways to help all into one PDF document, which was then pushed to the visitor upon submission of the survey.
 
To learn more about DDX and its use in ColdFusion 8, check out the Adobe ColdFusion Developer Center article Using DDX to unlock the potential of PDF manipulation in ColdFusion 8, Jeff Chastain.
 
 
Improving the site authoring workflow for "100 ways to help" and improving the The United Way of America's website in the process
The problem of user workflow plagues many websites, and the The United Way of America's volunteerism website was no exception. The particular issue they were facing involved 100 "badge" images that feature text on a background outlining "100 ways to help" make the world a better place. Their workflow was the traditional "open/edit/save/place," and it had two specific problems: it was difficult for the The United Way of America workers to add new ways to help to the list because of the site's use of static images across the site.
 
The second issue was easily solved, but the first issue was a challenge. The United Way of America needed a way to add and edit new "ways to help" easily and quickly. Once that was accomplished, and the way to make the images change across page views was implemented, we hoped that it would improve the user experience, and help make the site more compelling and interesting.
 
To solve the problem of creating new image assets dynamically, we used the as3corelib package from Adobe's Google Code Project. Since Flash has the ability to render any UIComponent to bitmap data using the BitmapData and Matrix classes, and since there are classes in the as3corelib project to convert bitmap data to PNG and JPG images, the problem was fairly simple to solve.
 
The Flex application we created was simple but effective. It contains a text field to add the text to the image, an "image preview" where the text is shown on the image background, a slider to change the font size of the text, and a list that shows the existing "ways to help" from the database to enable editing of existing text.
 
 
Creating a new image with Flash Bitmaps
The core requirement we faced was building the functionality for staffers to create the final image in a browser-friendly image format, such as PNG or JPG. We chose JPG because of its smaller file size and universal compatibility. The basic specification for the Flex application would be as follows:
 
  • Create a Canvas to contain the image background and the text.
  • Use the text field in the Flex application's UI to change the text and use bindings to make a "live preview" in the Canvas.
  • Provide a "Save Image" button that triggers the following process:
    • Capture the Canvas containing the "live preview" of the image background.
    • Convert that Canvas (and thus the image and text) into bitmap data.
    • Convert the bitmap data to JPG data.
    • Transmit the JPG data to a ColdFusion server through the Flash Remoting gateway to a CFC written to save the JPG data to disk.
The code for converting a UIComponent to bitmap and then JPG or PNG data looks similar to the following code:
 
package com.alagad.graphics { import flash.display.BitmapData; import flash.geom.Matrix; import flash.utils.ByteArray; import mx.core.UIComponent; import mx.graphics.codec.IImageEncoder; import mx.graphics.codec.JPEGEncoder; import mx.graphics.codec.PNGEncoder; public class UIComponentImageEncoder { public var imgType:String = new String(); public static const ENCODE_JPEG:String = "JPEG"; public static const ENCODE_PNG:String = "PNG"; public function UIComponentImageEncoder(imgType:String) { if (imgType != ENCODE_JPEG || imgType != ENCODE_PNG) throw "Invalid encoding specified; must be JPEG or PNG"; this.imgType = imgType; } public function encode(src:UIComponent):ByteArray { var bmpData:BitmapData = new BitmapData(src.height,src.width); var imageEncoder:IImageEncoder; bmpData.draw(src,new Matrix()); if (this.imgType == ENCODE_JPEG){ imageEncoder = new JPEGEncoder(100); } else if (this.imgType == ENCODE_PNG){ imageEncoder = new PNGEncoder(); } return imageEncoder.encode(bmpData); } } }
 
Saving the day with AJAX
Once we had the Flex application set up to send the JPG data through the Flash Remoting gateway, we used an AJAX call from within Flex to update the HTML UI so we could show the Flex app and the new image side by side. The code inside the Flex application that specifies that the HTML user interface should update itself looks like the following:
 
ExternalInterface.call("showLatestImage",event.result);
 
Nothing too hard so far...
 
Within the HTML user interface, there is a bit more code:
 
<script type="text/javascript" language="javascript"> var showLatestImage = function(img) { ColdFusion.navigate("getimage.cfm?image=" + img, "imagesColumn", callbackHandler,errorHandler); } var callbackHandler = function() { } var errorHandler = function() { } </script> ... <div class="v_right_column_holder"> <div class="v_right_column_image_crash_pad"> <cfdiv id="imagesColumn"></cfdiv> </div> </div>
Meanwhile, getImage.cfm contains:
 
<cfoutput> <img src="../img/100ways/#url.image#"/> </cfoutput>
Here's a breakdown of what's happening in the code. The Flex application calls the showLatestImage() function when a new image is added. The showLatestImage() function is a JavaScript AJAX function that replaces the content of the imagesColumn div tag in the above code snippet with the data returned when running the following code: getimage.cfm?image={image_name}. The file, getImage.cfm, in turn, simply contains the image tag that takes the url.image variable as the name of the image. As it turns out, imagesColumn is just a cfdiv tag that, when showLatestImage() is run, contains a new img tag pointing to the most recently created image.
 
While I'll be the first to admit that this is not complicated, we think it's pretty cool, especially how simply it is coded, and how quickly it came together.
 
The United Way of America image generation UI
Figure 3. The United Way of America image generation UI
 
 
Randomizing the images
Since we decided to use the image number as the file name, it was pretty easy to generate a random number from a range of 1:{count_of_images} and use that in an image tag to display a random Ways to Help" image created with the new Flex application.
 
The code for this looks something like the following:
 
<cfset randomize( year(Now()) + month(Now()) + day(Now()) + hour(Now()) + minute(Now()) + second(Now()) ) /> <div class="v_right_column_image_crash_pad"> <img src="img/100ways/<cfoutput>#imageNumber#</cfoutput>.jpg" alt="the volunteer post" width="162" height="162" border="0" /> </a> </div>
Now the workflow issue is solved and the application displays pages showing a random image in the application.
 
It was an honor working with Adobe and The United Way of America on this project, and on the very limited timeline it was also a great challenge.  Since ColdFusion 8 is backward compatible, we made no changes to the existing ColdFusion 7 codebase beyond the enhancements we implemented. Because of that and the core functionality of Flex and the rapid application development aspects of ColdFusion (especially relating to AJAX and PDF), our team was able to meet the needs of The United Way of America quickly and easily.
 

 
Where to go from here

Check out the Adobe Developer Connection to see what else you can do with Adobe technologies.