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

2009年8月21日金曜日

[mod_chxj] mod_chxjでFlashを使う

今のところ、mod_chxjにはFlashに関して、特になんの機能もないので、
chxj:ifタグを使用して以下のようにする。


<chxj:if lang="xhtml" lang="chtml" lang="hdml" lang="jhtml" lang="jxhtml">
<object data="/swf/top_logo.swf" type="application/x-shockwave-flash" width="240" height="100">
<param name="bgcolor" value="#000000">
<param name="loop" value="on">
<param name="quality" value="high">
</object>
</chxj:if>
 


っと。

そんだけ。
.

2009年8月3日月曜日

[Apache Shindig][お勉強][OpenSocial] メモ120 shindigで3legged OAuthなるものをしてみる

OAuthには、2legged OAuthなるものと、3legged OAuthなるものがあるらしい。
で、署名付きリクエストは、2legged OAuthというらしい。

2legged OAuthはHMACもRSAもやってみたので、次は3legged OAuthなるものをやってみる。
サービスプロバイダはGoogle。
サンプルとしてGoogle Contact(だっけ?)にアクセスして
自分で構築したShindig環境でアドレス張のデータを表示してみる。

おおきな作業の流れは以下な感じ。
1) ガジェットを用意
2) とりあえずShindigを動かして、該当するガジェット別のdomainを取得
3) google アカウントのManageDomainsページにアクセスし、2)で取得したドメインを登録
4) 3)で登録するとconsumer keyとconsumer secretが発行されるので、それをconfig/oauth.jsonへ記述。
5) ガジェットXMLの一部修正
6) shindigコンパイル&起動
でできるはず。


1) まずガジェットXMLを用意。

http://code.google.com/intl/ja/apis/gadgets/docs/oauth.html
のページを見ると、gadgetのサンプルXMLがあるので、それをコピペ。


<?xml version="1.0" encoding="UTF-8" ?>
<Module>
<ModulePrefs title="OAuth Contacts" scrolling="true">
<Require feature="opensocial-0.8" />
<Require feature="locked-domain"/>
<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.google.com/m8/feeds/" method="GET" />
<Authorization url="https://www.google.com/accounts/OAuthAuthorizeToken?oauth_callback=http://oauth.gmodules.com/gadgets/oauthcallback" />
</Service>
</OAuth>
</ModulePrefs>
<Content type="html">
<![CDATA[

<!-- shindig oauth popup handling code -->
<script src="http://gadget-doc-examples.googlecode.com/svn/trunk/opensocial-gadgets/popup.js"></script>

<style>
#main {
margin: 0px;
padding: 0px;
font-size: small;
}
</style>

<div id="main" style="display: none">
</div>

<div id="approval" style="display: none">
<img src="http://gadget-doc-examples.googlecode.com/svn/trunk/images/new.gif">
<a href="#" id="personalize">Personalize this gadget</a>
</div>

<div id="waiting" style="display: none">
Please click
<a href="#" id="approvaldone">I've approved access</a>
once you've approved access to your data.
</div>

<script type="text/javascript">
// Display UI depending on OAuth access state of the gadget (see <divs> above).
// If user hasn't approved access to data, provide a "Personalize this gadget" link
// that contains the oauthApprovalUrl returned from makeRequest.
//
// If the user has opened the popup window but hasn't yet approved access, display
// text prompting the user to confirm that s/he approved access to data. The user
// may not ever need to click this link, if the gadget is able to automatically
// detect when the user has approved access, but showing the link gives users
// an option to fetch their data even if the automatic detection fails.
//
// When the user confirms access, the fetchData() function is invoked again to
// obtain and display the user's data.
function showOneSection(toshow) {
var sections = [ 'main', 'approval', 'waiting' ];
for (var i=0; i < sections.length; ++i) {
var s = sections[i];
var el = document.getElementById(s);
if (s === toshow) {
el.style.display = "block";
} else {
el.style.display = "none";
}
}
}

// Process returned JSON feed to display data.
function showResults(result) {
showOneSection('main');

var titleElement = document.createElement('div');
var nameNode = document.createTextNode(result.feed.title.$t);
titleElement.appendChild(nameNode);
document.getElementById("main").appendChild(titleElement);
document.getElementById("main").appendChild(document.createElement("br"));

list = result.feed.entry;

for(var i = 0; i < list.length; i++) {
entry = list[i];
var divElement = document.createElement('div');
divElement.setAttribute('class', 'name');
var valueNode = document.createTextNode(entry.gd$email[0].address);
divElement.appendChild(nameNode);
divElement.appendChild(valueNode);
document.getElementById("main").appendChild(divElement);
}
}

// Invoke makeRequest() to fetch data from the service provider endpoint.
// Depending on the results of makeRequest, decide which version of the UI
// to ask showOneSection() to display. If user has approved access to his
// or her data, display data.
// If the user hasn't approved access yet, response.oauthApprovalUrl contains a
// URL that includes a Google-supplied request token. This is presented in the
// gadget as a link that the user clicks to begin the approval process.
function fetchData() {
var params = {};
url = "http://www.google.com/m8/feeds/contacts/default/base?alt=json";
params[gadgets.io.RequestParameters.CONTENT_TYPE] = gadgets.io.ContentType.JSON;
params[gadgets.io.RequestParameters.AUTHORIZATION] = gadgets.io.AuthorizationType.OAUTH;
params[gadgets.io.RequestParameters.OAUTH_SERVICE_NAME] = "google";
params[gadgets.io.RequestParameters.OAUTH_USE_TOKEN] = "always";
params[gadgets.io.RequestParameters.METHOD] = gadgets.io.MethodType.GET;

gadgets.io.makeRequest(url, function (response) {
if (response.oauthApprovalUrl) {
// Create the popup handler. The onOpen function is called when the user
// opens the popup window. The onClose function is called when the popup
// window is closed.
var popup = shindig.oauth.popup({
destination: response.oauthApprovalUrl,
windowOptions: null,
onOpen: function() { showOneSection('waiting'); },
onClose: function() { fetchData(); }
});
// Use the popup handler to attach onclick handlers to UI elements. The
// createOpenerOnClick() function returns an onclick handler to open the
// popup window. The createApprovedOnClick function returns an onclick
// handler that will close the popup window and attempt to fetch the user's
// data again.
var personalize = document.getElementById('personalize');
personalize.onclick = popup.createOpenerOnClick();
var approvaldone = document.getElementById('approvaldone');
approvaldone.onclick = popup.createApprovedOnClick();
showOneSection('approval');
} else if (response.data) {
showOneSection('main');
showResults(response.data);
} else {
// The response.oauthError and response.oauthErrorText values may help debug
// problems with your gadget.
var main = document.getElementById('main');
var err = document.createTextNode('OAuth error: ' +
response.oauthError + ': ' + response.oauthErrorText);
main.appendChild(err);
showOneSection('main');
}
}, params);
}
// Call fetchData() when gadget loads.
gadgets.util.registerOnLoadHandler(fetchData);
</script>
]]>
</Content>
</Module>


