WebOS Goodies

WebOS の未来を模索する、ゲームプログラマあがりの Web 開発者のブログ。

WebOS Goodies へようこそ! WebOS はインターネットの未来形。あらゆる Web サイトが繋がり、共有し、協力して創り上げる、ひとつの巨大な情報システムです。そこでは、あらゆる情報がネットワーク上に蓄積され、我々はいつでも、どこからでも、多彩なデバイスを使ってそれらにアクセスできます。 WebOS Goodies は、さまざまな情報提供やツール開発を通して、そんな世界の実現に少しでも貢献するべく活動していきます。
Subscribe       

Google カレンダーを拡張する「サイドバーガジェット」の作り方

このブログを読んでくださっている皆さんなら、 Google カレンダーはもちろんご存知ですよね。 Google が提供している Web ベースのカレンダーで、自分自身の予定管理はもちろん、他のユーザーとのカレンダーやイベントの共有などで共同作業にもとても便利なツールです。

本日はその Google カレンダーをさらに便利に拡張する「サイドバーガジェット」の作り方をご紹介します。先日、 Google Calendar Labs といっしょにヒッソリとリリースされたのですが、 Google カレンダーと密接に連携するガジェットを HTML / JavaScript で簡単に作成できるという、なかなかに画期的な機能です。日本語の資料はまだほとんどない(と言うか、英語でも公式の開発ガイド以外ほとんどない ^^;)状況なので、そこそこ価値ある情報ではないかと思います。 Google カレンダーのさらなる活用に、ぜひお役立てください!

※ 現在のところ、サイドバーガジェットは英語版の Google カレンダーでしか使えないようです。設定の言語を「English (US)」に変更してお試しください。

Google カレンダー サイドバーガジェットとは

Google カレンダー サイドバーガジェット(以下、サイドバーガジェット)は、基本的には Google カレンダーに iGoogle / OpenSocial ガジェットを追加する機能です。ちょうど Gmail のガジェット機能のように、既存ほとんどの iGoogle / OpenSocial ガジェットを Google カレンダーの横に配置できます(幅が狭いのが玉に瑕ですが・・・)。

しかし、単にガジェットを配置できるだけではありません。サイドバーガジェットのさらに優れている点は、ガジェットから Google カレンダー本体にアクセスできることです。専用の拡張 API を利用することで、 Google カレンダーから情報を取得したり、表示を操作したりできます。単に他のサイトの機能を取り込むだけでなく、それらを有機的に連携させることが可能なわけです。

このように画期的なサイドバーガジェットですが、開発は HTML / JavaScript のみで簡単に行うことができます。以降ではシンプルなガジェットを制作しつつその手順をご紹介していますので、参考にしていただければと思います。

友達のイベント表示ガジェットを作成

さて、まずは作成するガジェットの機能を決めましょう。あまり複雑なものは無理なので、指定したカレンダーから、現在表示中の日付範囲にマッチするイベントを表示するガジェットにします。

以下のリンクをクリックすると、実際に完成後のガジェットを Google カレンダーに追加できますので、お試しください。

「友達のイベント」ガジェットを Google カレンダーに追加

ガジェット上部にあるテキストボックスにカレンダーID(コンマ区切りで複数指定可能)を入力すると、そこから現在表示中の日付範囲に含まれるイベントを取得して表示します。さらに Google カレンダーで表示日付を変更すると、それに追従してガジェットの表示も更新されます。 Google カレンダー本体と連携しているのがおわかりいただけるかと思います。

表示したいカレンダーの ID がわからないときは、いったん Google カレンダーに追加して、カレンダー設定の以下の場所を確認してください。

それでは、このガジェットを実際に作っていきましょう。完成後のソースコードはこちらにありますので、以下の解説と平行してみていただけると良いかと思います。

ガジェット XML の骨組みを作る

サイドバーガジェットは、基本的には iGoogle / OpenSocial ガジェットそのものなので、作り方もほとんど同じです。 Gadget Spec と呼ばれる XML ファイルを作成し、それを任意の Web サーバーで公開することでガジェットになります。ここでは、今回のガジェットに必要な最低限の定義として以下の XML ファイルを作ります。

