Tag Archives: javascript

Premiers pas pour créer une application Facebook Connect avec Symfony

Ce tutoriel va vous expliquer comment créer une application très simple Facebook Connect, en gérant le fait d’être loggué sur Facebook ou pas, qui va nous permettre de faire des appels sur l’API Graph et utilisant le Javascript SDK.

Je passe très rapidement sur la création de l’application symfony. Il y a déjà des tas de tutoriaux pour cela, je vous conseille de reprendre les tutoriaux, en particulier celui de Jobeet.

On crée donc l’application (sans ORM dans mon cas):

cd devel/
mkdir -p sfbook/lib/vendor
curl http://www.symfony-project.org/get/symfony-1.4.8.tgz | tar -xzv -C !-1:2 -f -
SFBOOK=$(pwd)/sfbook; cd $SFBOOK; ./lib/vendor/symfony-1.4.8/data/bin/symfony generate:project sfbook --orm=none

Puis on récupère le php-sdk Facebook et on l’on active facebook dans l’autoloader:

cd lib/vendor ; git clone https://github.com/facebook/php-sdk.git
cat > $SFBOOK/config/autoload.yml << EOF
autoload:
    facebook:
        name: facebook
        path: %SF_LIB_DIR%/vendor/php-sdk/src
        recursive: off
EOF

On crée une application, ‘frontend’ et deux modules dans celle ci ‘default’ et ‘home’. Le premier module servira pour se logger, le 2nd pour l’application en elle même et dans notre exemple, on récupèrera juste la liste de ses amis.

php symfony generate:app frontend
php symfony generate:module frontend home
php symfony generate:module frontend default

Le module « home » ne sera accessible qu’aux utilisateurs authentifiés sur facebook. On le protège donc:

mkdir apps/frontend/modules/home/config
cat >> apps/frontend/modules/home/config/security.yml << EOF
all:
    is_secure: true
EOF

Et on oublie pas de modifier apps/frontend/config/settings.yml en rajoutant dans la section all: l’action suivantes:

 .actions:
    login_module
:          default
    login_action
:          login

A ce moment, on crée l’application sur facebook. On définit uniquement le Site URL (Onglet Website) pour accéder à notre application symfony et on note nos application Id, api key et application secret.

Comment ça va marcher ? On va mettre dans le layout général le code « connect »: Un script javascript facebook va être chargé et nous redirigera directement sur la page de login si jamais la session facebook venait à être fermée par l’utilisateur. Le template default/login contiendra le bouton facebook connect, et le template home/index qui ne s’affichera que si la session

On ajoute dans apps/frontend/template/layout.php le code suivant dans le :

<div id="fb-root"></div>
<script src="http://connect.facebook.net/en_US/all.js"></script>
<script>
  FB.init({appId: '[APP_ID]', status: true, cookie: true, xfbml: true});
  FB.Event.subscribe('auth.sessionChange', function(response) {
    if (response.session) {
        window.location = '<?php echo url_for('home/index'); ?>';
    } else {
        window.location = '<?php echo url_for('default/login'); ?>';
    }
  });
</script>

Le module « default » n’aura que l’action « login ». Cette action s’occupe d’authentifier l’utilisateur. Si l’utilisateur est loggué sur facebook, alors on le redirige vers home/index. Voilà le code de apps/frontend/default/actions/actions.class.php:

<?php
class defaultActions extends sfActions
{
    public function executeLogin(sfWebRequest $request)
    {
        $facebook = new Facebook(array('appId' => '[APP_ID]',
                                       'secret' => '[APP_SECRET]',
                                       'cookie' => TRUE ));
        $session = $facebook->getSession();

        if(NULL !== $session)
        {
            $me = $facebook->api('/me', 'GET', array('access_token' => $session['access_token']));

            // On verifie qu'on existe.
            if(NULL !== $me)
            {
                $this->getContext()->getUser()->setAuthenticated(true);
                $this->redirect('home/index');
            }
        }

        $this->getContext()->getUser()->setAuthenticated(false);

        return sfView::SUCCESS;
    }
}

Le template et default/login contiendra uniquement le bouton facebook connect:

<div id='content' align='center'>
<h1>Welcome !</h1>
<fb:login-button></fb:login-button>
</div>

Dans home, on va mettre un peu ce que l’on veut. Pour le coup, on va par exemple récupérer et afficher ses amis facebook:

<?php
class homeActions extends sfActions
{
    public function preExecute()
    {
        $this->facebook = new Facebook(array('appId' => '[APP_ID]',
                                             'secret' => '[APP_SECRET]',
                                             'cookie' => TRUE ));
        $this->session = $this->facebook->getSession();
    }

    public function executeIndex(sfWebRequest $request)
    {
        $this->friends = $this->facebook->api('/me/friends',
                                              'GET',
                                              array('access_token' => $this->session['access_token']));

        return sfView::SUCCESS;
    }
}

Le template associé est très simple:

