メモ代わり。てきとーに。 いや、ですからてきとーですって。 2年前ぐらいにPythonあたりでメールくれた方、ごめんなさい。メール紛失してしまい無視した形になってしまいました。。。

2009年8月1日土曜日

2009年7月31日金曜日

[Apache Shindig][お勉強][OpenSocial] メモ117 gadgets.io.makeRequest 認証タイプNONEでfeed取得

RSSやFEEDなんかもhttp://code.google.com/intl/ja/apis/gadgets/docs/remote-content.html#Fetch_Feed
にあるとおり、gadgetsの機能で扱えるらしい。

ということで、早速やってみた。
GadgetXMLは上記リンクにあるとおりで、"http://atkonn.blogspot.com/feeds/posts/default
を取得してみた。



・・・すげー。
できてるし。


そんだけ。
.

[Apache Shindig][お勉強][OpenSocial] メモ116 gadgets.io.makeRequest 認証タイプNONEでtext取得

gadgets.io.makeRequestというメソッドを使用して、
外部リソースへリクエストを投げるらしい。

まずは、認証タイプはNONEで、textファイルを取得してみる。

ガジェットは


<?xml version="1.0" encoding="UTF-8" ?>
<Module>
<ModulePrefs title="makeRequest NONE">
</ModulePrefs>
<Content type="html" view="canvas,profile,view">
<![CDATA[
<div id="content_div" style="height: 100px;"></div>
<script type="text/javascript">
function getHtml() {
var params = {};
/* テキストタイプ */
params[gadgets.io.RequestParameters.CONTENT_TYPE] = gadgets.io.ContentType.TEXT;
/* www.google.comからとってくる */
var url = "http://localhost/a.txt";
/* リクエスト */
gadgets.io.makeRequest(url, response, params);
};
/**
* コールバック
*/
function response(obj) {
/* obj.textにリクエストの応答が入っている。 */
var html = '';
if (obj.rc != 200) {
html = 'ぼよよん';
}
else {
var str = obj.text;
/* 最初の400文字取得 */
html = str.substr(0,400);
}
document.getElementById('content_div').innerHTML = html;
};
gadgets.util.registerOnLoadHandler(getHtml);
</script>
]]>
</Content>
</Module>


な感じ。
urlに指定するものは
サーバから見てのURLっぽい。上記のようにlocalhostで指定して取得できた。
ガジェットXMLのソースはGoogleのgadgets APIリファレンス。

そんだけ。
.

[Apache Shindig][お勉強][OpenSocial] メモ115 外部との通信

外部との通信の認証には、


gadgets.io.AuthorizationType.OAUTH (コンテナは OAuth プロトコルを使用する)
gadgets.io.AuthorizationType.SIGNED (コンテナが要求に署名する)
gadgets.io.AuthorizationType.NONE

の3種類があるらしい。

NONEはデフォルト。
SIGNEDは署名付きリクエストでリクエスト送信元を保証する。
OAUTHはOAuthによる認証を行うらしい。

まず、NONEからやってみようっと。

.

[Apache Shindig][お勉強][OpenSocial] メモ114 content-rewriterを使ってみる

shindigのソースを読む限り、使えそうなcontent-rewriter機能を使ってみる。
shindigのバージョンは1.1-SNAPSHOT。

まず、デフォルトでの機能をオフにする。
デフォルトでは全てのURLで有効になっている模様。
この設定を変更するには、

shindig.properties

をいじる。
デフォルトだと、


shindig.content-rewrite.include-urls=.*
shindig.content-rewrite.exclude-urls=
shindig.content-rewrite.include-tags=link,script,embed,img,style
shindig.content-rewrite.expires=86400
shindig.content-rewrite.proxy-url=/gadgets/proxy?url=
shindig.content-rewrite.concat-url=/gadgets/concat?

となっているので、

shindig.content-rewrite.exclude-urls=



shindig.content-rewrite.exclude-urls=.*

とする。

exclude-urlsとinclude-urlsはexclude-urlsが優先。
で、Patternクラス用正規表現で指定できる。

キャッシュを無効にするだけであれば、

shindig.content-rewrite.expires=86400



shindig.content-rewrite.expires=0

とすればよさそう、だけど試していない。
注意点としては、GadgetのXML自体のキャッシュの無効化、とは関係ない点。
content-rewriterは、あくまでGadgetXMLの中の要素にのみ有効となっている模様。
もし、GadgetのXMLファイルのキャッシュを無効化したいのであれば、
nocacheパラメータをつけるか、もしくは
shindig.propertiesのshindig.cache.xml.refreshIntervalを0なりに設定するか、
shindig.propertiesのshindig.cache.lru.gadgetSpecs.capacityの行を削除するか、
で、無効化できる。

ついでに、gadgetのXMLファイルのキャッシュも無効化。
shindig.propertiesの

shindig.cache.xml.refreshInterval=300000



shindig.cache.xml.refreshInterval=0

に修正。

修正したら、コンパイルして起動。
で、アクセスしてみると、修正の度に新しいGadgetXMLが読み込まれるし、
imgタグなんかのURLもrewriteされていない。

proxyUrlとconcatのURLは、container.jsに記述があれば、そちらが優先。
container.jsに記述が無ければshindig.propertiesが優先される。



そしたら、今度は、
content-rewriterフィーチャーを使ってみる。
で、使ってみたXMLは以下。

<?xml version="1.0" encoding="UTF-8" ?>
<Module>
<ModulePrefs title="content-rewrite">
<Require feature="opensocial-0.8" />
<Optional feature="content-rewrite">
<Param name="expires">86400</Param>
<Param name="include-urls">.*</Param>
<Param name="exclude-urls"></Param>
</Optional>
</ModulePrefs>
<Content type="html" view="canvas,profile,view">
<![CDATA[
<div id="content_div" style="height: 100px;"></div>
<script type="text/javascript">
</script>
どうかな,
どうかな,
どうかな
<img src="http://www.google.co.jp/intl/ja_jp/images/logo.gif" alt="googleだよ" />
]]>
</Content>
</Module>


imgタグはちゃんとproxy経由になった。
キャッシュも効いている模様。

さて、http://wiki.opensocial.org/index.php?title=Content_Rewriter_Feature

を見ると、

<Param name="include-urls">.*</Param>
<Param name="exclude-urls"></Param>

のところは、

<Param name="include-url">.*</Param>
<Param name="exclude-url"></Param>

と's'なしで指定すべきっぽいが、shindig-1.1-SNAPSHOTでは
's'ありで指定しなきゃ動かないっぽい。

あとexclude-urlsは空で定義してあるけど、
shindig.propertiesでexclude-urlsを指定してある場合には、
Optional featuresでは空で指定してあげないと、
shindig.propertiesの方のexclude-urlsが優先される。



おしまい。
.

[Apache Shindig][お勉強][OpenSocial] メモ113 setprefsフィーチャー

ガジェットのユーザ毎の設定(UserPref?)をガジェットから扱える機能を追加するもの。

使えるメソッドは、


gadgets.Prefs.set(key, value)



gadgets.Prefs.setArray(key,value)

のみ。
setArrayの方は、valueに文字列か数値の配列を指定する。
指定された配列は'|'によって連結され保存される。

setprefsフィーチャに関係なくgadgets.Prefsの読み込み系は使える。

で、やってみる。

