Tworzenie gadżetu danych Google

Eric Bidelman, zespół ds. 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 danych Google i bibliotekę klienta JavaScript. Znasz się także na języku JavaScript i masz doświadczenie w implementowaniu gadżetu OpenSocial wykorzystującego gadżety*. API.

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

Motywacja

Utworzenie gadżetu używającego JSON z jednym z interfejsów API danych Google wymaga bardzo niewiele kodu JavaScript. Bardzo irytujący jest fakt, że dane dane są publiczne i tylko do odczytu. Aby tworzyć bardziej interesujące gadżety, musisz mieć dostęp do prywatnych danych użytkownika (co wymaga uwierzytelnienia). Dotychczas nie można było korzystać z interfejsów API konta Google. AuthSub wymaga przekierowań przeglądarki, a ClientLogin ujawnia dane logowania użytkownika po stronie klienta. Nawet zhakowanie gadżetu type="url" było niewygodne.

Wpisz Serwer proxy OAuth.

Serwer proxy OAuth

Jeśli nie znasz protokołu OAuth, jego standard uwierzytelniania umożliwia użytkownikowi udostępnienie prywatnych danych innej witrynie lub gadżetowi. Specyfikacja OAuth wymaga, by wszystkie żądania danych były podpisane cyfrowo. To świetne rozwiązanie, jeśli chodzi o bezpieczeństwo, ale w przypadku gadżetu JavaScript zarządzanie kluczami prywatnymi i tworzeniem podpisów cyfrowych nie jest bezpieczne. Jest też komplikacja problemów w wielu domenach.

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 z myślą o ułatwieniu programistom poleceń gadget. Ukrywa wiele szczegółów uwierzytelniania OAuth i zajmuje się dla Ciebie ciężką pracą. Serwer proxy podpisuje żądania danych w imieniu gadżetu, więc nie musisz zarządzać kluczami prywatnymi ani martwić się o podpisywanie żądań. To po prostu działa.

Działanie serwera proxy OAuth opiera się na projekcie oprogramowania open source o nazwie Shindig, który jest implementacją specyfikacji gadżetu.

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

Pierwsze kroki

Pozostała część tego samouczka dotyczy tworzenia gadżetu umożliwiającego dostęp do danych z Bloggera użytkownika. Najpierw przeprowadzimy uwierzytelnianie (używając serwera proxy OAuth), używając biblioteki klienta JavaScript, a następnie opublikujemy wpis w Bloggerze.

Uwierzytelnianie

Przede wszystkim musimy poinformować gadżet, aby 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 adresu URL w elemencie <Service> odpowiadają punktom końcowym tokena OAuth Google. Objaśnienie parametrów zapytania:

  • scope

    Ten parametr jest wymagany w URL-u żądania. Gadżet będzie mógł uzyskać dostęp tylko do danych z zakresu scope używanych w tym parametrze. W tym przykładzie gadżet uzyska dostęp do Bloggera. Jeśli gadżet chce uzyskać dostęp do więcej niż jednego interfejsu Google Data API, połącz dodatkowe atrybuty scope z %20. Aby na przykład uzyskać dostęp zarówno do Kalendarza, jak i 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. Strona zgody OAuth zostanie przekierowana na ten adres URL, gdy użytkownik zatwierdzi dostęp do jego danych. Możesz pominąć ten parametr, ustawić go na własną „zatwierdzoną stronę” albo użyć http://oauth.gmodules.com/gadgets/oauthcallback. Następnym razem zapewni Ci największą wygodę, gdy użytkownik zainstaluje gadżet. Ta strona zawiera fragment kodu JavaScript, który automatycznie zamyka wyskakujące okienko.