コピペしただけ。
hogehoge.xmlとして保存する。
外部からhttp://www.example.com/opensocial/hogehoge.xmlとして見える場所に設置。


2) とりあえずShindigを動かして、該当するガジェット別のdomainを取得
lockedDomain機能を有効にしているので、gadgetXML毎のiframeのdomainが分からない。
SHA1の結果をBase32してみても良いんだけど、Shindig動かした方が楽しかったので、
そうした。

表示された結果のソースファイルを見てみると、、

http://59vs5qn45f2fqulv6shuu20n9v6ig218.gadget.example.com/gadgets/ifr?url=http://www.example.com/opensocial/hogehoge.xml

などとiframeのsrc属性だったので、ホスト名を抜き出す。
すると、

59vs5qn45f2fqulv6shuu20n9v6ig218.gadget.example.com

なんてなものが抜き出せる。


3) google アカウントのManageDomainsページにアクセスし、2)で取得したドメインを登録

2)で抜き出したgadgets毎のiframeのドメインをgoogleアカウントのmanageDomainsから登録する。
URLはhttps://www.google.com/accounts/ManageDomains
で、「Add a New Domain」ってところから2)で取得した

59vs5qn45f2fqulv6shuu20n9v6ig218.gadget.example.com

を登録。
Googleからverifyなリクエストが飛んでくるので、とある決まったファイルを置いておく。
(登録時にGoogleの画面に従えばOK)



4) 3)で登録するとconsumer keyとconsumer secretが発行されるので、それをconfig/oauth.jsonへ記述。
3)で登録完了すると、

OAuth Consumer Key: 59vs5qn45f2fqulv6shuu20n9v6ig218.gadget.example.com
OAuth Consumer Secret: hogehogehoge

などと表示されるので、それをshindigのconfig/oauth.jsonへ記述する。
記述したoauth.jsonは以下な感じ。

{
"http://www.example.com/opensocial/hello.xml" : {
"google" : {
"consumer_key" : "59vs5qn45f2fqulv6shuu20n9v6ig218.gadget.example.com",
"consumer_secret": "hogehogehoge",
"key_type" : "HMAC_SYMMETRIC"
}
}
}

3行目の"google"は、ガジェットの<Service name="google">と合わせておくっぽい。


5) ガジェットXMLの一部修正

Googleからコピーしてきたガジェットを修正する。
ガジェットXMLの10行目、

<Authorization url="https://www.google.com/accounts/OAuthAuthorizeToken?oauth_callback=http://oauth.gmodules.com/gadgets/oauthcallback" />

というところを

<Authorization url="https://www.google.com/accounts/OAuthAuthorizeToken" />

とoauth_callback以降を削除。
とりあえず、今はコールバックいらないんで。
また、shindig.propertiesのshindig.signing.global-callback-urlも空にセット。


6) shindigコンパイル&起動
そしたら、shindigをコンパイル&起動する。
で、ガジェットを表示してみる。

すると画面に

Personalize this gadget

というリンクが表示されるんで、クリックする。
すると、別Windowが開いて、Googleにログインしていなければ、Googleのログイン画面が表示される。
ログインすると、

The site 59vs5qn45f2fqulv6shuu20n9v6ig218.gadget.example.com is requesting access to your Google Account for the product(s) listed below.

なんて書かれたページが表示される。そのページの「Grant access」ボタンを押下。
ボタンを押下すると、

