Building a Web Service-driven Application with Flash in Drupal

Exclusive offer: get 50% off this eBook here
Flash with Drupal

Flash with Drupal — Save 50%

Build dynamic, content-rich Flash CS3 and CS4 applications for Drupal 6

$26.99    $13.50
by Travis Tidwell | May 2009 | Content Management Drupal Open Source PHP

In this article by Travis Tidwell, we will create a "Hello World" application that utilizes web services to populate a TextField or MovieClip with data from a remote location. Using web services to populate all of our Flash fields allows for the component abstraction necessary for an MVC architecture. Here Flash fulfills the role of the View, and Drupal fulfils the role as the Model and Component. This enables us to have a truly scalable and manageable web solution.

This approach allows for a Model-View-Controller (MVC) architecture for our Flash applications, which is considered ideal since it separates the User Interface (View) from the Data Set (Model) and Business Logic (Component) of our application.

So, let's take a step-by-step approach on how to accomplish this on the Flash side, which as far as I am concerned, is the fun side!

Click here to access all the codes used in this article.

Step 1: Creating our Flash application

With our chapter2 project open, we can shift our focus to the Actions panel within the Flash IDE. Although working with the Actions panel is great for small applications, we will eventually build onto this Flash application, which might make it impractical to keep all of our ActionScript code within the Actions panel. Because of this, we will first need to create a separate ActionScript file that will serve as our main entry point for our Flash application. This will allow us to easily expand our application and add to the functionality without modifying the Actions panel for every addition we make.

Step 2: Creating a main.as ActionScript file

For this step, we will simply create an empty file next to our chapter2.fla file called main.as. After you have created this new file, we will then need to reference it within our Actions panel. To do this, we will use the include keyword in ActionScript to include this file as the main entry point for our application. So, shifting our focus back to the chapter2.fla file, we will then place the following code within the Actions panel:

include "main.as";
stop();

Now that we are referencing the main.as file for any of the ActionScript functionality, we will no longer need to worry about the Actions panel and add any new functionality directly to the main.as file.

Now, for the following sections, we will use this main.as file to place all of our ActionScript code that will connect and extract information from our Drupal system, and then populate that information in a TextField that we will create later. So, let's jump right in and write some code that connects us with our Drupal system.

Step 3: Connecting to Drupal

For this step, we will first need to open up our empty main.as file so that we can add custom functionality to our Flash application. With this file open in our Flash IDE, our first task will be to connect with Drupal. Connecting to Drupal will require us to make a remote call to our Drupal installation, and then handle its response correctly. This will require the use of asynchronous programming techniques along with some standard remoting classes built into the ActionScript 3 library. I will spend some time here discussing the class used by ActionScript 3 to achieve remote communication. This class is called NetConnection.

Using the NetConnection class

The NetConnection class in ActionScript 3 is specifically used to achieve remote procedure calls within a Flash application. Luckily, this class is pretty straight forward and does not have a huge learning curve on understanding how to utilize it for communicating with Drupal. Using this class requires that we first create an instance of this class as an object, and then initialize that object with the proper settings for our communication. But let's tackle the creation first, which will look something like this in our main.as file:

// Declare our Drupal connection
var drupal:NetConnection = new NetConnection();

Now, you probably noticed that I decided to name my instance of this net connection drupal. The reason for this is to make it very clear that any place in our Flash application where we would like to interact with Drupal, we will do so by simply using our drupal NetConnection object. But before we use this connection, we must first specify what type of connection we will be using. In any NetConnection object, we can do this by providing a value for the variable objectEncoding . This variable lets the connection know how to structure the XML format when communicating back and forth between Flash and Drupal. Currently, there are only two types of encoding to choose from: AMF0 or AMF3. AMF0 is used for ActionScript versions less than 3, while AMF3 is used for ActionScript 3. ActionScript 1 and 2 are much less efficient than version 3, so it is highly recommended to use ActionScript 3 over 1 or 2. Since we are using ActionScript 3, we will need to use the AMF3 format, and we can provide this as follows:

// Declare our Drupal connection
var drupal:NetConnection = new NetConnection();
drupal.objectEncoding = ObjectEncoding.AMF3;

Now that we have an instance ready to go, our first task will be to connect to the Drupal gateway that we set up in the previous section.

Connecting to a remote gateway

Connecting to a remote gateway can be performed using the connect command on our drupal NetConnection object. But in order for us to connect, we must first determine the correct gateway URL to pass to this function. We can find this by going back to our Drupal installation and navigating to Administer | Services. In the Browse section, you will see a link to the servers available for remote procedure calls as shown in the following screenshot:

