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 :)

39 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.

  5. 5 Glenn

    This looks great, as a Drubal Noob a little tutorial would be hugely appreciated if anyone can make the time.

  6. 6 Derek

    Loved your previous version and will probably love this one too.

    I have a question on which folders I exactly need to get the drupal services working…there is a lot of classes and was just wondering which ones would be mandatory…just to keep some stuff a little cleaner.

    For example…the sound folder…is that necessary for drupal?

    Thanks

  7. 7 rolf vreijdenberger

    @Derek, thanks and a good question. Not all classes have dependencies on each other.
    From a quick guess, I think you can delete all but the gephyr package (other services can go), the collections package, specifications package and maybe the utils package.

    Otoh, it is a toolkit and as such uses features from other packages in it. Also ‘a little cleaner’ is something that might have to do with “a programmer’s control mindset”. If you don’t look at it, it is clean :) You can also build a .swc file from it, which is one file so it won’t look as much.

    For building with fdt, an swc might also help keeping building time lower, without losing code hinting etc.

    hope that helps

  8. 8 jeff

    What do your “on status” functions look like? (ie: onConnectStatus) Also I think the AsDocs need to be updated because there isnt documentation for DrupalProxy/DrupalData.
    Thanks!

  9. 9 Ma Abel

    Thank for post, mine english not good. I like this much.

  10. 10 Rolf Vreijdenberger

    Hey Jeff and Ma abel,

    status handlers will likely contain error handling logic to give feedback to the user. For critical stuff you application should halt.
    The documentation hasnt’ been updated, but you could make your own from the source code with asdocs, or read it directly from the source code.

    thanks Ma abel for the heads up!

  11. 11 Bruno

    Hey, great engine you have, that´s really awesome!!! I was working with wordpress but needed something more flexible, beeing able to use Drupal is just perfekt, and your framework looks great :)
    I´m completely new to Drupal though, and I have no idea how to get a sitemap for example.
    my project is based on categories and I need to get the site structure to parse…
    could you point me some way to achieve that?

    many thanks in advance!
    greetings from Austria
    Bruno

  12. 12 Rolf Vreijdenberger

    Hi Bruno,

    thanks man, glad you like it :) The best way would be to go to the drupal site and search through the modules.
    A simple search for sitemap will surely give you many useful hits, with documentation.

    good luck.

  13. 13 ADASADA

    Hey! very nice work guys, im thinking it would be better if all those event types where coded in static consts, instead of Strings like are right now :)

  14. 14 ash

    Hey,

    This looks pretty interesting… please help me to understand how to get this to work with gaia framework.

    I am visual… with very basic understanding of AS3… feel a bit frustrated with the error messages… that I do not know how to resolve:

    1120: Access of undefined property onConnectStatus.
    1120: Access of undefined property onNodeStatus.
    1120: Access of undefined property onLoginReult.
    1120: Access of undefined property onLoginStatus.
    1120: Access of undefined property ObjectUtil.
    1172: Definition mx.utils:ObjectUtil could not be found.

    I can clearly accept that this is my limitation… please help point me in the right direction.

    Many thanks

  15. 15 Rolf Vreijdenberger

    @adasada: there are no event types for the drupalproxy, since there are no related events. everything works with callback handlers. if you mean things like “system.connect” etc., this is dependent on the services backend, which can contain both core services (node, system etc) or custom services. You can ofcourse define those static const yourself.

    @ash: my bad, not all methods are implemented in the code example above. basically, what the error messages say is that there is no ‘onConnectStatus’ method defined, no “onNodestatus” etc. onLoginReult is a typo (it should be ‘Result’ instead of ‘Reult’). When you make those methods in the code example, you should be fine. Don’t use ObjectUtils if you cannot import them, remove the import and the trace/debug statements that use those

  16. 16 Nicola Rodriguez-Demers

    I want to use your library in a project under Drupal. I plan to release my Drupal module under the GPL license.

    I don’t think there’s any problem to use your library, but I want to make sure (before distributing it) that there’s no issue whatsoever.

    If you are interested, I can send you a link of the sources when we will release it.

  17. 17 Federico

    Any news on 7 version? Thanks a lot!

  18. 18 elfe2k

    great ! hope you’ll upgrade it for version 7 !

  19. 19 rolf vreijdenberger

    @ nicola: no problem, license is MIT, you can use it. I’d definitely would like to hear more :)
    @Frederico and elfe2k: No amf services integration in D7 yet. It will come though

  20. 20 rolf vreijdenberger

    Frederico and elfe2k, there is a new version of drupalproxy, that works for D7 with the amfserver module for D7: http://drupal.org/project/amfserver

  21. 21 drala

    Hi! On my onConnectStatus method I´m getting the following message:
    connect status: The class {system} could not be found under the class path {/home/.sites/293/site347/web/drupal6/modules/amfphp/amfphp/services/system.php}

    There are no services yet in the services folder inside amfphp. Should be something there?
    I´m using drupal 6.x…

    Many thnaks in advance!
    keep rocking

  22. 22 Peter

    Hi, all works fine!
    But how can i implement new resources to the Service/AmfServer Module?

    Many Thanks
    Peter

  23. 23 Marijn

    Hi

    Great work, tho i have some issues with a custom build Services Menu Resource.

    I want to get the menu-structure i.e. main-menu. Tho no matter what i try, i always get a empty array.

    I have Drupal 7 with Services 3.0. My Drupal installation is on another domein than the actual flash-site.
    i guess this is more of a Services issue, tho i was wondering if anyone else here has been able to get the menu-structure from Drupal7 with amfserver and Services.

    Thanks in advance!

  24. 24 rolf vreijdenberger

    @drala, did you configure your services correctly and did you activate the system.connect method? This is an amfphp error message..

    @peter: Good :) take a look at the hook_services_resources implementation in the amfserver.module file. there is an example there of how you could use that in your own module (with comments)

    @Marijn: it is indeed a drupal services thing. Services 7.3 has not yet implemented a menu resource. If you’d really want it, it would be pretty easy to write yourself if you take a look at the resources implemented so far and adjust the code for the menu implementation (and submit it as a patch too :))

  25. 25 Marijn

    Hi Rolf,

    Thanks for your reply. I indeed created a Resource for Services 3 myself, but it wont retrieve anything xcept empty array’s.

    I’m using the following code to get the menu-structure:
    <?php
    //@param i.e. main-menu or navigation
    function services_menu_retrieve($menu_name) {
    $data = menu_build_tree($menu_name);
    return $data;
    }

    Only when i call menu_get_names, i get some results, other than that i get nothing. Tho when i call the menu_build_tree() function from anywhere within my drupal-theme i get the expected array as result.
    I’m guessing it has something to do with the fact i am running on a different domain (or from flash-builder even)?

  26. 26 Marijn

    its fixed as you might have read on the Drupal page:

    the problem is more a AMF issue i guess. It seems AMF cannot handle string-indexed array’s.
    I re-build the array to an numeric indexed array and that solves the problem. Thanks for the help!

  27. 27 Marijn

    Hi Rolf,

    I got everything up and running, including some ‘fancy classmapping’ and stuff. I created an own services resource from where the classmapping runs fine.

    Tho i’m trying to add the content/example of the path http://drupal.org/node/391922 (module for amfphp) into your AmfServerServiceProxy class.

    in the execute() function i add the _explicitType property to the $data object (or to all objects in case of array). Tho this doesnt seem to work or get through. when i add any other random property i.e. $data->foo = “bar”;
    it gets through property.

    How do/would you add class-mapping to nodes/content-types (without creating a custom resource but with the default node.retrieve method?)

  28. 28 Rolf Vreijdenberger

    Hi Marijn,
    Sylvain Lecoy (check/contact him on drupal.org) has been working on an implementation of this.
    Right now you would need to create a custom resource to enable autoclassmapping for complex types of the drupal core like node etc.

  29. 29 Marijn

    Hi Rolf,

    Thanks, i’ll keep track of Sylvain Lecoy. For now i will use my own resources for this..

  30. 30 Owczarzak@hotmail.com

    I located your weblog on the internet and appearance several of the earlier posts. Preserve in the exceptional operate. I just additional up your Feed to my personal Windows live messenger News Readers. Seeking forward to studying extra of your stuff later on!?–

  31. 31 Viande

    Hi,
    does anyone know how to create a user ? I tried “user”,”create”,[name:"skjd",pass:"qsdf",email:"mqsdi"]
    and all i’ve got is always the same error message : “faultString = “Username field is required. E-mail address field is required.”

  32. 32 Swagger

    @ Viande

    Hey man, these re the only user related services you can use:
    user.delete
    user.get
    user.login
    user.logout
    user.save

    Hi Rolf,
    Just wanted to say how great this tool is. I have got everything working except the search.nodes service. I am currently trying: proxy.invoke(”search”, “nodes”,searchTerm); but no luck. Any ideas?

    Thanks

  33. 33 Swagger

    My bad, I had mistyped my handler. This is correct…

    proxy.setHandler(”search”, “nodes”, onSearchResult, onSearchStatus);

  34. 34 Swagger

    Sorry for the spam. In fact, it did not work - I receive the object code: (String) = AMFPHP_RUNTIME_ERROR

    When I look in drupal.services, search.nodes description is: Searches nodes according to keys via hook_search. What is hook_search? Any help would be most appreciated, I am floundering.

  35. 35 Swagger

    FIXED:
    had to make sure I had indexed in search setting within the CMS.
    got poormanscron to auto index
    I am on drupal 6.1 by the way.

    I did not change the AS.

    Hope that helps someone.

    Thanks again dpdk for this awesome api

  36. 36 Donn Chessor

    I realy enjoyed reading that post, i also own a proxy related domain where in it you can find proxy article. if you want we can share links, anyway i realy like your post, keep on the fine work.

  37. 37 SerhiyK

    Using your package with Services 2.4 and AMFPHP 1.9.

    When trying node.get I get error ‘Could not find node’. I setup watchdog in node_service_get function to see what’s id is coming. Instead of simple integer , it’s a string ‘ b2a5639f059c74c7d8fe945525625b593796a99fa6073a709a7770f96fe0bfed’.

    Can you help?

  38. 38 Ingo Bartsch

    Really nice!!

  39. 39 Devorah Percival

    Good blog! I truly love how it is simple on my eyes and the data are well written. I am wondering how I might be notified whenever a new post has been made. I’ve subscribed to your feed which must do the trick! Have a nice day!

  1. 1 DrupalService as a bridge between flash and drupal at dpdk Open Source
  2. 2 presentation and code of the adobe user group xl (augxl) 2010 online at dpdk Open Source
  3. 3 introducing the amfserver, a zend based drupal 7 module at dpdk Open Source

Leave a Reply