My friends:<br />
<br />
<?php foreach($friends['data'] as $friend) : ?>
<?php echo $friend['name'] . "<br />"; ?>
<?php endforeach; ?>

Et voilà. Si en visitant le site on est déjà loggué facebook, mais que l’utilisateur n’a jamais utilisé l’application, on aura le droit à l’écran de login pour installer l’application. Ensuite, l’application affichera directement home/index.

On pourra même rajouter un bouton « logout » dans le layout global:

<?php if($sf_user->isAuthenticated()) : ?>
<input type='button' onclick='FB.logout(function(response) {});' value='logout' />
<?php endif ; ?>

Synchronisez vos évènements avec node.js

J’ai passé mon après midi à tester node.js (framework basé sur V8 pour développer des serveurs d’I/O de façon évènementielle), node-websocket-server (composant websocket pour node.js) et l’élément <video> html5. Au final, ça m’a donné de synchroniser le lancement de plusieurs vidéos lancées dans plusieurs navigateurs à partir d’un point unique (j’ai comme idée de créer un cinéma virtuel sur le net où tout le monde pourra voir la même vidéo en même temps ;) ).

Au final, même si j’ai pris un peu de temps, c’est assez simple et il commence à y avoir pas mal d’exemples pour faire tout ça sur le net.

On commence par installer et compiler Node.js:

$ git clone http://github.com/ry/node.git
[...]
$ cd node
$ ./configure
[...]
$ make
[...]
$ file build/default/node
ELF 32-bit LSB executable, Intel 80386, version 1 (GNU/Linux), dynamically linked (uses shared libs), for GNU/Linux 2.6.18, not stripped

Une fois compilé, on pourra valider avec le petit test « hello world » présent sur la home page du site.

Ensuite, pour communiquer entre mon navigateur et le serveur, j’ai décidé d’utiliser les websockets. Pour cela, il faut avoir la lib spéciale pour node.js, et il s’agit de node-websocket-server. Il ne faut pas oublier de setter la variable NODE_PATH pour donner l’emplacement de la bibliothèque, car cela est indispensable au lancement de node.js

$ git clone http://github.com/miksago/node-websocket-server.git
[...]
$ export NODE_PATH=$(pwd)/node-websocket-server/lib

Il faut maintenant se fabriquer un tout petit serveur simple qui relaie juste les évènements.

C’est parti pour serveur.js:

// Modules nécessaires
var http = require("http");
var ws = require("ws");
var sys = require("sys");

function dummy(req, res){
};

var httpServer = http.createServer(dummy);

var server = ws.createServer({
  debug: true
}, httpServer);

server.addListener("listening", function(){
    sys.puts("Listening...");
});

server.addListener("connection", function(conn){
    server.send(conn.id, "Connected as: "+conn.id);
    conn.addListener("message", function(message){
        sys.puts("Message: " + message);
        conn.broadcast("<"+conn.id+"> "+message);
    });
});

server.addListener("close", function(conn){
    sys.puts("Closing...");
    conn.broadcast("<"+conn.id+"> disconnected");
});

// lancement du serveur sur le port 8000
server.listen(8000);

Il faut lancer le serveur. Rien de plus facile:

$ ./node/node serveur.js
Listening…

Pour la partie cliente, pour gagner du temps, on va utiliser jquery. Ca donnera ça pour le html:

<html>
<head>
<script src="jquery-1.4.2.min.js" type="text/javascript"></script>
<script src="client.js" type="text/javascript"></script>
</head>
<body>

<input id="start" type="button" value="start video" />
<input id="stop" type="button" value="stop video" />
<input id="reset" type="button" value="reset video" />

<video id="video" src="lavide.ogv" style="width: 100%; height: 100%;" tabindex="0"></video>

</body>
</html>

Et pour le javascript (client.js):

$(document).ready(function(){
    // La websocket que l'on stocke "globalement"
    var ws;

    if ("WebSocket" in window) {
    // Ou connecter la socket:
        ws = new WebSocket("ws://localhost:8000/service");
        ws.onopen = function() {};
        ws.onmessage = function (evt) {
            // Quand on reçoit un message du serveur...:
            var received_msg = evt.data;

            if(received_msg.match(/start/gi)) { $('#video').get(0).play(); };
            if(received_msg.match(/stop/gi)) { $('#video').get(0).pause(); };
            if(received_msg.match(/reset/gi)) { $('#video').get(0).currentTime = 0; };
        };
        ws.onclose = function() {};
    } else {
        // Pas de support WebSocket.
    }

    // Et les évènements locaux (les boutons)...:
    $('#start').click(function() {
        ws.send("start");
        $('#video').get(0).play();
    });

    $('#stop').click(function() {
        ws.send("stop");
        $('#video').get(0).pause();
    });

    $('#reset').click(function() {
        ws.send("reset");
        $('#video').get(0).currentTime = 0;
    });
});

Et voilà. C’est tout. Chargez la page dans plusieurs sessions du navigateur, et tout devrait rouler. ;)