<?xml version="1.0" encoding="UTF-8" ?>
<Module>
  <ModulePrefs
     title="友達のイベント"
     title_url="http://webos-goodies.jp/"
     description="カレンダーで表示中の日付範囲に含まれる他のユーザーのイベントを表示します。"
     author="WebOS Goodies"
     scrolling="true"
     height="200">
    <Require feature="setprefs" />
    <Require feature="opensocial-0.8" />
    <Require feature="google.calendar-0.5" />
    <Require feature="google.calendar-0.5.read" />
  </ModulePrefs>
  <UserPref name="email" datatype="hidden" default_value="" />

  <Content type="html" view="home,profile,canvas">
    <![CDATA[
      <!-- ここにガジェットの HTML を記述する -->
    ]]>
  </Content>
</Module>

ポイントは "google.calendar-0.5", "google.calendar-0.5.read" という 2 つの feature を取り込んでいるところです。こうすることにより、後述する専用の API を使って Google カレンダー本体との連携を実現できます。

その他の定義については、「ガジェット設定の詳細」、「ユーザー設定の詳細」あたりを参照していただければと思います。

ガジェットを Google カレンダーに追加する

先ほど作った Gadget Spec は(中身のない空の状態とはいえ)一応ガジェットとして動作するので、試しに Google カレンダーに追加してみましょう。そのためには、適当な Web サーバーに Gadget Spec をアップロードし、 Web ブラウザで以下の URL にアクセスします。

http://www.google.com/calendar/render?gadgeturl=<Gadget Spec の URL>

例えば、 Gadget Spec の URL が http://webos-goodies.jp/empty_gadget.xml であれば、ガジェットを追加する URL はこうなります。

http://www.google.com/calendar/render?gadgeturl=http://webos-goodies.jp/empty_gadget.xml

なんと、この URL にアクセスするだけで、 Google カレンダーのサイドバーにガジェットが追加されます。

余談ですが、ガジェットのせいでカレンダーの幅が狭くなったとお嘆きの方は、ガジェットとカレンダーの間の部分をクリックしてみてください。ガジェットが非表示になり、元通りカレンダーの幅が広がります。

さて、完成したガジェットを使うだけならこれで万事解決なのですが、開発時には若干問題があります。一度追加した Gadget Spec は Google のサーバーにキャッシュされるので、 Gadget Spec を更新しても、すぐには反映されません。そこで、以下のようにダミーのクエリーパラメータを URL の最後にくっつけるのが良いようです。

http://www.google.com/calendar/render?gadgeturl=http://webos-goodies.jp/empty_gadget.xml?ver=0

そして、 Gadget Spec を更新した際は古いガジェットを削除し、 URL 末尾の数字を増やして再度追加すれば、(当然ながら)すぐに更新内容を確認できます。その他にも OpenSocial Limited Invalidation API を使う方法もあるようなのですが、 JavaScript ではうまくいきませんでした。 REST / RPC を使うにしてもエンドポイントがわからないし、うーむ。このあたり詳細をご存知の方がおられましたら、ぜひ教えてください。

そうそう、もし Gadget Spec をアップロードする Web サーバーをお持ちでない場合は、 Google Code Project Hosting を使う方法がお勧めです。

表示範囲の変更を知る

骨組みができたので、機能の実装を進めていきましょう。まずは Google カレンダー本体で日付範囲が変更されたときに、その通知を受ける部分を実装します。これを実現するためには、 サイドバーガジェット特有の API である google.calendar.subscribeToDates を使います。ガジェット読み込み完了時の処理を gadgets.util.registerOnLoadHandler で登録し、その中で呼び出すのが良いでしょう。

function onDatesChanged(dates) {
  // 表示範囲が変更された際の処理
}

gadgets.util.registerOnLoadHandler(function() {
  google.calendar.subscribeToDates(onDatesChanged);
});

このように google.calendar.subscribeToDates にコールバック関数(ここでは onDatesChanged)を渡すと、日付範囲が変更されたときにそれを呼び出してくれます。コールバックの第一引数には、以下のメンバを持つ JSON 形式のオブジェクトが渡されます。

メンバ名内容
startTime表示範囲の開始日時
endTime表示範囲の終了日時

これで日付範囲が取得できるわけですね。それぞれの日時は JavaScript の Date オブジェクトではなく、 oogle.calendar.time 型のオブジェクトです。具体的には、以下のメンバを持つ JSON オブジェクトとなっています。

