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;
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(path:String, ba:ByteArray, message:String, token:String=null):MultipartURLLoader
{
var mpLoader:MultipartURLLoader = new MultipartURLLoader();
mpLoader.addVariable("message", message);
mpLoader.addFile(ba, "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);
}
[...] read article Extending FacebookOAuthGraph Class, where all the most comon extendings are [...]
Hi, I am using you class for a app I built and it works great as a webapp and as a canvas app but when I converted it over to FBML to use in a profile tab I am stuck. How can I get the token/login without using js/externalinterface since a facebook tabbed app has no script access? It seems like I would have to do something similar to your local connection method via js bridge or something. Any help would be great!
Thanks
UPDATE:
So I figured out how to get the token using the session_key thanks to this article: http://sambro.is-super-awesome.com/2010/05/28/facebook-access-tokens-from-canvas-apps/
and then passing that token to the FacebookOAuthGraph via the verifyToken() call.
Still not sure how to set up the login/application permissions from a tabbed app. Maybe call login.php and then retry the graph call on a timer? Can’t seem to find relevent documentation for this situation. I know this should probably go on the fb forums but since its using the FacebookOAuthGraph class for all of the graph calls it’s relevant.
Hi James,
glad you managed it to work. I am not very found in tabbed apps, I am not sure what parameters are sent into your .swf by facebook. But I guess this flow may work:
1. user clicks on your tab
2. you do not have access_token you redirect him to login.php (same window)
3. user allows app and is redirected to your callback (store access_token into some persistent place)
4. redirect user back to the tab and use access_token from your persistence
Hi jozef,
I can’t find where is define call function with 4 arguments…
This question is linked to your answer of yesterday (for wich i thank you) on facebook developers forum.
Etienne.
Sorry I posted too fast,
I directly make a facebook.call and it works.
I also managed to access profile pictures album through fql querry within the good extended permission session !!!
Thank you very much Jozef for your great work.
Hi Etienne,
make sure you have latest version of FacebookOAuthGraph class, call() method has been slightly changed (backward compatible). Feel free to paste here your fql solution + extended permissions you used, with your approval I can push it into the article.
Hi JC,
universal callback.swf (741 Bytes) main class seems to be a broken link.
Can I get this to work in Flex bymyself somehow?
Thanky you.
Great articles by the way!
Hi JC, maybe I posted a little too fast.
So, I created a Callback.swf from your code by using Flash CS4.
http://edutechwiki.unige.ch/en/AS3_Compiling_a_program
Hello Jozef, very nice articles.
Do possibly know how to implement the publishPost function so that a target_id and/or uid as in the php equivalents (http://wiki.developers.facebook.com/index.php/Stream.publish) can be handed over to the function call.
I would go absolutely nuts if I could get this to work somehow!
Ben Bee, link now fixed
Hi Boris B.,
indeed it can use that method, please read section “Publishing Feeds”, … you see? Now all you have to do is add target_id/uid parameter to data variable (just like message)
Hi Jozef,
Here is the call I make to get for exemple all the photos of all albums of 1 user.
Permission needed is only “user_photos”.
function getAllphotos(uid):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);
}
Etienne.
Hi Jozef,
So I am able to publish content on the wall of specific page via data.target_id = 1234567890 (thanks a lot for pointing that out!) – unfortunately the created posts however only appear on the page itself and not within the feeds of those people who like the page (unless of course they are befriended with the poster). I figure that this must be some sort of facebook settings issue but I cannot get to it. Do you know what I am talking about??? Do you have any idea how to get the desired results?
THANK YOU SO MUCH AGAIN!
OK. So as I now understand now only the pageadmin’s wallposts should appear on the users’ walls who like the particular page. So I figure that I might need to explicitly provide the session_key as well as uid to the data object instead of the target_id. But this ain’t working out so far either. Do you have any ***** idea what I am talking about? best wishes boris!
Hi Boris B., sure I know what you are talking about, I also use posting feed on page with onboard:
1. goto http://www.facebook.com/apps/application.php?id=110363680930 and like
2. goto the app http://onboard.yoz.sk/ and click facebook btn
3. there are 2 requests send to fb: 1st with no id (goes to your feed stream), 2nd with app page id goes to app page feed stream
… you are right only the page admin’s feed goes public and that is imho good, else people could use that app to spam, think about page/app with 100.000 users… Now you may want to “hack” your session so the request goes like from page admin, I suggest to do this on backend via php sdk (while you need to send your pwd) else anyone could hack your account
Hi Jozef,
thanks for posting so fast.
Very nice app. I Like it a lot.
As I see you are doing what I want to do.
So here is what I would like to do.
I am the admin of few pages with a lot of users.
So I naturally want to use my app to post to these pages – meaning that I want the app to post one of the pages wall and (thereby) post to the wall of all the people who like that page. Just like it would be the case if I as the admin would make a post on that page. Do I really need some sort of backend to provide a pw for this or is there another way to work this. And if I need some sort of extra authorization to demonstrate fb that I am the admin could you possibly be so kind and maybe give me some sort of advice on how to get there?
thank you very much for any thoughts or words of advice.
best wishes from germany,
Boris
@Boris B.
thnx,
I see what you are up to, but I have never done that… Anyway here is my idea:
1. make your app requring offline_access – this permission makes the access token returned by OAuth endpoint long-lived… not sure how long, maybe month maybe even longer
2. grant your app this permission while logged as admin and save generated access_token
3. prepare backend (php + curl) that would call the api with your saved access_token.
you can not make this token public because one could use it to harm your account, thats why you have to make calls from backend to facebook
Hi Jozef?
(is ur name Jozef?)
I am a flash developer from Singapore and I really want to drop a message and say a big thank you to you for what you have shared.
They have been so helpful!
Cheers!
Hi Terrence, yes my name is Jozef, I am glad you find it useful
Hi Jozef
In “Publish feed with flash” should the last line:
call(“method/stream.publish”, data, URLRequestMethod.POST, null, “https://api.facebook.com”);
be:
facebook.call(“method/stream.publish”, data, URLRequestMethod.POST, null, “https://api.facebook.com”);
Sorry, forget it. Nothings wrong
Hi Jozef,
I am wondering how to handle FB users as objects (as I did before the new API).
In particular getting and handling somebody’s friends, their account info and some additional app specific stuff can become a real mess if one has to extract the relevant information from seemingly endless strings.
Further on we are planning to save the user objects via remote. Do you have any sort of advice?
Thanks a lot.
Sam
@Sam the returned “string” is JSON, it is serialized object/array/values that is accessible like this:
// call(“me”)
private function callComplete(event:FacebookOAuthGraphEvent):void
{
trace(event.data.name);
trace(event.data.work[0].employer.name);
}
search for “JSON” in this article http://blog.yoz.sk/2010/05/facebook-graph-api-and-oauth-2-and-flash/
Hello,
I don’t know why, but http://apps.facebook.com/blogoauthgraph/ no longer works. Do you know if Facebook could changed some things?
Thanks for your feedbacks.
@Garcimore, yes, there have been some changes, please read:
http://blog.yoz.sk/2010/07/neverending-facebook-api-change/
thank you for noticing the changes
Hi,
Is it possible to include action links with our published feeds?
Thank you.
Koray
@Koray Hi, indeed it is. I have just updated the article, please read section “Publish feed with action links”
Thank you Jozef for your extremly fast response !!!
Facebook is also very fast in their changes :-s … Without notification, they have add a new option (automatically selected) for Facebook application configuration (in “migration” tab) to add a session information (that had deseapear yesterday…)
Your code works always ! It’s just for information.
Thanks.
Thank you Jozef
Hi Jozef,
Thanks for these wonderful articles. You really made my life so simple. Please keep up the good work.
Hey I had a small doubt on the publish stream example above. Suppose I want to prompt user for confirmation just before publishing the feed. May be a pop up window where user can add his personal comments before the feed is published on the wall.
Is it possible to do it with any simple modification in your example?
Koray, Amit you are welcome
Amit, I do not use facebook javascript sdk in my class so there is no method you could call to open the default publishing popup. However, you can create your own UI with text input and custom graphincs…
hi jozef i have some problems with uploading a picture to a specific album could you help me? here my code:
var jpg:JPGEncoder = new JPGEncoder();
var upImg:UploadImage = new UploadImage();
var obj1:Object = { byteArray: jpg.encode( bitmap ), fileName: TunnelConfig.baseUrl + “tunnelImages/images/” + box.userImageId.toString() + “.jpg”, imageTitle: box.userImageId.toString() + “.jpg”, message: “tunnel”};
var obj2:Object = { title: “Album: Tunnel ins Gesicht”, description: “Deine Freunde mit einem Tunnel im Gesicht”};
upImg.uploadImage( “Album: Tunnel ins Gesicht/photos”, obj1, obj2);
i tried to change “Album: Tunnel ins Gesicht/photos” to aid + “/photos” ( aid is the id from the album ), too
hi David, what library/sdk do you use? I do not find familiar any of these classes: UploadImage, TunnelConfig
hi jozef i’m using the UploadImage from http://blog.yoz.sk/examples/FacebookOAuthGraph/as/uploadImage.as and i’m using flash. In TunnelConfig is just a static link to the server where the images are.
i even tried to upload a image this way:
facebook.call( aid + “/photos”, dataObject, URLRequestMethod.POST );
but i’m getting a 500 Internal Server Error ? don’t know what i’m doing wrong
hi jozef, ^^ i got it to work with your / Seans Multiuploader Example
PS: This Page is realy awesome kepp it on
Hi Jozef. Thank you for all the great and helpful info. I was wondering if you could tell me if it’s possible to submit an array of friends’ ids to invite them to your application? I made a custom friend selector for a Flash site outside Facebook and want to be able to invite the selected friends to the application. I was thinking there might be a way via javascript or php request. Any help would be much appreciated. Thanks so much!
hi @gina, it is not possible to invite friends via api, you have to use FBML – Fb:request-form, please read http://blog.yoz.sk/2010/03/inviting-friends-into-facebook-application/
Hi Jozef,
Thanks for all these posts, they really mean a lot to all of us !
I ‘ve been toying with your OAuthGraph and I’m starting to build a framework around it (a bit similar, but far less advanced, to the one Adobe released), I’d be happy to share some of it later on…
the problem I’m facing is with your advanced crossdomain approach for local development.
I can’t make the callback work like intended. It seems the javascript breaks on :
if(window.opener && window.opener.confirmFacebookConnection)
When testing locally the script never makes it to the ‘else’ statement. Maybe you’ve seen this before, I really can’t get my head around it…
The only way I can make it work is to leave the if else out of there, and always embed the swf…
Thanks anyway for all the advice !
You may now close this window.
Hi Dmonkey, thank you
there were some reports that javascript in callback.html does not get executed correctly on macs:
http://blog.yoz.sk/2010/05/facebook-graph-api-and-oauth-2-and-flash/#comment-1779
is that your case?
Hi Jozef.
I’m trying to find out a way to logout / catch a logout using your method.
openGraph documentation explain how to do the connection / authentification, but i cannot find anything on disconnect / logout.
Have you ever test it ?
Thank you
Hi @daweed, please read http://blog.yoz.sk/2010/05/facebook-graph-api-and-oauth-2-and-flash/#comment-2001 and other comments containing “logout” phrase
Thans Jozef.
[...] Extending FacebookOAuthGraph Class [...]