Tworzenie gadżetu danych Google

Eric Bidelman, zespół interfejsów API danych Google
Październik 2008 r.

Wprowadzenie

Odbiorcy

Z tego artykułu dowiesz się, jak utworzyć gadżet Bloggera. Zakładamy, że znasz interfejsy API Google Data i bibliotekę klienta JavaScript. Musisz też dobrze znać JavaScript i mieć doświadczenie we wdrażaniu gadżetu OpenSocial za pomocą interfejsu gadgets.* API.

Ten przykład pokazuje też, jak skutecznie używać bibliotek zewnętrznych w gadżetach. Używam jQuery (głównie do efektów interfejsu) i TinyMCE, świetnej wtyczki edytora tekstu sformatowanego WYSIWYG.

Motywacja

Utworzenie gadżetu, który używa JSON z jednym z interfejsów Google Data API, wymaga bardzo niewielkiej ilości kodu JavaScript. Główną wadą takiego gadżetu jest to, że dane są publiczne i tylko do odczytu. Aby tworzyć ciekawsze gadżety, musisz mieć dostęp do prywatnych danych użytkownika (co wymaga uwierzytelniania). Do tej pory nie było dobrego sposobu na korzystanie z interfejsów API konta Google. AuthSub wymaga przekierowań przeglądarki, a ClientLogin udostępnia dane logowania użytkownika po stronie klienta. Nawet hakowanie gadżetu type="url" było niewygodne.

Wpisz serwer proxy OAuth.

Serwer proxy OAuth

Jeśli nie znasz protokołu OAuth, jest to standard uwierzytelniania, który umożliwia użytkownikowi udostępnianie danych prywatnych innej witrynie lub gadżetowi. Specyfikacja OAuth wymaga, aby wszystkie żądania danych były podpisane cyfrowo. Jest to świetne rozwiązanie z punktu widzenia bezpieczeństwa, ale w przypadku gadżetu JavaScript zarządzanie kluczami prywatnymi i tworzenie podpisów cyfrowych jest niebezpieczne. Dodatkowym utrudnieniem są problemy związane z wieloma domenami.

Na szczęście te problemy można rozwiązać, korzystając z funkcji platformy gadżetów o nazwie OAuth Proxy. Serwer proxy OAuth został zaprojektowany, aby ułatwić życie deweloperom gadżetów. Ukrywa on większość szczegółów uwierzytelniania OAuth i wykonuje za Ciebie całą ciężką pracę. Usługa Proxy podpisuje żądania danych w imieniu gadżetu, więc nie musisz zarządzać kluczami prywatnymi ani martwić się o podpisywanie żądań. Po prostu działa.

Serwer proxy OAuth jest oparty na projekcie open source o nazwie Shindig, który jest implementacją specyfikacji gadżetów.

Uwaga: serwer proxy OAuth jest obsługiwany tylko w przypadku gadżetów korzystających z interfejsu gadgets.* API i działających w kontenerach OpenSocial. Nie jest on obsługiwany w przypadku starszego interfejsu API gadżetów.

Pierwsze kroki

Pozostała część tego samouczka będzie poświęcona tworzeniu gadżetu, który umożliwi dostęp do danych użytkownika w Bloggerze. Omówimy uwierzytelnianie (za pomocą serwera proxy OAuth), korzystanie z biblioteki klienta JavaScript i publikowanie wpisu na blogu.

Uwierzytelnianie

Najpierw musimy poinformować gadżet, że ma używać protokołu OAuth. Aby to zrobić, dodaj element <OAuth> w sekcji <ModulePrefs> gadżetu:

<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>

3 punkty końcowe URL w elemencie <Service> odpowiadają punktom końcowym tokena OAuth Google. Wyjaśnienie parametrów zapytania:

  • scope

    Ten parametr jest wymagany w adresie URL żądania. Gadżet będzie mieć dostęp tylko do danych z scope, które są używane w tym parametrze. W tym przykładzie gadżet będzie mieć dostęp do Bloggera. Jeśli gadżet ma uzyskać dostęp do więcej niż jednego interfejsu Google Data API, połącz dodatkowe scope(s) z %20. Jeśli na przykład chcesz uzyskać dostęp zarówno do Kalendarza, jak i do Bloggera, ustaw zakres na http://www.blogger.com/feeds/%20http://www.google.com/calendar/feeds/.

  • oauth_callback

    Ten parametr jest opcjonalny w adresie URL autoryzacji. Po zatwierdzeniu przez użytkownika dostępu do jego danych strona akceptacji OAuth przekieruje go na ten adres URL. Możesz pominąć ten parametr, ustawić własną „zatwierdzoną stronę” lub, co jest preferowane, użyć http://oauth.gmodules.com/gadgets/oauthcallback. Ta druga opcja zapewnia najlepsze wrażenia użytkownikom, którzy po raz pierwszy instalują gadżet. Na tej stronie znajduje się fragment kodu JavaScript, który automatycznie zamyka wyskakujące okienko.