メンバ名内容
year年(2009 など)
month月(1〜12)
date日(1〜31)
hour時(0〜23)
minute分(0〜59)
second秒(0〜59)

google.calendar.time 型は google.calendar.utils.toDate を使うことで通常の Date 型に変換できます。詳細は後のリファレンスを参照してください。

イベント情報を取得

日付範囲の変更通知を受けたら、その範囲を使って指定されたカレンダーのイベントを取得します。それには、やはり専用 API である google.calendar.read.getEvents を使います。書式は以下のようになっています。

getEvents(コールバック関数, カレンダーID, 開始日時, 終了日時)

基本的には、カレンダーIDで指定したカレンダーのイベントを取得し、それを引数にしてコールバック関数を呼び出すという仕組みになっています。カレンダーIDは以下の値を取ります。

イベントを取得するカレンダー
文字列配列特定のカレンダーID(複数可)
"@viewer"現在のユーザーのカレンダー
"selected"現在表示中のすべてのカレンダー

明示的にカレンダーIDを指定する場合は、たとえひとつでも配列にしなければならない点に注意してください。逆に "@viewer", "selected" を指定するときは文字列でなければなりません。

開始日時終了日時は省略可能で、その場合は現在時刻から 1 時間後までのイベントを取得します。指定する場合は 前述の google.calendar.time 型を使い、年月日時分秒すべてに有効な値が入っていなければなりません。

取得したイベントデータは、コールバック関数の第一引数として渡されます。これは google.calendar.getEventsResults 型の配列で、各要素がひとつのカレンダーに対応しています。 getEvetsResults の実体は以下のメンバを持つ JSON 形式のオブジェクトです。

メンバ名内容
emailカレンダーID
errorエラーコード
eventsgoogle.calendar.event の配列

errorundefined だった場合は成功で、 email, events に有効な値が入っています。 events に格納されている各要素は、取得したイベントの詳細を格納した google.calendar.event 型のオブジェクトです。メンバの数が多いので、詳細は公式のドキュメントを参照してください。

ということで、これを使ってイベントデータを取得する処理は以下のようになります。

var startTime = google.calendar.utils.getNow();
var endTime   = google.calendar.utils.getNow();
var calendars = '';

function setTime(date, hour, minute, second) {
  return {
    year: date.year, month: date.month, date: date.date,
    hour: hour, minute: minute, second: second
  };
}

function onDatesChanged(dates) {
  var ids   = calendars.replace(/\s/g, '').split(',');
  startTime = setTime(dates.startTime,  0,  0,  0);
  endTime   = setTime(dates.endTime,   23, 59, 59);
  if(ids.length > 0 && ids[0]) {
    google.calendar.read.getEvents(function(response) {
      var html = [];
      for(var i = 0 ; i < response.length ; ++i) {
        var data = response[i];
        if(typeof data.error != 'undefined') {
          alert(data.error);
        } else if(data.events && data.events.length > 0) {
          html.push(renderFriend(data));
        }
      }
      document.getElementById('events').innerHTML = html.join("\n");
    }, ids, startTime, endTime);
  } else {
    document.getElementById('events').innerHTML = '';
  }
}

次で実装する入力インターフェースにより、 calendars にカレンダーIDがコンマ区切りで格納されているので、それを配列に分解して、開始・終了日時とともに getEvents に渡します。その際、 subscribeToDates から渡される開始・終了日時は時分秒が設定されていない場合があるので、それを明示的に指定しています。

そして、 getEvents のコールバックの中では、渡された getEventsResults 配列の各要素に対して、エラーをチェックした後にその内容(開始日時とタイトル)を HTML に変換して表示しています。 renderFriend 関数はイベント内容から HTML を生成する処理ですが、普通に文字列操作で HTML を生成しているだけなので、ここでは省略します。処理内容を確認したい方は、ソースを参照してください。

入力インターフェースを作る

最後に、表示するカレンダーを指定するためのインターフェースを作ります。ここでは簡略化のため、テキストボックスをひとつ表示して、そこにカレンダーIDをコンマ区切りで入力することにします。 HTML は以下のとおりです。 __UP_calendars__ はプレースホルダで、ユーザー設定 "calendars" の内容がここに展開されます。

