Membuat Gadget Data Google

Eric Bidelman, tim Google Data API
Oktober 2008

Pengantar

Audiens

Artikel ini akan memandu Anda membuat gadget Blogger. Panduan ini mengasumsikan bahwa Anda sudah memahami Google Data API dan library klien JavaScript. Anda juga harus fasih dalam JavaScript dan memiliki pengalaman dalam menerapkan gadget OpenSocial menggunakan gadgets.* API.

Contoh ini juga menunjukkan cara berhasil menggunakan library eksternal di gadget Anda. Saya telah menggunakan jQuery (terutama untuk efek UI-nya) dan TinyMCE, plugin editor teks kaya WYSIWYG yang hebat.

Motivasi

Hanya diperlukan sedikit JavaScript untuk membuat gadget yang menggunakan JSON dengan salah satu Google Data API. Masalah utama gadget semacam itu adalah data bersifat publik dan hanya baca. Untuk membuat gadget yang lebih menarik, Anda memerlukan akses ke data pribadi pengguna (sesuatu yang memerlukan autentikasi). Hingga saat ini, belum ada cara yang efektif untuk memanfaatkan Google Account API. AuthSub memerlukan pengalihan browser dan ClientLogin mengekspos kredensial pengguna di sisi klien. Bahkan meretas gadget type="url" pun tidak nyaman.

Masukkan OAuth Proxy.

Proxy OAuth

Jika Anda belum memahami OAuth, OAuth adalah standar autentikasi yang memungkinkan pengguna membagikan data pribadi mereka ke situs atau gadget lain. Spesifikasi OAuth mewajibkan semua permintaan data ditandatangani secara digital. Hal ini sangat bagus untuk keamanan, tetapi dalam kasus gadget JavaScript, mengelola kunci pribadi dan membuat tanda tangan digital tidak aman. Ada juga komplikasi tambahan terkait masalah lintas-domain.

Untungnya, masalah ini dapat diatasi dengan memanfaatkan fitur dari platform gadget yang disebut OAuth Proxy. OAuth Proxy dirancang untuk mempermudah pekerjaan developer gadget. Library ini menyembunyikan sebagian besar detail otentikasi OAuth dan melakukan tugas berat untuk Anda. Proxy menandatangani permintaan data atas nama gadget Anda, sehingga Anda tidak perlu mengelola kunci pribadi atau khawatir tentang penandatanganan permintaan. Berfungsi dengan baik.

OAuth Proxy didasarkan pada project open source bernama Shindig, yang merupakan implementasi spesifikasi gadget.

Catatan: OAuth Proxy hanya didukung untuk gadget yang menggunakan gadgets.* API dan berjalan di penampung OpenSocial. Layanan ini tidak didukung untuk API gadget lama.

Memulai

Bagian selanjutnya dari tutorial ini akan berfokus pada pembuatan gadget untuk mengakses data Blogger pengguna. Kita akan membahas autentikasi (menggunakan OAuth Proxy), penggunaan library klien JavaScript, dan terakhir, memposting entri ke Blogger.

Autentikasi

Pertama-tama, kita perlu memberi tahu gadget untuk menggunakan OAuth. Untuk melakukannya, tambahkan elemen <OAuth> di bagian <ModulePrefs> 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>

Tiga endpoint URL dalam elemen <Service> sesuai dengan endpoint token OAuth Google. Berikut penjelasan parameter kueri:

  • scope

    Parameter ini wajib ada di URL Permintaan. Gadget Anda hanya dapat mengakses data dari scope yang digunakan dalam parameter ini. Dalam contoh ini, gadget akan mengakses Blogger. Jika gadget Anda ingin mengakses lebih dari satu Google Data API, gabungkan scope tambahan dengan %20. Sebagai contoh, jika Anda ingin mengakses Kalender dan Blogger, tetapkan cakupan ke http://www.blogger.com/feeds/%20http://www.google.com/calendar/feeds/.

  • oauth_callback

    Parameter ini bersifat opsional di URL Otorisasi. Halaman persetujuan OAuth akan dialihkan ke URL ini setelah pengguna menyetujui akses ke datanya. Anda dapat memilih untuk tidak menggunakan parameter ini, menyetelnya ke "halaman yang disetujui" milik Anda, atau lebih baik lagi, menggunakan http://oauth.gmodules.com/gadgets/oauthcallback. Yang terakhir memberikan pengalaman pengguna terbaik saat pengguna pertama kali menginstal gadget Anda. Halaman tersebut menyediakan cuplikan JavaScript yang otomatis menutup jendela pop-up.

