iGoogleっぽいことをやるには その2

前回の続きです。

ウィジェットを識別

Xilinus.Portalのserialize()メソッドでウィジェットの並びを得ることができるのが判ったが、ウィジェットを識別するために、どうやって識別するための情報を付与する方法をです。すばり、書いてしまうとXilinus.Widgetのコンストラクタの第2引数にdivタグのid属性を指定できます。例えば下の例だとwidget-1_0としています。ちなみに、第1引数はdivタグのclass属性です。

PHP:
  1. portal.add(new Xilinus.Widget(null,'widget-1_0').setTitle("Widget #1.0").setContent(latin1), 0);

コンストラクタの引数を省略した場合の振る舞いについては、portal.jsを見るとclass属性は一律widget、id属性はwidget_連番でした。これを使って、DBなどにある情報と画面にでているウィジェットを対応付けることができますね。

iGoogleっぽいことをやるには

ユーザーが再配置可能なWedgetを実装するJavaScript (Prototype Portal Class)Prototype.jsでiGoogleっぽいポータルな仕組みを作れるJSライブラリ「Xilinus」で紹介されているPrototype Portal Classなるものを使うことで、簡単にできそうな雰囲気を見せているのだが、実際にどうやってつくっていけばいいのか(たとえば、移動した状態の取得や設定らしきボタンの振る舞い)を読み取れないので、svnにあったtest/index.html(現物は本家のlive exampleです)を元にもう少し具体的な使い方に踏み込んでみたいと思います。

移動した結果を知るには

Xilinus.Portalのserialize()メソッドを実行すると、

widget_col_0[]=widget-1_0&widget_col_0[]=widget-2_0&widget_col_0[]=widget-1_1&widget_col_0[]=widget-1_2&widget_col_1[]=widget-2_1&widget_col_2[]=widget-1_4&

という結果を得られます。widget_col_xはウィジェットを置いている(?)横に3つ並んだdivのidです。そして、wiget-x_xは個々のウィジェットとなるdivのidです。これの解釈はwidget_col_0には上からwidget-1_0,widget-2_0,widget-1_1、widget-1_2があり、widget_col_1にはwidget-2_1だけが、widget_col_2にはwidget-1_4だけがあるというのがわかります。PHPなら$_POSTから見るとうまい具合に配列になっているはず。

それをサーバに通知するには 【ajax編】

コンストラクタでurl パラメータを用いてpost先を指定します。そうすることで、ウィジェットを移動後にAjax.UpdateでサーバにPOSTされます。

PHP:
  1. portal = new Xilinus.Portal("#page div.col", {onOverWidget: onOverWidget, onOutWidget: onOutWidget, onChange: onChange, onUpdate: onUpdate, url : "http://www.akky.org/xilinux/test/index.php"});

それをサーバに通知するには【ボタン編】

グローバル変数で portal変数を宣言しているので、onlickイベントでportal.serialzie()を実行すると値を得られます。あとは、適宜Formを用意してsubmitすることになります。

HTML:
  1.  <input type="button" value="serialize" onclick="alert(portal.serialize())">
  2.  
  3.   <script type="text/javascript">
  4.      var portal;
  5. /* 途中省略 */
  6.      function init() {             
  7.        portal = new Xilinus.Portal("#page div.col", {onOverWidget: onOverWidget, onOutWidget: onOutWidget, onChange: onChange, onUpdate: onUpdate, url : "http://www.akky.org/xilinux/test/index.php"});
  8.        // Fake widgets
  9.        portal.add(new Xilinus.Widget(null,'widget-1_0').setTitle("Widget #1.0").setContent(latin1), 0);
  10.        portal.add(new Xilinus.Widget(null,'widget-1_1').setTitle("Widget #1.1").setContent(latin2), 0);
  11.        portal.add(new Xilinus.Widget(null,'widget-1_2').setTitle("Widget #1.2").setContent(latin3), 0);
  12.            
  13.        portal.add(new Xilinus.Widget(null,'widget-2_0').setTitle("Widget #2.0").setContent(latin2 + latin3), 1);
  14.        portal.add(new Xilinus.Widget(null,'widget-2_1').setTitle("Widget #2.1").setContent(latin3), 1);     
  15.      
  16.        portal.add(new Xilinus.Widget(null,'widget-1_4').setTitle("Widget #1.4").setContent(latin1+latin2+latin3), 2);
  17.        // Add controls buttons
  18.        portal.addWidgetControls("control_buttons");
  19.      }
  20.      Event.observe(window, "load", init);
  21.   </script>