Teraz, gdy mamy już gadżet wykorzystujący protokół OAuth, użytkownik musi zatwierdzić dostęp do swoich danych. Proces uwierzytelniania:

  1. Gadżet wczytuje się po raz pierwszy i próbuje uzyskać dostęp do danych z Bloggera użytkownika.
  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), do którego odsyłamy użytkownika do logowania. Gadżet wyświetla opcję „Zaloguj się w Bloggerze” i ustawia dla niego wartość oauthApprovalUrl.
  3. Następnie użytkownik klika „Zaloguj się w Bloggerze”, a strona z prośbą o potwierdzenie protokołu OAuth otwiera się w osobnym oknie. Gadżet czeka, aż użytkownik zakończy proces zatwierdzania, wyświetlając link: „Uprawnienia zostały zatwierdzone”.
  4. W wyskakującym okienku użytkownik wyrazi zgodę na dostęp do naszego gadżetu. Kliknięcie przycisku „Przyznaj dostęp” powoduje przejście do aplikacji http://oauth.gmodules.com/gadgets/oauthcallback, a okno zostaje zamknięte.
  5. Gadżet rozpoznaje okno zamknięte i po raz drugi próbuje uzyskać dostęp do Bloggera, wysyłając żądanie udostępnienia danych użytkownika. Aby wykryć zamknięcie okna, używam modułu obsługi wyskakujących okienek. Jeśli nie użyjesz tego kodu, użytkownik będzie mógł ręcznie kliknąć „Zezwolono na dostęp”.
  6. Gadżet wyświetla teraz normalny interfejs. Ten widok będzie widoczny, dopóki token uwierzytelniania nie zostanie unieważniony w sekcji IssuedAuthSubTokens.

W powyższych krokach gadżet ma 3 różne stany:

  1. Nieuwierzytelnione. Użytkownik musi rozpocząć proces zatwierdzania.
  2. Czekam, aż użytkownik zatwierdzi dostęp do jego danych.
  3. Uwierzytelniono. Gadżet wyświetla normalny stan działania.

W moim gadżecie używam <div> kontenerów do oddzielania poszczególnych etapów:

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

Każdy element <div> jest wyświetlany osobno przy użyciu showOnly(). Zobacz pełny przykład, aby dowiedzieć się więcej o tej funkcji.

Korzystanie z biblioteki klienta JavaScript

Aby pobrać treści zdalne z OpenSocial, musisz wywołać metodę gadgets.io.makeRequest za pomocą interfejsu gadgets.* API. Skoro jednak opracowujemy gadżet danych Google, nie ma potrzeby korzystania z interfejsów API gadgets.io.*. Zamiast tego skorzystaj z biblioteki klienta JavaScript, która zawiera specjalne metody przesyłania żądań do każdej usługi danych Google.

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

Wczytuję bibliotekę

Aby wczytać bibliotekę JavaScript, w sekcji <Content> uwzględnij wspólny program wczytujący i zaimportuj bibliotekę po zainicjowaniu gadżetu. Przekazywanie wywołania zwrotnego do domeny gadgets.util.registerOnLoadHandler() ułatwi określenie, kiedy gadżet jest 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>

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

Pobieram dane

Skoro wszystko jest już skonfigurowane, w jaki sposób mamy w Bloggerze dane GET lub POST?

Częstym schematem w OpenSocial jest definiowanie funkcji o nazwie fetchData() w gadżecie. Ta metoda zwykle obsługuje różne etapy uwierzytelniania i pobiera dane za pomocą gadgets.io.makeRequest. Używamy biblioteki klienta JavaScript, dlatego gadgets.io.makeRequest jest zastępowane wywołaniem metody 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 response.feed zawiera dane.

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

Opublikuj post w Bloggerze

Ostatni krok to opublikowanie nowego posta na blogu. Ten kod pokazuje, co się dzieje, gdy użytkownik klika 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

Teraz masz elementy składowe, które ułatwiają tworzenie kodu gadżetu na interfejsach API danych Google.

Być może udało Ci się docenić prostotę uwierzytelniania gadżetu za pomocą serwera proxy OAuth. Dzięki połączeniu tego skutecznego narzędzia z biblioteką klienta JavaScript Google Data możesz łatwo tworzyć interesujące, interaktywne i zaawansowane gadżety.

Jeśli masz pytania lub uwagi dotyczące tego artykułu, odwiedź forum dyskusyjne poświęcone interfejsom API kont Google.

Zasoby