<div id="contents">
  <div id="form">
    <form onsubmit="onEmailChanged(event);">
      <input id="calendars" type="text" value="__UP_calendars__" />
    </form>
  </div>
  <div id="events"></div>
</div>

さらに、テキストボックスに入力された値を処理するために以下のスクリプトも追加します。

var prefs     = new gadgets.Prefs();
var calendars = '';

function onEmailChanged(event) {
  event = event || window.event;
  event.stopPropagation && event.stopPropagation();
  event.preventDefault  && event.preventDefault();
  event.cancelBubble = true;
  event.returnValue  = false;
  calendars = document.getElementById('calendars').value;
  onDatesChanged({startTime: startTime, endTime: endTime});
  prefs.set('calendars', calendars);
}

gadgets.util.registerOnLoadHandler(function() {
  calendars = prefs.getString('calendars');
  google.calendar.subscribeToDates(onDatesChanged);
});

onEmailChanged 関数はテキストボックスで Enter キーが押されたときに呼び出される関数です。テキストボックスの内容をユーザー設定に保存し、先ほど実装した onDatesChanged 関数を呼び出して表示を更新しています。ユーザー設定については「ユーザー設定の詳細」を参照してください。さらに、ガジェットの読み込み完了時の処理にグローバル変数 calendars の初期化も追加しています。

これでガジェットは完成です。友達のカレンダーや興味のあるイベントカレンダーなどを表示すれば、本体のカレンダーは汚さずにそれらの情報がチェックでき、意外と便利です。ぜひ使ってみてください。

ガジェットを配布する

せっかく作ったガジェットを自分だけで使っているのももったいないので、やはり公開したいですよね。ただ、残念ながら iGoogle のような公式ガジェットディレクトリはまだありませんので、自分のブログなどで公開することになります。ただ、その方法はいたって簡単。自分の Google Calendar に追加したのと同じリンクをブログ記事などに貼れば OK です。繰り返しになりますが、 Gadget Spec の URL が http://webos-goodies.jp/empty_gadget.xml であれば、以下のようにリンクを記述すれば良いでしょう。

<a href="http://www.google.com/calendar/render?gadgeturl=http://webos-goodies.jp/empty_gadget.xml">Google Calendar にガジェットを追加</a>

なお、 Google Calendar にガジェットを追加する時のダイアログには、(指定すれば)ガジェットのサムネイルなども表示されます。公開する際にはそのあたりの指定も Gadget Spec に追加しておいた方が良いでしょう。詳細は「ガジェット設定の詳細」や「ガジェットディレクトリに登録する」を参照してください。

さらに高度なガジェットを作成するために

今回はごくシンプルなサイドバーガジェットの作り方を解説しましたが、もちろんサイドバーガジェットの機能はこれだけではありません。サイドバーガジェットの API と OpenSocial を活用すればさらに高度なガジェットも開発できます。前述のとおり日本語でサイドバーガジェットを解説したサイトは皆無だと思いますが、 OpenSocial については以下のサイト(および書籍)が参考になります。

また、英語ですがサイドバーガジェットおよび OpenSocial の公式サイトは以下です。

  • 公式開発ガイドには、 API の説明やサンプルガジェットなどがあります。
  • OpenSocial の本家である opensocial.org には、 OpenSocial JavaScript API の完全なリファレンスや解説記事などがあります。

これらのサイトを参考にして、素晴らしいサイドバーガジェットを作成してください!

Google Calendar 用の JavaScript Console も作りました

今回 Sidebar Gadgets のことを調査するにあたり、以前から公開している JavaScript Console ガジェットを Sidebar Gadgets 化しました。私が作成した iGoogle ガジェット開発用支援用のガジェットで、ガジェット内での JavaScript の実行などが簡単に行えます。以下のリンクをクリックすれば Google Calendar に追加できます。

JavaScript Console ガジェットを Google Calendar に追加

追加すると、以下のようなガジェットが追加され、

テキストボックスに JavaScript を入力して「スクリプトを実行」を押すことで、簡単にスクリプトを実行できます。そのほかにもいろいろ機能があるので、詳細はiGoogle で JavaScript 開発!「JavaScript Console ガジェット」を参照してください。ただし、ガジェットの幅の関係上、「ガジェット」と「ツール」のタブは省略していますので、ご了承ください。