<?xml version="1.0" encoding="UTF-8" ?>
<Module>
<ModulePrefs title="setprefsフィーチャー">
<Require feature="opensocial-0.8" />
<Require feature="setprefs" />
</ModulePrefs>
<UserPref name="counter" default_value="0" datatype="hidden"/>
<Content type="html" view="canvas,profile,view">
<![CDATA[
<div id="content_div" style="height: 100px;"></div>
<script type="text/javascript">

var prefs = null;
var html = "";
var div = '';
function incrementCounter() {
var count = prefs.getInt("counter");
div.innerHTML = "The count is " + count + ".";
prefs.set("counter", count + 1);
}
function resetCounter(){
prefs.set("counter", 0);
div.innerHTML = "Count reset to " + prefs.getInt("counter") + ".";
}

function init() {
prefs = new gadgets.Prefs();
html = "";
div = document.getElementById('content_div');
var count = prefs.getInt("counter");
div.innerHTML = "The count is " + count + ".";
}
gadgets.util.registerOnLoadHandler(init);
</script>
<input type=button value="Count" name="count" onClick="incrementCounter()">
<input type=button value="Reset" name="reset" onClick="resetCounter()">
]]>
</Content>
</Module>


な感じ。
Googleのgadgets APIリファレンスを参考、というかほぼそのまま使用した。


これは、もう何度もやっているので、動作する。
cookieに保存する場合はサンプルがあるので、コンテナ側で設定する必要あり。

そんだけ。
.

[Apache Shindig][お勉強][OpenSocial] メモ112 settitleフィーチャー

ガジェットのタイトルをプログラムから設定する場合に指定するもの。

使えるメソッドは、


gadgets.window.setTitle(title)

のみ。


_IG_SetTitle

という名前でもコールできる。

で、やってみる。

<?xml version="1.0" encoding="UTF-8" ?>
<Module>
<ModulePrefs title="settitleフィーチャー">
<Require feature="settitle"/>
</ModulePrefs>
<Content type="html" view="canvas,profile,home">
<![CDATA[
<script type="text/javascript">
function init() {
gadgets.window.setTitle('ああああ');
}
function changeTitle(form) {
_IG_SetTitle(form.inputbox.value);
}
gadgets.util.registerOnLoadHandler(init);
</script>
<FORM NAME="myform" ACTION="" METHOD="GET"><BR>
<INPUT TYPE="text" NAME="inputbox" VALUE="">
<INPUT TYPE="button" NAME="button" Value="Add" onClick="changeTitle(this.form)">
<INPUT TYPE="button" NAME="button2" Value="Clear" onClick="clearList(this.form)">
</FORM>
<div id="content_div"></div>
]]>
</Content>
</Module>


初期表示時にgadgets.window.setTitleを使ってタイトルを設定。
フォームから入力されると、_IG_SetTitleを使ってタイトルを設定。

ちゃんと動いた。
このメソッドも、結局は、gadgets.rpc.call(null,'set_title', null, title)な感じで
rpc経由。

rpc経由でコールされる側のメソッドでは、

gadgets.IfrGadgetService.prototype.setTitle = function(title) {
var element = document.getElementById(this.f + '_title');
if (element) {
element.innerHTML = title.replace(/&/g, '&amp;').replace(/</g, '&lt;');
}
};

となっているので、タイトルを出力したいところのidは

ガジェットを表示するiframeの名前+'_title'

とする必要がある。もちろんShindigデフォルトの場合。


そんだけ。
.

[Apache Shindig][お勉強][OpenSocial] メモ111 dynamic-heightフィーチャー

dynamic-heightフィーチャーを使うことで、ガジェットの高さを動的に変更できる。

使えるメソッドは、


gadgets.window.adjustHeight(opt_height)



gadgets.window.getViewportDimensions()

の2つ。

gadgets.window.adjustHeight(opt_height)は、

_IG_AdjustIFrameHeight

という名前でもコールできる。

ということで早速。

<?xml version="1.0" encoding="UTF-8" ?>
<Module>
<ModulePrefs title="Dynamic Height"
height="100">
<Require feature="dynamic-height"/>
</ModulePrefs>
<Content type="html">
<![CDATA[
<script type="text/javascript">
// This example lets users add items to a grocery list and then clear the list.
// When items are added or cleared, the gadget resizes itself.
var mylist = "";
var flag = 0;

// Function that is invoked whenever user clicks the Add button to add an
// item to the list.
function addToList (form) {
var input = _trim(form.inputbox.value);
if (input == "") {
return;
}

// Make alternating lines green/white, depending on value of flag variable.
var d = gadgets.window.getViewportDimensions();
if(flag == 0){
mylist += "<div style='padding-left: 5px;background-color: #E6FFE6; font-family:Arial, Helvetica;'>" +input + " width:" + d.width + " height:" + d.height + "</div>";
flag = 1;
}
else {
mylist += "<div style='padding-left: 5px;font-family:Arial, Helvetica;'>" +input + " width:" + d.width + " height:" + d.height + "</div>";
flag = 0;
}

// Call setContent to output HTML to div and resize gadget
setContent(mylist);
}

// Clear the list
function clearList(form) {
// Call setContent to remove all items from the list and resize the gadget
setContent("");
}

// Outputs content to the div and resizes the gadget
function setContent(html) {
document.getElementById('content_div').innerHTML = html;

// Tells gadget to resize itself
gadgets.window.adjustHeight();
}
gadgets.util.registerOnLoadHandler(_IG_AdjustIFrameHeight);
</script>
<FORM NAME="myform" ACTION="" METHOD="GET"><BR>
<INPUT TYPE="text" NAME="inputbox" VALUE="">
<INPUT TYPE="button" NAME="button" Value="Add" onClick="addToList(this.form)">
<INPUT TYPE="button" NAME="button2" Value="Clear" onClick="clearList(this.form)">
</FORM>
<div id="content_div"></div>
]]>
</Content>
</Module>


サンプルはGoogleのgadgets APIリファレンスから。
少々修正。

実行すると、自動でガジェットの高さが低くなり、
何か入力していくと、自動でガジェットの高さが調整される。


そんだけ。
.

[Apache Shindig][お勉強][OpenSocial] メモ110 viewsフィーチャーって何よ?

ガジェットにviewを切り替える機能を追加できるらしい。

ふーん。

ということで、やってみる。


<?xml version="1.0" encoding="UTF-8" ?>
<Module>
<ModulePrefs title="viewsフィーチャー" >
<Require feature="views" />
</ModulePrefs>
<Content type="html">
<![CDATA[
サポートされているビュー:<div id="supported_view"></div>
現在のビュー:<div id="current_view"></div>
<script type="text/javascript">
var views = gadgets.views.getSupportedViews();
var ul = document.createElement('ul');
for (var xx in views) {
var li = document.createElement('li');
var a = document.createElement('a');
a.href = 'javascript:void(0);';
a.innerHTML = '名前:' + xx + '<br />'
+ 'タイプ:' + views[xx].getName() + '<br /><hr />';
function setView(a, view) {
a.onclick = function(){gadgets.views.requestNavigateTo(view);};
}
setView(a, views[xx]);
li.appendChild(a);
ul.appendChild(li);
}

document.getElementById('supported_view').appendChild(ul);

var current = gadgets.views.getCurrentView();
document.getElementById('current_view').innerHTML = current.getName();
</script>
<br />
]]>
</Content>
</Module>