Flash with Drupal

For every listed server, we can click on each link to verify that the server is ready for communication. Let's do this by clicking on the link for AMFPHP, which should then bring up a page to let us know that our AMFPHP gateway is installed properly. We can also use this page to determine our AMFPHP gateway location, since it is the URL of this page. By observing the path of this page, we can add our AMFPHP server to our main.as file by combining the base URL of our site and then adding the AMFPHP services gateway to that base.

// Declare our baseURL and gateway string.
var baseURL:String = "http://localhost/drupal6";
var gateway:String = baseURL + "/services/amfphp";
// Declare our Drupal connection
var drupal:NetConnection = new NetConnection();
drupal.objectEncoding = ObjectEncoding.AMF3;
// Connect to the Drupal gateway
drupal.connect( gateway );

It is important to note that the connect routine is synchronous, which means that once this function is called, we can immediately start using that connection. However, any remote procedure call that we make afterwards, will be asynchronous, and will need to be handled as such. The function that can be used to make these remote procedure calls to Drupal is called call.

Flash with Drupal Build dynamic, content-rich Flash CS3 and CS4 applications for Drupal 6
Published: May 2009
eBook Price: $26.99
Book Price: $44.99
See more
Select your format and quantity:

Using the NetConnection call routine

If you look at the Adobe Help section about the NetConnection call function, you will find the following arguments:

function call( command:String,
responder:Responder, … arguments );

The first argument, called command, is very easy to understand and use. It is the command that you will send to your remote server to execute. In Drupal, this will be the Service functions that are provided by the System and Node Services that we installed on our server. Since our first task is to simply connect to Drupal, we will first need to use the system.connect command to send to our System Service on our Drupal installation.

The second argument is the responder. This is simply the object that holds the callback functions that are used when the server returns from a remote function call. One callback is used to handle the return value on a successful transfer, while the other callback function is used to handle any error that might have occurred. Since we are programming asynchronously here, we need to first create these two callback functions and then create a new Responder object using both our success and error callback functions. Within our main.as file, we can create the responder with the callback functions as follows:

// Declare our baseURL and gateway
var baseURL:String = "http://localhost/drupal6";
var gateway:String = baseURL + "/services/amfphp";
// Declare our Drupal connection
var drupal:NetConnection = new NetConnection();
drupal.objectEncoding = ObjectEncoding.AMF3;
// Connect to the Drupal gateway
drupal.connect( gateway );
// Set up our responder with the callbacks.
var responder:Responder = new Responder( onConnect, onError);
// Called when Drupal returns with a successful connection.
function onConnect( result:Object )
{
trace("We are connected!!!");
}
// Called when an error occurs connecting to Drupal.
function onError( error:Object )
{
for each (var item in error)
 {

trace(item);
}
}

The third argument to the call routine, and each subsequent argument afterwards, is what will be passed as argument(s) to the remote function that we are calling. You will notice in the function declaration that there are "…" in front of the arguments variable. This is called a variable argument function, which means that it can accept any number of arguments into this function. This allows us to provide any number of arguments, which will then be sent as arguments to the remote function that we are calling. For system.connect service, there are not many required arguments, so we can just omit this for now.

So, here is the ActionScript code that illustrates how to connect to Drupal using the function call.

// Declare our baseURL and gateway
var baseURL:String = "http://localhost/drupal6";
var gateway:String = baseURL + "/services/amfphp";
// Declare our Drupal connection
var drupal:NetConnection = new NetConnection();
drupal.objectEncoding = ObjectEncoding.AMF3;
// Connect to the Drupal gateway
drupal.connect( gateway );
// Set up our responder with the callbacks.
var responder:Responder = new Responder( onConnect, onError);
// Connect to Drupal
drupal.call("system.connect", responder);
// Called when Drupal returns with a successful connection.
function onConnect( result:Object )
{
trace("We are connected!!!");
}
// Called when an error occurs connecting to Drupal.
function onError( error:Object )
{
for each (var item in error)
{
trace(item);
}
}

Step 4: Session handling

Now that we are connected to Drupal, our next intuition is for us to dive in and start extracting data from our Drupal site; but this cannot be accomplished without first handling the session ID for our connection with Drupal. The session ID is simply a unique identifier for every connection made with any web site. The session ID allows a web site to keep a track of every single person navigating the site, and therefore, give them certain permissions depending on whether that person is logged in as a user who can perform certain tasks. Each browser then uses cookies to store the session ID of that user so that the next time they open up their browser, their session is restored by setting the session ID to the same value as what was saved in the cookie. We will use this session ID in our application for every call that we make to Drupal to validate our connection. By default, the Services module assumes that any application making calls to the Drupal system is an "anonymous" application, and therefore, does not allow that application to perform specific tasks. By utilizing the session ID, we are allowing our Flash application to validate itself with our Drupal installation so that the typical user management system that Drupal employs to access content is utilized. With that said, let's modify our previous code to handle the session ID for our connection with Drupal.

