WebOS Goodies

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

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

Gmailを拡張する「Gmail Contextual Gadget」の作り方

あまり話題にはなりませんでしたが、今年 3 月に「Gmail Contextual Gadgets API」が一部のテスター向けに公開されました。これはメール内容に特定のパターンがあったときに表示される Google ガジェットで、 Gmail Labs にある Google Docs / YouTube プレビューガジェットのような機能を外部開発者が提供できるものです。 Google Apps Marketplace では、すでにこの機能を使ったガジェットがいくつか公開されています

そして先日、この API がついに一般の Google Marketplace 開発者に公開されました!誰でもこのような便利な機能を Gmail 上に追加できるようになったのです。これは使わない手はないので、本日はこの「Gmail Contextual Gadget」の作り方をご紹介したいと思います。

なお、 Gmail Contextual Gadget は Google Apps 向けの機能ですので、(独自ドメインでない)一般の Gmail では(たぶん)利用できません。とても便利な機能なので、ぜひ一般の Gmail にも追加できるようにしてほしいですね。

Gmail Contextual Gadget の仕組み

実際にガジェット制作に入る前に、まずはその仕組みを理解しておきましょう。 Gmail Contextual Gadget は、基本的に Extractor, Filter, OpenSocial ガジェットという 3 つのコンポーネントから構成されています。以下、その 3 つを簡単にご説明します。

エクストラクタ

エクストラクタは、その名のとおりメールから必要な情報を抽出するコンポーネントです。 Gmail Contextual Gadget は便利な機能を提供しますが、そのためにすべてのメール内容がガジェット側に渡るようでは、安心して使うことができません。また、ガジェットが増えるとメールの表示にも時間がかかってしまいます。そこで、エクストラクタを介してデータを取得するようにして、ガジェットが取得できるデータの範囲を明確にするとともに、(後述のフィルターとの組み合わせで)必要なガジェットのみを表示するようになっています。

現在は、こちらの表にあるエクストラクタのみが利用可能です。 "google.com:Sender〜Extractor" というのがエクストラクタを識別する URL で、 Scope が必要な権限、 Output Fields が出力フィールドです。使用するエクストラクタを指定するには、後述のマニフェストファイル内で type 属性が "contentExtractor" である Extension タグを記述し、子要素の Url タグでエクストラクタの URL を指定します。

<Extension id="PictureLinkExtractor" type="contextExtractor">
  <Url>google.com:HttpLinkExtractor</Url>
  <!-- その他のパラメータ -->
</Extension>

複数のエクストラクタを使用する場合は、 Extension タグを必要な数だけ並べてください。

フィルタ

フィルタは、エクストラクタの出力に対して正規表現によるマッチングを行います。マッチした場合のみガジェットが表示されます。フィルタを指定するには、エクストラクタの Extension タグ内に Param タグを記述し、その name 属性にマッチングを行うエクストラクタの出力フィールド名(前述の表の Output Fields にある名前から先頭の '@' を除いたもの)、 value 属性に正規表現を記述します。

<Extension id="PictureLinkExtractor" type="contextExtractor">
  <Url>google.com:HttpLinkExtractor</Url>
  <Param name="url" value=".*\.(jpg|jpeg|png|gif)$"/>
  <!-- その他のパラメータ -->
</Extension>

フィルタを指定しなかった場合、常にガジェットが表示されます。

OpenSocial ガジェット

ガジェット自体は、 iGoogle 向けの OpenSocial ガジェットなどと同様に作成します。 OpenSocial ガジェット自体の作り方は、 gihyo.jp の連載記事などを参考にしてください。

通常の OpenSocial ガジェットと違う部分は、エクストラクタからの出力を受け取る google.contentmatch が使えることです。そのためには、ガジェット XML の ModulePrefs タグの子要素として以下ように Require タグを記述します。

<Require feature="google.contentmatch">
  <Param name="extractors">google.com:HttpLinkExtractor<Param>
</Require>

Require タグの内部には、使用するエクストラクタを指定する Param タグを記述します。もし複数のエクストラクタを使っている場合は、 Param タグも複数記述してください。そして、ガジェット内の JavaScript で google.contentmatch.getContentMatches() メソッドを呼び出せば、エクストラクタからの出力が取得できます。

var matches = google.contentmatch.getContentMatches();

返り値はひとつのマッチごとの結果を格納した配列で、ひとつのマッチの結果はエクストラクタ表の Output Fields の名前をキー、その出力値を値としたキー・バリューペアのオブジェクトです。

マニフェストファイルの作成