な感じ。

クリックすると、VIEWが切り替わる。
ShindigデフォルトのviewのurlTemplateとか使わずに、
jQuery使ってガジェットのフレームの大きさと位置を調整した。

とてもいい感じ。
.

[Apache Shindig][お勉強][OpenSocial] メモ109 flashフィーチャーって何よ?

flashのswfを実行できるflashフィーチャーというものがある。

面倒なので、Googleに張ってあったgadgetのサンプルをそのまま実行してみた。

あらら。
Shindigデフォルトでちゃんと動くっぽい。

すばらしいねー。

そんだけ。

[Apache Shindig][お勉強][OpenSocial] メモ108 minimessageフィーチャーって何よ?

gadgets APIのひとつにminimessageというフィーチャーがあるけど、
minimessageフィーチャーってなんだろね。

コメントによると、


メッセージをガジェットに表示する際に使用できるもの

らしい。

なんだか良く分からないので、使ってみる。

<?xml version="1.0" encoding="UTF-8" ?>
<Module>
<ModulePrefs title="minimessageフィーチャー">
<Require feature="minimessage" />
</ModulePrefs>
<UserPref name="selectedTab" value="0" />
<Content type="html" view="home,profile,canvas">
<![CDATA[
<script type="text/javascript">
var msgObj = null;
function getInstance() {
if (msgObj) {
return msgObj;
}
return new gadgets.MiniMessage();
}
function init() {
var msg = getInstance();
msg.createDismissibleMessage("ぼよよーん");
}
function onclick() {
var msg = getInstance();
msg.createDismissibleMessage("ぶよよーん");
}
gadgets.util.registerOnLoadHandler(init);
</script>
<input type="button" value="ボタンだよ" onclick="onclick();" />
]]>
</Content>
</Module>


とりあえず、GadgetXMLはこんな感じ。

で、表示させてみると。。

へぇ。。
gadgetの中に[x]ボタンが付いたメッセージが表示される。

なるほどねー。

そんだけ。
.

[Apache Shindig][お勉強][OpenSocial] メモ107 ShindigのtabsフィーチャーでUserPref name="selectedTab"

Shindigのgadgets.tabsでは、選択したタブをUserPrefsに自動保存する、
という機能が効かないみたい。
(コードにそのような記述はない。)

Google gadgets APIでは、


<?xml version="1.0" encoding="UTF-8" ?>
<Module>
<ModulePrefs title="tabsフィーチャー">
<Require feature="setprefs" />
<Require feature="tabs" />
</ModulePrefs>
<UserPref name="selectedTab" value="0" />
<Content type="html" view="home,profile,canvas">


などと書いておくと、再表示したときに、前回表示したときに選択したタブが選択される
らしい。

これをShindigでもやってみた。

--- tabs.js.old 2009-07-30 23:48:32.000000000 +0900
+++ tabs.js 2009-07-31 09:28:01.000000000 +0900
@@ -171,6 +171,9 @@
'text-decoration: none;',
'}'
].join(''));
+ if (gadgets.util.hasFeature('setprefs')) {
+ this.prefs = new gadgets.Prefs();
+ }
};

/**
@@ -230,6 +233,12 @@
if (tabName == this.defaultTabName_ || (!this.defaultTabName_ && tabIndex === 0)) {
this.selectTab_(tab);
}
+ if (this.prefs) {
+ var selectedTab = this.prefs.getString("selectedTab");
+ if (selectedTab === tabName) {
+ this.selectTab_(tab);
+ }
+ }

this.tabsAdded_++;
this.displayTabs(true);
@@ -602,7 +611,13 @@
* @return {Function} Callback function to select the tab.
*/
gadgets.TabSet.prototype.setSelectedTabGenerator_ = function(tab) {
- return function() { tab.handle_.selectTab_(tab); };
+ var self = this;
+ return function() {
+ if (self.prefs) {
+ self.prefs.set("selectedTab", tab.getName());
+ }
+ tab.handle_.selectTab_(tab);
+ };
};

/**



と、features/tabs/tabs.jsを修正する。
すると、あら不思議。

前回選択したタブが選択された状態で表示できるじゃありませんか。

そんだけ。
.

[Apache Shindig][お勉強][OpenSocial] gadgets APIとOpenSocialのgadgets API

どうも、Googleのgadgets APIとOpenSocialのgadgets APIは
とても似ているけど、別ものらしい。。

Google gadgets APIの内容とShindigのgadgets APIが微妙に違う。

おかしいなぁと思っていたら、OpenSocialのgadgets APIはgadgets APIの「拡張」と
書いてあった。
要は別もの。

なるほどねー。
.

2009年7月30日木曜日

[Apache Shindig][お勉強][OpenSocial] メモ105 tabs(2) 削除

リファレンス見ればわかるんだけど、一応出力。

tabの削除をしてみた。
使用するのはこれ。


gadgets.TabSet.prototype.removeTab = function(tabIndex) {


で、以下ガジェットXML。

<?xml version="1.0" encoding="UTF-8" ?>
<Module>
<ModulePrefs title="tabsフィーチャー">
<Require feature="opensocial-0.8" />
<Require feature="tabs" />
<Require feature="setprefs" />
<Require feature="jquery" />
</ModulePrefs>
<Content type="html" view="home,profile,canvas">
<![CDATA[
<script type="text/javascript">
var tabdayon = function() {
this.tabs = null;
this.tabDivId = {};
};
tabdayon.prototype.init = function() {
this.tabs = new gadgets.TabSet(__MODULE_ID__, "タブセットだよーん");
var oneId = this.tabs.addTab("One");
this.tabDivId = {};
this.tabDivId[oneId] = oneId;
var twoId = this.tabs.addTab("Two");
this.tabDivId[twoId] = twoId;
document.getElementById(oneId).innerHTML = "最初のタブのコンテンツだよ";
document.getElementById(twoId).innerHTML = "2番目のタブのコンテンツだよ";
};
/**
* タブの追加
*/
tabdayon.prototype.addTab = function(ev) {
var tabName = $('.tabName').val();
$('.tabName').val('');
var divId = this.tabs.addTab(tabName);
this.tabDivId[divId] = divId;
document.getElementById(divId).innerHTML = tabName + 'を追加したよ';
};
tabdayon.prototype.removeTab = function() {
var tab = this.tabs.getSelectedTab();
var cont = tab.getContentContainer();
this.tabs.removeTab(tab.getIndex());
this.tabDivId[cont.id] = null;
};
var tabInstance = null;
function init() {
tabInstance = new tabdayon();
tabInstance.init();
$('.addTab').click(function(ev) {tabInstance.addTab(ev);});
$('.removeTab').click(function(ev) {tabInstance.removeTab();});
}

gadgets.util.registerOnLoadHandler(init);
</script>
<form>
<input type="text" class="tabName" value="">
</form>
<a href="#" class='addTab'>タブ追加だよ</a>
<a href="#" class='removeTab'>タブ削除だよ</a>
<div id="result"></div>
]]>
</Content>
</Module>


な感じ。

これでタブも削除できるようになった。

.

[Apache Shindig][お勉強][OpenSocial] メモ104 tabs(1)

フィーチャーのtabsを見ていく。

ソースは、


features/tabs/tabs.js



で、これは何かというと、、