リファレンス

最後に、サイドバーガジェット特有の API に関する簡単なリファレンスを掲載しておきます。開発の際の参考にしていただければと思います。

google.calendar

addCalendar(カレンダーID, タイトル)
カレンダーを追加します。カレンダーIDは追加するカレンダーのID、タイトルはカレンダーのタイトル(省略可能)です。
composeEvent(イベントデータ)
イベント追加フォームを表示します。イベントデータはフォームにあらかじめ入力するデータで、 calendr.event の形式で指定します。
getPreferences(コールバック関数)
Google Calendar の設定の一部が取得できます。コールバック関数は第一引数に設定内容を格納した JSON オブジェクトを引数にとる関数です。渡される JSON オブジェクトの内容は こちらを参照してください。
refreshEvents()
サーバーから最新データを取得するように Google Calendar に要求します。 Google Calendar Data API などを使ってデータを更新した際に使うもののようです。
setRelay(relayURL)
type="url" のガジェットで一連のメソッドを利用可能にするために、 http://www.google.com/calendar/rpc_relay.html を自分のドメイン下にコピーしたファイルの URL を指定します。 type="html" のガジェットでは使いません。
showDate(年, 月, 日)
指定した年月日を表示します。
showEvent(イベントID)
指定したイベントを表示します。イベントIDには、 getEventssubscribeToEvents などで取得できる calendar.event.id を指定してください。
subscribeToDates(コールバック関数)
表示する日付の範囲が変更されるたびに呼び出される関数を指定します。詳細は上述の解説をご参照ください。
subscribeToDataChange(コールバック関数)
ユーザーが表示するカレンダーが変更したとき、ユーザーが各カレンダーのチェックを付けた・外したとき、ユーザーがカレンダーを追加・削除したときなどに呼び出される関数を指定します。コールバック関数に引数はありません。

google.calendar.utils

toDate(timeオブジェクト, オフセット)
calendar.time を JavaScript の Date オブジェクトに変換します。timeオブジェクト は変換する calendar.time オブジェクト、オフセットはミリ秒単位のオフセットです(省略可能)。
fromDate(dateオブジェクト, オフセット)
toDate とは逆に、 Date オブジェクトを calendar.time オブジェクトに変換します。dateオブジェクトは変換する Date オブジェクト、オフセットはミリ秒単位のオフセットです(省略可能)。
getNow(オフセット)
現在時刻を calendar.time 形式で取得します。オフセットはミリ秒単位のオフセットです(省略可能)。

google.calendar.read

getEvents(コールバック関数, カレンダーID, 開始日時, 終了日時)
指定した範囲のイベントを取得します。詳細は上述の解説をご参照ください。
subscribeToEvents(コールバック関数)
ユーザーがイベントを開いたとき、ユーザーがイベントをクリックしてバルーン表示が出たとき、ユーザーがカレンダー上でドラッグまたはクリックしてイベントの作成を始めたときに呼ばれるコールバック関数を指定します。該当イベントの情報はコールバックの第一引数に calendar.event 形式で渡されます。

以上、本日は Google カレンダーに追加できるサイドバーガジェットの作り方をご紹介しました。 Google カレンダーと密接に連携するガジェットが実現できる、非常に意欲的な API であることがおわかりいただけたと思います。

ただ、 Google カレンダーとの連携が優れているのに比べて、 OpenSocial の活用については、実はいまひとつです。 DataRequest を使ってフレンドリスト(Gmail コンタクトリストの友達グループ)は取得できるのですが、それぞれのフレンドが使っているカレンダーは(たとえそれが共有設定されていても)わからないのです。実はこの記事のサンプルも最初はフレンドリストから自動的にカレンダーを見つけようと思っていたのですが、このような事情で現在の仕様になってしまいました(笑)。プライバシーの都合もあるのでしょうが、なんとかしてほしいところです。

とまあ、少々残念な部分もありますが、サイドバーガジェットが Google カレンダーの可能性を大きく広げることは間違いありません。皆さんも、ぜひ便利なガジェットを開発してください!

関連記事

この記事にコメントする

Recommendations
Books
「Closure Library」の入門書です。
詳しくはこちらの記事をどうぞ!
Categories
Recent Articles