基礎知識がわかったところで、早速ガジェットを作ってみましょう。ここではサンプルとして、メール内に画像ファイルへの URL (拡張子が .jpg, .jpeg, .png, .gif のいずれか)があったときに、その画像を表示するガジェットを作成します。

まずは Google Apps Marketplace に登録するためのマニフェストファイルを作成しなければなりません。これはアプリケーションのメタ情報を記述した XML ファイルで、 Marketplace に登録する際のフォームでアップロードします。本来はいろいろと詳しい情報を書くべきなのですが、ここではガジェットを表示するのに最低限必要な項目のみに絞ります。それ以外の項目についてはリファレンスを参照してください。以下が今回のガジェット用のマニフェストファイルです。

<?xml version="1.0" encoding="UTF-8" ?>
<ApplicationManifest xmlns="http://schemas.google.com/ApplicationManifest/2009">

  <!-- マーケットプレイスやコントロールパネルに表示されるサポート情報 -->
  <Support>
    <!-- カスタマーサポートのURL -->
    <Link rel="support" href="http://groups.google.com/group/webos-goodies/" />
  </Support>

  <!-- アプリケーション名と説明 -->
  <Name>Picture Preview</Name>
  <Description>Preview picture links.</Description>

  <!-- エクストラクタとフィルターの情報 -->
  <Extension id="PictureLinkExtractor" type="contextExtractor">

    <!-- 人間が読める名前 -->
    <Name>Picture Link Extractor</Name>

    <!-- 使用するエクストラクタ -->
    <Url>google.com:HttpLinkExtractor</Url>

    <!-- フィルター -->
    <Param name="url" value=".*\.(jpg|jpeg|png|gif)$"/>

    <!-- 表示するガジェット(ref属性はExtensionタグのid) -->
    <Triggers ref="PicturePreviewGadget"/>

    <!-- 必要なスコープ(ref属性はScopeタグのid) -->
    <Scope ref="emailSubject"/>
    <Scope ref="emailBody"/>

    <!-- 対象のアプリケーション? -->
    <Container name="mail"/>
  </Extension>

  <!-- ガジェットの情報 -->
  <Extension id="PicturePreviewGadget" type="gadget">

    <!-- ガジェットの名前 -->
    <Name>Gmail Picture Preview contextual gadget</Name>

    <!-- ガジェットXMLのURL -->
    <Url>http://webos-goodies.googlecode.com/svn/trunk/blog/articles/how_to_make_a_gmail_contextual_gadget/gadget.xml</Url>

    <!-- 対象のアプリケーション? -->
    <Container name="mail"/>
  </Extension>

  <!-- スコープ(このアプリケーションが必要な権限) -->
  <Scope id="emailSubject">
    <Url>tag:google.com,2010:auth/contextual/extractor/SUBJECT</Url>
    <Reason>This application searches the Subject: line of each email for the link to pictures</Reason>
  </Scope>

  <Scope id="emailBody">
    <Url>tag:google.com,2010:auth/contextual/extractor/BODY</Url>
    <Reason>This application searches the message body of each email for the link to pictures</Reason>
  </Scope>

</ApplicationManifest>

以下、項目ごとに説明します。

マーケットプレイスやコントロールパネルに表示されるサポート情報

Support タグの内部には複数の Link タグが記述でき、アプリケーションのサポートページや設定ページなどの URL を記述します。必須なのはサポートページ(rel="suport")で、これは Google Apps の管理ページに表示されます。いつでも変更できるので、公開するまでは適当な URL で良いかと思います。

アプリケーション名と説明

Marketplace などで表示されるアプリケーション名(Name タグ)と説明(Description タグ)を記述します。こちらも公開するまでは適当で大丈夫です。

エクストラクタとフィルターの情報

ここから実質的なガジェットの動作に関わる設定です。まず type="contentExtractor" の Extension タグで、エクストラクタとフィルターを定義します。 Extension タグの id 属性は必須なので、他と重複しない適当な id を記述しておいてください。以下、子要素の説明です。

