Google データガジェットを作成する

Google Data APIs チーム、Eric Bidelman
2008 年 10 月

はじめに

オーディエンス

この記事では、Blogger ガジェットの作成手順について説明します。このドキュメントでは、Google Data APIsJavaScript クライアント ライブラリについて理解していることを前提としています。また、JavaScript に精通しており、gadgets.* API

この例では、ガジェットで外部ライブラリを正常に使用する方法も示します。jQuery(主に UI 効果用)と、優れた WYSIWYG リッチテキスト エディタ プラグインである TinyMCE を使用しました。

目的

Google Data API のいずれかで JSON を使用するガジェットを作成するには、JavaScript をほとんど使用しません。このようなガジェットの大きな問題は、データが公開されており、読み取り専用であることです。より興味深いガジェットを作成するには、ユーザーのプライベート データ(認証が必要なもの)にアクセスする必要があります。これまで、Google アカウント API を活用する優れた方法はありませんでした。AuthSub ではブラウザのリダイレクトが必要で、ClientLogin ではユーザーの認証情報がクライアントサイドで公開されます。type="url" ガジェットをハッキングするのも面倒でした。

OAuth プロキシを入力します。

OAuth プロキシ

OAuth は、ユーザーが自分のプライベート データを別のウェブサイトやガジェットと共有できるようにする認証標準です。OAuth 仕様では、すべてのデータ リクエストにデジタル署名が必要とされています。これはセキュリティ上は優れていますが、JavaScript ガジェットの場合、秘密鍵の管理とデジタル署名の作成は安全ではありません。また、クロスドメインの問題という複雑な要素も加わります。

幸いなことに、ガジェット プラットフォームの OAuth プロキシという機能を利用することで、これらの問題を解決できます。OAuth プロキシは、ガジェット デベロッパーの作業を簡素化するために設計されています。OAuth の認証の詳細の多くを隠蔽し、重い処理を代行します。プロキシはガジェットに代わってデータ リクエストに署名するため、秘密鍵を管理したり、リクエストの署名について心配したりする必要はありません。問題なく動作します。

OAuth プロキシは、ガジェット仕様の実装である Shindig というオープンソース プロジェクトに基づいています。

注: OAuth プロキシは、gadgets.* API を使用し、OpenSocial コンテナで実行されるガジェットでのみサポートされます。以前のガジェット API ではサポートされていません。

スタートガイド

このチュートリアルの残りの部分では、ユーザーの Blogger データにアクセスするためのガジェットの作成に焦点を当てます。認証(OAuth プロキシを使用)、JavaScript クライアント ライブラリの使用、最後に Blogger へのエントリの投稿について説明します。

認証

まず、ガジェットに OAuth を使用するように指示する必要があります。そのためには、ガジェットの <ModulePrefs> セクションに <OAuth> 要素を追加します。

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

<Service> 要素の 3 つの URL エンドポイントは、Google の OAuth トークン エンドポイントに対応しています。クエリ パラメータの説明は次のとおりです。

  • scope

    このパラメータはリクエスト URL で必須です。ガジェットは、このパラメータで使用されている scope からのデータにのみアクセスできます。この例では、ガジェットは Blogger にアクセスします。ガジェットが複数の Google Data API にアクセスする場合は、追加の scope%20 で連結します。たとえば、カレンダーと Blogger の両方にアクセスする場合は、スコープを http://www.blogger.com/feeds/%20http://www.google.com/calendar/feeds/ に設定します。

  • oauth_callback

    このパラメータは、認証 URL では省略可能です。ユーザーがデータへのアクセスを承認すると、OAuth 承認ページはこの URL にリダイレクトされます。このパラメータは省略しても、独自の「承認済みページ」に設定してもかまいません。ただし、http://oauth.gmodules.com/gadgets/oauthcallback を使用することをおすすめします。後者は、ユーザーがガジェットを初めてインストールする際に最適なユーザー エクスペリエンスを提供します。そのページには、ポップアップ ウィンドウを自動的に閉じる JavaScript のスニペットが用意されています。