Connecting to Drupal using system.connect

Although session handling may sound complicated, the effort involved is minor since the ID is returned to us after the Drupal connection has been made using the system.connect call. And since we will be using this session ID for other routines, we need to declare the following:

// Declare our variables
var baseURL:String = "http://localhost/drupal6";
var gateway:String = baseURL + "/services/amfphp";
var sessionId:String = "";
// Declare our Drupal connection
var drupal:NetConnection = new NetConnection();
drupal.objectEncoding = ObjectEncoding.AMF3;
// Connect to the Drupal gateway
drupal.connect( gateway );
// Set up our responder with the callbacks.
var responder:Responder = new Responder( onConnect, onError);
// Connect to Drupal
drupal.call("system.connect", responder);
// Called when Drupal returns with a successful connection.
function onConnect( result:Object )
{
// Set our sessionId variable.
sessionId = result.sessid;
trace("We are connected!!!");
trace("Session Id: " + sessionId);
}
// Called when an error occurs connecting to Drupal.
function onError( error:Object )
{
for each (var item in error)
{
trace(item);
}
}

We can now verify that this works by running our application. We should see, within the output panel, our connection with Drupal followed with a valid session ID. We are now ready to move on.

Step 5: Drupal says "Hello World"

Now, we are getting to the fun part… loading Drupal data into our Flash application. But before we can write the ActionScript to load node data from Drupal, we must first revisit our Drupal web site and create a new node that we will use to say "Hello". Moving back to our Drupal web site, let's create a new node by going to Create Content | Page. The page title is what we will use to say hello, so where it asks for a title, put in the text "Hello World!", and then go down to the very bottom of the page and hit the Submit button. We should then see our new page created that says, "Hello World!" for the title. It is very important for us to also remember the node ID of the node that we just created. We can do this by simply looking at the URL in our browser and writing down the number that comes after http://locahost/drupal6/node/. We will use this number to load our node in Flash, so just make sure you either remember it or write it down.

Loading a node in Flash

Now that our node is ready, we can write the code in ActionScript that loads this node and then parses out the title. This is how we are going to build our "Hello World" application using Flash and Drupal. So, let's move back to our Flash IDE and take a look at the code that we have so far. Picking up where we left off, we are finally at the point where we have successfully connected to our Drupal web site and received the session ID. The next step in this process is to get the node information from the node that we just created in our Drupal site. To accomplish this, we will be using the Service method called node.get , which takes two arguments: the session ID, and the node ID of the node we wish to load. This is where our variable arguments come into play, since we have two arguments that we need to provide.

At this point, it is considered best practice to create a separate function that combines the functionality of creating our responder with the Drupal service call to load a node. For the sake of simplicity, we will call this new function loadNode, where it will take a single argument (the node ID), and then use that argument and pass it along to the node.get service function. The Drupal node service will then return the node object for the node that we are requesting by calling the callback function that we provided within the responder. Within each node object, we will have access to the Title, Body, and any other fields of data that are associated with a Drupal node. Since we used the node title to say "Hello World", we can create an onNodeLoad function to print out the title field for the node object returned from Drupal. Each of these functions will look as follows:

// Connect to Drupal
drupal.call("system.connect", responder);
// Loads a Drupal node.
function loadNode( nid:Number )
{
// Set up our responder with the callbacks.
var nodeResponse:Responder = new Responder( onNodeLoad, onError);
// Call Drupal to get the node.
drupal.call( "node.get", nodeResponse, sessionId, nid );
}
// Called when Drupal returns with our node.
function onNodeLoad( node:Object )
{
// Print out the node title.
trace( node.title );
}

We can now use the loadNode function to load any Drupal node by passing in the node ID for the node we wish to load. For example, if we wish to load a node with an ID of 2, we can simply call loadNode(2), and all the complex functionality is now abstracted within that single function. Now for the big question…where do we place the call to loadNode so that we can load our "Hello World!" node that we just created? This seems simple enough, but leads to a significant gotcha where asynchronous software becomes a little confusing. To illustrate, let's take a look at the following code where I am attempting to connect to Drupal and then make a simple call to loadNode directly afterwards. Many developers who just start out using web services very often attempt to do the following and get very frustrated when they discover that it just does not work.