You have successfully granted 59vs5qn45f2fqulv6shuu20n9v6ig218.gadget.example.com access to your Google Account. You can revoke access at any time under 'My Account'.

なんて書かれたページが表示される。このWindowはもういらないので、閉じる。

するとあら不思議。
ガジェットにメールアドレス一覧が表示されているではありませんか!


そんだけ。

--
まだいろいろ試したわけじゃないので、なんとも言えないが、
ガジェットを表示する際のOWNERとVIEWERをGoogleのアカウント名と合わせておかないと
駄目かも。
--
OWNERを合わせておかないと、ダメかも。
OWNERが違うときにはGoogle側でエラーになる。
--
ちなみに上記は、Shindig-1.1-SNAPSHOTでやった。
(Java版)

--(2009/08/11)
OwnerIdとViewerIdが違うとShindigでエラーになる。
OAuthRequest.javaの320行目あたり参照。
.

2009年8月2日日曜日

[その他][CentOS] CentOS

http://ssig33.ddo.jp/blog/2009-07-31-1.html

へぇ。
CentOSやばいんだ。。

.

[Apache Shindig][お勉強][OpenSocial] メモ119 gadgets.io.makeRequest 認証認可タイプSIGNEDをやってみる(MHAC-SHA1で署名)

ついでにMHAC-SHA1でのSIGNEDもやってみる。

MHACはパスワードを自分と先方とで共有する方式。

まずconfig/oauth.json。


"http://localhost/opensocial/hello.xml" : {
"" : {
"consumer_key" : "gadgetConsumer",
"consumer_secret": "testpass",
"key_type" : "MHAC_SYMMETRIC"
}
},


な感じに修正。

key_typeはMHAC_SYMMETRIC。
別にMHAC_SYMMETRIC使いたい場合は、"UNKO"とかでもOK。(BasicOAuthStoreの場合)

そしてそして、
通信相手側(サービス側というのか?)のサーブレットも修正。

OAuthConsumer consumer =
new OAuthConsumer(null, "gadgetConsumer", "testpass", provider);

OAuthConsumerをnewするときのパラメータにconsumer_secretで指定したパスワードを
渡してあげる。

で、おしまい。

ガジェットは「メモ118」と一緒。

で実行すると、画面に、

*** OAuthMessage Params:
URL: http://localhost/oauth/SignedFetchVerifyServlet
Param Name-->opensocial_viewer_id Value-->root
Param Name-->oauth_signature Value-->hcnKb9xBH6xlXrcFXNtkhNcccKI=
Param Name-->oauth_body_hash Value-->2jmj7l5rSw0yVb/vlWAYkK/YBwk=
Param Name-->oauth_nonce Value-->1249151173936074000
Param Name-->oauth_version Value-->1.0
Param Name-->oauth_signature_method Value-->HMAC-SHA1
Param Name-->oauth_consumer_key Value-->gadgetConsumer
Param Name-->opensocial_owner_id Value-->root
Param Name-->opensocial_app_id Value-->http://localhost/opensocial/hello.xml
Param Name-->opensocial_app_url Value-->http://localhost/opensocial/hello.xml
Param Name-->oauth_timestamp Value-->1249151173
VALIDATING SIGNATURE
REQUEST STATUS::OK


と表示される。oauth_signature_method ValueもHMAC-SHA1になってる。


おしまい。
.

[Apache Shindig][お勉強][OpenSocial] メモ118 gadgets.io.makeRequest 認証認可タイプSIGNEDをやってみる(RSA-SHA1で署名)

makeRequestでsignatureをリクエストにつけて通信することができるとのこと。
これもOAuthの一種っぽい。

ということでMy Shindigでもやってみる。
参考にしたサイトはhttp://wiki.opensocial.org/index.php?title=Validating_Signed_Requests

Shindigでは、RSA-SHA1かHMAC-SHA1が使えるらしい。

まず、shindigでoauthを使えるようにセットアップする。


config/oauth.json

に設定できる。

oauth.jsonの中身は、デフォルトでは

{
"http://localhost:8080/gadgets/files/samplecontainer/examples/oauth.xml" : {
"" : {
"consumer_key" : "gadgetConsumer",
"consumer_secret" : "gadgetSecret",
"key_type" : "HMAC_SYMMETRIC"
}
},
"http://localhost:8080/gadgets/files/samplecontainer/examples/shindigoauth.xml" : {
"shindig" : {
"consumer_key" : "http://localhost:8080/gadgets/files/samplecontainer/examples/shindigoauth.xml",
"consumer_secret" : "secret",
"key_type" : "HMAC_SYMMETRIC"
}
}
}

なんてなふうになっているんで、自分のガジェット用定義にする。

で、下記自分用。

{
"http://localhost/opensocial/oauth.xml" : {
"" : {
"consumer_key" : "gadgetConsumer",
"consumer_secret" : "gadgetSecret",
"key_type" : "RSA_PRIVATE"
}
}
}

最初のURLの"http://localhost:8080/opensocial/oauth.xml"は、
ガジェットのURL。shindigはmakeRequestをSIGNEDで受け取ると、
現在実行中のガジェットのURLをキーに、oauth.jsonの中身を探しにいく。