ガジェットにタブ付きのユーザー インターフェースを追加できます。

とのこと。

ほほう。
通常は、setprefsフィーチャーと一緒に使うのだそうだ。

さっそくやってみる。

<?xml version="1.0" encoding="UTF-8" ?>
<Module>
<ModulePrefs title="tabsフィーチャー">
<Require feature="opensocial-0.8" />
<Require feature="tabs" />
<Require feature="setprefs" />
<Require feature="jquery" />
</ModulePrefs>
<Content type="html" view="home,profile,canvas">
<![CDATA[
<script type="text/javascript">
var tabdayon = function() {
this.tabs = null;
this.tabDivId = {};
};
tabdayon.prototype.init = function() {
this.tabs = new gadgets.TabSet(__MODULE_ID__, "タブセットだよーん");
var oneId = this.tabs.addTab("One");
this.tabDivId = {};
this.tabDivId[oneId] = oneId;
var twoId = this.tabs.addTab("Two");
this.tabDivId[twoId] = twoId;
document.getElementById(oneId).innerHTML = "最初のタブのコンテンツだよ";
document.getElementById(twoId).innerHTML = "2番目のタブのコンテンツだよ";
};
/**
* タブの追加
*/
tabdayon.prototype.addTab = function(ev) {
var tabName = $('.tabName').val();
$('.tabName').val('');
var divId = this.tabs.addTab(tabName);
this.tabDivId[divId] = divId;
document.getElementById(divId).innerHTML = tabName + 'を追加したよ';
};
var tabInstance = null;
function init() {
tabInstance = new tabdayon();
tabInstance.init();
$('.addTab').click(function(ev) {tabInstance.addTab(ev);});
}

gadgets.util.registerOnLoadHandler(init);
</script>
<form>
<input type="text" class="tabName" value="">
</form>
<a href="#" class='addTab'>タブ追加だよ</a>
<div id="result"></div>
]]>
</Content>
</Module>


ってな感じ。

最初に表示させたときは感動した。。

さらにいろいろできるみたい。
詳細は、http://code.google.com/intl/ja/apis/gadgets/docs/ui.html#Tabs
を見ればよくわかると。

ちなみに<Require feature="jquery" />はgooホームでは使えるようだけど、
shindigをインストールしただけでは使えない。


.

[Apache Shindig][お勉強][OpenSocial] メモ103 サーバ側ひととおり触った

たぶんこれでサーバ側ひととおり触った、ような気がする。

次はgadgets APIを見ていく。


とにかくJavaScriptにはまった。。
.

2009年7月29日水曜日

[Apache Shindig][お勉強][OpenSocial] メモ102 opensocial.requestShareAppのサーバ側実装をしてみる(1)

opensocial.requestShareAppをサーバ側まで含めて動かしてみる。

方針は、
1) requestShareAppがコールされると、ダイアログを表示する。
2) ダイアログには友達一覧が表示され、requestShareApp先を選択できる。
3) ダイアログで「送信」ボタンを押下すると、messages.modifyがサーバ側へ発行される。
4) サーバ側はmessageサービスのmodifyメソッドにて処理する。
5) DBに保存。
といった感じ。

requestSendMessageと分けるために、msgCollIdにrequestShareAppから来たデータで
あることが分かるような値をセットする。

まずは
features/opensocial-jsonrpc/jsonrpccontainer.jsで、ガジェットと同じフレームで
実行されるrequestShareAppを定義。(前やったけどやりなおし。)


JsonRpcContainer.prototype.requestShareApp = function(recipientIds, reason,
opt_callback, opt_params) {
var callbackId = "cId_" + Math.random();

var self = this;
var callback = function(success, recip, title, body) {
var req = opensocial.newDataRequest();
var viewer = new opensocial.IdSpec({'userId' : 'VIEWER'});
var rpc = { method : "messages.modify" };
rpc.params = self.translateIdSpec(viewer);
rpc.params.appId = "@app";

FieldTranslations.translateNetworkDistance(viewer, rpc.params);

rpc.params.msgCollId = 'shareApp';
rpc.params.entity = {};
rpc.params.entity["title"] = title;
rpc.params.entity["body"] = body;
rpc.params.entity["recipients"] = recip

var shareAppRequest = new JsonRpcRequestItem(rpc);

req.add(shareAppRequest, 'key');
req.send(function(response) {
opt_callback(response.get('key'));
});
};
callbackIdStore[callbackId] = callback;

var body = gadgets.util.unescapeString(reason.getField(
opensocial.Message.Field.BODY));

if (!body || body.length === 0) {
var bodyMsgKey = gadgets.util.unescapeString(reason.getField(
opensocial.Message.Field.BODY_ID));
body = gadgets.Prefs.getMsg(bodyMsgKey);
}

/* 修正ここから */
/* 友達一覧を取得する */
var friendsArray = [];
var handleGetFriends = function(data) {
var friends = data.get("get_friends");
if (friends.hadError()) {
return;
}
var data = friends.getData();
data.each(function(friend) {
var tmp = {};
tmp.thumbnailUrl = friend.getField(opensocial.Person.Field.THUMBNAIL_URL);
tmp.nickname = friend.getField(opensocial.Person.Field.NICKNAME );
tmp.id = friend.getField(opensocial.Person.Field.ID );
friendsArray.push(tmp);
});
gadgets.rpc.call('..', 'shindig.requestShareApp',
null,
callbackId,
friendsArray,
body);
};
var getfriends = function() {
var params = {};
params[opensocial.DataRequest.PeopleRequestFields.PROFILE_DETAILS] = [
opensocial.Person.Field.ID,
opensocial.Person.Field.NICKNAME,
opensocial.Person.Field.THUMBNAIL_URL,
opensocial.Person.Field.PROFILE_URL
];
var req = opensocial.newDataRequest();
var idSpecParam = {};
idSpecParam[opensocial.IdSpec.Field.USER_ID] = opensocial.IdSpec.PersonId.VIEWER;
idSpecParam[opensocial.IdSpec.Field.GROUP_ID] = opensocial.IdSpec.GroupId.FRIENDS;
var idSpec = opensocial.newIdSpec(idSpecParam);

req.add(req.newFetchPeopleRequest(idSpec, params), "get_friends");
req.send(handleGetFriends);
};
getfriends();
};


な感じ。callbackはここでは実行せずcallbackIdStoreに入れておく。
親フレームでダイアログ表示後、「送信」ボタンが押下されたときに、
callbackを実行する。callbackではダイアログで選択された友達のID一覧を
受け取り、messages.modifyを発行する。
このcallbackは、親フレームからのクロスドメイン通信により、requestShareAppCallback_がコールされたときにコールされる。

callbackをつくり終わったら、友達一覧を取得する。
これは親フレームで表示するダイアログに友達一覧として出力するため。

で、友達一覧を取得し終わったら、
gadgets.rpc.callで親フレームのrequestShareAppをコールする。
次は親フレームのrequestShareApp。