// Connect to Drupal
drupal.call("system.connect", responder);
// Load our "Hello World" node (ID = 2)
loadNode( 2 );

If we were to run this application, we will quickly see an issue by looking at the Output panel. This panel prints out the response returned from our Services module after we make our call to load a node. Since an error occurred, our onError function is called, and then the following error is printed out:

Flash with Drupal

This error is the result of an elusive software bug called race condition.

Programming without race conditions

If we take another look at the previous code, we will see that we are making a call to loadNode directly after the system.connect function call. In the world of synchronous software, this would work just fine, but since we are dealing with web service communication, our function calls to Drupal are not returned after each call is made. Instead, each call that we make to Drupal can take any amount of time before the result is returned using a callback function . To the trained eye, this is obvious, but for the developers just learning asynchronous software behavior, this can be quite the head scratcher. Taking asynchronous software interaction into account, we can now determine that our error occurred because we were making the call to get the node information before we received any indication that we have successfully connected, from the system.connect command. There is no guarantee that a response from the server will make it back in time before we end up making the call to loadNode. In asynchronous programming, this is most commonly referred to as a race condition, where you are betting that the return from system.connect will beat the call to loadNode. Unfortunately, you will most likely lose this bet, and the result will be a strange error that does nothing to expose the smoking gun.

Programming with race conditions is considered very poor programming practice, and will most often lead to an extremely elusive software bug in your application. In fact, I can easily say that with all my experience debugging software in complex applications, it is always the race condition that is the hardest bug to find and correct. So, let's take a moment and learn how to modify the previous code so that it will never hit a race condition. The trick is to simply move the call to loadNode after we receive notification from Drupal that we are connected and have a valid session ID. After we look at the following code, it will seem very obvious, but you would be surprised how often this gotcha seems to crop up in complex software applications. The modified code should look like the following (assuming the node ID you created was 2):

// Connect to the Drupal gateway
drupal.connect( gateway );
// Set up our responder with the callbacks.
var responder:Responder = new Responder( onConnect, onError);
// Connect to Drupal
drupal.call("system.connect", responder);
// Called when Drupal returns with a successful connection.
function onConnect( result:Object )
{
// Set our sessionId variable.
sessionId = result.sessid;
trace("We are connected!!!");
trace("Session Id: " + sessionId);
// Load our "Hello World" node (ID = 2)
loadNode( 2 );
}

So, after we run this application, we should get a very nice surprise… a "Hello World" from Drupal! But we are still not done here; our next step is to hook up the text in our Flash TextField to show this exciting text.

Step 6: Hooking up the text

In this next step, we will open up our chapter2.fla project file, where we will give our TextField an instance name so that it can be referenced within ActionScript. Fortunately, this step is very simple and only requires that we select the TextField, and then give it an instance of title in the PROPERTIES panel as shown in the following screenshot:

Flash with Drupal

Now that we have given our TextField an instance, the next step is to remove the text "Hello Drupal" from thisTextField so that we can determine if Drupal node data is used instead.

Flash with Drupal

We can now shift our focus back to the main.as file, where we will change our trace statement within our onNodeLoad function so that it sets the text of this TextField instead of just printing it to the Output panel. We can do this in ActionScript by using the following code:

// Called when Drupal returns with our node.
function onNodeLoad( node:Object )
{
// Print out the node title.
title.text = node.title;
}

We can now run our application and see our TextField show the title for our Hello World! node.

Flash with Drupal

Now that our application is starting to look like a real Flash application, we need to take some extra steps to make sure that it is flexible by allowing any node ID to be used to say "Hello World".

Step 7: Passing the node ID using FlashVars

Since the goal of our Flash application is to dynamically load Drupal node information, we will need a way to tell our Flash application which node to load. We can easily hard code the node ID of our "Hello World" Drupal node, but this approach does not give us much flexibility to apply it to any node within our Drupal system. We can solve this issue by utilizing FlashVars to pass the node ID to our Flash application.

Using FlashVars in a Flash application

Flash variables (or FlashVars) are special variables that are passed to a Flash application that are used to provide a specific functionality for a common application. They are passed to the Flash application when it is embedded within an HTML page using the object element. For example, we can tell our Hello World application to load the node data from node 2 using the following HTML code:

<object width="320" height="240">
<param name="movie" value="helloworld.swf" />
<param name="wmode" value="transparent" />
<param name="allowfullscreen" value="true" />
<param name="FlashVars" value="node=2" />
<param name="quality" value="high" />
</object>