実際に動くもの

これを試してみてください
http://www.akky.org/xilinux/test/index.html
移動した後には、画面下部にserialize()結果を表示しています。他にも手を加えているので、、、続きます。

hiernate ハマリ道 @Table書き忘れ。

Criteria.list()のタイミングで結果を取得できない。第2弾

hibernate.cfg.xmlにmappingのエンティティクラスの名前を書いたけど、エンティティのソースに

@Entity
@Table(name = "テーブル名")

を書いていなかった。

log4jを使っている場合に、hibernateのログをやったら出して、下のログが出ていればマッピング情報が作られている。

Bind entity エンティティクラス名 on table テーブル名

マッピングの状態を

hiernate ハマリ道 hibernate.xfg.xmlを入れ忘れ

hibernate3でannotationを使ってコーディングを実施。org.hibernate.Criteria#list()を何度実行しても、空のリストが帰ってくる。DB側のログを見ると接続してはいるが、SQLを実行している形跡がない。テーブルを変えてみても駄目。

それで、別プロジェクトで作られた同じやり方をしているソースコード見ても特段の違いを感じられない。もう、こうなったらということで、一通りのソースを見ていると、hibernate.xfg.xmlを発見。これが無いからだorz。半日無駄にしてしまった。

php.iniの設定を.htaccessで上書きできない

レンタルサーバーを使っていてphp.iniの上書きをしたいときに、レンタルサーバのマニュアルに従って.htaccessを編集しても、phpinfo()で見ると設置値が反映されていない場合の対応。

display_errorsのマニュアルを見ていて知った。ini_set関数を使うことで、解決できる場合もある。

注意: display_errors は実行時にも設定可能(ini_set() 関数を用いて)ですが、スクリプトが致命的(fatal)なエラーを発生した場合は その設定は反映されません。なぜなら、要求されたアクションは 実行されなかったからです。

ということで、文法エラー系には効かないと解釈したのだが、requireしているファイルの文法エラーはメッセージを出してくれた。set_ini()を記述しているファイルでなければ問題ないということなのだろうか。

例えばdisplay_errors を有効にしたい場合の記述はこれ。

ini_set("display_errors","On");

エラーの原因がわからないために、1日つぶしてしまった。ちなみに、PEAR.phpが無いのが原因だった。

webalizer-2.01-10をインストール

Fedra6にwebalizer-2.01-10をインストールした顛末です。

You must have a V1.85 compatable DB libraryっていわれる

ホスト名を逆引きしたいので

CODE:
  1. ./configure  --with-language=japanese --enable-dns
  2. とすると
  3. [code]
  4. [root@vesalius webalizer-2.01-10]# ./configure  --with-language=japanese --e
  5. nable-dns
  6. loading cache ./config.cache
  7. checking for gcc... (cached) gcc
  8. checking whether the C compiler (gcc  ) works... yes
  9. checking whether the C compiler (gcc  ) is a cross-compiler... no
  10. checking whether we are using GNU C... (cached) yes
  11. checking whether gcc accepts -g... (cached) yes
  12. checking whether ln -s works... (cached) yes
  13. checking for a BSD compatible install... (cached) /usr/bin/install -c
  14. checking how to run the C preprocessor... (cached) gcc -E
  15. checking whether char is unsigned... (cached) no
  16. checking for dbopen... (cached) no
  17. checking for library containing dbopen... (cached) no
  18. configure: warning: You must have a V1.85 compatable DB library!
  19. configure: warning: DNS lookup code will be disabled...
  20. checking for socket... (cached) yes
  21. checking for sys/socket.h... (cached) yes
  22. checking for main in -lnsl... (cached) yes
  23. checking for main in -l44bsd... (cached) no
  24. checking for main in -lm... (cached) yes
  25. checking for main in -lz... (cached) yes
  26. checking for gzrewind in -lz... (cached) yes
  27. checking for main in -lpng... (cached) yes
  28. checking for gdImagePng in -lgd... (cached) yes
  29. checking for gd.h... (cached) /usr/include
  30. checking for getopt.h... (cached) yes
  31. checking for math.h... (cached) yes
  32. checking default config dir... (cached) /etc
  33. checking for language file... yes - japanese
  34. creating ./config.status
  35. creating Makefile
  36. linking ./lang/webalizer_lang.japanese to webalizer_lang.h
  37. [root@vesalius webalizer-2.01-10]#