consumer_keyというのは、どこのコンテナから送られてきたリクエストかを指し示す。
consumer_secretというのは、key_typeにHMAC_SYMMETRICを指定した場合の、先方と共有すべき
パスワード、みたいなものらしい。
RSA_PRIVATEの場合、consumer_secretには、BasicOAuthStore.java(デフォルト)を使うのであれば、opensslで作成した証明書の秘密鍵を指定する。


次は、証明書と秘密鍵の作成。
特に設定しないで起動すると、ログに

Couldn't load OAuth signing key. To create a key, run:
openssl req -newkey rsa:1024 -days 365 -nodes -x509 -keyout testkey.pem \
-out testkey.pem -subj '/CN=mytestkey'
openssl pkcs8 -in testkey.pem -out oauthkey.pem -topk8 -nocrypt -outform PEM

Then edit gadgets.properties and add these lines:
shindig.signing.key-file=
shindig.signing.key-name=mykey


などと出力されているんで、そのとおりにopnessl、設定を実行。

$ openssl req -newkey rsa:1024 -days 365 -nodes -x509 -keyout testkey.pem -out testkey.pem -subj '/CN=mytestkey'
$ openssl pkcs8 -in testkey.pem -out oauthkey.pem -topk8 -nocrypt -outform PEM

で、できあがったファイルをWEB-INFの直下に配置。(getResourceで見える場所ならどこでも良い)

$ cp ./oauthkey.pem /tmp/

で、shindig.propertiesを修正。

shindig.signing.key-file=/tmp/oauthkey.pem
shindig.signing.key-name=mytestkey

と。

さらに、oauthkey.pemをoauth.jsonのconsumer_secretにセットする。

"http://localhost/opensocial/hello.xml" : {
"" : {
"consumer_key" : "gadgetConsumer",
"consumer_secret": "-----BEGIN PRIVATE KEY-----\nMIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAMMTit67BWHr6lY/\nsIGrNljDff5ZG6M5RCSDvIwHKwmhMFIRE1rhfdzmxst1wpih6X4D/r9W6rMVqMEz\nD8UVz+D7guS6VAJLzOVMi08bo3aOvUuTbnqt6vsVTO41wwPkoHzBWkQsIk5BLBia\neDr/nTR0Hih8cHCDo8KOjyl6lUJBAgMBAAECgYAx/Fr0MaXybnoQeadnwpBddFJf\nj0O2smDUnnD8zIVppUT1cmysN+WBahz8W4NuiuaCpNFtA/+FvgIM52GeFbW7NInS\nkCKQ/d0IxFII9qiE/VHGiSIx2tM978LtEZ90oUDcBSfOfkmuAiZmNNa7MPvxzXBH\n6ykLtK6I0Jdsc7vutQJBAPX6waYp8oyjR/ju6dN/BpYknzeTcH4xV6sX1gtAD1SR\noFpui5VgkPrTAHsHNHNlEakH+olzOyW/kEmteg4WiUMCQQDLBe7CrkZQt1UjhIij\nazE2TzpNHkxocsFa27vp3fnfSo3U+RxsX+Veyz09SoSEVfj40Zbaz9u35BUDV5Hk\nlbwrAkA870iHjD6svko3DaBZJ0+PZDUZBGpKqJiKJtZ8ZPsnPQgXPulPMOPZbung\nkFFbAL+WOwbChAwz9recuN/Sm5yXAkAr4LhdcvTXdQf9iSNOzlfweOcMy8OPv11y\nmZLfVI4waBiHMt+lvIQCHMK7gGNrhnksrX+BAvNlpvGzqzVoxcPJAkA4m5u5skPY\nmbpIPAPda/lr8XN88U5QROr/LdWeu46WO/402U86R+cVRwM/m7WVHAC8MCxzQ+8U\nDOWOrYDKefew\n-----END PRIVATE KEY-----",
"key_type" : "RSA_PRIVATE"
}
},

な感じ。(BasicOAuthStoreを使用の場合。デフォルト)
BasicOAuthStoreは実運用には耐えられるものではないとshindig-devのメーリングリストで
見つけた。実運用する際には別途実装せよ、とのこと。

でコンパイル。で、起動。


今度は通信先のサーブレットを実装。
http://wiki.opensocial.org/index.php?title=Validating_Signed_Requests
にあるものそのままだけど、一応コピペ。
そのままじゃ動かないので、一部現状のOAuthライブラリに合わせて修正。
さらに環境に合わせて修正。

package net.oauth.example.provider.servlets;

import net.oauth.OAuth;
import net.oauth.OAuthAccessor;
import net.oauth.OAuthConsumer;
import net.oauth.OAuthMessage;
/* ここから修正 */
import net.oauth.OAuthValidator;
import net.oauth.SimpleOAuthValidator;
/* ここまで修正 */
import net.oauth.OAuthProblemException;
import net.oauth.OAuthServiceProvider;
import net.oauth.server.OAuthServlet;
import net.oauth.signature.RSA_SHA1;