gadgets.IfrGadgetService.prototype.requestShareApp = function(rpc, callbackId, friends, body) {
if (!gadgets.container.gadgetService.shareAppDialog_) {
return;
}
var title = gadgets.container.getGadget(this.getGadgetIdFromModuleId(rpc.f))
.title + 'はお勧めだよ!';
var shareAppDialog = gadgets.container.gadgetService.shareAppDialog_;
var onSubmit = function() {
var friendsIds = this.getData().shareAppDialog_friendsId;
var recipients = [];
if (typeof friendsIds === 'boolean') {
if (friendsIds) {
recipients = [friends[0].id];
}
}
else {
var len = friendsIds.length;
for (var ii=0; ii<len; ii++) {
if (friendsIds[ii]) {
recipients.push(friends[ii].id);
}
}
}
if (callbackId) {
window.setTimeout(function() {
gadgets.rpc.call(rpc.f,
'shindig.requestShareApp_callback',
null,
callbackId,
true,
recipients,
title,
body);
}, 0);
}
this.cancel();
};
var onCancel = function() {
if (callbackId) {
window.setTimeout(function() {
gadgets.rpc.call(rpc.f,
'shindig.requestShareApp_callback',
null,
callbackId,
false,
[],
title,
body);
}, 0);
}
this.cancel();
};
document.getElementById('shareAppDialog_title').innerHTML = title;
document.getElementById('shareAppDialog_body').innerHTML = body;
var ul = document.createElement('ul');
var fc = 0;
document.getElementById('shareAppDialog_friends').innerHTML = '';
for (xx in friends) {
var li = document.createElement('li');
li.innerHTML = ''
+ ''
+ '';
ul.appendChild(li);
fc++;
}
if (fc == 0) {
document.getElementById('shareAppDialog_friends').innerHTML = 'ひとりぼっちだよ';
}
else {
document.getElementById('shareAppDialog_friends').appendChild(ul);
}

shareAppDialog.setHeader('確認');
shareAppDialog.cfg.queueProperty("icon",YAHOO.widget.SimpleDialog.ICON_WARN);
shareAppDialog.cfg.queueProperty("buttons", [
{text: "送信", handler: onSubmit, isDefault: true},
{text: "キャンセル", handler: onCancel}
]);
shareAppDialog.registerForm();
shareAppDialog.render();
shareAppDialog.show();
};


とりあえず動かしたかったのでfiles/container/gadgets.jsに追加した。
ガジェット側フレームのrequestShareAppから渡された友達一覧を、
ダイアログに展開し、ダイアログを表示する。
「送信」ボタンが押下されたら、選択された友達のIDを配列にして、
ガジェット側フレームワークへクロスドメイン間通信により、渡す。


次に、ガジェット側requestShareApp_callback。
これはまた戻ってきてjsonrpccontainer.jsに記述した。

JsonRpcContainer.requestShareAppCallback_ = function(callbackId,
success, recipientIds, title, body) {
callback = callbackIdStore[callbackId];
if (callback && success) {
callbackIdStore[callbackId] = null;
callback(success, recipientIds, title, body);
}
};


こんだけ。
Shindigのデフォルト状態からちょっと修正。
requestShareAppで登録されたコールバックをコールするだけ。

これでサーバ側へmessages.modifyが発行される。

次にサーバ側。
サーバ側はrequestSendMessageを実装してみたときのまま。


で、実行すると、DBに登録された。

ちなみにDialogはYUIを使用。

そんだけ。
.

[Apache Shindig][お勉強][OpenSocial] メモ101 requestShareAppを動かす

なんだかshindig-1.1-SNAPSHOTだと動かないっぽいので、
動かす。

まず、jsonrpccontainer.jsを見てみる。



JsonRpcContainer.prototype.requestShareApp = function(recipientIds, reason,
opt_callback, opt_params) {
var callbackId = "cId_" + Math.random();
callbackIdStore[callbackId] = opt_callback;

var body = gadgets.util.unescapeString(reason.getField(
opensocial.Message.Field.BODY));

if (!body || body.length === 0) {
var bodyMsgKey = gadgets.util.unescapeString(reason.getField(
opensocial.Message.Field.BODY_ID));
body = gadgets.Prefs.getMsg(bodyMsgKey);
}

gadgets.rpc.call('..', 'shindig.requestShareApp',
null,
callbackId,
recipientIds,
body);
};


opensocial.requestShareAppがコールされると、この関数がコールされる。
関数の最後にgadgets.rpc.callで、親の'shindig.requestShareApp'がコールされている。

ということで、今度は、gadgets.jsを見てみる。

gadgets.IfrGadgetService = function() {
var self = this;
gadgets.GadgetService.call(this);
gadgets.rpc.register('resize_iframe', this.setHeight);
gadgets.rpc.register('set_pref', this.setUserPref);
gadgets.rpc.register('set_title', this.setTitle);
gadgets.rpc.register('requestNavigateTo', this.requestNavigateTo);
};


となっていて、shindig.requestShareAppの登録はされていない。
ま、そりゃそうか。

jsonrpccontainer.jsも、gadgets.jsも、本来自分で実装すべきところっぽい。
だからサンプルが動く程度の実装しかされていないんで、「追加」という形で実装してみる。

呼び出し元のパラメータは

gadgets.rpc.call('..', 'shindig.requestShareApp',
null,
callbackId,
recipientIds,
body);

となっているので、
gadgets.rpc.registerで記述すべきは、

gadgets.rpc.register('shindig.requestShareApp', function(callbackId,recipientIds,body) {
self.requestShareApp.apply(self,[this, callbackId, recipientIds, body]);
});

と、こんな感じか。

でrequestShareAppの実装。

gadgets.IfrGadgetService.prototype.shareAppDialog_ = null;
gadgets.IfrGadgetService.prototype.setShareAppDialog = function(dialog) {
gadgets.container.gadgetService.shareAppDialog_ = dialog;
};
gadgets.IfrGadgetService.prototype.requestShareApp = function(rpc, callbackId, recipients, body) {
if (gadgets.container.gadgetService.shareAppDialog_) {
/* 以下YUI前提 */
var onSubmit = function() {
if (callbackId) {
window.setTimeout(function() {
gadgets.rpc.call(rpc.f, 'shindig.requestShareApp_callback', null, callbackId, true, recipients, body);
}, 0);
}
this.cancel();
};
var onCancel = function() {
if (callbackId) {
window.setTimeout(function() {
gadgets.rpc.call(rpc.f, 'shindig.requestShareApp_callback', null, callbackId, false, recipients, body);
}, 0);
}
this.cancel();
};
gadgets.container.gadgetService.shareAppDialog_.cfg.queueProperty("buttons", [
{text: "送信", handler: onSubmit, isDefault: true},
{text: "キャンセル", handler: onCancel}
]);
document.getElementById('shareAppDialog_body').innerHTML = body;
gadgets.container.gadgetService.shareAppDialog_.render();
gadgets.container.gadgetService.shareAppDialog_.show();
}
else {
if (callbackId) {
window.setTimeout(function() {
gadgets.rpc.call(rpc.f, 'shindig.requestShareApp_callback', null, callbackId, true, recipients, body);
}, 0);
}
}
};


と、ざっくりこんな感じ。馬鹿の一つ覚えでYUIをまた使用。
親側でダイアログを出して、子フレーム側のrequestShareApp_callbackをコールできればOK。
shindigのままだと、bodyをコールバックに渡せないので、
jsonrpccontainer.jsの法をちょっとだけいじる。

JsonRpcContainer.requestShareAppCallback_ = function(callbackId,
success, recipientIds, body) {
callback = callbackIdStore[callbackId];
if (callback) {
callbackIdStore[callbackId] = null;

var data = null;
if (recipientIds) {
data = {'recipientIds': recipientIds};
}

var responseItem = new opensocial.ResponseItem(null, data, opensocial.ResponseItem.Error.BAD_REQUEST);
callback(responseItem);
}
};


