Extending FacebookOAuthGraph Class
While The Facebook Graph API article becomes pretty popular and a lot of developers keep asking me to publish some practices I use, I managed to create this blog post. Here is a list o some functions you may want to extend FacebookOAuthGraph Class with.
Extending & Singletonize
First, lets make our class singleton, so the whole flash project can simply access and use one instance
public class Facebook extends FacebookOAuthGraph { private static const _instance:Facebook = new Facebook(); public function Facebook() { super(); useSecuredPath = true; if(instance) throw new Error("Use Facebook.instance!"); } public static function get instance():Facebook { return _instance; } .... }
Now anywhere in your project:
var facebook:Facebook = Facebook.instance;
Auto Execution After Authorization:
Lets say, when user clicks on publish post before your application is connected, just make connect() followed by desired function:
if(!facebook.authorized) facebook.connect(); facebook.publishPost(...)
publishPost() automatically executes again after connected thanks to Callbacks class
import sk.yoz.utils.Callbacks; private function onAuthorized(event:FacebookOAuthGraphEvent):void { Callbacks.execute("FBstuff"); } public function publishPost(...):void { if(!authorized) return Callbacks.add(this, arguments.callee, arguments, "FBstuff"); // here continues the function code ... }
Application Friends
Lets get list of friends using the application:
private function getAppFriends():void { var data:URLVariables = new URLVariables(); data.query = "SELECT uid, name, pic_square " + "FROM user " + "WHERE uid IN " + "(SELECT uid2 FROM friend WHERE uid1=" + me.id + ") " + "AND is_app_user"; var loader:URLLoader = call("method/fql.query", data, URLRequestMethod.POST, null, "https://api.facebook.com"); loader.addEventListener(FacebookOAuthGraphEvent.DATA, onGetAppFriends); } private function onGetAppFriends(event:FacebookOAuthGraphEvent):void { var xml:XML = new XML(event.rawData); var ns:Namespace = xml.namespace(); default xml namespace = ns; xml.namespace(ns.prefix); xml.ignoreWhite = true; for each(var user:XML in xml.user) { user.uid.toString(); user.name.toString(); ... } default xml namespace = new Namespace(""); }
Publishing Feeds
Publish feed with image:
public function publishPost(message:String, attachmentName:String, attachmentDescription:String):void { var media:Object = {}; media.src = "http://mydomain.com/feedimage.jpg"; media.href = "http://apps.facebook.com/myapp"; media.type = "image"; var attachment:Object = {}; attachment.name = attachmentName; attachment.href = "http://apps.facebook.com/myapp"; attachment.description = attachmentDescription; //attachment.caption = "test caption"; attachment.media = [media]; var data:URLVariables = new URLVariables(); data.message = message; data.attachment = JSON.encode(attachment); call("method/stream.publish", data, URLRequestMethod.POST, null, "https://api.facebook.com"); }
Publish feed with flash:
public function publishFlash(message:String, href:String, swfSrc:String, mediaSrc:String, attachmentName:String, attachmentCaption:String, attachmentDescription:String, properties:Object=null):void { var media:Object = {}; media.type = "flash"; media.swfsrc = swfSrc; media.imgsrc = mediaSrc; //media.width = "80"; //media.height = "80"; media.expanded_width = "460"; media.expanded_height = "460"; var attachment:Object = {}; attachment.name = attachmentName; attachment.href = href; attachment.caption = attachmentCaption; attachment.description = attachmentDescription; attachment.media = [media]; attachment.properties = properties; // action links // properties {prop1:{text: "value 1", href:"http://"}, prop2:...}; var data:URLVariables = new URLVariables(); data.message = message; data.attachment = JSON.encode(attachment); call("method/stream.publish", data, URLRequestMethod.POST, null, "https://api.facebook.com"); }
Publish feed with action links:
var properties:Object = {}; properties.MOVE = { text: "mouse drag", href:config.urlManager.boardURL}; properties.ZOOM = { text: "double click, [Page Up], [Page Down]", href:config.urlManager.boardURL}; properties.ROTATE = { text: "[Ctrl] + mouse move", href:config.urlManager.boardURL}; attachment.properties = properties;
Publishing feed using Graph API (docs, from facebook bugzilla):
- forget everything related to the old “media” stuff of the attachment
- put what you had in
swfsrcinto source (mandatory) - put what you had in
imgsrcinto picture (mandatory) - link is mandatory also and must points to the connect or canvas URL
- Feed arguments: message, picture, link, name, caption, description, source
Uploading Photo
This is a piece of working code from Sean, more upload handling functions and album creating can be found here. thnx Sean:
// MultipartURLLoader by Eugene Zatepyakin can be found here: http://bit.ly/9wx4q7 public function uploadImageCall(bytes:ByteArray, message:String):MultipartURLLoader { var mpLoader:MultipartURLLoader = new MultipartURLLoader(); mpLoader.addVariable("message", message); mpLoader.addFile(bytes, "image.jpg", "image"); loaderAddListeners(mpLoader.loader); mpLoader.load(apiSecuredPath + "/me/photos?access_token="+ token); return mpLoader; }
Palming Token
When localy debuging your app you may want to palm off custom access_token:
if(parameters.debug) facebook.hackToken(parameters, "110363ABXY..."); // copypaste from callback.html hash facebook.autoConnect(parameters);
in Facebook.as:
public function hackToken(parameters:Object, token:String):Object { if(!parameters.session) parameters.session = JSON.encode({ access_token:String(token).replace(/\%7C/g, "|")}); return parameters; }
Advanced Crossdomain Working Authorization
Or if you prefer or need advanced callback, that works crossdomain, and also for local debuging: callback.html
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="sk" lang="sk" dir="ltr"> <head> <script type="text/javascript" src="js/swfobject.js"></script> <script type="text/javascript"> <!-- if(window.opener && window.opener.confirmFacebookConnection){ window.opener.confirmFacebookConnection(window.location.hash); self.close(); }else{ var flashvars = {}; flashvars.connectionName = "_facebookConnector"; flashvars.methodName = "confirmConnection"; flashvars.hash = window.location.hash; swfobject.embedSWF("flash/Callback.swf", "flash", "1", "1", "10.0.0", "expressInstall.swf", flashvars, {}, {}); } //--> </script> </head> <body> <p>You may now close this window.</p> <div id="flash"></div> </body> </html>
universal callback.swf (741 Bytes) main class:
package { import flash.display.Sprite; import flash.net.LocalConnection; public class Callback extends Sprite { public function Callback() { super(); var parameters:Object = loaderInfo.parameters var localConnection:LocalConnection = new LocalConnection(); localConnection.send( parameters.connectionName, parameters.methodName, parameters.hash); } } }
facebook.as class:
private var localConnection:LocalConnection; override public function connect():void { super.connect(); if(!localConnection) { localConnection = new LocalConnection(); localConnection.client = this; localConnection.allowDomain("*"); localConnection.allowInsecureDomain("*"); localConnection.connect("_facebookConnector"); } } private function onAuthorized(event:FacebookOAuthGraphEvent):void { // listener defined in Facebook constructor: // addEventListener(FacebookOAuthGraphEvent.AUTHORIZED, onAuthorized); try { localConnection.close(); localConnection = null; } catch(error:Error){} }
Getting All The Photos Of All Albums Of 1 User
Credits to Etienne, thnx
function getAllphotos(uid:String):void { var data:URLVariables = new URLVariables(); data.query = "SELECT src_small, src_big " + "FROM photo " + "WHERE aid IN ( SELECT aid FROM album WHERE owner='"+HERE PUT THE USER ID+"' )" var loader:URLLoader = facebook.call("method/fql.query", data,URLRequestMethod.POST, null, "https://api.facebook.com"); loader.addEventListener(FacebookOAuthGraphEvent.DATA, onGetAllphotos); }
Tagging photos
This script uses photos.addTag – part of old REST api (some more reading):
var data:URLVariables = new URLVariables(); data.pid = photoID; data.tag_uid = userID; // user on photo we are tagging data.x = 50; // 50% means center data.y = 50; // 50% means middle call("method/photos.addTag", data, URLRequestMethod.GET, null, "https://api.facebook.com");
Important: Graph API upload call returns the object_id. In order to get pid you have to lookup in photo table:
SELECT pid FROM photo WHERE object_id=response.id
Catching verify token error
In order to catch the ERROR event with verifyToken() method Nils suggested the following:
override public function verifyToken(token:String):URLLoader { var loader:URLLoader = super.verifyToken(token); loader.addEventListener(FacebookOAuthGraphEvent.ERROR, function(event:FacebookOAuthGraphEvent):void { _authorized = false; _me = null; _token = null; EventDispatcher(event.currentTarget) .removeEventListener(event.type, arguments.callee); var type:String = FacebookOAuthGraphEvent.UNAUTHORIZED; dispatchEvent(new FacebookOAuthGraphEvent(type)); }); return loader; }
Hi Jozef,
Great class, thanks for this, we’ve been using PHP solution for FB apps, but I think we’ll start using your solution from now on!
Have one question though..
It’s about autoconnect function.. It has a event callback : “AUTHORIZED” when the user get’s successfully authorised. Is there a callback when autoconnect couln’t authorise the user ?
I’ve tried almost everything.. adding UNAUTHORIZED event to facebook instance, DATA event – none of the them get called on autoconnect fail..
Also tried adding listeners to URLLoader instance returned from facebook.autoconnect (DATA, COMPLETE) no luck..
Any idea how to solve this ?
Thanks.
Hi Slavo,
I like to hear that you like it. I hope we can fix the issues now…
there is an autoConnect() function that can take parameters (flashvars):
– if parameters contains session and access_token the lib tries to verifyToken()
– if it does not containt session lib uses token stored in sharedObject to verifyToken()
verifyToken() creates URLLoader contacting facebook to see if the token is valid:
– if the response is valid verifyTokenSuccess() is called dispatching AUTHORIZED on FacebookOAuthGraph instance
– if the response is not valid there is no handler for this by default. you can extend verifyToken() function to receive error events over URLLoader…
I am just reading that you are already able to handle events here (on autoConnect()), so what you have to do is, listen for HTTPStatusEvent and IOErrorEvent. I believe facebook responses with error when the authorization fail, that is why you failed with catching it like Event.COMPLETE
Jozef:
IOErrorEvent.IO_ERROR did catch the autoconnect fail. Thanks for your help!
I try the crossdomain solution, but FlashPlayer always return an error:
#2082: Connect failed because the object is already connected.
PS :
thanks for this great class !
Jerome, you can not establish 2 LocalConnections with the same connectionName. make sure to close my blog or any other flash that may collide with your app
Hello again Jeff!
Tell me something, we still can’t invite friends to use the application?
If you can’t, is there any way to send a message, event or something similar to some id (writing the invitation there)?
Cheers
When i say we can’t invite friends, i mean without using that standard FBML code.
Hi PVieira,
see http://blog.yoz.sk/2010/03/inviting-friends-into-facebook-application/ for inviting friends, there is no other option
Thanks Josef, i will try to use the light popup (i want to integrate this feature in a existing app).
Hi Josef,
I have a problem using the method video.upload, that parameter should not go to [no name]- data – The file for the video data. This is a ByteArray? I hope you can help as facebook returns me this error “error_code”: 7, “error_msg”: “This method must-run on api-video.facebook.com. If possible an example on using this method would be helpful. Thank you for your attention.
Hi Garo,
based on documentation you have to make video upload calls to http://api-video.facebook.com use MultipartURLLoader (see “Uploading Photo”) to insert bytes into request content
Hello,
As Garo’m confused when trying to upload video to Facebook, this is my code:
First call the method this way:
var file: FileReference = FileReference (event.target);
trace (file.dat is ByteArray);
/ / Video Uploader
face.uploadVideoCall (file.data, “Video Test”);
UploadVideoCall function is this:
public function uploadVideoCall (bytes: ByteArray, message: String): void
{
trace (“Video UPLOADER”);
var mpLoader: MultipartURLLoader MultipartURLLoader = new ();
mpLoader.addEventListener (Event.COMPLETE, onMultiPartVideoLoader, false, 0, true);
mpLoader.addVariable (“message”, message);
mpLoader.addFile (bytes, “Video.flv”, “Test Video”);
super.loaderAddListeners (mpLoader.loader)
trace (“String GRAPH API:” + apiSecuredPath + “/ me / videos? access_token =” + _token)
mpLoader.load (apiSecuredPath + “/ me / videos? access_token =” + _token)
}
onMultiPartVideoLoader private function (e: Event): void {
trace (“Upload Sucefull)
}
The error I get is:
Error # 2044: uncontrolled ioError: text = Error # 2032: Error sequence. URL:
https: / / graph.facebook.com / me / videos?
access_token = 127807137233234 | 2.gDyuwi_sPsqbg2QBkgE6tg__.86400.1292518800-
1061543699 | U7XoBuXfxiDo0xhONxVQzpoCsxU
Josef could tell me where I am failing to solve this problem.
In this case, that part is not using the method “video.upload” because in this way was like I got a facebook pictures and it works, I think the method used in this case is this http://developers. facebook.com / docs / reference / api / video, seeing the image works with this http://developers.facebook.com/docs/reference/api/photo honestly I feel very lost on this problem I hope suggestions.
Hahahaha, MauroX I did this:
var file: FileReference = FileReference (event.target);
face.uploaderVideo (face.uploadVideo (file.data, “Video”));
Wherein the method UploaderVideo:
uploaderVideo public function (data: MultipartURLLoader): void {
trace (“Data:” + data);
var data: URLVariables URLVariables = new ();
data.dat = data;
data.title = “Video Title”;
data.description = “Video Description”;
var loader: URLLoader = super.call (“method / video.upload”, data, URLRequestMethod.GET, null, “http://api-video.facebook.com”);
loader.addEventListener (FacebookOAuthGraphEvent.DATA, videoComplete, false, 0, true);
}
videoComplete private function (event: FacebookOAuthGraphEvent): void {
trace (“Video Complete”);
}
This is the Method of Josef Upload Image modified to Video:
public function uploadVideo (bytes: ByteArray, message: String): MultipartURLLoader
{
var mpLoader: MultipartURLLoader MultipartURLLoader = new ();
mpLoader.addVariable (“message”, message);
mpLoader.addFile (bytes, “videos.flv” “videos”);
super.loaderAddListeners (mpLoader.loader)
mpLoader.load (apiSecuredPath + “/ me / videos? access_token =” + _token)
mpLoader return;
}
And I came out the following error:
Security Sandbox Error.
Well, I managed to repair the problem that i had to type IOError, Facebook has managed to answer me upload the video to Facebook, but
review and did not. And started to lose hope jajajajaja.
Hi Josef,
I think here I come … This code is supposed to upload a video to Facebook,
//Call Video Graph API
public function uploadVideoCall(bytes:ByteArray, message:String):void
{
trace(“Video UPLOADER”);
var mpLoader:MultipartURLLoader = new MultipartURLLoader();
mpLoader.addEventListener(Event.COMPLETE,onMultiPartVideoLoader,false,0,true);
mpLoader.addEventListener(IOErrorEvent.IO_ERROR,onMultiPartVideoLoader,false,0,true);
mpLoader.addEventListener(ProgressEvent.PROGRESS,onMultiPartVideoLoaderProgress,false,0,true);
mpLoader.addVariable(“message”, message);
mpLoader.addFile(bytes, “Video.flv”, “Test Video”);
super.loaderAddListeners(mpLoader.loader);
mpLoader.load(apiSecuredPath + “/me/videos?access_token=”+ _token);
}
private function onMultiPartVideoLoader(e:Event):void{
trace(“Sucefull Upload: “+e.currentTarget);
}
private function onMultiPartVideoLoaderError(e:IOErrorEvent):void{
trace(“IO ERROR: “+e.currentTarget);
}
private function onMultiPartVideoLoaderProgress(event:ProgressEvent):void{
trace(“\n” + event.bytesLoaded + ” bytes de ” + event.bytesTotal + ” bytes subidos.”);
}
///////////////////////////////////////////////////////////////////////
the problem is that I receive Uploaded response, but does not really. I think the problem is due to deliver a missing parameter type, but honestly not which is (I tried uploading a picture and works perfectly using this code and only changing mpLoader.load (apiSecuredPath “/ me / photos? access_token =” _token)) I have read and found that it is possible that this method or called in facebook is bad, I like me’d say if it’s true or not the video upload is still not well done on facebook. I await your response and I apologize for so much spam
MauroX, Garo hi guys
I really think you should use old rest api method for now for this. read
http://developers.facebook.com/docs/reference/rest/video.upload
what you should do is to use the proper arguments with the request resulting in something like this:
mpLoader.load(“http://api-video.facebook.com/method/video.upload”);
I could not remove this error, any ideas?
*** Security Sandbox Violation ***
Has stopped connecting to http://api-video.facebook.com/method/video.upload
– not permitted from
http://www.quatiodevelopments.com/TestFacebook/FacebookMauroX.swf
Security Error: Error # 2048: Security Sandbox Violation:
http://www.quatiodevelopments.com/TestFacebook/FacebookMauroX.swf can not
load data from http://api-video.facebook.com/method/video.upload.
Hi Josef,
I tried everything:
This is my Cross Domain:
Also I’ve put
Security.loadPolicyFile (“https://graph.facebook.com/crossdomain.xml”);
but nothing … this is what I’ve read, but I failed to take this error:
http://www.adobe.com/devnet/flashplayer/articles/fplayer9_security.html # _Findi
ng_and_Fixing
http://livedocs.adobe.com/flash/9.0/main/wwhelp/wwhimpl/common/html/wwhelp.htm
? context = LiveDocs_Parts & file = 00000349.html
http://www.adobe.com/devnet/flashplayer/articles/fplayer9_security.html
if you got any suggestions, I appreciate.
MauroX, it really seems like there is an restrictive crossdomain on:
http://api-video.facebook.com/crossdomain.xml
so you will only be able to test it when running debug build from your local… what you should do is to try everything works there, and than make server side proxy using php or something you are more familiar with:
http://efreedom.com/Question/1-3257729/Uploading-Video-Old-Facebook-REST-API
Hi again Jeff.
I’m thinking about this method to post to wall and i was wondering: it’s only possible to post to the current user wall? Is not possible to post to his wall and to some page (for example the app profile page)?
Cheers!
Hi PVieira,
check out
http://blog.yoz.sk/2010/06/extending-facebookoauthgraph-class/#comment-2336
and other material over this article with “target_id”
Hello Jozef
Thanks for your great blog!
Here is my problem :
The html page that wraps the swf is in domainA.com and declared in the facebook app, but the swf is in domainB.com (caching server, cdn with transparent redirection). When I try to connect to facebook, the auhorisation fails. Any idea?
P.S. When the html and the swf are in the same domain, everything worksfine…
Thanks for your help!
Here is the link:
http://www.monstromatic.com/bin/index.html
@Ben,
I can see you defined .swf script access for crossdomain for both .swf files, as well as localconnection name with underscore correctly. Popup opens and closes by itself (however I am not able to find close command in javascript – maybe injected from callback.swf?) … I can also see the redirect from facebook to
http://www.monstromatic.com/bin/callback.html#access_token=***********&expires_in=4560
so window.location.hash should contain correct info… I believe there is some issue with handling hash value in your flash. You should debug that
Hi – I’m able to authorize in my flash only app using your great code – dumb question tho – I have this return object where I can get first name , last name like so
var obj:Object = event.currentTarget.me as Object;
_firstname = obj.first_name;
_lastname = obj.last_name;
I’m confused about how to get access to the profile pic of the user tho – when i try the callComplete portion of the code – a status update gets posted but the result is event.rawdata is not recognized as a ByteArray – any idea what I might be doing wrong?
Cheers
mike, you can not access ByteArray without correct crossdomain.xml hosted on facebook domains, please follow:
http://blog.yoz.sk/2010/05/facebook-graph-api-and-oauth-2-and-flash/#comment-2450 or other comments and responses containing “profile picture”
Hi Jozef Chúťka,
I could post on authenticated user’s friends wall successfully only when the pop-up window (normal callback.html) closed automatically. Firefox and crome browsers are able to close the callback.html after authentication, but Internet explorer unable to close the callback.html automatically.
please suggest !
I would also like to know the purpose of Advanced callback.html
-Gopi
Hi Gopi,
In order to make calls to api you need a token. When you authorize your app in popup, the token is pushed in the last step into callback.html. Now callback.html hast to push it back to your main app (via javascript-simple or LocalConnection-advanced callback). It it succeed with sending the token to the main app, it tries to close itself using javascript window.close() method. Could you somehow debug the issue with IE and send some report where the javascript fails?
Hi, Jozef..
Please, i used the photo uploading code through the graph API but i don’t know how i used it in my project..
can you send me the step by step code for this with file names in which i have to update the code..
hi Vaishali
have a look at “Uploading Photo” section. copy and paste the conde into your Facebook class (extends FacebookOAuthGraph). you may be missing MultipartURLLoader dependency, you can download it from internet. everything else should be selfexplanatory
Hi Jozef
Great Job. Tanx. When I try to Publishing Feeds, all data is posted beside the image. No image is posted. I followed your code “as is”. Please advice.
Hi Ronen,
It may be caused by deprication of the rest api stream.publish ( https://developers.facebook.com/docs/reference/rest/stream.publish/ ) is replaced by graph api /feed … read section “Publishing feed using Graph API (docs, from facebook bugzilla):” in this article