Articles

DrupalProxy as a bridge between flash as3 and drupal via amf

Drupal is a multiple award winning opensource content management system. With the drupal services module, it’s possible to consume data from drupal in flash. Drupal is a very powerful tool to provide data to flash movies and with the community investing much time in drupal services, now is a great time to be using drupal to power your flash websites. Services will be part of the drupal 7 core release.

We decided to UPGRADE our nl.dpdk.services.gephyr package, which is a drupal as3 service which acts as a bridge between drupal and flash. It has all the functionality of the drupal services built in, including the ability to connect to existing core services and any custom service implemented, out of the box. There is no need to extend it or customize it. Furthermore, it makes use of all drupals’ security mechanisms via key and session based authentication.
It has more features and packs more power than any other opensource actionscript 3 based drupal package out there at the moment, inluding our now obsolete DrupalService, so be sure to check it out from svn and start using it today.

It has a well thought out and consistent api, very tight and tested code, is easy to use, and features the ability to use the security mechanisms that are used for the drupal services backend module.

We feel that this is the implementation that ends it all and provides no more need for any other package for communicating from as3 to a drupal backend via amf and the services module. If you do however need to alter or add code, you can either subclass the class and implement some of the protected hook methods we have put there just for that purpose, OR, you can write an adapter for it. As stated before, that should not be necessary.

The architecture of the nl.dpdk.services.gephyr.DrupalProxy class is based upon using our drupal code extensively in commercial projects and from user feedback from the drupal community. It is based on the proxy design pattern as a remote proxy to the drupal services module.

We have made the explicit choice to step away from a strongly typed api on core services offered by drupal and instead focus on creating code that is able to connect to all services, either custom services or core services.( core services are based on nodes, users, taxonomy, system, search and views). Since a flash programmer needs to now what method he calls on what service anyway, it was no longer necessary to provide strong typing on our api (as we did in previous versions) and thereby provide an even simpler interface for the programmer. The as3 programmer will decide with the backend/drupal programmer what methods he should call on what service and what the arguments are and can then easily communicate with the drupal backend via 1 line of code! To be able to do this, we parse the arguments of the invoke() method into a sensible format for the service, this is important because of the low level implementation of the security mechanism offered by drupal (which we completely abstract away for the programmer).

//core call. first argument is the service, the second the method
proxy.invoke("system", "connect");
//or a custom call. arguments for the remote method are also provided here as an example
proxy.invoke("myService", "myMethod", "myArgument", "andAnotherArgument");

Furthermore, we have completely abolished the use of events for the DrupalProxy, to provide a consistent api that uses callbacks only. This allows you to easily consume data from the drupal backend in a consistent way and also solves an earlier problem in our obsolete DrupalService which forced you to subclass the DrupalService when doing calls on custom services.

A thing to notice is that each callback/handler you set for either handling the result or the error of a service call, the callback/handler you set for generic (low level) errors and the callback/handler you set for timeouts on a service call will receive a DrupalData object as their only argument. The DrupalData object contains all information necessary to process the data.

proxy.setHandler("node", "get", onNodeGet, onNodeGetStatus);
proxy.setHandler("user", "get", onUserGet, onUserGetStatus);
 
//elsewhere
public function onUserGet(data:DrupalData):void
{
	trace("onUserGet " + data.getData());
}
 
public function onUserGetStatus(data:DrupalData):void
{
	trace(".onUserGetStatus id : " + data.getRemoteCallId() + ": " + data.getMessage());
}

We have also made it possible to uniquely identify a result that comes back from the drupal backend even if you make multiple calls to the same remote method. This is very convenient when using views on the services for example. When making a call to views.get, the method takes the name of the view as an argument. The name of the view is not returned with the result from drupal. So if you want to get the data for a view named “news” and a view named “faq” it’s impossible to distinguish in your method that handles the result what the source is for the data that is coming in. We have solved this by allowing you to uniquely identify the next call you are going to make on the DrupalProxy with a string, which can be retrieved from the DrupalData.

proxy.setRemoteCallId("faq");
proxy.invoke('views', "get", "faq");
proxy.setRemoteCallId("news");
proxy.invoke('views', "get", "news");
 
//elsewhere
private function onViewsGet(data:DrupalData):void 
{
	switch(data.getRemoteCallId())
	{
		case "news":
			break;
		case "faq":
			break;				
	}
}

Another aspect is that it’s very easy to use drupals’ security features. They are based on sessions and on using authentication keys. You can use either one of them, both, or none. DrupalProxy will handle it for you with the help of a DrupalSecurity object without extra trouble on your side. Just configure the DrupalProxy by providing the right data to the constructor. It is written for Drupal 6 at the moment, but will be upgraded to be able to work with both drupal 6 and 7 when 7 is released. We will probably only change the constructor then to make it able to work for both versions when drupal 7 will use other means of authorization such as OAuth.