こんな感じ。今のところはエラーを返す。

これでよいはず。

で動かした。
で動いた。

BAD_REQUESTになった。
次はサーバとの連携。
--
と思ったけど、何しよう。
.

[Apache Shindig][お勉強][OpenSocial] メモ100 ガジェットを友達に勧めるだけのガジェット

opensocial.requestShareAppを使ってみる。

とりあえずガジェットXML。


<?xml version="1.0" encoding="UTF-8" ?>
<Module>
<ModulePrefs title="opensocial.requestShareApp">
<Require feature="opensocial-0.8" />
</ModulePrefs>
<Content type="html" view="home,profile,canvas">
<![CDATA[
<script type="text/javascript">
function shareApp() {
opensocial.requestShareApp(
"VIEWER_FRIENDS",
opensocial.newMessage("ぼよよーん"),
call_back);
}
function call_back(status) {
if (status.hadError()) {
document.getElementById('result').innerHTML = '失敗したよ:' + status.getErrorCode();
} else {
document.getElementById('result').innerHTML = '送ったよ';
}
}

</script>
<div id="result"></div>
<input type="button" value="このガジェットを友達と共有する" onclick="shareApp();" /><br />
]]>
</Content>
</Module>



な感じ。
今のところ、ボタンを押下すると、

Unknown RPC service: shindig.requestShareApp

といわれる。

さて、調べよう。。
.

[Apache Shindig][お勉強][OpenSocial] メモ99 opensocial.requestShareAppって何よ

goo Developer's キッチンによると、


opensocial.requestShareAppを使うことで、ビューアーが友達にガジェットを教えることができます。


とのこと。

ほほう。

opensocial-0.8のリファレンスによると、

指定したユーザーとこのガジェットを共有するようにコンテナに要求します。


とのこと。

なんだかよくわからない。
いきなり友達のところにガジェットをインストールしちゃうわけにはいかないから
gooホームみたいな感じがいいのかしらん。
.

[Apache Shindig][お勉強][OpenSocial] メモ98 opensocial.requestSendMessageのサーバ側実装をしてみる(2)

とりあえず、サーバ側の実装してみた。

requestSendMessage時に発行するmethodはmessages.modifyにした。

で対応するサービスは、MessageServiceのcreateMessage。
messages.modifyがクライアントから飛んでくると、必ずcreateMessageが呼ばれる、
のではなく、msgCollIdなるパラメータに何かしらの値が入っているとき、
のみらしい。

msgCollIdというのは、どうもMessageを束ねるもののIDらしい。よくわかんないけど。
とりあえず使わないので考えない。
クライアントからはダミーのmsgCollIdを送付するようにして、messages.modifyのときは
必ずcreateMessageが呼ばれるようにした。

で、以下createMessageの実装。


public Future<Void> createMessage(UserId userId, String appId, String msgCollId, Message message,
SecurityToken token) throws ProtocolException {
logic.createMessage(userId, appId, msgCollId, message, token);
return ImmediateFuture.newInstance(null);
}


こんだけ。
何も考えずにlogicへパス。

でlogic。

public void createMessage(UserId userId, String appId, String msgCollId, Message message,
SecurityToken token) throws ProtocolException {
logger.info("メッセージ生成開始:");
String viewerId = token.getViewerId();
String ownerId = token.getOwnerId();
GmsPerson viewer = null;
try {
if (StringUtils.hasText(viewerId)) {
viewer = gmsPersonDao.selectByLoginId(viewerId);
}
}
catch (ObjectRetrievalFailureException ex) {
}

if (viewer == null) {
if (logger.isDebugEnabled()) {
logger.debug("Guestは送信できない:");
}
throw new ProtocolException(HttpServletResponse.SC_FORBIDDEN,
"Guestのため、送信できない");
}
if (! isInstalledGadget(viewer, token.getModuleId())) {
if (logger.isDebugEnabled()) {
logger.debug("ガジェット未インストール:"
+ "viewerId:[" + viewer.getLoginId() + "]:"
+ "ガジェットID:[" + token.getModuleId() + "]"
);
}
throw new ProtocolException(HttpServletResponse.SC_FORBIDDEN,
"未インストールのため、送信できない:loginid:[" + viewer.getLoginId() + "]");
}
GmsPerson owner = gmsPersonDao.selectByLoginId(ownerId);
String fromLoginId = userId.getUserId(token);
GmsPerson fromUser = gmsPersonDao.selectByLoginId(fromLoginId);

for (String recip: message.getRecipients()) {
GmsMessage gmsMessage = new GmsMessage();
gmsMessage.setId(UuidGenerator.compress(UuidGenerator.generate()));
gmsMessage.setFromPersonId(fromUser.getId());
UserId toUserId = new UserId(UserId.Type.jsonValueOf(recip), recip);
String recipLoginId = toUserId.getUserId(token);
GmsPerson recipPerson = gmsPersonDao.selectByLoginId(recipLoginId);
gmsMessage.setToPersonId(recipPerson.getId());
if (StringUtils.hasText(message.getBody())) {
gmsMessage.setBody(message.getBody());
}
else {
gmsMessage.setBody("");
}
gmsMessage.setBodyId("");
if (StringUtils.hasText(message.getTitle())) {
gmsMessage.setTitle(message.getTitle());
}
else {
gmsMessage.setTitle("");
}
gmsMessage.setTitleId("");
gmsMessage.setType(message.getType().toString());
gmsMessage.setCreatedAt(new Timestamp(System.currentTimeMillis()));
gmsMessageDao.insert(gmsMessage);
}
logger.info("メッセージ生成終了:");
}


へっぽこだけどまぁいいや。
ただ単にmessageテーブルにinsert。
チェックとは考えてない。

lockedDomain機能を有効にしてガジェット毎に別ドメインにしているので、
認証済みクッキーは飛んでこない。なので、securityTokenのみでチェックしている。

で、実行と。

ちゃんとDBに保存された。

.

[Apache Shindig][お勉強][OpenSocial] メモ97 messagesサービスのmethod

requestSendMessageに対応するサービスはmessages.createかと思いきや、
そうとは限らないっぽい。

OpenSocialでは特に規定が無いので、
実装しやすそうな
messages.modify
を使用しようかと。

で、その際のjsonrpccontainer.jsのrequestSendMessageの今のところの実装。