import java.util.ArrayList;
import java.io.IOException;
import java.util.Map;
import java.io.PrintWriter;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class SignedFetchVerifyServlet extends HttpServlet {

private final static String CERTIFICATE =
"-----BEGIN CERTIFICATE-----\n"
+ "MIIDHDCCAoWgAwIBAgIJAMbTCksqLiWeMA0GCSqGSIb3DQEBBQUAMGgxCzAJBgNV\n"
+ "BAYTAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEUMBIG\n"
+ "A1UEChMLR29vZ2xlIEluYy4xDjAMBgNVBAsTBU9ya3V0MQ4wDAYDVQQDEwVscnlh\n"
+ "bjAeFw0wODAxMDgxOTE1MjdaFw0wOTAxMDcxOTE1MjdaMGgxCzAJBgNVBAYTAlVT\n"
+ "MQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEUMBIGA1UEChML\n"
+ "R29vZ2xlIEluYy4xDjAMBgNVBAsTBU9ya3V0MQ4wDAYDVQQDEwVscnlhbjCBnzAN\n"
+ "BgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAseBXZ4NDhm24nX3sJRiZJhvy9eDZX12G\n"
+ "j4HWAMmhAcnm2iBgYpAigwhVHtOs+ZIUIdzQHvHeNd0ydc1Jg8e+C+Mlzo38OvaG\n"
+ "D3qwvzJ0LNn7L80c0XVrvEALdD9zrO+0XSZpTK9PJrl2W59lZlJFUk3pV+jFR8NY\n"
+ "eB/fto7AVtECAwEAAaOBzTCByjAdBgNVHQ4EFgQUv7TZGZaI+FifzjpTVjtPHSvb\n"
+ "XqUwgZoGA1UdIwSBkjCBj4AUv7TZGZaI+FifzjpTVjtPHSvbXqWhbKRqMGgxCzAJ\n"
+ "BgNVBAYTAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEU\n"
+ "MBIGA1UEChMLR29vZ2xlIEluYy4xDjAMBgNVBAsTBU9ya3V0MQ4wDAYDVQQDEwVs\n"
+ "cnlhboIJAMbTCksqLiWeMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEA\n"
+ "CETnhlEnCJVDXoEtSSwUBLP/147sqiu9a4TNqchTHJObwTwDPUMaU6XIs2OTMmFu\n"
+ "GeIYpkHXzTa9Q6IKlc7Bt2xkSeY3siRWCxvZekMxPvv7YTcnaVlZzHrVfAzqNsTG\n"
+ "P3J//C0j+8JWg6G+zuo5k7pNRKDY76GxxHPYamdLfwk=\n"
+ "-----END CERTIFICATE-----";

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
verifyFetch(req, resp);
}


@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
verifyFetch(req, resp);
}


private void verifyFetch(HttpServletRequest request, HttpServletResponse resp)
throws IOException, ServletException {
resp.setContentType("text/html; charset=UTF-8");
PrintWriter out = resp.getWriter();

try {
OAuthServiceProvider provider =
new OAuthServiceProvider(null, null, null);
/* ここから修正(oauth.jsonに書いたconsumerKeyをセット) */
OAuthConsumer consumer =
new OAuthConsumer(null, "gadgetConsumer", null, provider);
/* OAuthConsumer consumer =
new OAuthConsumer(null, "orkut.com", null, provider);
*/
/* ここまで修正 */
consumer.setProperty(RSA_SHA1.X509_CERTIFICATE, CERTIFICATE);

String method = request.getMethod();
String requestUrl = getRequestUrl(request);
List<OAuth.Parameter> requestParameters = getRequestParameters(request);

/* ここから修正 */
OAuthMessage message =
new OAuthMessage(method, requestUrl + "/SignedFetchVerifyServlet", requestParameters);
/*
OAuthMessage message =
new OAuthMessage(method, requestUrl, requestParameters);
*/
/* ここまで修正 */
OAuthAccessor accessor = new OAuthAccessor(consumer);
out.print("*** OAuthMessage Params:");
out.print("<br>URL: " + OAuthServlet.htmlEncode(message.URL));
for (java.util.Map.Entry param : message.getParameters()) {
String key = param.getKey().toString();
String value = param.getValue().toString();
out.print("<br>");
out.print("Param Name-->" + OAuthServlet.htmlEncode(key));
out.print(" ");
out.print("Value-->" + OAuthServlet.htmlEncode(value));
}
out.print("<br>");
out.print(" VALIDATING SIGNATURE ");
out.print("<br>");
/* ここから修正 */
OAuthValidator validator = new SimpleOAuthValidator();
message.validateMessage(accessor, validator);
/* message.validateSignature(accessor); */
/* ここまで修正 */
out.print("REQUEST STATUS::OK");
out.print("<br>");
} catch (OAuthProblemException ope) {
out.print("<br>");
out.print("OAuthProblemException-->"
+ OAuthServlet.htmlEncode(ope.getProblem()));
} catch (Exception e) {
out.println(e);
System.out.println(e);
throw new ServletException(e);
} finally {
out.flush();
}
}

/**
* Constructs and returns the full URL associated with the passed request
* object.
*
* @param request Servlet request object with methods for retrieving the
* various components of the request URL
*/
public static String getRequestUrl(HttpServletRequest request) {
StringBuilder requestUrl = new StringBuilder();
String scheme = request.getScheme();
int port = request.getLocalPort();

requestUrl.append(scheme);
requestUrl.append("://");
requestUrl.append(request.getServerName());

if ((scheme.equals("http") && port != 80)
|| (scheme.equals("https") && port != 443)) {
requestUrl.append(":");
requestUrl.append(port);
}

requestUrl.append(request.getContextPath());
requestUrl.append(request.getServletPath());

return requestUrl.toString();
}