ってなる。コンパイルもでき、実行もできるのだけど、DNSCacheDNSChildrenの設定をしてあると

Warning: Invalid keyword 'DNSCache' (/etc/webalizer.conf)
Warning: Invalid keyword 'DNSChildren' (/etc/webalizer.conf)

っていわれてしまい、逆引きもできていない。configure時の

configure: warning: You must have a V1.85 compatable DB library!
configure: warning: DNS lookup code will be disabled...

の警告に起因しているみたいだ。Berkeley DBの1.85を用意せよとおっしゃる。なので、rpmから

  • db4-devel-4.3.29-9.fc6
  • db4-4.3.29-9.fc6

を入れておく

Berkeley DBを使ってコンパイル

rpmから入れたBerkeleyDBを使うように

./configure  --with-language=japanese --enable-dns --with-dblib=/usr/lib -with-db=/usr/include

とすると、configure時の警告は消えるが、今後は、コンパイルが通らない

dns_resolv.c: In function `resolve_dns':
dns_resolv.c:149: warning: passing arg 2 of pointer to function from incompatible pointer type
dns_resolv.c:149: error: too few arguments to function
dns_resolv.c: In function `dns_resolver':
dns_resolv.c:218: warning: implicit declaration of function `dbopen'
dns_resolv.c:218: warning: assignment makes pointer from integer without a cast
dns_resolv.c:228: error: too few arguments to function
dns_resolv.c:235: error: too few arguments to function
dns_resolv.c:275: warning: passing arg 2 of pointer to function from incompatible pointer type
dns_resolv.c:275: error: too few arguments to function
dns_resolv.c:329: error: too few arguments to function
dns_resolv.c:359: error: too few arguments to function
dns_resolv.c: In function `db_put':
dns_resolv.c:745: warning: passing arg 2 of pointer to function from incompatible pointer type
dns_resolv.c:745: error: too few arguments to function
dns_resolv.c: In function `open_cache':
dns_resolv.c:793: warning: assignment makes pointer from integer without a cast
dns_resolv.c:801: error: too few arguments to function
dns_resolv.c:807: error: too few arguments to function
dns_resolv.c: In function `close_cache':
dns_resolv.c:829: error: too few arguments to function
make: *** [dns_resolv.o] Error 1

引数の数が違うとな。/usr/include/db.hdns_resolv.cを見比べるとやっぱり、引数数が合わない。むむむと、dns_resolv.cと睨めっこしていると

C:
  1. ifdef HAVE_DB_185_H
  2. #include &lt;db_185.h&gt;                            /* on my RH6.0 system ?!?   */
  3. #else
  4. #include &lt;db.h&gt;                                /* DB header ****************/
  5. #endif /* HAVE_DB_185_H */

という記述を発見。db_185.hがあるとな。最初の警告にあるV1.85と符合する。db_185.hを探すと/usr/include/db4/にヘッダファイルを発見。これを読ませればいいんだな。ということで、

C:
  1. CFLAGS='-DHAVE_DB_185_H' ./configure  --with-language=japanese --enable-dns --with-dblib=/usr/lib --with-db=/usr/include

としてコンパイル。これでコンパイル時の問題と実行時の問題が無事解決です。

Services_HyperEstraierを使ってPHPでノードから検索

HyperestraylerのPHPバインディングは標準では配布されてないので、移植されたものを http://page2.xrea.jp/ からダウンロード。今回使うのは、PHP5 専用のServices_HyperEstraier の方。