JsonRpcContainer.prototype.requestSendMessage = function(recipientIds, message, opt_callback, opt_params) {
opt_callback = opt_callback || function(){};
opt_params = opt_params || {}; /* 使わないよ */

var body = gadgets.util.unescapeString(message.getField(
opensocial.Message.Field.BODY));
var title = gadgets.util.unescapeString(message.getField(
opensocial.Message.Field.TITLE));
var type = gadgets.util.unescapeString(message.getField(
opensocial.Message.Field.TYPE));

if (!body || body.length === 0) {
var bodyMsgKey = gadgets.util.unescapeString(message.getField(
opensocial.Message.Field.BODY_ID));
body = gadgets.Prefs.getMsg(bodyMsgKey);
}
if (!title || title.length === 0) {
var titleMsgKey = gadgets.util.unescapeString(message.getField(
opensocial.Message.Field.TITLE_ID));
title = gadgets.Prefs.getMsg(titleMsgKey);
}
var self = this;
var callback = function() {
var req = opensocial.newDataRequest();
var viewer = new opensocial.IdSpec({'userId' : 'VIEWER'});
var rpc = { method : "messages.modify" };
rpc.params = self.translateIdSpec(viewer);
rpc.params.appId = "@app";

FieldTranslations.translateNetworkDistance(viewer, rpc.params);

rpc.params.msgCollId = "dummyMsgCollId";
rpc.params.entity = {};
rpc.params.entity["title"] = title;
rpc.params.entity["body"] = body;
rpc.params.entity["type"] = type;
rpc.params.entity["recipients"] = self.translateIdSpec(self.makeIdSpec(recipientIds))["userId"];

var messageRequest = new JsonRpcRequestItem(rpc);

req.add(messageRequest, 'key');
req.send(function(response) {
opt_callback(response.get('key'));
});
};
var callbackId = "cId_" + Math.random();
callbackIdStore[callbackId] = callback;
gadgets.rpc.call(null, 'requestSendMessage',
null,
callbackId,
recipientIds,
title,
body);
};


ってな感じ。
サーバ側のサービスを実装してみないとわからないけど、
今度こそ、とりあえずはrequestSendMessageは動作させることができそう。

はてさて。

.

[Apache Shindig][お勉強][OpenSocial] メモ96 messagesサービスのmethod

JSON-RPCで使用するmessagesサービスのmethod一覧。

* messages.delete
* messages.get
* messages.modify
* messages.create

ソースから抽出。

ドキュメントは見てない。
--
messages系はOpenSocialの仕様じゃないっぽい。

.

2009年7月28日火曜日

[Apache Shindig][お勉強][OpenSocial] メモ95 opensocial.requestSendMessageのサーバ側実装をしてみる(1)

opensocial.requestSendMessageの実装をしてみる。

とりあえず、JavaScript。
方針
ざっと実装方針。
GadgetXML内でrequestSendMessageがコールされると、
ダイアログが出て、送信していいか確認する。
確認後、message.postmessages.createを発行する。
ってな感じ。
ダイアログはYUIをしてみる。

ガジェットXML
ガジェットXMLはこんな感じ。


<?xml version="1.0" encoding="UTF-8" ?>
<Module>
<ModulePrefs title="opensocial.Message">
<Require feature="opensocial-0.8" />
</ModulePrefs>
<Content type="html" view="home,profile,canvas">
<![CDATA[
<script type="text/javascript">
function sendMessage() {
var opt_params = [];
opt_params[opensocial.Message.Field.TITLE] = 'タイトルだよーん';
opt_params[opensocial.Message.Field.TYPE] = opensocial.Message.Type.PRIVATE_MESSAGE;
var body = 'テストメッセージだよーん';
var msg = opensocial.newMessage(body, opt_params);
opensocial.requestSendMessage(opensocial.IdSpec.PersonId.OWNER, msg, call_back);
}

function call_back(status) {
if (status.hadError()) {
document.getElementById('result').innerHTML = '失敗したよ:' + status.getErrorCode();
} else {
document.getElementById('result').innerHTML = '送ったよ';
}
}

</script>
<div id="result"></div>
<input type="button" value="メッセージ送信" onclick="sendMessage();" /><br />
]]>
</Content>
</Module>


TITLEとBODYをセットしてrequestSendMessageをコールするだけ。

サーバ側ContainerのJavaScript
さて、1.1-SNAPSHOTのデフォルトだとrequestSendMessageは、NOTIMPLEMENTを返すだけ
なんだけど、どうも動作しない。
動作しないものはほっといて、jsonrpccontainer.jsなどをいじくってみる。
以下いじった結果。


GadgetからrequestSendMessageがコールされると、jsonrpccontainer.jsの
requestSendMessageが呼ばれるようにする。
Gadgetはiframeの中にいて、jsonrpccontainer.jsも同じiframeの中にいる。
jsonrpccontainer.jsのrequestSendMessageは、ダイアログを出すために、
iframeの親のrequestSendMessageをコールする。ここでgadgets.rpcを使用する。
親のrequestSendMessageではYUIのダイアログを表示後、OKを押下されたなら、
今度はiframeの方のrequestSendMessageCallback_関数をコールする。
ここでもgadgets.rpcを使用。

ということで、まずjsonrpccontainer.jsのrequestSendMessage。

JsonRpcContainer.prototype.requestSendMessage = function(recipientIds, message, opt_callback, opt_params) {
opt_callback = opt_callback || function(){};
opt_params = opt_params || {}; /* 使わないよ */

var body = gadgets.util.unescapeString(message.getField(
opensocial.Message.Field.BODY));
var title = gadgets.util.unescapeString(message.getField(
opensocial.Message.Field.TITLE));

if (!body || body.length === 0) {
var bodyMsgKey = gadgets.util.unescapeString(message.getField(
opensocial.Message.Field.BODY_ID));
body = gadgets.Prefs.getMsg(bodyMsgKey);
}
if (!title || title.length === 0) {
var titleMsgKey = gadgets.util.unescapeString(message.getField(
opensocial.Message.Field.TITLE_ID));
title = gadgets.Prefs.getMsg(titleMsgKey);
}
var self = this;
var callback = function() {
var req = opensocial.newDataRequest();
var viewer = new opensocial.IdSpec({'userId' : 'VIEWER'});
var rpc = { method : "messages.create" };
rpc.params = self.translateIdSpec(viewer);
rpc.params.appId = "@app";

FieldTranslations.translateNetworkDistance(viewer, rpc.params);

/* ここから、とりあえず版。後でちゃんとする */
rpc.params.message = {};
rpc.params.message["title"] = title;
rpc.params.message["body"] = body;
rpc.params.message["recipients"] = self.translateIdSpec(self.makeIdSpec(recipientIds))["userId"];
/* ここまで、とりあえず版。後でちゃんとする */

var messageRequest = new JsonRpcRequestItem(rpc);

req.add(messageRequest, 'key');
req.send(function(response) {
opt_callback(response.get('key'));
});
};

var callbackId = "cId_" + Math.random();
callbackIdStore[callbackId] = callback;
gadgets.rpc.call(null, 'requestSendMessage',
null,
callbackId,
recipientIds,
title,
body);
};


callbackIdStoreにcallbackを入れておきたいので、
callbackIdStoreが定義されている、

(function() {
})();

の中に定義。
実際にサーバにデータを送信するコードをコールバックとして生成し、
gadgets.rpc.callを読んで親の'requestSendMessage'をコールする。
ちなみに、gadgets.rpc.callの第一引数がnullになっているが、
これは(というか'..'というかルートというか)
という意味。

で、クロスドメイン間通信で親'requestSendMessage'がコールされると。
で以下親の方のrequestSendMessage。

