2008-10-07

日本の祝日についての参考になる情報

Google Calendar API で日本の祝日データを取得 - floatingdays さん

国民の祝日については、国立天文台の暦要綱によってデータファイルを更新していました。外部の情報が使えたら便利だな。

2008-08-28

script.aculo.us の Sortable にまつわる制限

table の行(tr)は、ダメなんですね。

http://github.com/madrobby/scriptaculous/wikis/sortable-create より、

Notes

Important: You can use Sortable.create on any container element that contains Block Elements, with the exception of TABLE, THEAD, TBODY and TR. This is a technical restriction with current browsers. A sortable nested somewhere inside a table won’t work well under IE unless the table has a “position:relative” style. If you use the css display: table property, sortable lists will work a little, but doesn’t allow true drag and drop of the elements.

TABLE, THEAD, TBODY と TR を除く、ブロック要素を含むどんなコンテナ要素でも、Sortable.create を使うことができます。これは、現在のブラウザーによる技術的な制限です。テーブルに「position:relative」というスタイルがないと、テーブルの中のどこかでネストされた sortable はIEではうまくいかないでしょう。もし、css の display: テーブルプロパティを使っている場合、要素の本当のドラッグアンドドロップを許容しないのを除いて、sortable リストは、少しだけ動くでしょう。

If you want your sortable list to be scrollable, wrap the list in a div and set the div to scrollable as apposed to making the ul element scrollable. Also, in IE you must set “position:relative” on the scrollable div.

sortable リストをスクロールできるようにしたい場合、リストを div で包んで、スクロール可能にした ul 要素と div をスクロール可能に設定してください。 また、IEでは、あなたは「position:relative」をスクロール可能なdivに設定しなければなりません。