ファイルの構成は下の通り。ServiesはServices_HyperEstraierから取得したものです。

common.php
index.php
Services/HyperEstraier.php
Services/HyperEstraier/Error.php
Services/HyperEstraier/Condition.php
Services/HyperEstraier/ResultDocument.php
Services/HyperEstraier/Node.php
Services/HyperEstraier/Document.php
Services/HyperEstraier/Utility.php
Services/HyperEstraier/HttpResponse.php
Services/HyperEstraier/.ResultDocument.php.swp
Services/HyperEstraier/NodeResult.php
Services/HyperEstraier.php

common.php

<?php
define('SERVICES_HYPERESTRAIER_DEBUG', 1);
error_reporting(E_ALL & ~E_STRICT);

require_once 'Services/HyperEstraier/Node.php';

$uri = 'http://localhost:1978/node/main';
$user = 'admin';
$pass = 'admin';

index.php

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<meta http-equiv="Cache-Control" content="no-cache, must-revalidate, no-transform" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta http-equiv="Content-Style-Type" content="text/css" />
<meta http-equiv="Content-Script-Type" content="text/javascript" />
</head>
<body>
<?php
$phrase = isset($_GET["phrase"]) ? $_GET["phrase"] : null;
?>
<form action="<?php echo $_SERVER["PHP_SELF"] ?>" method="get">
検索:
<input type="text" name="phrase" value="<?php echo $phrase ?>" size="32" id="phrase" class="text" tabindex="1" accesskey="0" />
<!--
並び替え
<select name="sort">
  <option value="score">候補</option>
  <option value="updateDate">更新日付</option>
  <option value="url">URL(ディレクトリ)</option>
  <option value="size">サイズ</option>
</select>
-->
<input type="submit" value="検索"/>
</form>

<?php
require_once dirname(__FILE__) . DIRECTORY_SEPARATOR . 'common.php';
// create and configure the node connecton object

if  ($phrase != null ) {

  $node = new Services_HyperEstraier_Node;
  $node->setUrl($uri);

  // create a search condition object
  $cond = new Services_HyperEstraier_Condition;
  $cond->setPhrase($phrase);
  $cond->setMax(10);
  $cond->setSkip(0);

  // get the result of search
  $nres = $node->search($cond, 1);
  if ($nres) {
      if ($nres->docNum() == 0) {
          printf( "%s: not found.\n", $cond->getPhrase());
      } else {
          foreach ($nres as $rdoc) {
              printf("<h2><a href='%s'>%s<a/></h2>\n"
                ,$rdoc->getAttribute('@uri')
                ,$rdoc->getAttribute('@title')) ;
              printf("%s<br/>\n",$rdoc->getAttribute('@uri'));
              print("<BLOCKQUOTE>");
              foreach( split("\n",$rdoc->getSnippet()) as $line ){
                if ( mbereg('   ',$line) ){
                  printf("<strong>%s</strong>",mbereg_replace(' .*$','',$line));
                }else{
                  printf("%s",$line);
                }
              }
              print("</BLOCKQUOTE>\n");
          }
      }
  } else {
      printf("error: %d\n", $node->status);
      if (Services_HyperEstraier_Error::hasErrors()) {
          printf( print_r(Services_HyperEstraier_Error::getErrors(), true));
      }
  }
}
?>

</body>
</html>

最寄り駅Webサービス

緯度経度や住所から最寄駅を教えてくれるAPIサービス
http://map.simpleapi.net/

指定した位置からの直線距離も教えてくれる。
目的とする位置の緯度/経度はわからないので、これは駅名から緯度経度に変換すればいいのかな。
駅の緯度/経度がわかればマーカーなりは表示できるし。

ExcelVBAでUTF-8のcsvファイルを書き出す

表題の通り。

ADODB.Streamdでは、クローズするときにファイル名を指定するのはしっくりこないな。名前の通りにストリームだから「ファイル名なんて飾りですよ」ののりなのかな。
http://msdn.microsoft.com/library/ja/default.asp?url=/library/ja/jpado260/htm/mdobjstream.aspにあるマニュアルを探し出せるまで満足に実行することもできなかった。