gadgets.IfrGadgetService.prototype.requestSendMessage = function(rpc, callbackId, recipients, title, body) {
if (gadgets.container.gadgetService.sendMessageDialog_) {
/* 以下YUI前提 */
var onSubmit = function() {
if (callbackId) {
window.setTimeout(function() {
gadgets.rpc.call(rpc.f, 'requestSendMessage_callback', null, callbackId, recipients, title, body);
}, 0);
}
this.cancel();
};
var onCancel = function() {
this.cancel();
};
gadgets.container.gadgetService.sendMessageDialog_.cfg.queueProperty("buttons", [
{text: "OK", handler: onSubmit, isDefault: true},
{text: "キャンセル", handler: onCancel}
]);
document.getElementById('sendMessageDialog_title').innerHTML = title;
document.getElementById('sendMessageDialog_body').innerHTML = body;
gadgets.container.gadgetService.sendMessageDialog_.render();
gadgets.container.gadgetService.sendMessageDialog_.show();
}
else {
if (callbackId) {
window.setTimeout(function() {
gadgets.rpc.call(rpc.f, 'requestSendMessage_callback', null, callbackId, recipients, title, body);
}, 0);
}

}
};


な感じ。
多分これじゃだめで、jsonrpccontainer.jsのrequestSendMessageをコールした際に
積み上げたcallbackIdStoreをクリアさせなきゃいけない。
とりあえず、おいといて、
gadgets.container.gadgetService.sendMessageDialog_
というのはYUIのダイアログ。
"OK"ボタンが押下されると、

gadgets.rpc.call(rpc.f, 'requestSendMessage_callback', null, callbackId, recipients, title, body);

がコール。
gadgets.rpc.callの第一引数にrpc.fが指定されているが、これは
呼び出し元のwindow.nameが入っている。
意味的には、gadgetの'requestSendMessage_callback'をコールせよ、といった感じか。

gadgets.IfrGadgetService.prototype.requestSendMessage
のパラメータはGadget内でのgadgets.rpc.call時と違うんだけど、これは

gadgets.rpc.register('requestSendMessage', function(callbackId, recipients, title, body) {
self.requestSendMessage.apply(self, [this, callbackId, recipients, title, body]);
});

と定義したから。
第一引数に呼び出し元を判断するために、rpcがわたるようになっている。

でrequestSendMessage_callbackは、、

JsonRpcContainer.requestSendMessageCallback_ = function(callbackId, recipients, title, body) {
var callback = callbackIdStore[callbackId];
if (callback) {
callbackIdStore[callbackId] = null;
callback();
}
};


こんだけ。で完了。

で実行してみると、サーバ側にリクエストが飛ぶ。
今はまだサーバ側の実装はしていないので、

org.apache.shindig.protocol.ProtocolException: The method message.post is not implemented
at jp.qsdn.gms.social.protocol.SpringHandlerRegistry.getRpcHandler(SpringHandlerRegistry.java:137)


ってなExceptionが出ている。
message.postというものは0.8.1には無い??っぽいので、
message.postというものをハンドラから実装してみる。

messages.createが正解。ハンドらから実装する必要なかった。。


ちなみに、
"message.post"という文字列などは、
http://www.mail-archive.com/shindig-dev@incubator.apache.org/msg08286.html
を参考にした。


--
間違えた。
message.postじゃなくて、messages.post。
ちゃんと0.8.1で動くっぽい。
0.8.1じゃなく、Apache Shindig-1.0とのこと。
OpenSocialの仕様にはsendしかないらしい。

--
さらに間違えた。
messages.postじゃなくて、messages.create。
.

[Apache Shindig][お勉強][OpenSocial] はまった

gadgets.rpcを使って、子フレームから親フレームの関数をコールすることは簡単にできた。

で、親フレームから子フレームの関数をコールしてみたら・・・、
できない!!

でほぼ丸1日費やした・・。
goo ホームのサンドボックス見てみたり、外人さんのページみたり、
ソースのあっちゃこっちゃに


if (window['console'] && window['console']['log']) {
window['console']['log']('あへあへ');
}

なんてのを入れたり。console.logって、「へー、こうやってやるんだー」なんて思ったり。



結局スペルミス。。


でもおかげでクロスドメインのフレーム間通信はかなり分かった、と思う。

いじょう。
.

2009年7月27日月曜日

[Apache Shindig][お勉強][OpenSocial] メモ94 lockedDomainを有効にする

gadgets.rpcを理解するにあたって、別ドメイン別フレームという環境がほしくなった。

そこで、ShindigのlockedDomain機能を有効にしてみた。

以下手順。
1) container.jsで、lockedDomainを有効にする。
container.jsのgadgets.lockedDomainRequiredとgadgets.lockedDomainSuffixを有効にすべく
設定する。以下設定後の値。


"gadgets.lockedDomainRequired" : true,
"gadgets.lockedDomainSuffix" : ".gadget.example.com"

期待するdomainは、

<変な値>.gadget.example.com

として、<変な値>はgadgetXMLと一対一の値。

Shindigとしては、GadgetXMLのRequireタグにlocked-domainフィーチャーが指定されている
と、lockedDomain機能が有効になるが、それはまた別のお話。

2) /gadgets/metadataリクエスト経由でiframeに渡すURLを取得する。
参考になるのはShindig付属のsamplecontainer.js。
これをみて、/gadgets/metadataへリクエストし、gadgetのiframeUrlを取得する。
取得したiframeUrlは

//<変な値>.gadget.example.com/gadgets/ifr・・・・・

と先頭が'//'で始まっているかもしれない。そんときは、現在のschemeを付加してやればよい。

で、上記iframeUrlをiframeタグのsrcに指定してあげる。
これでクライアント側はOKのはず。

3) DNSでワイルドカード指定

<変な値>.gadget.example.comで、IPが返るようにDNSでワイルドカード指定する。
bindでもtinydnsでも可能。

4) Apache mod_proxy

もしApache mod_proxyを使用しているなら、
ShindigでlockedDomain機能を有効にしている場合、もともとのHostヘッダを見て
チェックするので、

ProxyPreserveHost On


と設定してやる必要がある。


これでガジェット毎に別ドメインになっていい感じ。

さて、locked-domain機能だけど、
gadgetごとに決まったURLじゃないとInvalid domainを返す機能・・・
で合っているかどうかは良くわからない。

ちなみに<変な値>の生成ロジックは以下のとおり。
1) gadgetXMLファイルへのURLからSHA1でハッシュ値を求める。
2) 1)で求めたSHA1ハッシュ値をBase64ではなく、Base32で文字列化
3) 2)で求めた文字列にcontainer.jsのgadgets.lockedDomainSuffixで指定した文字列を単純に連結。

な感じ。

なのでわざわざ/gadgets/metadata経由でDefaultUrlGeneratorを使って生成しなくても、
自前でもっと簡単に生成しても別にいい感じ。


そんだけ。
.

[Apache Shindig][お勉強][OpenSocial] メモ93 ふと

Apache Shindigに関して、バグが多いという記事を良く見かける。

多分、そもそもの前提が間違っている気がする。

Apache Shindigのデフォルト実装では、たいしたことしていない
というか、サンプル程度の実装しかしていない
というか、手抜きっぽい。
1.1はまだつくり中で0.9の仕様とそもそも合っていないっぽい。
中途半端に手が入っているようで、そもそも動作しない機能もあったりするみたい。

だから、Apache Shindigをダウンロードしてきて、インストールした
だけで、バグが多いというのは、ちょっと違うと思う。

Apache Shindigを使って実装してみて、はじめてバグが多いかどうか判断できるのではないかなー。

なんとなく、Shindigの中身みてて思ったよ。


--
Shindig試すなら、「OpenSocial入門」に書かれてあるpartuzaを使うか、
OpenPNEを使うか、とかじゃないのかなー。


.