/**
* Constructs and returns a List of OAuth.Parameter objects, one per
* parameter in the passed request.
*
* @param request Servlet request object with methods for retrieving the
* full set of parameters passed with the request
*/
public static List<OAuth.Parameter> getRequestParameters(
HttpServletRequest request) {

List<OAuth.Parameter> parameters = new ArrayList<OAuth.Parameter>();

for (Object e : request.getParameterMap().entrySet()) {
Map.Entry<String, String[]> entry = (Map.Entry<String, String[]>) e;

for (String value : entry.getValue()) {
parameters.add(new OAuth.Parameter(entry.getKey(), value));
}
}

return parameters;
}


で、CERTIFICATEの値をさっきつくったtestkey.pemの
BEGIN CERTIRICATEの方、証明書をコピペ。

で、コンパイル。OAuthライブラリは、
http://code.google.com/p/oauth/
にある。

クラスパス通してコンパイル。


次にSINGEDでリクエストするガジェットを用意。

<?xml version="1.0" encoding="UTF-8"?>
<Module>
<ModulePrefs title="oAuth Java Example">
<Require feature="opensocial-0.8"></Require>
<Require feature="dynamic-height"></Require>
</ModulePrefs>
<Content type="html">
<![CDATA[
<script type="text/javascript">
var servletUrl="http://localhost/oauth/SignedFetchVerifyServlet";

function response(data) {
document.getElementById('dom_handle').innerHTML=data.text;
};

function request() {
var params={};
params[gadgets.io.RequestParameters.CONTENT_TYPE] = gadgets.io.ContentType.TEXT;
params[gadgets.io.RequestParameters.AUTHORIZATION] = gadgets.io.AuthorizationType.SIGNED;
gadgets.io.makeRequest(servletUrl,response,params);
};

gadgets.util.registerOnLoadHandler(request);
</script>

<div id="dom_handle"></div>
]]>
</Content>
</Module>


な感じ。

実行させると、、、

*** OAuthMessage Params:
URL: http://localhost/oauth/SignedFetchVerifyServlet
Param Name-->opensocial_viewer_id Value-->root
Param Name-->oauth_signature Value-->uR7NP8VHHIvUTVl6wQI6iDUtp25DSrg1zrdfzLByvLR1/4xE2aT3+J+l9aGDBQGO8jFky9qC6CEQE1UFruif4cSDXY7PhQZZnCcaNxPLvPMmknc2lNafYt1h0xlTCZ1YupgwCXegCYTCrEcONTD2X+xPs9amKFIcRTpsHCojNb4=
Param Name-->oauth_body_hash Value-->2jmj7l5rSw0yVb/vlWAYkK/YBwk=
Param Name-->oauth_nonce Value-->1249149514626651000
Param Name-->oauth_version Value-->1.0
Param Name-->oauth_signature_method Value-->RSA-SHA1
Param Name-->oauth_consumer_key Value-->gadgetConsumer
Param Name-->opensocial_owner_id Value-->root
Param Name-->opensocial_app_id Value-->http://localhost/opensocial/hello.xml
Param Name-->opensocial_app_url Value-->http://localhost/opensocial/hello.xml
Param Name-->oauth_timestamp Value-->1249149514
VALIDATING SIGNATURE
REQUEST STATUS::OK


なんてなふうに表示される。

完璧。これで、
RSA-SHA1での署名付きリクエスト、
さらにはRSA-SHA1の署名VERIFYまでできた。


--
というか、config/oauth.jsonとshindig.propertiesの両方に秘密鍵指定しないと
動かないのは、なんか変。何か間違えたかも。
.

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を使うか、とかじゃないのかなー。


.

2009年7月25日土曜日

[OpenSocial][その他] gooホームでディベロッパー申請承認された

数日かかるものかと思いきや、ほぼ1日で承認いただけた

とりあえず、ほっとしたー。

.

[Apache Shindig][お勉強][OpenSocial] メモ92 shindig開発のメーリングリストのアーカイブ

http://www.mail-archive.com/shindig-dev@incubator.apache.org/

.

[Apache Shindig][お勉強][OpenSocial] メモ91 gadgets.rpc(3)

JavaScript難しい・・。

features/rpc/rpc.jsをざっと眺めてみた。

あっているかどうかしらないけど、多分流れ的にはこんな感じだと思う。

gadgets.rpcの流れ
1) gadgets.rpc.callが呼ばれる
2) gadgets.rpc.callの中で、postMessageなるものを使って別ドメインのiframeな感じのJavaScriptへメッセージ送信
3) rpc_relay.htm内でgadgets.rpc.receiveをコールし、postMessageで送信されたメッセージを受信し、該当コールバックを実行。
メッセージの内容はJSON形式の文字列で、


s: Service Name
f: From
c: The callback ID or 0 if none.
a: The arguments for this RPC call.
t: The authentication token.

といった内容。