Teraz, gdy gadżet korzysta z OAuth, użytkownik musi zatwierdzić dostęp do swoich danych. Proces uwierzytelniania wygląda tak:

  1. Gadżet wczytuje się po raz pierwszy i próbuje uzyskać dostęp do danych użytkownika w Bloggerze.
  2. Żądanie nie zostało zrealizowane, ponieważ użytkownik nie przyznał dostępu do gadżetu. Na szczęście obiekt zwrócony w odpowiedzi zawiera adres URL (response.oauthApprovalUrl), na który przekierujemy użytkownika w celu zalogowania się. Gadżet wyświetla „Zaloguj się w Bloggerze” i ustawia wartość href na oauthApprovalUrl.
  3. Następnie użytkownik klika „Zaloguj się w Bloggerze”, a strona autoryzacji OAuth otwiera się w osobnym oknie. Gadżet czeka, aż użytkownik zakończy proces zatwierdzania, wyświetlając link „Zatwierdzam dostęp”.
  4. W wyskakującym okienku użytkownik może przyznać lub odmówić dostępu do naszego gadżetu. Gdy klikną „Przyznaj dostęp”, otworzy się http://oauth.gmodules.com/gadgets/oauthcallback, a okno zostanie zamknięte.
  5. Gadżet rozpoznaje zamknięcie okna i ponownie próbuje uzyskać dostęp do Bloggera, ponownie wysyłając prośbę o dane użytkownika. Aby wykryć zamknięcie okna, użyłem obsługi wyskakujących okienek. Jeśli nie użyjesz takiego kodu, użytkownik może ręcznie kliknąć „Zatwierdzam dostęp”.
  6. Gadżet wyświetla teraz normalny interfejs. Ten widok będzie się utrzymywać, dopóki token uwierzytelniania nie zostanie cofnięty w sekcji IssuedAuthSubTokens.

Z powyższych kroków wynika, że gadżety mogą mieć 3 różne stany:

  1. Nieuwierzytelnione. Użytkownik musi rozpocząć proces zatwierdzania.
  2. Oczekiwanie na zatwierdzenie przez użytkownika dostępu do jego danych.
  3. Uwierzytelniono. Gadżet wyświetla swój normalny stan funkcjonalny.

W gadżecie użyłem <div> kontenerów, aby rozdzielić poszczególne etapy:

<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&gt

Każdy znak <div> jest wyświetlany osobno za pomocą znaku showOnly(). Szczegółowe informacje o tej funkcji znajdziesz w pełnym przykładzie gadżetu.

Korzystanie z biblioteki klienta JavaScript

Aby pobrać zdalne treści w OpenSocial, wywołaj metodę gadgets.io.makeRequest za pomocą interfejsu API gadgets.*. Ponieważ tworzymy gadżet Google Data, nie musimy korzystać z interfejsów API gadgets.io.*. Zamiast tego używaj biblioteki klienta JavaScript, która ma specjalne metody wysyłania żądań do każdej usługi Google Data.

Uwaga: w momencie pisania tego artykułu biblioteka JavaScript obsługuje tylko Blogger, Kalendarz, Kontakty, FinanseGoogle Base. Aby użyć jednego z pozostałych interfejsów API, użyj gadgets.io.makeRequest bez biblioteki.

Wczytywanie biblioteki

Aby wczytać bibliotekę JavaScript, umieść wspólny moduł wczytujący w sekcji <Content> i zaimportuj bibliotekę po zainicjowaniu gadżetu. Przekazanie wywołania zwrotnego do gadgets.util.registerOnLoadHandler() pomoże określić, kiedy gadżet będzie gotowy:

<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&gt

Wywołanie blogger.useOAuth('google') informuje bibliotekę, że ma używać serwera proxy OAuth (zamiast AuthSubJS, czyli zwykłej metody uwierzytelniania). Na koniec gadżet próbuje pobrać dane użytkownika z Bloggera, wywołując funkcję fetchData(). Metoda ta jest zdefiniowana poniżej.

Pobieram dane

Skoro wszystko jest już skonfigurowane, jak GET lub POST dane do Bloggera?

W OpenSocial często definiuje się w gadżecie funkcję o nazwie fetchData(). Ta metoda zwykle obsługuje różne etapy uwierzytelniania i pobiera dane za pomocą gadgets.io.makeRequest. Ponieważ używamy biblioteki klienta JavaScript, gadgets.io.makeRequest zostaje zastąpiony wywołaniem 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);
}

Przy drugim wywołaniu tej funkcji zmienna response.feed zawiera dane.

Uwaga: getBlogFeed() używa tej samej funkcji w przypadku wywołania zwrotnego i obsługi błędów.

Opublikuj wpis na Bloggerze

Ostatnim krokiem jest opublikowanie nowego wpisu na blogu. Poniższy kod pokazuje, co się stanie, gdy użytkownik kliknie przycisk „Zapisz”.

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);
}

Podsumowanie

Masz już podstawowe elementy, aby zacząć pisać gadżet na podstawie interfejsów Google Data API.

Mamy nadzieję, że ten artykuł pomógł Ci zrozumieć, jak bardzo proxy OAuth ułatwia uwierzytelnianie gadżetów. Połączenie tego zaawansowanego narzędzia z biblioteką klienta Google Data JavaScript ułatwia tworzenie ciekawych, interaktywnych i zaawansowanych gadżetów.

Jeśli masz pytania lub uwagi dotyczące tego artykułu, odwiedź forum dyskusyjne interfejsów API kont Google.

Zasoby