Got it working using tbody as container and TR as the sortables (IE6 (pc) and Firefox (mac/pc).

tbody をコンテナとし、TR を使えば sortable は IE6(pc) と Firefox(mac/pc) で動作します。

A call to Sortable.create implicitly calls on Sortable.destroy if the referenced element was already a Sortable.

参照された要素がすでに Sortable であるならば、Sortable.createへの呼び出しは暗黙のうちにSortable.destroyを呼び出します。

tbody の中の tr では動作しました。引っかかる書き方だなぁ。

後で見つけたけど、こちら

マウスで握って並び替える。 - ザリガニが見ていた...。 さん

が、参考になりました。

2008-07-01

mscgen

Trac に Graphviz の調べものをしていた時に見つけた mscgen というものがあるみたいです。


A Message Sequence Chart GENerator ということでシーケンス図を dot 記法に似た文字情報から生成できるようです。未導入。あとでやってみるときの備忘録。

考えを整理するときや人に説明するときにシーケンス図は、とても有用。これなら、元図が行方不明になったりして散らかることもないし、修正もその場で可能。是非、導入したいな。

2008-05-22

Trac に Graphviz

ダイアグラムをチケットに貼りたいときに、OpenOffice Draw を使ってましたがノード数が少ない簡単なものの時は面倒でした。整形済みテキストマークアップ {{{...}}} で ASCII アートすると、今度は修正が大変。

TracGraphviz が使える GraphvizPlugin があることは知っていたけど、一度挑戦してうまく行ってなかったが今回、再チャレンジで成功。なんのことは無い、「エラーメッセージをよく読みましょう」と、いう類のことでした。

こんな環境です。

まず、Graphviz を単体で動くようにしておきます。RHEL 5 用のパッケージを使いました。

  • graphviz-2.16.1-1.el5.i386.rpm
  • graphviz-gd-2.16.1-1.el5.i386.rpm

GraphvizPlugin では、PNG 出力を使うため graphviz-gd が必要だったことが最初の躓き。

echo "digraph G {Hello->World}" | dot -Tpng >hello.png


これが、動作すれば OK だと思う。



次に GraphvizPlugin 。ここからダウンロードした、graphvizplugin.zip を展開して



# cd graphvizplugin/0.10
# python setup.py bdist_egg
running bdist_egg
~~~
~~~
creating 'dist/graphviz-0.6.11-py2.4.egg' and adding 'build/bdist.linux-i686/egg' to it
removing 'build/bdist.linux-i686/egg' (and everything under it)

# python setup.py install
running install
~~~
~~~
Installed /usr/lib/python2.4/site-packages/graphviz-0.6.11-py2.4.egg
Processing dependencies for graphviz==0.6.11
Finished processing dependencies for graphviz==0.6.11


これで、サイトワイドにインストールできたので httpd 再起動。



Trac の WebAdmin で Plugins に graphviz 0.6.11 が現れるので enabled して、Apply changes 。



trac.ini に 以下追加



[graphviz]
cmd_path = /usr/bin
cache_dir = /キャッシュ/への/フルパス
cache_manager = yes
cache_max_size = 10000000
cache_min_size = 5000000
cache_max_count = 2000
cache_min_count = 1500


cache_dir は、web サーバーが書き込めるディレクトリにする。これで、以下のマークアップで日本語を含むダイアグラムが表示されました。



{{{
#!graphviz
digraph sample {
node [fontname="/usr/share/fonts/japanese/TrueType/sazanami-gothic.ttf"];
こんちわ -> みなさん;
}
}}}

2008-04-24

dojo.io.bind ではない?

現時点での最新版 Dojo Toolkit 1.1.0 を使ってます。
情報は少なくないのですが実際にやってみると、どうもかみ合わないと思ったら、

Dojo Porting Guide: 0.4.x to 0.9

0.9 以降と 0.4 以前のリリース間では、後方非互換とのこと。 dojo.io.bind については、 IO Transports [AJAX] にあるように dojo.xhrXXX に変更になったみたいです。

では、HTML フォームの submit を抑止し、XmlHttpRequest する実験の続き

以下のようなコードになりました。

    function receiveData(response, ioArg) {
}

function onSubmitTestForm() {
var testFormNode = dojo.byId('TestForm');
var deffered = dojo.xhrPost({
url: testFormNode.action + '.json',
handleAs: 'json',
load: receiveData,
form: testFormNode});
}

function init() {
var formWidget = dijit.byId('TestForm');
dojo.connect(formWidget , 'onSubmit', 'onSubmitTestForm');
}

dojo.addOnLoad(init);
...
...
<form id="TestForm" onsubmit="return false;" action="..." method="post" dojotype="dijit.form.Form">



  1. form タグの onsubmit で return false し、送信抑止。


  2. ページの onLoad 時に init 関数内でフォームの onSubmit イベントに onSubmitTestForm を接続


  3. フォームの送信時に onSubmitTestForm が実行され、フォームの内容を伴った Post リクエストが行われる。


  4. レスポンスを受け取ると、receiveData が実行される。



と、いう動作を firebug と デバッガ で確認できました。



嵌ったところが一箇所あって、




  • dojo.connect() では、第1引数に dijit.byId() で取得したオブジェクト


  • dojo.xhrPost() の form 引数には、dojo.byId() で取得した DOM node



でした。dojo.xhrPost() に、dijit.byId() を使うと、form.getAttributeNode is not a function なんてのが firebug のエラーに捕捉されてました。

2008-04-22

dijit.form.Form submit 抑止はどうするの?

Dojo Toolkit 1.1.0 でのお話。

dojo の connect 機構を使ってやってみた。

    function onSubmitTestForm() {
return false;
}

function init() {
var formTest = dijit.byId('TestForm');
dojo.connect(formTest, 'onSubmit', 'onSubmitTestForm');
}

dojo.addOnLoad(init);


これでは、フォームは送信されてしまう。



オフィシャルの trac に Broken Backwards compatibility for dijit.form.Form と、いうチケットを見つけた。まさにこのことについてのやり取り。 接続からのリターン値は、無視される。オーバーライドする必要がある。とのこと。オーバーライド?


他にも、いくつか同様のチケット(#6280 とか)上がってて、斜め読みした感じでは、



<form dojoType="dijit.form.Form" onSubmit="return false;" id="TestForm" method="post" action="...">


こうすると、リクエストは送信されず、onSubmitTestForm が実行されました。onSubmit="dojo.stopEvent(arguments[0]);" でも、同じ。何が違うんだろう?



とりあえず onSubmitTestForm から dojo.io.bind してみます。

2008-04-16

PHP で Excel 2003 の SummaryInformation を取得したい。

SummaryInformation は、エクスプローラで xls ファイルを右クリックしてコンテキストメニューの「プロパティ」を開き、「概要」タブなどに表示されている情報です。ワークシートの内容にアクセスするものは、いくつかヒットしました。

  • 茶漬けブログさんの Excel_Peruser
    ワークシートが読めます。ちょっと使ってみた範囲では完璧でした。素晴らしい。 Excel_Reviser と組合わせて既存の Excel ファイルをテンプレートとして使えます。
  • PHPExcel
    Excel 2007 (.xslx)の読み込みは、できるらしい。PHPExcel_DocumentProperties->getTitle など、それらしい実装があります。Excel 2007 持ってないから、チェックできない。
  • PHP-ExcelReader
    ワークシートが読める。
  • PEAR::Spreadsheet_Excel_Writer
    ワークシートの書き込みができるらしい。

ワークシートがやっぱりメインだよな。その他の資料としては、

1日やって、だめだったらあきらめるつもりで PHP-ExcelReader を使ってダンプを出して眺めたりしてました。それらしい、箇所はあるのですがたどり着く方法がなかなかわかりませんでした。そろそろ、見切りをつけようと思っていたところで、jakarta になんかあったことを思い出して、探してみると The Apache POI Project になってました。いつの間にかトッププロジェクトに昇格してる。

で、PropertySet 辺りの実装を参考に改めてダンプを見てみると、

「読めるっ!!読めるぞぉ」(ラピュタのムスカ風に)

甚だ、中途半端ですが必要な部分のみ parse できるだけの PHP 実装をしてみました。

    function parseProperty($data){
$offset = 0;
$result = array();
$result['byteOrder'] = GetInt2d($data, $offset);
$offset += 2;
// 以下は、Intel little endian きめうち
$result['format'] = GetInt2d($data, $offset);
$offset += 2;
$result['osVersion'] = GetInt4d($data, $offset);
$offset += 4;
$result['classID'] = unpack('C*', substr($data, $offset, 16));
$offset += 16;
$result['numSections'] = GetInt4d($data, $offset);
$offset += 4;
// section を取得
for ($secidx = 0 ; $secidx < $result['numSections'] ; $secidx++) {
$section = array();
$section['formatID'] = unpack('C*', substr($data, $offset, 16));
$offset += 16;
$section['offset'] = GetInt4d($data, $offset);
$offset += 4;
$propTop = $propTblOffset = $section['offset'];
$section['size'] = GetInt4d($data, $propTblOffset);
$propTblOffset += 4;
$section['numProperties'] = GetInt4d($data, $propTblOffset);
$propTblOffset += 4;
// UTF-16 の可能性もあるようだが今のところ SHIFT-JIS きめうち
// 以下の propertyID = 1 がコードページなので対応は可能じゃないかな。
$srcenc = 'SJIS-win';
for($propidx = 0 ; $propidx < $section['numProperties'] ; $propidx++) {
$property = array();
$property['propertyID'] = GetInt4d($data, $propTblOffset);
$propTblOffset += 4;
$property['propertyOffset'] = GetInt4d($data, $propTblOffset);
$propTblOffset += 4;
$propOffset = $propTop + $property['propertyOffset'];
$property['propertyType'] = GetInt4d($data, $propOffset);
$propOffset += 4;
// ここで、propertyType (Variant型)ごとの値取得
switch($property['propertyType']) {
case 2: // VT_I2 = 2 byte signed int.
$property['propertyLength'] = 2;
$property['propertyValue'] = GetInt2d($data, $propOffset);
$propOffset += $property['propertyLength'];
break;
case 30: // VT_LPSTR = null terminated string.
$property['propertyLength'] = GetInt4d($data, $propOffset);
$propOffset += 4;
$strval = mb_convert_encoding(
substr($data, $propOffset, $property['propertyLength']),
"UTF-8", $srcenc);
$property['propertyValue'] = str_replace("\x00", "", $strval);
$propOffset += $property['propertyLength'];
break;
case 64: // VT_LPSTR = null terminated string.
$property['propertyLength'] = 8;
$lowWord = GetInt4d($data, $propOffset);
$propOffset += 4;
$highWord = GetInt4d($data, $propOffset);
$propOffset += 4;
// EPOC の違いを変換。
// 1601-01-01 00:00:00 (Windows EPOC/100nsec)
// -> 1970-01-01 00:00:00 (unix EPOC/1sec) に変換
$unixTime = $highWord * 4294967296 + $lowWord;
$unixTime = floor($unixTime / (1000 * 1000 * 10));
$unixTime -= 11644473600;
$property['propertyValue'] = date('Y/m/d H:i:s', $unixTime);
break;
}
$section['properties'][$propidx] = $property;
}
$result['sections'][$secidx] = $section;
}
return $result;
}

これで、propertyID = 2 の propertyValue にタイトルの文字列が取得できます。日付関係も問題ないよう。手持ちのファイルでは、概ね OK でした。

もっと、効率的に書けるでしょうし、エンコーディングのきめうちや未対応の Variant 型など突込みどころも多いですが、後で必要になったらがんばるかもしれません。

2008-04-11

こんな model はこうする(ことができるかも?) 空振り

こんな model はこうする(ことができるかも?) の続きです。

185 個の hasMany を bindModel する為に、 memory_limit と max_execution_time を適宜調整して、debug を 1 に設定し、なんとか完走しました。しかし、サーバ TA が3~5分かかっていたのでそのままではちょっと使えない。ボタン何回も押されちゃったりするし。

で、必要なテーブルに絞って hasMany することに。この時点で、不整合が発生する危険を作りこんでしまいました。最終的に、アソシエーション定義の dependent = true とすることで、頭の Model->del によって3段のアソシエーションを辿って削除することが出来ました。

がっ!! del も deleteAll もトランザクションが実装されてない...orz (rev.6596)

saveAll ではトランザクションされてたのにな。仕方ないから query 使うかな。

2008-04-10

こんな model はこうする(ことができるかも?)

こんな model はどうする? の続きです。

Oyatbl - hasMany - Kotbl - belongsTo - Betsutbl

のような関係の kotbls_xxx が 200 個近くあります。
アクセスする kotbls_xxx は、oyatbls のフィールドの値で決まります。kotbls_xxx は、別の masters テーブルの id に対応(xxx = id)した個数あります。

$this->Oyatbl->Kotbl->setSource('kotbls_' . $id);

で、すんなり動きました。ただし、Kotbl モデルの定義で、あとで setSource するからといって Kotbl->useTable = false としてしまうと belongsTo Betsutbl アソシエーションが効きません。setSource では、アソシエーションの再構築が行われないようです。
Kotbl->useTable = 'kotbls_xxxどれか' と定義しておくか、setSource のあとで bindModel/unbindModel する必要がありました。

また、上の記述とアソシエーション定義を見て気付いたのですが

var $hasMany = array(
'assocalias' = array(
'className' => 'Kotbl',
...

この場合は、className Kotbl モデルのインスタンス(オブジェクトだっけ?)が assocalias という名前で生成されるということらしいです。
なので、こんな荒業も

$ids = $this->Master->find('list', array('id'));
foreach ($ids as $id) {
$assocKey = 'Kotbl' . $id;
$tblName = 'kotbls_' . $id;
$this->Oyatbl->bindModel(array(
'hasMany' => array(
$assocKey => array(
'className' => 'Kotbl',
'foreignKey' => 'fkey',
'dependent' => false
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'exclusive' => '',
'finderQuery' => '',
'counterQuery' => ''
)
)
, false
)
);
$this->Oyatbl->{$assocKey}->setSource($tblName);
}

デバッガで覗いた限りでは、model オブジェクトの中身はそれらしくなっていました。動作の確認は、また明日。しかし、かなりヘビー。大量の SQL 発行とかなりメモリも消費します。実用になるのか?

delete をトランザクションで行いたいのですが、なんか、苦労の方向性が間違っている気がする。DB 設計を見直してリレーションを設定して delete cascade で良いんじゃないか? ただ、既存 DB なので影響範囲にすべて手を入れるとなると...

2008-04-09

アソシエーションで集約関数を使いたい

CakePHP 1.2(6598)。 PostgreSQL 8.1

はまった。親テーブルに対して、子テーブルを集約関数(COUNT,SUM など)でサマった結果を結合したい場合です。

例えば、 User hasMany Post という関係について User hasOne (user_id 毎のPost 数) と、いうアソシエーションを設定できないか。理想は、

    Users
LEFT JOIN
(SELECT user_id, COUNT(id) AS cnt FROM Posts GROUP BY user_id) postcounts
ON
Users.id = postcounts.user_id


こんな問合せが組立てられること。

conditions に GROUP BY を記述する方法は、使えませんでした。GROUP BY が結合条件(ON 節)に入ってしまって、 syntax error です。

とりあえず、2点考えてみましたが、何れも搦め手ですっきりしません。

1. データベースビューを使う


PostgreSQL なのでデータベースビューを作成することができます。

CREATE OR REPLACE VIEW postcounts AS
SELECT user_id, COUNT(id) AS cnt
FROM Posts
GROUP BY user_id

CakePHP でも、テーブルと同様に扱えるようです。データベースビュー (postcounts) に対する model (Postcount) を bake することができました。あとは、通常の model 同様、hasOne に設定します。
長所
  • hasOne アソシエーションなので model->recursive = 0 での問合せに含まれます。検索条件に使えるので PaginatorHelper を使いやすいです。
短所
  • 集約する条件を動的に指定できない。「ある期間内の投稿数」のような条件でサマることは、よくあるんじゃないかな。


2. hasMany の結果をカウントする



hasOne アソシエーションではないのですが、User hasMany Post が設定されているので、model->recursive = 1 で問合せて count(kekka['Post']) で件数は得ることができます。大量の問合せが発行されることを危惧していたのですが、WHERE 節に IN 句が使用された1文が発行されていました。

長所
  • hasMany の conditions に指定することで集約条件を動的に設定できる。

短所
  • 別の文なので、検索条件に含めることはできない。
  • 親テーブルの件数が多い場合には、IN 句が大量となり遅くなる(と、思う)。

CakePHP 的には、hasMany をカウントの方が素直でしょうか。勝手に思っていることですが、model->query を使うと、負けてしまった気分になると思います(^_^;

2008-04-07

こんな model はどうする?

CakePHP 1.2 でのお話。PostgreSQL 8.1。

同一構造で関連ID毎に185個のテーブルがある。2つのマスタテーブルに対して belongsTo でマスタテーブルからは、 hasMany。残念(?)ながら、パーティショニングされてはいないし、するには移行手順が必要。

さぁ、どうする? 185個モデル作るべきなのか? まさかね。

全てのテーブルを同時に参照する必要はなさそうなので参照するテーブルを動的に切り替えるロジックは作れそう。

model->setSource と model->bindModel/unbindModel で行けるのかなぁ? 続きは、明日。

2008-04-03

CakePHP で Dojo toolkit

CakePHP 1.2 で Dojo Toolkit を使ってみた。


全てのページで必要ではないので、layout の scripts_for_cake 変数を使います。

CakePHP Users in Japan フォーラム scripts_for_layoutとは?


試しに Dijit のポップアップカレンダー付き日付入力テキストボックス(dijit.form.DateTextBox)を使ってみます。


Dojo 配置


Dojo を app/webroot/js 以下に配置します。今回は、dojo というディレクトリを掘りました。


app
└─webroot
└─js
└─dojo
├─dijit
├─dojo
├─dojox
└─util


dojo.js と CSS 読み込み



テンプレートに以下を追加します。全てのページで Dojo を必要とはしていないので、 layout ではなくテンプレートに記述して、scripts_for_cake メカニズムを使います。



<?php
$html->css('../js/dojo/dijit/themes/tundra/tundra', 'import', array(), false);
$html->css('../js/dojo/dojo/resources/dojo', 'import', array(), false);
$this->addScript('<script type="text/javascript" src="/絶対URL/js/dojo/dojo/dojo.js" djConfig="isDebug: true, parseOnLoad: true"></script>');
$javascript->codeBlock(' dojo.require("dijit.form.DateTextBox");
dojo.require("dojo.date.locale");
dojo.require("dojo.parser"); // scan page for widgets and instantiate them', array('inline' => false));
?>


Javascript ヘルパの link では、大事な大事な djConfig が設定できなかったので addScript を使いました。弊害として、src 属性に絶対 URL を書かなければいけなくて、なんともかっこ悪い。



フォームにインプット要素を記述



他の form ヘルパの要素と混在可能です。



<?php echo $form->label('created.min', '登録日'); ?>
<input name="data[Moderu][created][min]" type="text" value="<?php echo $this->data['Moderu']['created']['min'] ?>" id="UsrpramCreatedMin"
dojoType="dijit.form.DateTextBox"
constraints="{min:'2003-01-01',max:'2013-12-31',datePattern:'yyyy/MM/dd'}"
promptMessage="登録日の検索範囲"
trim="true" />


注意点



これで、そこそこ表示されますが、落し穴が2点ほど




  1. layout の body タグに dijit テーマの class 属性が必要。

    今回は、tundra を使っているので <body class="tundra"> としないとかっこよくならない。

  2. CSS の基本セレクタが衝突。 cake.generic.css を一部変更して使っていると妙な表示(今回の場合、input ボックスの高さが異常にでかかった)になりました。

    form div セレクタを削除して対処しました。



djConfig や constraints などをうまく扱うためには、やはりヘルパが欲しいところです。

2008-03-13

CakePHP 1.2 は、webservices に対応しない。

CakePHP アップデートで携帯ページ NG の件の「次回」は、割と早い時期にやってきそうです。

ソースコードリポジトリの 1.2.x.x ブランチのChangeset [6453] で webservices 関係がばっさり削除されてます。1.2 リリースでは、確実に別の実装を行わないとだなぁ。

これは、別件調べ事のついででした。それは、PostgreSQL を使っている場合に UPDATE 文に alias が入って失敗するというもの。

http://cakephp.jp/modules/newbb/viewtopic.php?post_id=1814&topic_id=934&forum=6

こちらでも議論がされていますが、1.2.0.6311-beta のはまりねたらしいです。部分的に差し替えようかと思ったのですが、変更範囲がかなり広い。幸か不幸か、携帯ページを使っていなかったので cake ディレクトリを HEAD Revision に全とっかえして、UPDATE 文は、通るようになりました。まだ、時間的に余裕があるから良いけど、今後も、こんなことはありそうだから、ユニットテストはきちんと書いておこう。

2008-03-12

CakePHP アップデートで携帯ページ NG

CakePHP で作成したサイトの携帯ページを CakePHP 携帯用ビューを表示する(Shin x blog さん) などを参考に実装していました。当初は、1.2.0.5875-pre-beta を使ってうまく行っていたのですが 1.2.0.6311-beta にアップデートしたところ、文字化けに見舞われました。念のため Subversion にブランチして作業していたのですぐに戻せるのですが、悔しいので少し調べてみました。

状況を落ち着いてみてみると、どうやら View は、webservice のものが選択されているが、Layouts が通常のものの模様。render まわりと当たりをつけて cake/libs/view/view.php を diff してみたら、かなり大幅に変ってる。他は、ほぼ問題ないので _getViewFileName と _getLayoutFileName のみに着目すると、やっぱり、webservices 判定が _getViewFileName にはあるが、_getLayoutFileName からはなくなってました。

@@ -821,6 +821,9 @@
}
$subDir = null;

+ if (!is_null($this->webservices)) {
+ $subDir = strtolower($this->webservices) . DS;
+ }
if (!is_null($this->layoutPath)) {
$subDir = $this->layoutPath . DS;
}


こんな修正で動くようにはなります。しかし、コアに変更入れるのは、さて、どうしたものか...



cake/libs/router.php には 「webservice は、非推奨で将来的にはサポートしなくなるよ。Router::parseExtensions() を使ってね。」という記述がありますね。1.2 系で作業始めたときも情報の少なさに「しくじったか?」とは、思ったのですが...



次回からがんばります。

2008-03-05

How to make an exhibit from data fed directly from a Google Spreadsheet

原文

Google Spreadsheet から直接供給されるデータから exhibit する方法

This tutorial shows you how to feed data directly from a Google Spreadsheet rather than from a file. The advantage of this is that you can edit your data in Google Spreadsheets' user interface. Here is an example.

Formatting the Data

You need to make sure that your Google spreadsheet follows a particular format, which can be seen here:

あなたはあなたの Google スプレッドシートが特定のフォーマットに従うことを確認する必要があります。そして、それはここで見ることができます:

 http://spreadsheets.google.com/pub?key=pLvsUS-CftHo21r-0xjKvVA


The first row contains the property names wrapped in {}. You can specify the value type for a property, e.g., {rating:number}. You can have multiple values for each field and they should be separated by semicolons, e.g., "Drama; Epic". If you want to tell Exhibit not to split a field by semicolons, add ":single" to the column header, e.g., {plot:single}.



最初の行は、{} の中に包まれたプロパティ名を含みます。プロパティの型を定義することも出来ます、例えば、{rating:number}。複数の値を同一フィールドに持つことができ、それらは、セミコロンで区切られます、例えば、"Drama; Epic"。もし、セミコロンでフィールドを分割しないよう Exhibit に指示したい場合は、カラムヘッダーに ":single" を追加してください、例えば、{plot:single}



Important note! Be sure to make the first column {label}. Otherwise Exhibit will use what it finds in cell A1 as {label}, since the Google Spreadsheet export format does not convey the data listed in that cell.



重要な注意! 必ず最初のカラムとして{label}を作って下さい。さもないと、Google スプレッドシートエクスポート形式がそのセルにリストされたデータを伝達しないので、Exhibit は、A1 セルで見つけたものを {label} として使います。



Getting the Feed



First, open your Google spreadsheet and then click on the Publish tab on the right. If it's not published yet, click Publish Now. Then you will see something like this:



まず最初に、あなたの Google スプレッドシートを開き、次に、右の方の Publish タブをクリックしてください。 まだ発行していない場合は、 Publish Now をクリックしてください。 すると、あなたはこのような物を見るでしょう。



 Publish at: http://spreadsheets.google.com/pub?key=pLvsUS-CftHo21r-0xjKvVA


Click on the URL to get a new window with your spreadsheet in view only mode.



URL をクリックすると表示のみモードのスプレッドシートの新しいウィンドウが開きます。



For Firefox


If you're using Firefox, right click anywhere on the page and choose View Page Info. Then click on the Links tab, and look for a row in which Name is "alternate", Type is "Related Item", "Address" looks something like this:



 http://spreadsheets.google.com/feeds/list/o08841867754116283182.6102151849127695926/od6/public/basic


Right click on that row and choose Copy. Then close the dialog box. What you've copied is the URL of the feed of the spreadsheet.



For Internet Explorer


Right click somewhere on the page and choose View Source. Then use the source editor's Find command to search for "feeds". That should land you in the middle of the feed URL. Copy that URL to the clipboard and close the source editor.



Setting up the Exhibit



To link in your data, paste in that feed URL and append ?alt=json-in-script to it. Then, make sure the data link has type="application/jsonp" (note the "p") and ex:converter="googleSpreadsheets":



 <link rel="exhibit/data" 
type="application/jsonp"
href="http://spreadsheets.google.com/feeds/list/o08841867754116283182.6102151849127695926/od6/public/basic?alt=json-in-script"
ex:converter="googleSpreadsheets" />


This is assuming that you are using Exhibit version 2.0.



Re-publishing



You can set your Google Spreadsheet to re-publish every so often, or you have to manually click the Re-publish button in Google Spreadsheets.



But that's it!

2008-02-12

Plugin moblog

Description

This plugin allows to easily post new articles via email or even MMS in those terminals that support this feature. The plugin can also handle attachments such as images or videos, and those will first be added as new files to the resource centre and then a link to these files will be added to the contents of the post. The text contents of the email will be published as a post in the main page, and assigned to the category that we select in the plugin configuration page.

このプラグインは、この特徴をサポートするそれらの端末にメールやMMSを通して簡単に新しい記事のポストを可能とします。プラグインはイメージまたはビデオのような添付を取り扱うこともでき、最初に新しいファイルとしてresource centre に追加し、それから、これらのファイルへのリンクをポストの中身に加えます。メールのテキストコンテンツは、メインページのポストとして発行されて我々がプラグイン構成ページで選ぶカテゴリーに割り当てられます。

How it Works

Email or MMS messages are sent to an email address that is especially set up for moblogging to LifeType (one e-mail address can be used to post to more than one blog. But if you use Realtime publishing it's also possible to use dedicated e-mail addresses per blog).

Attachments are extracted from the message and processed. All attached files will be saved to the resource centre and in the case of images, they will processed as any other image and different thumbnails will be generated. Once attachments have been processed, a link to the file or image will be added to the final post that will be published in the blog page.

It supports two different methods for publishing posts:

  • Realtime publishing (push method)
  • Batch publishing (pull method)

Sending Messages

Moblogged messages must have some mandatory information in them so that the plugin can identify who is sending the message and where it should be posted. A sample message could look like this:

USER: youruser
PASS: yourpassword
BLOG: your blog name
This is a very simple example message, which is
being sent to blog 'your blog name', on behalf of
user 'youruser' whose password is 'yourpassword'.

This message would post a new article to blog your blog name for user youruser and it would try to authenticate using password yourpassword. Needless to say, the lines containing this kind of information will be removed before the post is added to the blog.

In order to specify a topic for the post, we can use in order of importance:


  1. A TOPIC: yourtopic line in the body of the message.
  2. The 'Subject' header (if we are sending an email message)
  3. The first 50 characters of the body of the message, stripped of all markup

Additionally, we can use '"BLOGID: blog_id instead of BLOG: your blog name in the body of the message, if we know the internal identifier of the blog. Using the blog id is obviously much shorter to type in mobile devices.

In order to summarize, these are the "commands" that can be used within the message body:


  • USER: the name of the user on behalf of whom this article is going to be posted
  • PASS: the password in order to authenticate
  • BLOG: the name of the blog to where this is going to be published. It can include spaces if necessary and can be in either lower case or upper case.
  • BLOGID: instead of typing the blog name, we can use the blog identifier if we know it. Much easier to type.
  • TOPIC: allows to specify a topic for the post.

From the items above, USER, PASS and either BLOG or BLOGID are necessary in every message and please keep in mind that each one of these has to appear in its own line.

It is not possible to configure to which category posts are added in the message body, only via the plugin configuration page (see "User configuration options" below).


Configuration


Before using this plugin, move the files moblog.php and moblogbatch.php from plugins/moblog/ to the root folder of LifeType (at the same level as index.php, admin.php, rss.php and so on). It is possible to leave it there by default but all examples in this document refer to moblog.php being in the root folder of LifeType.


Batch Publishing


This is the method you may prefer, if your blog is set up on a shared hosting or if you don't have access to the mailserver configuration and/or Curl. So if you don't know what Curl is, you probably want to use batch publishing. ;-) All you need to get up and running is POP3 access to an email account. Optionally access to some kind of cronjob can improve performance.

WARNING: you need an email account that is exclusively used for your Moblog. All messages in your inbox will be deleted no matter if they are valid moblog posts and get published or not. This will keep your moblog inbox clean from spam.


Configuring Batch Publishing

After installing the plugin you will get a new menu entry unter "Administration" called "Moblog Batch Settings". There are two ways to configure the batch publishing: Either you set up a batch job (aka "cronjob") that calls moblogbatch.php to check for new moblog posts or you let LifeType do the work uppon visitor requests ("Pseudobatch"). Needless to say that the second version may have a performance impact on visitors each time LifeType checks for new moblog posts. Thats why we recommend setting up a cronjob.


Batch Setup

As explained on the configuration page, you should set the "Pseudobatch" option to "disable" if you would like to set up a cronjob. A cronjob simply calls moblogbatch.php within a predefined interval. You can either use your servers cronjob feature or use one of the free cronjob services out there.

Your call to the script may look similar to this:

 /usr/local/bin/php5.cgi -d allow_url_fopen=1 /home/username/www/lifetype/moblogbatch.php silent

Pseudobatch Setup

The pseudobatch is the least efficient but the most simple method of setting up the moblog plugin. If you set the pseudobatch to, say, 10min, every visitor that requests one of your blogs pages will initiate a call to moblogbatch.php in the background, if it has been more than 10min since the last check to your moblog e-mail account.


Realtime Publishing


In order to get the push-method to work, it is necessary to have access to an email server running Postfix, Sendmail or similar SMTP server that accepts forwarding incoming messages to external commands via virtual (aliased) email addresses.

Additionally, the curl tool is also necessary in order to be able to forward email messages to a web server.

The e-mail address will be a virtual one, since the contents of the incoming email will be passed via standard input to curl, which will make an HTTP request to our moblog.php script. Moblog.php is the entry point to the functionality, and implements some code which is capable of parsing emails and extracting attachments from it.


Configuring Curl

curl is a commonly available tool in most Linux distros so there should not be any problem getting it to work.


Configuring the Email Alias

A dedicated email address is needed in order to use this functionality. The email address will be set up in the aliases file used by our SMTP server. Its location of this file may vary but it should be something like /etc/aliases or /etc/mail/aliases.

The new alias address should look like this:

moblog: "|curl -F message='<-' http://yourhost.com/moblog.php"

Once this new alias has been added, please do not forget to run the newaliases command.

In order to test this, please send a message to moblog@yourhost.com (please replace host names wherever relevant) If you wish to use a different email address, please replace moblog in the example above with your new address.

It is also worth mentioning that with this current configuration, this email address can be used as some kind of gateway for as many blogs as needed available in the current site, so there is no need to create a new email address for each one of our blog users (although this is possible, see the last section of this page)

If case of problems, one good way to debug possible SMTP server issues is by keeping an eye on the SMTP server log file and see if there is any error message in there.


User Configuration Options


The plugin adds a new configuration page for each blog under the Control Centre->Blog Settings, and it is necessary to first enable the plugin before using it.

In the plugin configuration page we can also configure the following things:


  • The article category to which new moblogged posts will be added. Just select one from the list of categories available in the blog.
  • The album to which attachments will be added.
  • When images are attached to images, this plugin will automatically include the image in the contents of the post, right below the content of the message. It is possible to choose how the image will be included in the post: as a small thumbnail, as a medium-sized thumbnail, or as full-size image. This setting defaults to a small thumbnail.

Compatibility List


Below you find a compatibility list of clients and mobile network operators. Let us know if you have tested the plugin with other clients or on other networks.


E-Mail Clients


  • Apple Mail.app 2.0
  • Microsoft Entourage 2004
  • Horde IMP
  • Microsoft Outlook 2000
  • Mozilla Thunderbird
  • Gmail
  • GMX

Mobile Devices (either e-mail or mms)


  • Nokia N91
  • Nokia 7610
  • Nokia 9500 Communicator
  • SonyEricsson P800
  • SonyEricsson K700i
  • SonyEricsson K750i
  • Nokia 3100 works, but has problems with encoding.
  • Siemens M65 works, but has problems with encoding, especially german characters called "Umlaute" (e.g. ä, ö, ü)

MMS to SMTP Gateways


  • Elisa (Finnland)
  • Swisscom (Switzerland)

The feasability of moblogging via MMS greatly depends on the operator. Most operators have MMS-to-SMTP gateways that can convert MMS messages to valid emails, that can be processed by any email clients. However, how these email messages are created greatly depends on the operator.

This feature has only been tested with the Finnish mobile operator Elisa. Its MMS to SMTP gateway will reformat MMS messages to mail messages with MIME content multipart/alternative with at least 4 MIME parts: an HTML part with some information about the message, a plain/text part with the text contents of the body of the message, the image attached (if any) and a SMIL file with information about how to play the presentation/MMS message.

This plugin is capable of handling this kind of message. It will ignore any text/html and application/smil MIME parts and will use the text/plain as the body of the message, as many as it can find (it will basically append one after each other). Image attachments will be processed as expected.

In case you manage to successfully use the plugin in a different network, please update this page with the operator name.


Advanced Configuration


Automatically Register an Album and Category


If you would like to automatically set up a default category and default folder for your users, some modifications to dofinishregister.class.php are needed:

// create a new default category called Moblog
$article2Categories = new ArticleCategories();
$article2Category = new ArticleCategory( Moblog, "", $newblogId, true );
$moblogCat = $article2Categories->addArticleCategory( $article2Category );
// create a new default folder called Moblog
$moblogAlbum = new GalleryAlbum(
$blogInfo->getId(), // id of the new blog
"Moblog", // new name
"Default category for moblog postings", // description
0, // no flags
0, // no parent id yet
, // date not needed, will be set when adding the album
Array(), // no properties
true // the album should appear
);
$albums = new GalleryAlbums();
$albums->addAlbum( $moblogAlbum );
//enable the moblog plugin for each new user by default
$blogInfo->setValue( "plugin_moblog_article_category_id", $this->_categoryId );
$blogInfo->setValue( "plugin_moblog_gallery_resource_album_id", $this->_albumId );
$blogInfo->setValue( "plugin_moblog_resource_preview_type", 1 );
$blogInfo->setValue( "plugin_moblog_enabled", 1 );

Forcing a certain blog ID and/or user name


If we find that typing USER:xxx, PASS:yyy and BLOG:zzz in every single message is too much (which could be a valid reason when using this plugin from mobile devices), it is possible to "hardcode" the user name and the blog identifier in the email alias, so we could rewrite the sample email alias from above as:

moblog: "|curl -F user=youruser -F blogId=1 -F message='<-' http://yourhost.com/moblog.php"

This means that we can also reduce the contents of messages that should be moblogged. The sample above could be reduced to the following contents, effectively removing the lines containing USER and BLOG:

PASS: yourpassword
This is a very simple example message, which is being sent to blog '
your blog name', on behalf of user 'youruser' whose password
is 'yourpassword'.

The new email alias will default to using 'youruser' as the user name and '1' as the blog identifier, and this means that it will not be necessary to include this information anymore in the body of the email message. However, it is still possible to override these default settings by including again a USER and/or BLOGID (or BLOG) line in the body of the message, since the contents of the message will always take more priority than whatever was configured in the email alias.

NOTE: Passwords cannot be hardcoded in the email alias for security reasons, since if somebody found out the email address that we are using for moblogging contents, it would mean free access for publishing stuff via email!


Enabling Logging


It is possible to tell the plugin to log messages to the standard log file (tmp/lifetype.log) for debugging purposes.

In the moblog.php file find the following line:

define( "MOBLOG_DEBUG", false );

and set it to true:

define( "MOBLOG_DEBUG", true );

Also make sure that the default logger is using the appender file instead of null, in config/logging.properties.php:

$config["default"] = Array( 
"layout" => "%d %N - [%f:%l (%c:%F)] %m%n",
"appender" => "file",
"file" => "tmp/lifetype.log",
"prio" => "debug"
);

The plugin will log a lot of information so keep an eye on the size of the log file.


Known issues



  • There could be potential issues related to character encodings, specially when it comes to encoded file names (in the case of files with Chinese file names)


  • If you are experiencing some problems with this plugin and all you can see in the logs is something like this:
12-10-2005 10:03:30 DEBUG - [mobloglogger.class.php:21 (logger:debug)] -- Initialized
12-10-2005 10:03:30 DEBUG - [mobloglogger.class.php:21 (logger:debug)] -- message --
12-10-2005 10:03:30 DEBUG - [mobloglogger.class.php:21 (logger:debug)] '<-'
12-10-2005 10:03:30 DEBUG - [mobloglogger.class.php:21 (logger:debug)] -- end --
12-10-2005 10:03:30 DEBUG - [mobloglogger.class.php:21 (logger:debug)] From REQUEST: user = - blogId =
12-10-2005 10:03:30 DEBUG - [mobloglogger.class.php:21 (logger:debug)] parseBody ---> body =
12-10-2005 10:03:30 DEBUG - [mobloglogger.class.php:21 (logger:debug)] There are 0 MIME parts available to parse
12-10-2005 10:03:30 DEBUG - [mobloglogger.class.php:21 (logger:debug)] subject is = No Topic
12-10-2005 10:03:30 DEBUG - [mobloglogger.class.php:21 (logger:debug)] user = ''
12-10-2005 10:03:30 DEBUG - [mobloglogger.class.php:21 (logger:debug)] blog id =
12-10-2005 10:03:30 DEBUG - [mobloglogger.class.php:21 (logger:debug)] topic = No Topic
12-10-2005 10:03:30 DEBUG - [mobloglogger.class.php:21 (logger:debug)] reply to =
12-10-2005 10:03:30 DEBUG - [mobloglogger.class.php:21 (logger:debug)] body =
12-10-2005 10:03:30 DEBUG - [mobloglogger.class.php:21 (logger:debug)] User did not authenticate correctly.
12-10-2005 10:03:30 DEBUG - [mobloglogger.class.php:21 (logger:debug)] Response message sent!

Please change the line in your /etc/aliases file to make it look like:

|curl -F message=<- http://.../plog/moblog.php

That is, without the single quotes surrounding <-


  • If using the lightppd web server, you have to add the following parameter to the alias calling curl to make it actually POST the info to the web page, otherwise lighttpd just ignores the entire request, without even logging anything:
-H "Expect:"
  

2008-02-07

携帯電話のスケジュールに Jリーグ日程を入れる

携帯電話のスケジュール機能だが、入力が面倒なのであまり使っていなかった。DoCoMo の場合、Winであれば、 ドコモケータイdatalink というのが無料で利用できる。スケジュールにも、CSV インポートが出来るようなのでやってみた。

フォーマットは、最低限以下のフィールドがあれば良いらしい。

要約 内容 開始日時 終了日時

文字コードは、MS932(いわゆるシフトJIS)。日時フォーマットが少し特殊で、iCal(ics)ファイルなどで使われてた奴からタイムゾーン除いたもの(YYYYMMDDTHHMMSS)。ISO 表記?だっけ?

で、作ってみたファイル。ここには、画像以外貼れないので以下のリンクは、外部リンクです。

ドコモケータイ datalink に、インポート自体は出来たが携帯側に300件制限があることが多いようだ。両方、300件超有るから、適宜削って携帯に転送。もちろん両方携帯に入れとくのは無理だった。まぁ、贔屓のチームだけとか、直近3ヶ月もあれば良いことだからね。

SO902iwp+ では、転送できた。SH905i では、何件か抜ける。なんで?

2008-01-30

2008-01-26

ザスパ草津 2008 J2リーグ日程

google カレンダーで iCal ファイルを作ってみた。Vista の Windows カレンダーで購読出来ることは、確認した。

11/29 ホーム最終戦の開催地が空白なのが気になる。松本開催とか無いよね。まさかねぇ...

2008-01-20

必勝祈願@草津

今年も、性懲りも無く行ってきた。0740 頃、前橋を出て、0945 頃到着。やっぱり寒い。たぶん氷点下。

shiranejinja

1年間怪我無く、楽しいゲームを見せてください。

お神酒の代わりは水でした。

必勝祈願の後は、徒歩で湯畑に移動。今年は、寒いけど雪が少なくて歩きやすい。

全員での写真撮影の後、トップチームは、前橋へ帰っていきました。

こちらは、大滝の湯へ。露天風呂で、髪の毛が凍った(笑

龍燕で昼飯食って、U-23 の練習見学のため本白根第2グラウンドへ。

motoshirane#2_1

何名か、テストがあるので帰ったそうですが、今年は、対戦形式の練習が出来そうな人数が集まってくれました。この、一面雪のグラウンドからJのピッチに辿り着いてください。

そうそ、クラブの偉い方もみえてました。

2時間ほどで、終了。練習してる方も見てる方も大変でした。冷え切ったのでベルツ温泉センターで温まって、本家ちちやで温泉まんじゅうをお土産に買って帰ってきました。



大きな地図で見る

2008-01-18

DoCoMo 携帯にステレオイヤホンを繋いだ

悩んだ末、ウイルコム株式会社(某PHS屋さんとは無関係) WS-05 を購入。\4,980-也。FM トランスミッター と USB 電源出力付き。なんか、一番豪華なの買っちまった。

FM トランスミッターの方は...少し残念な感じ。まぁ、こんなもんなのかな。車中で充電できるようにもなったし、ヘッドフォンではもちろんもう少しましな音質で聞けます。

bitweaver で Graphviz

Graphviz が使える Wiki を探してたら、bitweaver が引っかかりました。Wiki も使えますが、機能満載の CMS といった趣です。バージョンは、2.0.0 を使いました。

CentOS 5 の環境に設置しました。

Graphviz から、RHEL5 用の rpm をダウンロードしてきてインストール。graphviz-gd は、png などのラスタフォーマットで出力するときに必要な Graphviz プラグインらしい。

  • graphviz-2.16.1-1.el5.i386.rpm
  • graphviz-gd-2.16.1-1.el5.i386.rpm

Tiki管理パネル - Liberty - Plugins で Graphviz をチェックして有効にします。ヘルプリンクが DataPluginExample になってます。ドキュメントが見当たらないので、ソースを参照。

{GRAPHVIZ}{/GRAPHVIZ} タグの中に dot マークアップを書けばよいようです。日本語は、fontname 指定で問題なく出力されました。

{GRAPHVIZ}
digraph sample {
node [fontname="/usr/share/fonts/japanese/TrueType/sazanami-gothic.ttf"];
こんちわ -> みなさん;
}
{/GRAPHVIZ}


こんな図が出力されました。



graphviz 出力結果

2008-01-11

DoCoMo 携帯にステレオイヤホンを繋ぐ

もし可能ならば、リモコン機能とか欲しい。

量販店では、様々な商品が売られていて、その場でどれを選ぶか決められなかった。人間が小さい。
ドコモさんは、FOMA(フォーマ)に関する資料 のページ周辺にいろいろ情報があるのでちょっと調べてみる。

JEITA RC-5240 携帯電話用角形コネクタは、

  • ヘッドセット接続(モノラル/ステレオ)
  • AV出力ケーブル(端末が対応していれば)

この2つの用途に使えるらしい。ヘッドセット接続の場合は、ステレオにももちろん対応。ただし、SWは1接点のみ。オン/オフフックくらいしか出来ないのね。

アンプも必要ないみたいだから、機能的には、差異が出来にくい。デザインと値段で選んでかまわないな。ほとんど彼の国製だろうからはずれだったらあきらめよう。

2008-01-08

CakePHP で携帯ブラウザ判定

CakePHP 1.2 でのおはなし。

PEAR::Net_UserAgent_Mobile を使おうと思っていた。

参考:

CakePHP プログラマーズ リファレンスガイド を読んでたら、 RequestHandler に isMobile なるメソッドを発見。ソースを見てみたら 'DoCoMo' なども判定できるようなので簡易的には、行けそう。

コントローラでコンポーネントの宣言をして

    var $components = array('RequestHandler');




以下の判定で携帯用のURLにリダイレクトをしたりする。




            if ((!isset($this->params["webservices"]) || $this->params["webservices"] != "Mobile")) {
if ($this->RequestHandler->isMobile()) {
$this->redirect('携帯のURL', null, true);
}
}




Vodafone 時代の携帯は、判定できた。あとは、新しめの Softbank と au。