Name
エクストラクタの名前です。ガジェットを Google Apps に追加する際の画面に表示されるので、わかりやすい名前を付けておいてください。
Url
使用するエクストラクタの指定です。前述のとおり、エクストラクタ表にある "google.com:Sender〜Extractor" という文字列を記述してください。
Param
フィルタの指定です。前述のとおり、 name 属性にフィールド名を、 value 属性に正規表現を記述してください。
Triggers
表示するガジェットの指定です。 ref 属性にガジェットを定義する Extension タグ(後述)の id を記述してください。
Scope
エクストラクタが必要とする権限です。 ref 属性に後述の Scope タグの id を記述してください。
Container
よくわかりませんが(^^; name 属性を "mail" にしておけば良いようです。

ガジェットの情報

表示するガジェットの情報も Extension タグで記述します。こちらの type 属性は "gadget" です。こちらもユニークな id を指定して、エクストラクタの Triggers タグからリンクできるようにします。以下、子要素の説明です。

Name
ガジェットの名前です。エクストラクタと同じく、 Google Apps に追加する際の画面に表示されます。
Url
ガジェット XML ファイルの URL を指定してください。
Container
エクストラクタと同じく、 name 属性を "mail" にしてください。

スコープの指定

最後に、ガジェットが必要とする権限を Scope タグで指定します。必要とする権限はエクストラクタ表の Scope の欄にあります。複数の権限が必要な場合は、その数だけ Scope タグを記述してください。そしてそれぞれの Scope タグにユニークな id を指定し、エクストラクタの Scope タグからリンクします。以下、子要素の説明です。

Url
スコープの URL を指定します。エクストラクタ表の Scope の欄にある "tag:google.com,2010:auth/contextual/extractor/〜" という文字列を記述してください。
Reason
その権限が必要な理由を記述してください。ガジェットを追加する際の画面や、 Google Apps の管理画面に表示されます。

ガジェット XML の作成

次は、ガジェットを定義する XML ファイルを作成します。これは通常の OpenSocial ガジェットとほぼ同じです。以下に今回のガジェットの XML ファイルを示します。

<?xml version="1.0" encoding="UTF-8"?>
<Module>
  <ModulePrefs title="Picture Preview"
               description="Picture previews."
               height="100"
               author="WebOS Goodies"
               author_email="support@webos-goodies.jp"
               author_location="Tokyo, Japan">
    <Require feature="dynamic-height"/>
    <!-- 使用するExtractorのリスト -->
    <Require feature="google.contentmatch">
      <Param name="extractors">google.com:HttpLinkExtractor</Param>
    </Require>
  </ModulePrefs>

  <!-- type="html", view="card" に固定 -->
  <Content type="html" view="card">
    <![CDATA[

<style type="text/css">
  #imagelist img { padding: 4px; }
</style>

<div id="imagelist"></div>

<script type="text/javascript">
  (function () {
    var matches   = google.contentmatch.getContentMatches();
    var parent    = document.createDocumentFragment();
    var numLoaded = 0;

    for(var i = 0 ; i < matches.length ; ++i) {
      var img    = document.createElement('IMG');
      img.onload = imageLoaded;
      img.src    = matches[i].url;
      parent.appendChild(img);
    }
    document.getElementById('imagelist').appendChild(parent);

    function imageLoaded() {
      numLoaded += 1;
      if(numLoaded >= matches.length)
        gadgets.window.adjustHeight();
    }
  })();
</script>

    ]]>
  </Content>
</Module>

ガジェット XML の書き方については、少し古い情報ですが以下の記事を参照してください。

前述のとおり、エクストラクタの出力を取得するための google.contentmatch を Require するのを忘れないでください。 Content タグの属性は、 type="html" view="card" に固定です。 URL タイプや home, canvas ビューなどは使えません。あとは、 Content タグ内にガジェットに表示する HTML を記述するだけです。

作成した XML ファイルは、公開された Web サーバーにアップロードしておきます。もし適切な Web サーバーがない場合は、こちらの記事にある方法で Google Code Project Hosting を利用すると良いでしょう。その後、マニフェストファイルにガジェット XML ファイルの URL を記述するのを忘れないでください。

ガジェットのテスト

これでガジェットのソースファイルが完成したので、さっそく Gmail に追加して実行してみたいところですね。そのためには Google Apps Marketplace にベンダー登録し、アプリケーションを追加する必要があります。 URL を入力するだけでガジェットを追加できる iGoogle や Gmail のサイドバーガジェットと比べると面倒ですが、とくに難しい作業ではありません。以下で手順をご紹介しますので、ぜひやってみてください。

まずは、 Google Apps Marketplace へ行き、右上のリンクでログインします。このときに使うアカウントは Google Apps のものでも、通常の Google アカウントでもかまいません。

ログインすると、右上に「Become a Vendor」というリンクが現れるので、それをクリックします。

ベンダー登録フォームが表示されますので、必要事項を入力して保存します。後ほど変更できますので、適当に入力するだけで良いかと思います。ただし、ここで入力した情報は基本的に公開情報として扱われる点は注意してください。

登録が完了するとベンダープロフィール画面になります。そこにある「Create your first listing」というボタンをクリックして、新しいアプリケーションの登録を始めます。すると利用規約が表示されますので、確認した後、「I agree. Continue.」ボタンで先に進んでください。

アプリケーションの情報入力画面になります。まず、「My product may be directly installed...」というチェックボックスにチェックを入れてください。すると「Manifest」のテキストボックスが現れるので、そこに上で作成したマニフェストファイルの内容をコピー&ペーストします。あとは、他の項目も適当に入力して(後ほど変更できます)、ページ下部の「Save and Preview」ボタンをクリックしてください。

これでアプリケーションが登録され、アプリケーションの製品ページが表示されます。この状態ではまだ Marketplace には公開されておらず、所有者だけがアクセスできる状態です。ここで、右にある「Add it now」のボタンをクリックし、ガジェットを Google Apps にサービスとして追加します。ドメイン名を入力するテキストボックスが現れるので、ご自分のドメインを入力してください。

アプリケーションのライセンス確認の画面が表示されます。自分が作ったアプリケーションでライセンスもなにもないので、「I agree. Continue.」ボタンをクリックして先に進みます。

アプリケーションに許可される権限の確認画面が表示されます。マニフェストファイルで指定した Scope タグの情報が正しく反映されているかを確認し、「Grant data access」ボタンをクリックしてください。

これでガジェットが追加されました。すぐにガジェットを有効にするかを訪ねてくるので、「Enable app now」ボタンをクリックして有効にしてください。

以上で追加完了です。 Google Apps の Gmail を表示し、ガジェットのエクストラクタ・フィルタの条件にマッチするメールを表示すれば、メール内容の下にガジェットが表示されるはずです。

もしうまく表示されないときは、 Gmail の URL の最後('#' がある場合は、その直前)に "?nogadgetcache=1" を付けて起動し、メールを表示して、ページをリロードしてください。それでも表示されない場合は、マニフェストやガジェット XML の記述が間違っていないか確認してください。

以上でガジェットを自分のドメインに追加することができました。さらにベンダープロフィール画面にある「Submit for approval」をクリックすればガジェットを Marketplace に公開することも可能ですが、それには $100 支払わないといけないので(最初に一度だけ)、そこまでは試していません。 Apple の AppStore もそうですが、フリーウェアの公開にはちょっと厳しいハードルですよね。

注意点

ということで、なかなか面白い Gmail Contextual Gadget ですが、まだ公開されたばかりだけに問題もいくつかあります。とりあえず私が知っている限りのものを挙げておきます。

テスト中はキャッシュを無効にする
Gmail はガジェット XML をキャッシュするので、ガジェット XML を変更してもすぐには反映されません。これでは開発には不便なので、上述のとおり Gmail の URL に "?nogadgetcache=1" を付加することで、キャッシュを無効にできます。 "https://mail.google.com/a/ドメイン名/?nogadgetcache=1" という URL で Gmail を起動すると良いでしょう。
マニフェストの変更が面倒
ガジェット XML の変更に関しては上記の対処で良いのですが、マニフェストの変更をすぐに反映させる方法が不明です。私も詳しい法則はわからないのですが、どうも一度 Google Apps からアプリケーションを削除して、再度追加しないと反映されないような気がします。エクストラクタやフィルタの試行錯誤は非常に面倒でした。
エクストラクタが日本語に対応していない?
私が試した限り、 google.com:EmailBodyExtractor は日本語のメールの内容を抽出することができませんでした。最初はサンプルガジェットとして「日本の住所を抽出して Google マップを表示する」というのをやろうと思ったのですが、このおかげで断念しました orz 早急に改善して欲しい点です。
将来的に Caja に対応しなければならない
将来的に Caja を使って IFRAME なしでガジェットを表示する仕組みに変更する予定があるようです。 Caja は安全に第三者の作成したスクリプトを実行する仕組みですが、そのために JavaScript のマイナーな機能を禁止します。具体的には、 document.write() やブラウザ固有の非標準な機能などが使えなくなります。ドキュメントでは GWT や JQuery などのライブラリを使うように推奨していますが、それでもアプリケーション側で禁止コードを使ってしまったらダメなような・・・。いずれにせよ、現時点で正しく Caja 対応(cajolable と言うらしい ^^;)するというのも難しいので、ある程度意識はしつつ、 Caja へ移行してからコードを修正するしかないような気がします。

以上、本日は Gmail Contextual Gadget の作り方をご紹介しました。 Gmail と外部アプリケーションをディープに統合する手段として、非常に興味深い仕組みだと思います。また、 Google Apps Marketplace に入門するための題材としても手軽でいいですね。 Google Apps ユーザーの皆さん、ぜひご活用ください!

関連記事

この記事にコメントする

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