gadgets.rpc.receive
postMessageで送られてくるJSONを元に、実際に処理をコールするところっぽい。
やっていることは以下の感じ。
1) securityTokenの値をチェック。だめならException
2) AsyncなRPCのためにcallbackをrpcコンテキストにくっつけておく
3) すでに登録されているサービスを実行
4) コールバックをコール。(gadgets.rpc.call経由で)

多分こんな感じ。
すでに登録されているサービスというのは、
gadgets.rpc.registerメソッドで登録される。
gadgets.rpc.registerの定義は、

register: function(serviceName, handler) {
if (serviceName === CALLBACK_NAME) {
throw new Error("Cannot overwrite callback service");
}

if (serviceName === DEFAULT_NAME) {
throw new Error("Cannot overwrite default service: use registerDefault");
}

services[serviceName] = handler;
},

となっていて、registerがコールされるとservicesメンバ変数(呼び方がわからん!)に
function(上記だとhandlerがそれにあたる)が保存される。




ところで、

===

って'='が3つ続いているけど、これは何????


--
大体分かったつもりだけど、どうしてrequestSendMessageはgadgets.rpc経由にするんだろう。。
そんな必要ない気がするんだけど。。

requestSendMessage should use the RPC mechanism as similar features do

って言ってる。。

ってことは、親なり別frameなりでダイアログでも表示させるってことかしらー。

--
ははぁーん。

What we want to do is just send a message as soon as a gadget asks us with
the requestSendMessage method.

という投稿発見。
それに対して、

I think, though, the default implementation should really be to send
an RPC to the containing page

だって。

これでRPC経由になったのかしら。
ところで、何て仰っているのかな?


.

[Apache Shindig][お勉強][OpenSocial] メモ90 gadgets.rpc(2)

gadgets.rpcの本体は、


features/rpc/rpc.js

にて定義されている模様。

そんだけ。
.

[その他] 死語ランキング

http://ranking.goo.ne.jp/ranking/999/dead_language/

うそピョーン

.

[その他] 週間新記録

週間POST数新記録。

.

[その他] フレームワーク思考がはやっているんですか?

徘徊しているとフレームワーク思考なるものを見かける。

はやっているのかなー。

みっしーとか7つの習慣とかですかね。



ここは、みっしーとは程遠いブログということぐらいは知っているよ、多分。

.

[Apache Shindig][お勉強][OpenSocial] メモ89 gadgets.rpc(1)

gadgets.rpcを理解していない。。
ので、ちゃんとコード読んでみようっと。

requestSendMessageに限らず、requestNavicateToもgadgets.rpc.callって呼ばれているのよねー。

なんだろかー。

.

[Apache Shindig][お勉強][OpenSocial] メモ88 opensocial.requestSendMessageするガジェット

とりあえず、簡単なガジェットを作成し、Shindig上で実行してみる。

作成したガジェットは以下のとおり。


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



でMy Shindig環境で表示させて、メッセージ送信ボタンを押下してみる。

すると、ありゃ?

