Facebook Anwendung Teil1


Ziel dieses Projektes ist es, eine Facebook-Anwendung zu erstellen, bei der man mit Processing auf Informationen von Facebook zugreifen kann. Konkret möchte ich den aktuell eingeloggten Facebook identifizieren (als JavaScript umgesetzt) und seine Facebook UserID in mein Processing Applett übertragen. Dann werden Userdaten und das UserImage heruntergeladen und auf den Bildschirm gezeichnet.Damit ist Teil 1 erledigt.

Wie bekomme ich einen App-Account?

Einen App-Account kann jeder Facebook User erstellen.Dafür meldet man sich bei Facebook an und aktiviert die Anwendung „Developer“. Dort kann man dann rechts oben eine „Neue Anwendung erstellen“.

http://www.facebook.com/developers/

Neuerdings muss dann das eigene Konto verfiziert werden und würde euch dringend raten das mit Handy und nicht mit der Kreditkartennummer zu machen (auch Handy ist unsicher, meiner Ansicht nach noch das kleinere Übel).  Beim registrieren der App sind dann folgende Einstellungen vorzunehmen:

  • Anwendungs-ID (wird zugewiesen, ist dann in die von mir erstellte Seitenvorlage einzufügen)
  • API-Schlüssel (wird dann im Processing Applet benötigt)
  • Anwendungs-Geheimcode (für Processing Applet)

  • Seitenadresse ist auch bei Canvas-URL einzutragen (reale Internetadresse deiner Seite (Bsp: http://xxxx.bplaced.net/xxxxxx)
  • Leinwandadresse oder Canvas-Seite (die Adresse deiner App in Facebook – die Seite liegt nicht wirklich dort!!! Bsp: http://apps.facebook.com/xxxxxxxxx/
  • Sandkastenmodus aktivieren (nur du hast Zugriff auf deine App)

Hier gibt es allgemeine Info zur App-Registrierung.

Prinzipiell funktioniert das so, dass man auf irgendeiner URL seine Seite veröffentlicht. Diese wird dann in einem sog. IFrame (HTML-Fenster) unter der Adresse  der Facebook app angezeigt.

Erstellen eines geeigneten HTML – Javaskript-Unterbaus

Hilfreich ist dieses Tutorial . Hier findet man ein Seitengerüst mit dem man über HTML/JavaSkript Abfragen auf Facebook durchführen kann.Dafür wird die aktuelle von Facebook angebotene Programmierschnittstelle, die Graph-API verwendet. JavaSkript kann nun zum Beispiel einen Login/Logout Button rendern und den aktuellen Anmeldestatus abfragen.

Konkret brauchen wir dieses Gerüst um erst einmal an die UserID des Besuchers der Seite zu kommen. Ich habe diesen Unterbau ein wenig modifiziert. Damit die Seite funktioniert muss man noch die eigene Facebook App-ID einsetzen.

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:fb="http://www.facebook.com/2008/fbml">
 <head>
 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
 <title>MyFriends</title>
 </head>
 <body>
 <div id="fb-root"></div>
 <script type="text/javascript">
 window.fbAsyncInit = function() {
 FB.init({appId: 'YourAppID', status: true, cookie: true, xfbml: true});

 /* All the events registered */
 FB.Event.subscribe('auth.login', function(response) {
 // do something with response
 login();
 });
 FB.Event.subscribe('auth.logout', function(response) {
 // do something with response
 logout();
 });

 FB.getLoginStatus(function(response) {
 if (response.session) {
 // logged in and connected user, someone you know
 login();
 }
 });
 };
 (function() {
 var e = document.createElement('script');
 e.type = 'text/javascript';
 e.src = document.location.protocol +
 '//connect.facebook.net/en_US/all.js';
 e.async = true;
 document.getElementById('fb-root').appendChild(e);
 }());

 function login(){
 FB.api('/me', function(response) {
 document.getElementById('login').style.display = "block";
 document.getElementById('login').innerHTML = response.name + "
 " +response.id + " succsessfully logged in!";
 document.Hallo.setString(response.id)
 });
 }
 function logout(){
 document.getElementById('login').style.display = "none";
 }

 //stream publish method
 function streamPublish(name, description, hrefTitle, hrefLink, userPrompt){
 FB.ui(
 {
 method: 'stream.publish',
 message: '',
 attachment: {
 name: name,
 caption: '',
 description: (description),
 href: hrefLink
 },
 action_links: [
 { text: hrefTitle, href: hrefLink }
 ],
 user_prompt_message: userPrompt
 },
 function(response) {

 });

 }
 function showStream(){
 FB.api('/me', function(response) {
 //console.log(response.id);
 streamPublish(response.name, 'Thinkdiff.net contains geeky stuff',
'hrefTitle', 'http://thinkdiff.net', "Share                 thinkdiff.net");
 });
 }

 function share(){
 var share = {
 method: 'stream.share',
 u: 'http://thinkdiff.net/'
 };

 FB.ui(share, function(response) { console.log(response); });
 }

 function graphStreamPublish(){
 var body = 'Reading New Graph api & Javascript Base FBConnect Tutorial';
 FB.api('/me/feed', 'post', { message: body }, function(response) {
 if (!response || response.error) {
 alert('Error occured');
 } else {
 alert('Post ID: ' + response.id);
 }
 });
 }

 function fqlQuery(){
 FB.api('/me', function(response) {
 var query = FB.Data.query('select name, hometown_location,
sex, pic_square from user where uid={0}', response.id);
 query.wait(function(rows) {

 document.getElementById('name').innerHTML =
 'Your name: ' + rows[0].name + "<br />" +
 '<img src="' + rows[0].pic_square + '" alt="" />' + "<br />";
 });
 });
 }

 function setStatus(){
 status1 = document.getElementById('status').value;
 FB.api(
 {
 method: 'status.set',
 status: status1
 },
 function(response) {
 if (response == 0){
 alert('Your facebook status not updated. Give Status Update Permission.');
 }
 else{
 alert('Your facebook status updated');
 }
 }
 );
 }
 </script>
 <h3>MyFriends</h3>
 <p><fb:login-button autologoutlink="true" perms="email,user_birthday,
status_update,publish_stream"></fb:login-button></p>
 <div id="login" style ="display:none"></div>
 <div id="name"></div>
 <script src="http://connect.facebook.net/en_US/all.js#xfbml=1"></script>
<fb:like href="http://apps.facebook.com/YourAppAddress"       
 show_faces="true" width="450"></fb:like>
###hier kommt das Processing-Applet hin###
 </body>
</html>
 function login(){
 FB.api('/me', function(response) {
 document.getElementById('login').style.display = "block";
 document.getElementById('login').innerHTML = response.name + "
 " +response.id + " succsessfully logged in!";
 document.Hallo.setString(response.id)
 });
 }

Die UserID an das Applet übertragen

Damit die UserID nun von JavaSkript (läuft schon in der HTML-Seite) in das Processing Applet übertragen werden kann, habe ich in dem Beispiel oben die Funktion login um diese Zeile ergänzt:

 function login(){
 FB.api('/me', function(response) {
 document.getElementById('login').style.display = "block";
 document.getElementById('login').innerHTML = response.name + "
 " +response.id + " succsessfully logged in!";
 document.Hallo.setString(response.id)
 });
 }

Sie bewirkt, dass in einem Applet mit dem Namen Hallo die Funktion setString aufgerufen, und ein String (response.id –> dieser enthält die Facebook-UserID des eingeloggten Users) an das Applet übergeben wird.Damit das funktioniert, braucht man die Library java.applet.Applet. Sie wird mit folgender Anweisung importiert: import java.applet.Applet;

Die Funktion sieht im unserem Fall so aus:

public void setString(String aString)
{
 fbUserIDs = aString;
 MyIDisloaded =true;
}

In der markierten Zeile wird die von JavaSkript übergebene String Variable aString an die globale Variable fbUserIDs übergeben.

Das funktioniert aber nur, wenn man dem exportierten Processing Applet noch händisch einen Namen, wie in diesem Fall, Hallo, zuweist.

var attributes = {
 code: 'facebook_myid_1_0.class',
name:'Hallo',
 archive: 'facebook_myid_1_0.jar,jpen-2.jar,generativedesign.jar,core.jar',
 width: 1000,
 height: 600,
 image: 'loading.gif'
 };

Damit haben wir die UserID in dem Applet zur Verfügung.

Der Rest der Daten

Um an die Daten zu kommen, die wir sonst noch für unser Projekt benötigen, benutzen wir nun eine andere, etwas ältere Programmierschnittstelle von Facebook, nämlich die Old REST API.  Auf http://wiki.processing.org/w/Facebook,_REST_API findet man Beispielcode, um Daten über die Facebook API abzurufen. Man schickt eine rel. komplexe Anfrage und bekommt als Antwort ein XML-File, aus dem man dann die Daten extrahieren und in die einzelnen Strings umwandeln muss. Ich habe die Anfragen, die Überwachung des asynchronen Ladevorgangs und auch das Verarbeiten der XML-Files in eine Klasse mit dem Namen Facebook gepackt. Die Methode für die Anfragen sieht so aus:

void getmyXML () {
 int currentUser = 0;
 String xmlRequest = this.CallMethod( new String[] {
 "method=facebook.Users.getInfo",  
 "uids=" + UserIDs,
 "fields=first_name,last_name,hometown_location,pic", // see link above for more options
 "format=XML"
 }
 );
 myXML = GenerativeDesign.loadXMLAsync(thisPApplet, xmlRequest);
 dataloaded = false;
 }

Hier wird in Zeile 3 die Methode CallMethod  aufgerufen, Sie gibt dann die Anfrage mit div. Signaturen und verschlüsselten Elementen als String zurück.

String CallMethod ( String[] args )
 {
 String[] params = new String[args.length + 3];
 System.arraycopy( args, 0, params, 0, args.length );
 params[params.length-3] = "api_key=" + ApiKey;
 params[params.length-2] = "call_id=" + System.currentTimeMillis();
 params[params.length-1] = "v=1.0";

 String sig = this.GenerateSIG ( params );
 String paramString = join( params, "&" ) + "&sig=" + sig;
 String response = RestServer + RestNode + "?" + paramString;
 return response;
 }

 /**
 *  Generate a call signature, see:
 *  http://wiki.developers.facebook.com/index.php/How_Facebook_Authenticates_Your_Application
 *  http://wiki.developers.facebook.com/index.php/Authorization_and_Authentication_for_Desktop_Applications
 */
 String GenerateSIG ( String[] args )
 {
 java.util.Arrays.sort( args );
 String argString = join( args, "" );
 argString += ApiSecret;

 return this.md5Encode( argString );
 }

 /**
 * MD5 encode a String using Processing API ( hex() )
 */
 String md5Encode ( String data )
 {
 java.security.MessageDigest digest = null;
 try {
 digest = java.security.MessageDigest.getInstance("MD5");
 }
 catch ( java.security.NoSuchAlgorithmException nsae ) {
 nsae.printStackTrace();
 }
 digest.update( data.getBytes() );
 byte[] hash = digest.digest();

 StringBuilder hexed = new StringBuilder();

 for ( int i = 0; i < hash.length; i++ )
 {
 hexed.append( hex( hash[i], 2 ) );
 }
 return hexed.toString().toLowerCase();
 }

Dann wird in getXML mit der Anfrage als Parameter die Methode loadXMLAsync aus der generative-design-library aufgerufen (http://www.generative-gestaltung.de/). Dieses asynchrone Laden der Daten ist wichtig, da sonst ja jegliche Animation, die in draw() läuft, unterbrochen würde, bis die Daten geladen sind. So werden die Datensätze im Hintergrund geladen, während das Programm weiterläuft. Um die fertig geladenen Daten dann im laufenden Programm verwenden zu können, muss man nun immer wieder prüfen, ob sie schon fertig geladen sind. Das macht bei mir die Methode loaderloop(), bzw. imageloaderloop(). Diese werden immer wieder in draw() aufgerufen.

boolean loaderloop () {
 if (myXML != null) {
 if (myXML.hasChildren() == false) {
 println("not loaded yet");
 }
 else {
 dataloaded=true;
 return dataloaded;
 }
 }
 return dataloaded;
 }

 boolean imgloaderloop () {
 if (imgisloading) {
 if (myImage.width == 0) {
 // image is not yet loaded
 println("not loaded yet");
 }
 else if (myImage.width == -1) {
 // this means an error occurred during image loading
 }
 else {
 // image is ready to go, draw it
 imgloaded=true;
 imgisloading =false;
 return imgloaded;
 }
 }
 return imgloaded;
 }

Sind die XML-Daten geladen, wird (auch aus draw()) die Methode getMyData() aufgerufen und das XML File wird in die einzelnen Strings umgewandelt. Von hier aus wird dann auch, nachdem man die Adresse für das Profilbild ausgelesen hat der asysnchrone ImageLoader aufgerufen. Der dann das Profilbild analog zu den Daten vorher aus dem Web lädt.

String getmyData () {
 usersXml = myXML.getChildren();
 String myinfo = usersXml[currentUser].getChild("first_name").getContent();
 myinfo += " "+usersXml[currentUser].getChild("last_name").getContent();
 String mypicurl= usersXml[currentUser].getChild("pic").getContent();
 myImage = GenerativeDesign.loadImageAsync(thisPApplet, mypicurl);
 imgisloading=true;
 myXML = null;
 dataloaded = false;
 return   myinfo;
 }

Zu guter Letzt gibt es noch das Hauptprogramm, aus dem dann diese Objekte und Methoden aufgerufen werden. Die Erklärungen dazu sind als Kommentar im Code enthalten.

//import der benötigten Programmbibliotheken
import generativedesign.*;
import java.awt.Graphics;
import java.applet.Applet;

// application api key and secret
String fbApiKey = "Dein Appkey";
String fbApiSecret = "Dien AppSecret";

// Initialisieren der Variablen und Objekte
String fbUserIDs = null;
facebook mydata;
PFont font;
PApplet thisPApplet = this;
boolean MyIDisloaded = false;
boolean MyObjektiscreated =false;
String myfull_name=null;
PImage my_image=null;

void setup ()
{
 size( 1000, 600 );
 fill( 250);
 stroke(29,64,136);
 strokeWeight(3);
 smooth();
 textAlign( CENTER );
 font = createFont("Impact", 12);
}

void draw ()
{
 background(50);
 //hier die Abfage, ob die UserId schon im Applet angekommen ist
 if (MyIDisloaded && !MyObjektiscreated) {
 mydata = new facebook(fbApiKey, fbApiSecret, fbUserIDs);
 mydata.getmyXML();
 MyObjektiscreated =true;
 }
 //wenn nicht, wird ... is loading angezeigt
 if (!MyIDisloaded && !MyObjektiscreated) {
 text("...is loading", 20,20);
 }
 // andernfalls wird geprüft, ob die XML-Daten schon vorhanden sind
 else {

 if (mydata.loaderloop()) {
 //wenn ja, werden diese als Strings abgerufen
 myfull_name=mydata.getmyData();   
 }
 // analog wird auch beim Profilbild geprüft, ob es schon geladen ist
 if (mydata.imgloaderloop()) {
 // wenn ja, wird es in my_image gespeichert
 my_image=mydata.getmyImage();
 }
 }
 //wenn das Bild dann da ist, wird am Bildschirm ausgegeben.
 if(my_image!=null) {
 fill(98,122,173);
Zip-File ellipse(width/2,height/2+10,200,200);
 fill(255);
 text(myfull_name,width/2,height/2+my_image.height/2+20);
 image(my_image,width/2-my_image.width/2,height/2-my_image.height/2);
 }
}

public void setString(String aString)
{
 fbUserIDs = aString;
 MyIDisloaded =true;
}

Hier das Ganze noch mal als Zip-File.

Funkt aber nur, wenn vorher eine Facebook App registriert und die entsprechenden Schlüssel in der Datei eingetragen wurden.

Advertisements

3 Kommentare

  1. Pingback: XMLElement « processing – tutorial

  2. Pingback: Processing – Über dieses Weblog « processing – tutorial

  3. Lisa

    Danke für diesen tollen Beitrag!!! Das ist genau das was ich für meine Bachelor-projekt brauche…ich will mit dem Processing Generative Design Tool M_6_4_01 die Vernetzung bestimmter FbUser visualisieren. Ist das damit möglich?
    Leider bin ich absoluter Neueinsteiger und dein UserID Tuturial hat bei mir nicht geklappt 😦
    Ich weiß auch nicht was ich falsch gemacht habe, glaube mein Problem ist, das ich evtl die Codes falsch zugeordnet habe, weil ich nicht genau weiß was Processing Code ist und was HTML. Das mit der FB Anfrage ging bei mir auch nicht…

    Vielleicht kannst du mir da jemand helfen, brauche das dringend für ein Projekt!
    Freue mich über jede Antwort!!!

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden / Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden / Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden / Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden / Ändern )

Verbinde mit %s

%d Bloggern gefällt das: