Création d'un gadget Google Data

Eric Bidelman, équipe Google Data APIs
Octobre 2008

Introduction

Audience

Cet article vous explique comment créer un gadget Blogger. Nous partons du principe que vous connaissez les API Google Data et la bibliothèque cliente JavaScript. Vous devez également connaître JavaScript et maîtriser l'implémentation d'un gadget OpenSocial à l'aide des gadgets.* API.

Cet exemple montre également comment utiliser des bibliothèques externes dans vos gadgets. J'ai utilisé jQuery (principalement pour ses effets d'interface utilisateur) et TinyMCE, un excellent plug-in d'éditeur de texte enrichi WYSIWYG.

Motivation

La création de gadgets utilisant JSON avec l'une des API de données Google ne nécessite que très peu de code JavaScript. Le principal inconvénient de ce type de gadget, c'est que les données sont publiques et en lecture seule. Pour créer des gadgets plus intéressants, vous devez avoir accès aux données privées d'un utilisateur (ce qui nécessite une authentification). Jusqu'à présent, vous n'aviez pas de moyen efficace de tirer parti des API Account de Google. AuthSub nécessite des redirections de navigateur et ClientLogin expose les identifiants de l'utilisateur, côté client. Même le piratage d'un gadget type="url" a été compliqué.

Saisissez le proxy OAuth.

Proxy OAuth

Si vous ne connaissez pas le protocole OAuth, il s'agit d'une norme d'authentification qui permet à un utilisateur de partager ses données privées avec un autre site Web ou un autre gadget. La spécification OAuth nécessite que toutes les requêtes de données soient signées numériquement. C'est une bonne pratique pour la sécurité, mais dans le cas d'un gadget JavaScript, la gestion des clés privées et la création de signatures numériques ne sont pas sécurisées. Il existe aussi une complication supplémentaire liée aux problèmes interdomaines.

Heureusement, ces problèmes sont résolus grâce à une fonctionnalité de la plate-forme de gadgets appelée proxy OAuth. Le proxy OAuth est conçu pour faciliter la tâche des développeurs de gadgets. Il masque une grande partie des informations d'authentification OAuth et fait le plus gros du travail à votre place. Le proxy signe les requêtes de données au nom de votre gadget. Vous n'avez donc pas besoin de gérer de clés privées ni de vous préoccuper des demandes de signature. Cela fonctionne !

Le proxy OAuth est basé sur un projet Open Source appelé Shindig, qui est une mise en œuvre de la spécification du gadget.

Remarque : Le proxy OAuth n'est pris en charge que pour les gadgets qui utilisent l'API gadgets.* et s'exécutent dans des conteneurs OpenSocial. Elle n'est pas compatible avec l'ancienne API Gadgets.

Premiers pas

Le reste de ce didacticiel sera consacré à la création d'un gadget permettant d'accéder aux données Blogger d'un utilisateur. Nous allons nous authentifier (à l'aide du proxy OAuth), utiliser la bibliothèque cliente JavaScript, puis publier une entrée dans Blogger.

Authentification

Tout d'abord, nous devons demander au gadget d'utiliser OAuth. Pour cela, ajoutez l'élément <OAuth> dans la section <ModulePrefs> du gadget:

<ModulePrefs>
...
<OAuth>
  <Service name="google">
    <Access url="https://www.google.com/accounts/OAuthGetAccessToken" method="GET" /> 
    <Request url="https://www.google.com/accounts/OAuthGetRequestToken?scope=http://www.blogger.com/feeds/" method="GET" /> 
    <Authorization url="https://www.google.com/accounts/OAuthAuthorizeToken?
                        oauth_callback=http://oauth.gmodules.com/gadgets/oauthcallback" /> 
  </Service>
</OAuth>
...
</ModulePrefs>

Les trois points de terminaison d'URL dans l'élément <Service> correspondent aux points de terminaison du jeton OAuth de Google. Voici les explications des paramètres de requête:

  • scope

    Ce paramètre est obligatoire dans l'URL de la requête. Votre gadget pourra uniquement accéder aux données du ou des scope utilisés dans ce paramètre. Dans cet exemple, le gadget accède à Blogger. Si votre gadget souhaite accéder à plusieurs API Google Data, concaténez le ou les scope supplémentaires avec un %20. Par exemple, si vous souhaitez accéder à la fois à Agenda et à Blogger, définissez la portée sur http://www.blogger.com/feeds/%20http://www.google.com/calendar/feeds/.

  • oauth_callback

    Ce paramètre est facultatif dans l'URL d'autorisation. La page d'approbation OAuth redirigera l'utilisateur vers cette URL une fois que l'utilisateur aura approuvé l'accès à ses données. Vous pouvez ignorer ce paramètre, le définir sur votre propre "page approuvée" ou utiliser de préférence http://oauth.gmodules.com/gadgets/oauthcallback. La version ultérieure offre la meilleure expérience utilisateur lorsque les utilisateurs installent votre gadget pour la première fois. Cette page fournit un extrait de code JavaScript qui ferme automatiquement la fenêtre pop-up.

Maintenant que notre gadget utilise OAuth, l'utilisateur doit autoriser l'accès à ses données. Voici le flux d'authentification:

  1. Le gadget se charge pour la première fois et tente d'accéder aux données Blogger de l'utilisateur.
  2. La requête échoue, car l'utilisateur n'a pas accordé l'accès au gadget. Heureusement, l'objet renvoyé dans la réponse contient une URL (response.oauthApprovalUrl) vers laquelle nous allons rediriger l'utilisateur. Le gadget affiche la page de connexion à Blogger et définit sa valeur sur oauthApprovalUrl.
  3. Ensuite, l'utilisateur clique sur "Se connecter à Blogger" et la page d'approbation OAuth s'ouvre dans une fenêtre distincte. Le gadget attend que l'utilisateur termine le processus d'approbation en affichant un lien : "J'ai approuvé l'accès".
  4. Dans la fenêtre pop-up, l'utilisateur peut choisir d'accorder ou de refuser l'accès à notre gadget. Lorsqu'il clique sur "Accorder l'accès", il est redirigé vers http://oauth.gmodules.com/gadgets/oauthcallback et la fenêtre se ferme.
  5. Le gadget reconnaît la fenêtre fermée et tente d'accéder à nouveau à Blogger en redemandant les données de l'utilisateur. Pour détecter la fermeture de la fenêtre, j'ai utilisé un gestionnaire de pop-ups. Si vous n'utilisez pas ce code, l'utilisateur peut cliquer manuellement sur "J'ai approuvé l'accès".
  6. L'interface utilisateur habituelle du gadget s'affiche désormais. Cette vue persistera, sauf si le jeton d'authentification est révoqué sous IssuedAuthSubTokens.

Ainsi, à partir des étapes ci-dessus, les gadgets présentent trois concepts différents:

  1. Non authentifié. L'utilisateur doit lancer le processus d'approbation.
  2. En attente que l'utilisateur approuve l'accès à ses données.
  3. Authentifié. Le gadget affiche son état de fonctionnement normal.

Dans mon gadget, j'ai utilisé <div> conteneurs pour séparer chaque étape:

<Content type="html">
<![CDATA[

<!-- Normal state of the gadget. The user is authenticated -->       
<div id="main" style="display:none">
  <form id="postForm" name="postForm" onsubmit="savePost(this); return false;">
     <div id="messages" style="display: none"></div>
     <div class="selectFeed">Publish to:
       <select id="postFeedUri" name="postFeedUri" disabled="disabled"><option>loading blog list...</option></select>
     </div>
     <h4 style="clear:both">Title</h4>
     <input type="text" id="title" name="title"/>
     <h4>Content</h4>
     <textarea id="content" name="content" style="width:100%;height:200px;"></textarea>
     <h4 style="float:left;">Labels (comma separated)</h4><img src="blogger.png" style="float:right"/>
     <input type="text" id="categories" name="categories"/>
     <p><input type="submit" id="submitButton" value="Save"/> 
     <input type="checkbox" id="draft" name="draft" checked="checked"/> <label for="draft">Draft?</label></p>
  </form>
</div>

<div id="approval" style="display: none">
  <a href="#" id="personalize">Sign in to Blogger</a>
</div>

<div id="waiting" style="display: none">
  <a href="#" id="approvalLink">I've approved access</a>
</di

<!-- An errors section is not necessary but great to have -->
<div id="errors" style="display: none"></div>
 
<!-- Also not necessary, but great for informing users -->     
<div id="loading">
  <h3>Loading...</h3>
  <p><img src="ajax-loader.gif"></p>
</div>

]]> 
</Content>

Chaque <div> est affiché seul avec showOnly(). Consultez l'exemple complet de gadget pour en savoir plus sur cette fonction.

Utiliser la bibliothèque cliente JavaScript

Pour récupérer du contenu distant dans OpenSocial, appelez la méthode gadgets.io.makeRequest à l'aide de l'API gadgets.*. Toutefois, comme nous développons un gadget de données Google, il n'est pas nécessaire d'appuyer sur les API gadgets.io.*. Utilisez plutôt la bibliothèque cliente JavaScript qui offre des méthodes spéciales pour envoyer des requêtes à chaque service de données Google.

Remarque : Au moment de la rédaction de cet article, la bibliothèque JavaScript n'est compatible qu'avec Blogger, Agenda, Contacts, Finance et Google Base. Pour utiliser l'une des autres API, utilisez gadgets.io.makeRequest sans la bibliothèque.

Charger la bibliothèque

Pour charger la bibliothèque JavaScript, incluez le chargeur commun dans la section <Content> et importez la bibliothèque une fois le gadget initialisé. Envoyer un rappel à gadgets.util.registerOnLoadHandler() permet de déterminer quand le gadget est prêt:

<Content type="html">
<![CDATA[
  ...
  <script src="https://www.google.com/jsapi"></script>
  <script type="text/javascript">
  var blogger = null;  // make our service object global for later
  
  // Load the JS library and try to fetch data once it's ready
  function initGadget() {  
    google.load('gdata', '1.x', {packages: ['blogger']});  // Save overhead, only load the Blogger service
    google.setOnLoadCallback(function () {
      blogger = new google.gdata.blogger.BloggerService('google-BloggerGadget-v1.0');
      blogger.useOAuth('google');
      fetchData();
    });
  }
  gadgets.util.registerOnLoadHandler(initGadget);
  </script>
  ...
]]> 
</Content>

L'appel de blogger.useOAuth('google') indique à la bibliothèque d'utiliser le proxy OAuth (au lieu de AuthSubJS, sa méthode d'authentification normale). Enfin, le gadget tente de récupérer les données Blogger de l'utilisateur en appelant fetchData(). Cette méthode est définie ci-dessous.

Récupération des données en cours

Maintenant que tout est configuré, comment transférer les données de GET ou de POST vers Blogger ?

Dans OpenSocial, le paradigme consiste à définir une fonction appelée fetchData() dans votre gadget. Cette méthode gère généralement les différentes étapes d'authentification et extrait les données à l'aide de gadgets.io.makeRequest. Comme nous utilisons la bibliothèque cliente JavaScript, gadgets.io.makeRequest est remplacé par un appel à blogger.getBlogFeed():

function fetchData() {
  jQuery('#errors').hide();
  
  var callback = function(response) {
    if (response.oauthApprovalUrl) {
      // You can set the sign in link directly:
      // jQuery('#personalize').get(0).href = response.oauthApprovalUrl
      
      // OR use the popup.js handler
      var popup = shindig.oauth.popup({
        destination: response.oauthApprovalUrl,
        windowOptions: 'height=600,width=800',
        onOpen: function() {
          showOnly('waiting');
        },
        onClose: function() {
          showOnly('loading');
          fetchData();
        }
      });
      jQuery('#personalize').get(0).onclick = popup.createOpenerOnClick();
      jQuery('#approvalLink').get(0).onclick = popup.createApprovedOnClick();
      
      showOnly('approval');
    } else if (response.feed) {
      showResults(response);
      showOnly('main');
    } else {
      jQuery('#errors').html('Something went wrong').fadeIn();
      showOnly('errors');
    }
  };
  
  blogger.getBlogFeed('http://www.blogger.com/feeds/default/blogs', callback, callback);
}

La deuxième fois que cette fonction est appelée, response.feed contient des données.

Remarque : getBlogFeed() utilise la même fonction pour son rappel et son gestionnaire d'erreurs.

Publier une entrée sur Blogger

La dernière étape consiste à publier une nouvelle entrée dans un blog. Le code ci-dessous décrit ce qui se passe lorsque l'utilisateur clique sur le bouton "Enregistrer".

function savePost(form) { 
  jQuery('#messages').fadeOut();
  jQuery('#submitButton').val('Publishing...').attr('disabled', 'disabled');
  
  // trim whitespace from the input tags
  var input = form.categories.value;
  var categories = jQuery.trim(input) != '' ? input.split(',') : [];   
  jQuery.each(categories, function(i, value) {
    var label = jQuery.trim(value);
    categories[i] = {
      scheme: 'http://www.blogger.com/atom/ns#',
      term: label
    };
  });

  // construct the blog post entry
  var newEntry = new google.gdata.blogger.BlogPostEntry({
    title: {
      type: 'text', 
      text: form.title.value
    },
    content: {
      type: 'text', 
      text: form.content.value
    },
    categories: categories
  });
  
  // publish as draft?
  var isDraft = form.draft.checked;
  if (isDraft) {
    newEntry.setControl({draft: {value: google.gdata.Draft.VALUE_YES}});
  }
  
  // callback for insertEntry()
  var handleInsert = function(entryRoot) {
    var entry = entryRoot.entry;
    var str = isDraft ? '(as draft)' : '<a href="' + entry.getHtmlLink().getHref() + '" target="_blankt">View it</a>';

    jQuery('#messages').html('Post published! ' + str).fadeIn();
    jQuery('#submitButton').val('Save').removeAttr('disabled');
  };
  
  // error handler for insertEntry()
  var handleError = function(e) {
    var msg = e.cause ? e.cause.statusText + ': ' : '';
    msg += e.message;
    alert('Error: ' + msg);
  };
  
  blogger.insertEntry(form.postFeedUri.value, newEntry, handleInsert, handleError);
}

Conclusion

Vous disposez désormais des éléments de base pour commencer à coder un gadget sur les API Google Data.

Nous espérons que cet article vous a permis de mieux comprendre la simplicité avec laquelle le proxy OAuth rend l'authentification des gadgets. En associant cet outil performant à la bibliothèque cliente JavaScript de données Google, vous pouvez créer facilement des gadgets intéressants, interactifs et sophistiqués.

Si vous avez des questions ou des commentaires sur cet article, veuillez consulter le forum de discussion sur les API Google Accounts.

Ressources