Setelah gadget kita menggunakan OAuth, pengguna harus menyetujui akses ke datanya. Berikut alur autentikasinya:

  1. Gadget dimuat untuk pertama kalinya dan mencoba mengakses data Blogger pengguna.
  2. Permintaan gagal karena pengguna belum memberikan akses ke gadget. Untungnya, objek yang ditampilkan dalam respons berisi URL (response.oauthApprovalUrl) tempat kita akan mengarahkan pengguna untuk login. Gadget menampilkan "Login ke Blogger" dan menetapkan href-nya ke nilai oauthApprovalUrl.
  3. Selanjutnya, pengguna mengklik "Login ke Blogger" dan halaman persetujuan OAuth akan terbuka di jendela terpisah. Gadget menunggu pengguna menyelesaikan proses persetujuan dengan menampilkan link: "Saya telah menyetujui akses".
  4. Di jendela pop-up, pengguna akan memilih untuk memberikan/menolak akses ke gadget kita. Setelah mengklik "Beri akses", pengguna akan diarahkan ke http://oauth.gmodules.com/gadgets/oauthcallback dan jendela akan ditutup.
  5. Gadget mengenali jendela yang ditutup dan mencoba mengakses Blogger untuk kedua kalinya dengan meminta ulang data pengguna. Untuk mendeteksi penutupan jendela, saya telah menggunakan handler pop-up. Jika Anda tidak menggunakan kode tersebut, pengguna dapat mengklik "Saya telah menyetujui akses" secara manual.
  6. Gadget kini menampilkan UI normalnya. Tampilan ini akan tetap ada kecuali jika token autentikasi dicabut di IssuedAuthSubTokens.

Jadi, dari langkah-langkah di atas, gadget memiliki tiga status yang berbeda:

  1. Tidak diautentikasi. Pengguna harus memulai proses persetujuan.
  2. Menunggu pengguna menyetujui akses ke datanya.
  3. Diautentikasi. Gadget menampilkan status fungsional normalnya.

Di gadget saya, saya telah menggunakan <div> penampung untuk memisahkan setiap tahap:

<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

Setiap <div> ditampilkan sendiri menggunakan showOnly(). Lihat gadget contoh lengkap untuk mengetahui detail fungsi tersebut.

Menggunakan library klien JavaScript

Untuk mengambil konten jarak jauh di OpenSocial, Anda melakukan panggilan ke metode gadgets.io.makeRequest menggunakan gadgets.* API. Namun, karena kita membuat gadget Google Data, tidak perlu menyentuh API gadgets.io.*. Sebagai gantinya, manfaatkan library klien JavaScript yang memiliki metode khusus untuk membuat permintaan ke setiap layanan Data Google.

Catatan: Pada saat penulisan artikel ini, pustaka JavaScript hanya mendukung Blogger, Kalender, Kontak, Finance, dan Google Base. Untuk menggunakan salah satu API lainnya, gunakan gadgets.io.makeRequest tanpa library.

Memuat library

Untuk memuat library JavaScript, sertakan loader umum di bagian <Content> dan impor library setelah gadget diinisialisasi. Memberikan callback ke gadgets.util.registerOnLoadHandler() akan membantu menentukan kapan gadget siap:

<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

Panggilan ke blogger.useOAuth('google') memberi tahu library untuk menggunakan Proxy OAuth (bukan AuthSubJS - metode autentikasi normalnya). Terakhir, gadget mencoba mengambil data Blogger pengguna dengan memanggil fetchData(). Metode tersebut dijelaskan di bawah.

Mengambil data

Setelah semuanya disiapkan, bagaimana cara kita benar-benar GET atau POST data ke Blogger?

Paradigma umum di OpenSocial adalah menentukan fungsi yang disebut fetchData() di gadget Anda. Metode ini biasanya menangani berbagai tahap autentikasi dan mengambil data menggunakan gadgets.io.makeRequest. Karena kita menggunakan library klien JavaScript, gadgets.io.makeRequest digantikan oleh panggilan ke 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);
}

Saat fungsi ini dipanggil untuk kedua kalinya, response.feed berisi data.

Catatan: getBlogFeed() menggunakan fungsi yang sama untuk callback dan pengendali errornya.

Memposting entri ke Blogger

Langkah terakhir adalah memposting entri baru ke Blog. Kode di bawah menunjukkan apa yang terjadi saat pengguna mengklik tombol "Simpan".

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

Kesimpulan

Sekarang Anda memiliki elemen dasar untuk mulai membuat kode gadget di atas Google Data API.

Semoga artikel ini membuat Anda mengapresiasi betapa sederhananya autentikasi gadget dengan OAuth Proxy. Dengan menggabungkan alat canggih ini dengan library klien JavaScript Data Google, Anda dapat dengan mudah membuat gadget yang menarik, interaktif, dan canggih.

Jika ada pertanyaan atau komentar tentang artikel ini, kunjungi kami di forum diskusi Google Account API.

Resource