ガジェットで OAuth を使用するようになったので、ユーザーはデータへのアクセスを承認する必要があります。認証フローは次のとおりです。

  1. ガジェットが初めて読み込まれ、ユーザーの Blogger データにアクセスしようとします。
  2. ユーザーがガジェットへのアクセスを許可していないため、リクエストは失敗します。幸いなことに、レスポンスで返されるオブジェクトには、ユーザーをログインに誘導する URL(response.oauthApprovalUrl)が含まれています。ガジェットに [Blogger にログイン] が表示され、その href が oauthApprovalUrl の値に設定されます。
  3. 次に、ユーザーが [Blogger にログイン] をクリックすると、OAuth 承認ページが別のウィンドウで開きます。ガジェットは、ユーザーが承認プロセスを完了するまで、「アクセスを承認しました」というリンクを表示して待機します。
  4. ポップアップで、ユーザーはガジェットへのアクセスを許可するか拒否するかを選択します。[アクセスを許可] をクリックすると、http://oauth.gmodules.com/gadgets/oauthcallback に移動し、ウィンドウが閉じます。
  5. ガジェットはウィンドウが閉じられたことを認識し、ユーザーのデータを再リクエストして Blogger に再度アクセスしようとします。ウィンドウが閉じられたことを検出するために、ポップアップ ハンドラを使用しました。このコードを使用しない場合、ユーザーは [アクセスを承認しました] を手動でクリックできます。
  6. ガジェットに通常の UI が表示されるようになりました。このビューは、IssuedAuthSubTokens で認証トークンが取り消されない限り、保持されます。

上記の手順から、ガジェットには次の 3 つの状態の概念があることがわかります。

  1. 未認証。お客様が承認プロセスを開始する必要があります。
  2. ユーザーがデータへのアクセスを承認するのを待っています。
  3. 認証済み。ガジェットは通常の機能状態を表示します。

ガジェットでは、<div> コンテナを使用して各ステージを分離しました。

<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

<div>showOnly() を使用して単独で表示されます。この関数の詳細については、完全な例のガジェットをご覧ください。

JavaScript クライアント ライブラリの使用

OpenSocial でリモート コンテンツを取得するには、gadgets.* API を使用して gadgets.io.makeRequest メソッドを呼び出します。ただし、Google データ ガジェットを構築するため、gadgets.io.* API を操作する必要はありません。代わりに、各 Google Data サービスにリクエストを行うための特別なメソッドを備えた JavaScript クライアント ライブラリを活用してください。

: この記事の執筆時点では、JavaScript ライブラリは Bloggerカレンダー連絡先ファイナンスGoogle Base のみをサポートしています。他の API のいずれかを使用するには、ライブラリなしで gadgets.io.makeRequest を使用します。

ライブラリを読み込む

JavaScript ライブラリを読み込むには、<Content> セクションに共通ローダを含め、ガジェットが初期化されたらライブラリをインポートします。コールバックを gadgets.util.registerOnLoadHandler() に渡すと、ガジェットの準備が整ったタイミングを判断できます。

<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

blogger.useOAuth('google') の呼び出しは、ライブラリに OAuth プロキシ(通常の認証方法である AuthSubJS ではなく)を使用するよう指示します。最後に、ガジェットは fetchData() を呼び出してユーザーの Blogger データの取得を試みます。このメソッドは以下のように定義されます。

データを取得しています

設定が完了したら、実際に Blogger にデータを GET または POST するにはどうすればよいでしょうか?

OpenSocial の一般的なパラダイムは、ガジェットで fetchData() という関数を定義することです。このメソッドは通常、認証のさまざまな段階を処理し、gadgets.io.makeRequest を使用してデータを取得します。JavaScript クライアント ライブラリを使用しているため、gadgets.io.makeRequestblogger.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);
}

この関数が 2 回目に呼び出されると、response.feed にデータが含まれます。

: getBlogFeed() は、コールバックとエラー ハンドラに同じ関数を使用します。

Blogger にエントリを投稿する

最後のステップは、ブログに新しいエントリを投稿することです。以下のコードは、ユーザーが [保存] ボタンをクリックしたときに何が起きるかを示しています。

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

まとめ

これで、Google Data API を基盤とするガジェットのコーディングを開始するための構成要素が揃いました。

この記事で、OAuth プロキシによってガジェット認証がどれほど簡単になるかをご理解いただけたかと思います。この強力なツールと Google データ JavaScript クライアント ライブラリを組み合わせることで、興味深く、インタラクティブで、洗練されたガジェットを簡単に作成できます。

この記事についてご質問やご意見がある場合は、Google Accounts APIs ディスカッション フォーラムをご覧ください。

リソース