Const SHEETNAME As String = "Sheet1" ' 対象とするシート名
Const STARTROW = 2 ' 開始行
Const LASTCOL = 13 ' 最終列

Private Sub cmdCSVoutput_Click()
    Dim excelFiles As Variant
    Dim exceiFile As String
    Dim csvFile As String
    Dim i As Integer

    ' 複数指定で対象とするファイルを指定
    exelfiles = Application.GetOpenFilename(FileFilter:="Microsoft Excelブック,*.xls", MultiSelect:=True)
    ' キャンセル
    If Not IsArray(exelfiles) Then
        Exit Sub
    End If

    For i = 1 To UBound(exelfiles)
    ' 同じパスで拡張子をcsvへ変更してCSVファイルを書き出す
        exceiFile = exelfiles(i)
        csvFile = Mid(exceiFile, 1, Len(exceiFile) - 3) + "csv"
        csvputput exceiFile, csvFile
    Next i

End Sub

'
' 指定されたExcelファイル
'
Private Sub csvputput(ByVal excelFile As String, ByVal csvFile As String)
    Dim wb As Workbook

    Dim ws As Worksheet ' データがあるシート
    Dim lastRow As Long ' 最終行
    Dim col As Long ' 現在の対象列
    Dim row As Long ' 現在の対象行

    Dim buf As String
    Dim v As Variant

    ' 文字コードにUTF-8を指定して書き出すCSVファイルをオープン
    Set outStream = CreateObject("ADODB.Stream")
    outStream.Open
    outStream.Charset = "UTF-8" ' ファイルの文字コード
    outStream.LineSeparator = -1 ' CRLF
    outStream.Type = 2    ' テキスト

    ' 指定されたシートを開く
    Set wb = Workbooks.Open(excelFile)
    Set ws = wb.Worksheets(SHEETNAME)
    ' 出力するCSVファイルを開く

    ' ヘッダを飛ばして2行目からを書き出す
    lastRow = getLastRow(ws)
    For row = STARTROW To lastRow
        ' 1行分のデータを作成
        buf = ""
        For col = 1 To LASTCOL

            buf = buf & ",""" & ws.Cells(row, col) & """"
        Next
        buf = Mid(buf, 2)

        ' ファイルへ書き出し 文字コード変換は ADODB.Stream がやってくれる
        outStream.WriteText buf, 1

    Next
    ' ファイル名を指定してクローズ
    outStream.SaveToFile csvFile, 2
    outStream.Close

    ' Excelを閉じる
    wb.Close

End Sub

'
' 指定されたシートの最終行を取得する
'
' 行の各セルに値がなければ無効行と判断する
'
Private Function getLastRow(ws As Worksheet)
    Dim i As Long

    ' シートの末尾から先頭方向へ向かって有効な行がある位置を探す
    For i = ws.Cells(65535, 1).End(xlUp).row To 1 Step -1
        If isValidRow(ws, i) Then
            Exit For
        End If
    Next

    ' 最終行を戻り値に設定
    getLastRow = i
End Function

'
' 指定された行の値を検査して、一つでも値が入っていれば有効な行と判断する
'
Private Function isValidRow(ws As Worksheet, row As Long)
    Dim i As Long
    Dim b As Boolean

    b = False

    For i = 1 To LASTCOL
        If ws.Cells(row, i) <> "" Then
            b = True
            Exit For
        End If
    Next

    isValidRow = b
End Function

Centos4.6でapache1.3がコンパイルエラー

コンパイルしようとすると、

 ndbm.h No suche file or directory

とおっしゃられる。ぐぐってみるとgdbmのヘッダらしいけど、それ自体も見つからず。そもそも、gdbm-develのパッケージから入れないといけないらしい。というわけで、さくっとyumでインストール。

最近はgccすら明示的に入れないといけないんだよな・・・

ヘッダファイルは/usr/include/gdbmにあるので、

 CLFAG=/usr/include/gdbm ./configure ~

とやって解決。シンボリックリンクを張っている記事もあるけど、コンパイルの王道はこうだよな。

←Older