Within ActionScript, the node variable is then passed to the root structure and can be referenced within our main.as file using the root.loaderInfo.parametersconstruct. For example, we can determine the node ID passed to our Flash application by using the following code within our main.as file:

root.loaderInfo.parameters.node

Using this information, we can now create a nodeID variable at the top of our main.as file, where we will set it to the node ID passed to our Flash application. We can then replace our hard-coded node value with this variable so that our Flash application is no longer dependent on a specific node value.

// Declare our variables
var baseURL:String = "http://localhost/drupalbook";
var gateway:String = baseURL + "/services/amfphp";
var sessionId:String = "";
var nodeId:Number = root.loaderInfo.parameters.node;
// Called when Drupal returns with a successful connection.
function onConnect( result:Object )
{
// Set our sessionId variable.
sessionId = result.sessid;
trace("We are connected!!!");
trace("Session Id: " + sessionId);
// Load our node.
loadNode( nodeId );
}

We are now finished with our "Hello World" application. At this point, we can run our Flash application so that it will create the chapter2.swf file, which we will then use to add to our Drupal web site.

Step 8: Adding it to Drupal

In this step, we can go back to the lessons learned from the previous chapter where we used the FlashNode module to add a SWF file to our Drupal website. To start, we will create a new Flash node by navigating to Create Content | Flash, and then give it a title of "Hello World Application". After we have done that, we can select our newly created SWF file using the Flash file input field. The next step, however, will require us to pass in the FlashVar so that we can tell our application which node to load. To do this, we will simply expand the Advanced flash node options, and place node=2 within the FlashVars text field.

Flash with Drupal

We can now save our new node, and see our new dynamic Flash application in action.

Flash with Drupal

Summary

When dealing with web services, it is very important to understand how two remote applications communicate and how to develop our software to account for its asynchronous interaction. This is typically always overlooked when developers create their first Flash applications for Drupal, and can easily be avoided if the concepts of web service interaction are understood and taken into account. Each key concept is highlighted as follows:

  • Flash and Drupal communicate asynchronously. This means that each function call made to Drupal from Flash does not return the result immediately after the call was made. Instead, we need to utilize a callback function that is triggered when Drupal returns the result from our function call.
  • We need to wait until the Flash application has finished connecting to Drupal before making any other calls. Otherwise, we will have a race condition where our sessionID is invalid for our other calls.

If you have read this article you may be interested to view :


Flash with Drupal Build dynamic, content-rich Flash CS3 and CS4 applications for Drupal 6
Published: May 2009
eBook Price: $26.99
Book Price: $44.99
See more
Select your format and quantity:

About the Author :


Travis Tidwell

Travis Tidwell is the founder and CTO for TMT Digital (http://www.tmtdigital.com), a company that specializes in the development of Flash applications for the Drupal content management system. He is also the sole developer for the Dash Media Player (http://www.tmtdigital.com/project/dash_player, http://www.drupal.org/project/dashplayer), which is a media player built specifically for Drupal. As well as contributing this media player, Travis is also the author and co-maintainer for the FlashVideo module (http://www.drupal.org/project/flashvideo), which is a complete video solution for Drupal.

Travis graduated with a Bachelors of Science in Electrical and Computer Engineering from Oklahoma State University and has worked as an Embedded Systems Engineer for companies specializing in automotive and agricultural GPS products. Travis then fell in love with web development and more specifically with Drupal and Flash, where he has developed numerous sites including http://www.delicioso.com for Food Network's Ingrid Hoffmann.

Travis is happily married to his beautiful wife, Erin, and is the proud parent of a feisty one-year-old named Brycen. When Travis isn't working on the computer (which is rare these days), he enjoys the performing arts including playing guitar, singing, and tap dancing (Search for "Soul Man Tap" at http://www.youtube.com to see him in action).

Books From Packt


Drupal Multimedia
Drupal Multimedia

Learning Ext JS
Learning Ext JS

Drupal 5 Views Recipes
Drupal 5 Views Recipes

Learning Joomla! 1.5 Extension Development
Learning Joomla! 1.5 Extension Development

Spring Web Flow 2 Web Development
Spring Web Flow 2 Web Development

Choosing an Open Source CMS: Beginner's Guide
Choosing an Open Source CMS: Beginner's Guide

Joomla! E-Commerce with VirtueMart
Joomla! E-Commerce with VirtueMart

Mastering phpMyAdmin 3.1 for Effective MySQL Management
Mastering phpMyAdmin 3.1 for Effective MySQL Management


Your rating: None Average: 5 (1 vote)

Post new comment

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.
e
i
4
H
j
2
Enter the code without spaces and pay attention to upper/lower case.
Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software