By having a seperate DrupalSecurity object, we have encapsulated all stuff that is related to sessions and authentication keys in one object that can be used seperately of the DrupalProxy class, so if you wish to use your own drupal service, you can also take advantage of the fact that we have provided functionality for providing the authentication hashes, nonce etc. for you.
Special thanks to the guys from hurlant that provide a kickass cryptology library for encryption and hashing stuff. This is the first external library we have used for our opensource package. Be sure to check out their license conditions, it’s licensed under BSD and hurlant’s license text can be found here. Our nl.dpdk package is licensed under the MIT license.

There is a simple DrupalUtils class that will be filled with easy methods for setting up objects native to drupal. For instance, it is sometimes necessary to provide a timestamp to an object when you want to change it. The DrupalUtils class provides for such functionality, including getting a new user and node object, ready to be used or saved.

On top of that, we will take suggestions on adding more functionality in the DrupalProxy base class if the community needs it.

The class itself is heavily documented so you can all see what’s happening on the inside of the class.

When using the DrupalProxy class, be sure to properly configure drupal permissions. You can use sessions, you can use keys, you can set permissions for different users or different user roles. Also don’t forget that it’s possible to log in, so you can have even better control over which user does what, just by having him log in to the drupal backend. The possibilities for configuration are endless and so are the things you can do with drupal and flash or flex.

let’s demonstrate how it all works with some code.

package nl.dpdk.services {
	import mx.utils.ObjectUtil;
	import nl.dpdk.services.gephyr.DrupalUtils;
	import nl.dpdk.services.gephyr.DrupalProxy;
	import nl.dpdk.services.gephyr.DrupalData;
 
	import flash.display.Sprite;
	[SWF(width="600", height="600", backgroundColor="0x2C3054", frameRate="31")]
	public class DrupalApplication extends Sprite {
                //the proxy
		private var proxy : DrupalProxy;
 
 
		public function DrupalApplication() {
                       //constructor, takes a gateway, whether we need sessions (optional), and a key and domain (optional)
			proxy = new DrupalProxy("http://drupal.dpdk.nl/services/amfphp", true, "8736e2c9e27b48a4a526ef922b4e9250" , "drupal.dpdk.nl");
 
                       //the generic error event
			proxy.setErrorHandler(onError);
			//the timeout error
			proxy.setTimeOut(500, onTimeOut);
			//set a handler for the result or error that comes back from the drupal backend for the unique combination of servicename and method name
			proxy.setHandler("system", "connect", onConnectResult, onConnectStatus);
			proxy.setHandler("node", "get", onNodeResult, onNodeStatus);
			proxy.setHandler("user", "login", onLoginReult, onLoginStatus);
 
 
                       //since we use a session, connect to the system first, DrupalProxy will store the sessionId for you.
			proxy.invoke("system", "connect");
		}
 
               //the handler for when the system connects
		private function onConnectResult(data : DrupalData) : void {
			trace("onConnect, sessionId: " + proxy.getSessionId());
 
                        //log in the drupal backend
			proxy.invoke("user", "login", "MyUserName", "MyPassword");
		}
 
 
                //the handler for when the user logs in
		private function onLoginResult(data : DrupalData) : void {
			trace("onLoginResult");
			trace(ObjectUtil.toString(data.getData()));
			//it's now possible to get the logged in user via proxy.getUser();
 
			//set the id for the next call we will make
			proxy.setRemoteCallId("node1");	
 
			//now get the first node, with only the selected fields nid, title and body_value
			proxy.invoke("node", "get", 1, new Array("nid", "title", "body_value"));			
		}
 
 
                //the handler for when the data of a node has come in.	
		private function onNodeResult(data : DrupalData) : void {
			trace("onNodeResult");
			trace(ObjectUtil.toString(data.getData()));
 
			//get the id associated with this call
			trace(data.getRemoteCallId()));
 
 
                        //handle your data here....
		}
 
 
               //generic error handler
		private function onError(data : DrupalData) : void {
			trace("onError: " + data.getMessage());
		}
 
 
               //generic timeout handler
		private function onTimeOut(data : DrupalData) : void {
			trace("onTimeOut: " + data.getMessage());
		}
	}
}

Any feedback would be greatly appreciated. happy coding :)

4 Responses to “DrupalProxy as a bridge between flash as3 and drupal via amf”


  1. 1 Nik

    Perfect timing, I was in the throws of wrestling with DrupalSite and trying to debug it whilst trying to make my own DrupalService and then i stumbled upon this. I love your work, my Air app is now functioning fantastically with Drupal.

    Many many thanks,
    Nik

  2. 2 rolf vreijdenberger

    thanks Nik, glad it’s working out for you. what are you building?

  3. 3 Matt Cook

    Have you had any luck getting AMFPHP going with Drupal 7? I’ve got a test site going with AMFPHP and Drupal 6 but would love to use D7 with a flash front-end.

  4. 4 rolf vreijdenberger

    Hey matt, we’d love that too.

    we’re waiting for the release of D7 to implement the version for D7. The DrupalProxy will work with both 6 and 7.

  1. 1 DrupalService as a bridge between flash and drupal at dpdk Open Source

Leave a Reply