opensocial is not defined
(?)()()gadgets.js (line 244)
[Break on this error] opt_callback(new opensocial.ResponseItem(

とFirebugのコンソールに表示されて動かない。

Shindigのサンプル実装では実装されていないのか、
それともMy Shindig環境がおかしいのかは、
これから調べる。

.

[Apache Shindig][お勉強][OpenSocial] メモ87 opensocial.Message

opensocial.Message周りについて実装しながら理解する。

多分友達やある特定ユーザに対してメッセージを送るような仕組みを提供するもの
ではないか、と思う。

さて、
opensocial.Messageそのもののサーバ側の仕組みを作る。
多分普通は既存のSNSなんかを使用するのだろうが、面白くないので、
最初から実装する。

opensocial.Messageの項目

Google Codeのopensocial 0.8.1のドキュメントを見ると、


<static> object BODY
メッセージのメイン テキストです。
<static> object BODY_ID
メッセージ テンプレートとしてのメッセージのメイン テキストです。
<static> object TITLE
メッセージのタイトルです。
<static> object TITLE_ID
メッセージ テンプレートとしてのメッセージのタイトルです。
<static> object TYPE
メッセージのタイトルです。opensocial.Message.Type として指定します。

なんてなふうに書かれている。

opensocial.requestSendMessageのAPIリファレンスなんかを見ながら、
必要そうなテーブルを作ってみた。

CREATE TABLE gms_message (
id VARCHAR(64) not null,
from_person_id VARCHAR(64) not null,
to_person_id VARCHAR(64) not null,
body text not null,
body_id VARCHAR(256),
title text not null,
title_id VARCHAR(256),
type VARCHAR(32) not null, /* EMAIL, NOTIFICATION, PRIVATE_MESSAGE, PUBLIC_MESSAGE */
created_at DATETIME not null,
PRIMARY KEY(id)
)
TYPE=innoDB;


良い悪いは別として、大体こんな感じですかねー。

DAOも用意して、準備完了。

さて、次からrequestSendMessageあたりから実装してみる。
Shindigデフォルトで動作するならそのまま。

.

[myminicity] int使い切った?

myminicityに行ってみたら、MySQLのエラー。
思いっきりint使い切ったって感じのエラー。

うーむ。
.

[YUI][JavaScript] やべ

YUI使ってdialog出して見た。

かっこいいー

今さらだけど。


My Shindig コンテナで、requestPermissionのときとか、sendMessageのときとかに
使おうっと。
.

[YUI][JavaScript] YUIダウンロード中

YUIをダウンロード中。

GoogleにしろYahooにしろ、すごいねー。

そんだけ。

--
ダウンロード終わった。
何をすればよいのやら。。
.

[OpenSocial][その他]とりあえず、gooホームでディベロッパー申請してきた

これから審査するんだそうで。

審査きらい。


mixiは、ユーザ名、パスワードともに忘れた・・。
.

[Apache Shindig][お勉強][OpenSocial] メモ86 DataRequest.newFetchActivitiesRequestのサーバ側実装をしてみる(4)

DataRequest.newFetchActivitiesRequestのactivityId指定しない版できた。

まず、GadgetXMLのJavaScriptは、


function getData() {
/*===================================================================*/
/* OWNER IdSpec */
/*===================================================================*/
var idspec = new opensocial.IdSpec();
idspec.setField(opensocial.IdSpec.Field.USER_ID, opensocial.IdSpec.PersonId.OWNER);
idspec.setField(opensocial.IdSpec.Field.GROUP_ID, opensocial.IdSpec.GroupId.SELF);
/*===================================================================*/
/* DataRequestオブジェクトを作成し、 */
/* そこにActivity取得リクエストを追加. */
/* レスポンスを取得するときのキーは'get_activity' */
/*===================================================================*/

var req = opensocial.newDataRequest();
req.add(req.newFetchActivitiesRequest(idspec), 'get_activity');
/*===================================================================*/
/* 送信! */
/*===================================================================*/
req.send(function(response){
var activity = response.get('get_activity')
if (activity.hadError()) {
document.getElementById('result').innerHTML
= 'エラーだったよ:' + activity.getErrorMessage();
return;
}
var out = document.createElement('ul');
if (activity.getData().size() == 0) {
var li = document.createElement('li');
li.innerHTML = 'Activity0件';
out.appendChild(li);
}
activity.getData().each(function(act) {
var li = document.createElement('li');
var title = act.getField(opensocial.Activity.Field.TITLE);
var media = act.getField(opensocial.Activity.Field.MEDIA_ITEMS);
if (media != undefined) {
for (var ii=0, len = media.length; ii < len; ii++) {
if (media[ii].getField(opensocial.MediaItem.Field.TYPE) == opensocial.MediaItem.Type.IMAGE) {
title +=
'<br /><img src="'
+ media[ii].getField(opensocial.MediaItem.Field.URL);
+ '/>';
}
}
}
li.innerHTML = title;
out.appendChild(li);
});

document.getElementById('result').appendChild(out);

});
}


と、こんな感じ。
newFetchActivitiesRequestの第二引数はactivityIdを指定しないので、必要なし。

で、このリクエストに対応するサーバ側の処理は、
まずAcitivityServiceのjava、

public Future<RestfulCollection<Activity>> getActivities(Set<UserId> userIds,
GroupId groupId, String appId, Set<String> fields, CollectionOptions options, SecurityToken token)
throws ProtocolException {
return ImmediateFuture.newInstance(
new RestfulCollection<Activity>(logic.getActivities(userIds,groupId,appId,fields,options,token)));

}


な感じ。
とりあえずパラメータはそのままlogicの方に渡す。
で、そのlogic。

public List<Activity> getActivities(Set<UserId> userIds, GroupId groupId, String appId, Set<String> fields, CollectionOptions options, SecurityToken token)
throws ProtocolException {
GmsPerson viewer = getUser();
List<Activity> activities = new ArrayList<Activity>();
logger.info("アクティビティ取得(userId複数指定)開始:");
if (viewer == null) {
if (logger.isDebugEnabled()) {
logger.debug("Guestは取得できない:");
}
throw new ProtocolException(HttpServletResponse.SC_FORBIDDEN,
"Guestのため、取得できない:loginid:[" + viewer.getLoginId() + "]");
}
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() + "]");
}
List<String> userLoginIds = getUserList(userIds, token);
ExtendedGmsPerson[] gmsPersons = null;
List<String> personIds = new ArrayList<String>();
int first = 0;
int max = 20;
for (;;) {
gmsPersons = gmsPersonDao.peopleGet(
userLoginIds,
groupId.getType().toString(),
groupId.getGroupId(),
null,
null,
null,
null,
null,
first,
max,
null);
if (logger.isDebugEnabled()) {
logger.debug("count:[" + gmsPersons.length + "]");
}
int length = gmsPersons.length;
for (int ii=0; ii<length; ii++) {
personIds.add(gmsPersons[ii].getId());
}
if (length < max) {
break;
}
first += max;
}
ExtendedGmsActivity[] gmsActivities = gmsActivityDao.selectByGmsPersonIds(personIds, options.getFirst(), options.getMax());
for (int ii=0,len = gmsActivities.length; ii<len; ii++) {
activities.add(mapToActivity(gmsActivities[ii], gmsActivities[ii].getGmsPerson().getLoginId()));
}
logger.info("アクティビティ取得(userId複数指定)終了:");
return activities;
}


大したことはしていない。
まずパーミッションをチェックして、
userIdとgroupIdからUserIdのリストを取得と。
そしたら、そのUserIdとひもづくactivityを取得する。

そんだけ。

.