2013年6月24日月曜日

医療スタッフツール添付文書検索 for Android

医療情報事務所一貫堂ではこれまでに「添付文書検索ツール」を薬剤師必携の添付文書閲覧アプリケーションとしてご提供させて頂いて参りました。

この度さらに機能が充実して新たに「医療スタッフツール添付文書検索」として生れ変りました。






これまでの医療用医薬品と一般用医薬品の添付文書のみならずPMDAで提供されている体外診断薬や医療機器の添付文書検索・閲覧にも対応しました。

またタブレットでの2ペイン表示や添付文書の複数比較閲覧、そして表示履歴リスト等にも新たに対応しまったく新しいアプリケーションとして生まれ変わりました。

それぞれの機能については本ブログにて少しずつご紹介して行きたいと考えております。

添付文書検索ツール同様、新しくなった「医療スタッフツール添付文書検索」を宜しくお願い致します。

お薬手帳カバー作成webサービス公開

医療情報事務所一貫堂ではさまざまなサービスを展開するべく日夜開発を続けております。

本日ご紹介させて頂くのはお薬手帳カバー作成webサービスです。



お薬手帳カバー作成webサービス

お薬手帳は薬剤師の方々には云わずと知れたものだと思いますがまだまだ一般の方の認知度はそれほど高いと云える状況ではないかもしれません。しかし数年前に比べれば徐々にその度合いは高まってきていることは日々の業務の中からも実感出来ていることと思います。

しかし医療期間受診のたび、薬局への来局のたびに毎回きちんと持参してくださる患者さんはまだまだ少ないといった現状でしょうか。

本webサービスでは、患者さんにお持ち頂くお薬手帳に好みのカバーを付けることでお持ち頂く患者さんオリジナルのお薬手帳とすることが出来ます。お薬手帳の衣替えすることでより愛着を持って頂き、日々医療機関や薬局へ持参して頂けるような工夫をするためのツールとしてお役立てください。

是非上記リンクよりwebサービスサイトへご移動頂きお薬手帳カバーを作成してみてください。A4用紙に印刷後点線に従ってハサミでカットし手帳のビニールカバー内に折り込んで入れることでカバーが完成します。

2012年7月12日木曜日

GitHubを使ってみる。

だいぶ前にGitHubのアカウントは作成していたもののこれまでちゃんと使ってみたことはなかった。

しかし今回BEAR.Sundayを勉強しはじめてtypoを発見したり解決できない問題にぶちあたったりとしている状況の中pull requestやらissueのopenやらをやってみた。

その際に以下のサイトを参考にさせていただいた。



まだGitをちゃんと使えるようになれていないし、GitHubについてもわからないことだらけではあるものの今回思ったことは、「手を動かしてみないとなにも理解できない。」ということだった。

2012年7月10日火曜日

BEAR.Sundayのphpunitの実行

先日からいじりはじめたBEAR.Sundayにはしっかりとunittestが書かれており動作確認の意味でもこのテストの実行はとても重要なものだと思われる。先日もインストール後にはphpunitを実行してオールグリーンを確認していた。

BEAR.Sundayのことを教えてくれたNEKOGETさんがmysqlのrootにパスワードが設定してあるとテストが通らないということを云ってたのでちょっと調べてみた。

2012/7/12 追記
※ MySQLのrootにパスワードが設定されている場合の対処法が本体の方で修正が入りました。

http://code.google.com/p/bearsunday/wiki/Install をご参照ください。
./tests/PagingQueryTest.php:        $this->pdo = new \PDO("mysql:host=localhost; dbname=blogbeartest", "root", "");
./tests/PagerTest.php:        $this->pdo = new \PDO("mysql:host=localhost; dbname=blogbeartest", "root", "");
./tests/DoctrineDbalAdapterTest.php:        $this->pdo = new \PDO("mysql:host=localhost; dbname=blogbeartest", "root", "");
./apps/sandbox/tests/PageBlogPostsTest.php:        $pdo = new \PDO("mysql:host=localhost; dbname=blogbeartest", "root", "");
./apps/sandbox/tests/AppPostsTest.php:        $pdo = new \PDO("mysql:host=localhost; dbname=blogbeartest", "root", "");

./sandbox/scripts/test.config.php:        'password' => null,
./sandbox/scripts/test.config.php:        'password' => null,

ざくっと上記7つのファイルに対してrootのパスワードを設定しないとphpunitがEとSの嵐になってしまう。
前者5つは"password"後者2つは'password'という形での設定が必要となるようだ。

私はmysqlにまったくもって慣れていないのでrootのパスワードの設定やパスワードリセットに苦労したのは云うまでもないw

ちなみにmysqlのrootパスワードの設定方法は

# mysql

mysql> SET PASSWORD FOR root@localhost=PASSWORD('hoge');
Query OK, 0 rows affected (0.00 sec)

mysql> quit

のような感じで行う。

またパスワードクリアについては

# killall mysqld
# /usr/bin/mysqld_safe --user=root --skip-grant-tables & mysql mysql

mysql> update user set Password=null where Host='localhost' and User='root';
mysql> quit

# /etc/rc.d/init.d/mysqld restart

な感じでいけるので備忘録。

2012年7月9日月曜日

BEAR.Sundayをインストールしてみた。(1)

NEKOGETさんからおもしろそうと勧められたPHPのフレームワークBEAR.Sundayを試してみる。
当初Ubuntuにインストールを試みたのだが途中からcomposerによる外部ライブラリのインストールが上手くいかなくなってしまいFedora17に入れてみた。

Fedora-17-x86_64-DVD.isoをVMWare環境にインストールを敢行。

○仮想マシン設定
メモリ2GB
プロセッサ4コア Intel VT-x/EPTまたはAMD-V/RVIを仮想化にチェック
ネットワークアダプタ ブリッジ
ディスク 20GB
CD/DVDにisoイメージをマウント
ディスプレイモニタ設定を指定 解像度1024x768

ディスプレイモニタを指定しないとFedoraのインストーラ下部のボタンが表示されなかった。


パッケージ選択でソフトウエア開発のデフォルトインストールを行ないGnome環境を構築。
vmware-toolsをインストールしyum updateを実行し最新環境に再起動。

Fedora17のベース環境が出来たところでapache2やmysqlそして必要なパッケージを導入しておく。
$ sudo yum install httpd mysql-server git curl
apache2とmysql-serverを起動し、自動起動の設定を行い動作確認。
$ sudo systemctl start httpd.service
$ sudo systemctl enable httpd.service
ln -s '/usr/lib/systemd/system/httpd.service' '/etc/systemd/system/multi-user.target.wants/httpd.service'
$ sudo systemctl start mysqld.service
$ sudo systemctl enable mysqld.service
ln -s '/usr/lib/systemd/system/mysqld.service' '/etc/systemd/system/multi-user.target.wants/mysqld.service'
php関係のパッケージを導入。以下のものでおそらく事足りるかと思われる。
$ sudo yum install php php-mbstring php-pear php-pecl-apc php-xml php-mysql php-pecl-xhprof

そしてphp.iniを編集。

$sudo vi /etc/php.ini

default_charser = UTF-8
date.timezone = Asia/Tokyo

mbstring.language = Japanese
mbstring.internal_encoding = UTF-8
mbstring.http_input = pass
mbstring.http_output = pass
mbstring.encoding_translation = Off
bstring.detect_order = auto
mbstring.substitute_character = none;
bstring.func_overload = 0
mbstring.http_output_conv_mimetype=

apc.enable_cli = 1
xhprof.output_dir = /tmp
上記を設定し、phpのバージョンを確認。
$ php -v
PHP 5.4.4 (cli) (built: Jun 14 2012 17:00:36)
Copyright (c) 1997-2012 The PHP Group
Zend Engine v2.4.0, Copyright (c) 1998-2012 Zend Technologies
ホームディレクトリにとりあえずBEARディレクトリを作成してgithubよりclone。
$ mkdir BEAR
$ cd BEAR/
$ git clone git://github.com/koriym/BEAR.Sunday.git
Cloning into 'BEAR.Sunday'...
remote: Counting objects: 5747, done.
remote: Compressing objects: 100% (1923/1923), done.
remote: Total 5747 (delta 2891), reused 5720 (delta 2864)
Receiving objects: 100% (5747/5747), 2.76 MiB | 69 KiB/s, done.
Resolving deltas: 100% (2891/2891), done.
composerのインストールでの変化を確認するためにディレクトリ構造を保存しておいた。
curlでcomposerのインストーラを取得してphpコマンドに渡しcomposer.pharをインストールした。
$ ls -R BEAR.Sunday >List1
$ cd BEAR.Sunday
$ curl -s http://getcomposer.org/installer | php
#!/usr/bin/env php
All settings correct for using Composer
Downloading...

Composer successfully installed to: /home/shige/BEAR/BEAR.Sunday/composer.phar
Use it: php composer.phar
composerのインストール前とインストール後での違いを見るとcomposer.pharファイルのみが導入されていることが確認できた。
$ cd ../
$ ls -R BEAR.Sunday >List2
$ diff List1 List2
6a7
> composer.phar
導入したcomposerを利用して外部ライブラリを導入する。
$ cd BEAR.Sunday
$ ./composer.phar install
Installing dependencies
  - Installing aura/autoload (dev-master)
    Cloning 1b8028cd301a8c95da7f0b1725bbf4fc8a495529

  - Installing aura/router (dev-master)
    Cloning 9b7cceaf1914214ac615cf1697f39d4f4f71e52d

  - Installing aura/web (dev-master)
    Cloning 111c884927e597f25fe8e14bf2d32860469b3730

  - Installing monolog/monolog (1.1.0)
    Downloading: 100%       

  - Installing symfony/event-dispatcher (v2.0.15)
    Downloading: 100%      

  - Installing guzzle/guzzle (v2.7.2)
    Downloading: 100%      

  - Installing facebook/xhprof (0.9.2)
    Downloading: 100%       

  - Installing aura/di (dev-master)
    Cloning 2bc8695f2efe040935e461f8fa945fc73ebbfe11

  - Installing doctrine/common (dev-master)
    Cloning b3afbb80506d909743475c5b0904285ab0c07941

  - Installing ray/aop (dev-master)
    Cloning e7e5125f829130c716bd797b389cf4c5e3f8c2fd

  - Installing ray/di (dev-master)
    Cloning 460bb44f25742147becd22a3afd8b1b92d3183a0

  - Installing aura/signal (dev-master)
    Cloning 501f622f7905a5b42f98265937e84c02db6e78bd

  - Installing bear/resource (dev-master)
    Cloning ae5acc46be3aa39241067e613f6e1b7edbf27a8b

  - Installing doctrine/dbal (dev-master)
    Cloning 78dbf28923059545b24ba753c112560ad59ca89e

  - Installing pagerfanta/pagerfanta (dev-master)
    Cloning c490684def33e992241e7fad33bcbd03d9d18643

  - Installing vdump/vdump (0.1.0)
    Downloading: 100%       

  - Installing smarty/smarty (v3.1.11)
    Checking out /tags/v3.1.11/@4636

  - Installing symfony/console (v2.0.15)
    Downloading: 100%      

  - Installing symfony/http-foundation (v2.0.15)
    Downloading: 100%      

monolog/monolog suggests installing mlehner/gelf-php (Allow sending log messages to a GrayLog2 server)
pagerfanta/pagerfanta suggests installing doctrine/orm (2.*)
pagerfanta/pagerfanta suggests installing doctrine/mongodb-odm (2.*)
pagerfanta/pagerfanta suggests installing solarium/solarium (2.*)
Writing lock file
Generating autoload files
外部ライブラリ導入前と導入後を比較してみました。
$ cd ..
$ ls -R BEAR.Sunday>List3
$ diff List2 List3>diff23
$ cat diff23
6a7
> composer.lock
11a13
> vendor
559a562,5967
>
> BEAR.Sunday/vendor:
> Aura
> BEAR
> Ray
> autoload.php
> composer
> doctrine
> facebook
> guzzle
> monolog
> pagerfanta
> smarty
> symfony
> vdump
>
・・・以下略
composer.lockとvenderが追加されたようで外部ライブラリの入れ直しではこの2つの削除でOKかな?

sandboxアプリのtmp, logディレクトリの権限を変更しておきます。
$ cd BEAR.Sunday
$ chmod -R 777 apps/sandbox/tmp apps/sandbox/log
phpunit関係の導入。
auto_discoverをonにして 新規チャネルを自動的に探すように設定。
$ sudo pear config-set auto_discover 1
config-set succeeded

$ sudo pear install pear.phpunit.de/PHPUnit
Attempting to discover channel "pear.phpunit.de"...
downloading channel.xml ...
Starting to download channel.xml (804 bytes)
....done: 804 bytes
Auto-discovered channel "pear.phpunit.de", alias "phpunit", adding to registry
Attempting to discover channel "pear.symfony-project.com"...
downloading channel.xml ...
Starting to download channel.xml (865 bytes)
...done: 865 bytes
Auto-discovered channel "pear.symfony-project.com", alias "symfony", adding to registry
Did not download optional dependencies: phpunit/PHP_Invoker, use --alldeps to download automatically
phpunit/PHPUnit can optionally use package "phpunit/PHP_Invoker" (version >= 1.1.0)
phpunit/PHP_CodeCoverage can optionally use PHP extension "xdebug" (version >= 2.0.5)
phpunit/PHPUnit_MockObject can optionally use PHP extension "soap"
downloading PHPUnit-3.6.11.tgz ...
Starting to download PHPUnit-3.6.11.tgz (119,171 bytes)
...done: 119,171 bytes
downloading File_Iterator-1.3.1.tgz ...
Starting to download File_Iterator-1.3.1.tgz (5,157 bytes)
...done: 5,157 bytes
downloading Text_Template-1.1.1.tgz ...
Starting to download Text_Template-1.1.1.tgz (3,622 bytes)
...done: 3,622 bytes
downloading PHP_CodeCoverage-1.1.2.tgz ...
Starting to download PHP_CodeCoverage-1.1.2.tgz (132,552 bytes)
...done: 132,552 bytes
downloading PHP_Timer-1.0.2.tgz ...
Starting to download PHP_Timer-1.0.2.tgz (3,686 bytes)
...done: 3,686 bytes
downloading PHPUnit_MockObject-1.1.1.tgz ...
Starting to download PHPUnit_MockObject-1.1.1.tgz (19,897 bytes)
...done: 19,897 bytes
downloading YAML-1.0.6.tgz ...
Starting to download YAML-1.0.6.tgz (10,010 bytes)
...done: 10,010 bytes
downloading PHP_TokenStream-1.1.3.tgz ...
Starting to download PHP_TokenStream-1.1.3.tgz (9,860 bytes)
...done: 9,860 bytes
install ok: channel://pear.phpunit.de/File_Iterator-1.3.1
install ok: channel://pear.phpunit.de/Text_Template-1.1.1
install ok: channel://pear.phpunit.de/PHP_Timer-1.0.2
install ok: channel://pear.symfony-project.com/YAML-1.0.6
install ok: channel://pear.phpunit.de/PHP_TokenStream-1.1.3
install ok: channel://pear.phpunit.de/PHP_CodeCoverage-1.1.2
install ok: channel://pear.phpunit.de/PHPUnit_MockObject-1.1.1
install ok: channel://pear.phpunit.de/PHPUnit-3.6.11

$ sudo pear install phpunit/DbUnit
downloading DbUnit-1.1.2.tgz ...
Starting to download DbUnit-1.1.2.tgz (41,895 bytes)
............done: 41,895 bytes
install ok: channel://pear.phpunit.de/DbUnit-1.1.2
$ sudo pear install phpunit/PHP_Invoker
downloading PHP_Invoker-1.1.0.tgz ...
Starting to download PHP_Invoker-1.1.0.tgz (3,727 bytes)
....done: 3,727 bytes
install ok: channel://pear.phpunit.de/PHP_Invoker-1.1.0

$ sudo pear install phpunit/PHP_Invoker
downloading PHP_Invoker-1.1.0.tgz ...
Starting to download PHP_Invoker-1.1.0.tgz (3,727 bytes)
....done: 3,727 bytes
install ok: channel://pear.phpunit.de/PHP_Invoker-1.1.0
phpunitテストを実行するためにデータベースの準備を行う。
アプリ用DB blogbearとテスト用DB blogbeartestをCREATEする。
$ sudo mysql
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 3
Server version: 5.5.24 MySQL Community Server (GPL)

Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| test               |
+--------------------+
4 rows in set (0.00 sec)

mysql> CREATE DATABASE `blogbear` DEFAULT CHARACTER SET 'utf8';
Query OK, 1 row affected (0.00 sec)

mysql> CREATE DATABASE `blogbeartest` DEFAULT CHARACTER SET 'utf8';
Query OK, 1 row affected (0.00 sec)

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| blogbear           |
| blogbeartest       |
| mysql              |
| performance_schema |
| test               |
+--------------------+
6 rows in set (0.00 sec)

mysql> quit
Bye
データベースblogbearにpostsテーブルを作成しダミーデータを投入する。
$ sudo mysql blogbear
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 5
Server version: 5.5.24 MySQL Community Server (GPL)

Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> CREATE TABLE posts (
    -> id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    -> title VARCHAR(50),
    -> body TEXT,
    -> created DATETIME DEFAULT NULL,
    -> modified DATETIME DEFAULT NULL
    -> );
Query OK, 0 rows affected (0.04 sec)

mysql> INSERT INTO posts (title,body,created)
    -> VALUES ('タイトル', 'これは、記事の本文です。', NOW());
Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO posts (title,body,created)
    -> VALUES ('またタイトル', 'そこに本文が続きます。', NOW());
Query OK, 1 row affected (0.01 sec)

mysql> INSERT INTO posts (title,body,created)
    -> VALUES ('タイトルの逆襲', 'こりゃ本当に面白そう!うそ。', NOW());
Query OK, 1 row affected (0.01 sec)

mysql> show tables;
+--------------------+
| Tables_in_blogbear |
+--------------------+
| posts              |
+--------------------+
1 row in set (0.00 sec)

mysql> select * from posts;
+----+-----------------------+--------------------------------------------+---------------------+----------+
| id | title                 | body                                       | created             | modified |
+----+-----------------------+--------------------------------------------+---------------------+----------+
|  1 | タイトル              | これは、記事の本文です。                   | 2012-07-08 10:17:18 | NULL     |
|  2 | またタイトル          | そこに本文が続きます。                     | 2012-07-08 10:17:18 | NULL     |
|  3 | タイトルの逆襲        | こりゃ本当に面白そう!うそ。               | 2012-07-08 10:17:19 | NULL     |
+----+-----------------------+--------------------------------------------+---------------------+----------+
3 rows in set (0.00 sec)

mysql> quit
Bye
データベースblogbeartestにpostsテーブルを作成する。
$ sudo mysql blogbeartest
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 8
Server version: 5.5.24 MySQL Community Server (GPL)

Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> CREATE TABLE posts (
    -> id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    -> title VARCHAR(50),
    -> body TEXT,
    -> created DATETIME DEFAULT NULL,
    -> modified DATETIME DEFAULT NULL
    -> );
Query OK, 0 rows affected (0.03 sec)

mysql> show tables;
+------------------------+
| Tables_in_blogbeartest |
+------------------------+
| posts                  |
+------------------------+
1 row in set (0.00 sec)

mysql> quit
Bye
テスト実行準備が終わったのでphpunitテストを実行する。

$ cd ~/BEAR/BEAR.Sunday
$ phpunit
PHPUnit 3.6.11 by Sebastian Bergmann.

The Xdebug extension is not loaded. No code coverage will be generated.

run:sandbox mode=Dev cahce=enable
.............................................

Time: 4 seconds, Memory: 17.25Mb

OK (45 tests, 55 assertions)
sandboxを実際にビルトインサーバーで実行して挙動をブラウザから確認する。

$ php -S localhost:8088 web.php 
PHP 5.4.4 Development Server started at Sun Jul  8 10:57:08 2012
Listening on localhost:8088
Document root is /home/shige/BEAR/BEAR.Sunday/apps/sandbox/htdocs
Press Ctrl-C to quit.
[Sun Jul  8 10:57:26 2012] run:sandbox mode=Dev cahce=enable
[Sun Jul  8 10:57:26 2012] ::1:42119 [200]: /assets/css/bootstrap.css
[Sun Jul  8 10:57:26 2012] ::1:42120 [200]: /assets/css/bootstrap-responsive.css
[Sun Jul  8 10:57:26 2012] ::1:42121 [200]: /assets/js/jquery.js
[Sun Jul  8 10:57:26 2012] ::1:42122 [200]: /assets/js/bootstrap-tooltip.js
[Sun Jul  8 10:57:26 2012] ::1:42123 [200]: /assets/js/bootstrap-popover.js
[Sun Jul  8 10:57:26 2012] ::1:42124 [200]: /assets/js/bootstrap-collapse.js
[Sun Jul  8 10:57:26 2012] ::1:42125 [200]: /assets/js/bootstrap-tab.js
[Sun Jul  8 10:57:26 2012] ::1:42126 [200]: /assets/ico/favicon.ico
[Sun Jul  8 10:57:26 2012] ::1:42128 [200]: /assets/img/glyphicons-halflings.png
[Sun Jul  8 10:57:26 2012] ::1:42129 [200]: /assets/ico/favicon.ico
[Sun Jul  8 10:58:03 2012] run:sandbox mode=Dev cahce=enable
[Sun Jul  8 10:58:03 2012] ::1:42133 [200]: /assets/css/bootstrap.css
[Sun Jul  8 10:58:03 2012] ::1:42134 [200]: /assets/js/bootstrap-tooltip.js
[Sun Jul  8 10:58:03 2012] ::1:42135 [200]: /assets/js/bootstrap-popover.js
[Sun Jul  8 10:58:03 2012] ::1:42136 [200]: /assets/js/bootstrap-collapse.js
[Sun Jul  8 10:58:03 2012] ::1:42137 [200]: /assets/js/bootstrap-tab.js
・・・以下略
ブラウザからアクセスしてページ内容を確認。


起動時に指定したweb.phpがhttp://localhost:8088/へのアクセスで表示される。ページ内にあるリンクからHelloworldを表示する。



ちゃんとHelloWorldページが表示された。


こちらはblogbearの一覧表示画面。


個々の記事を表示する画面。


最後に記事編集画面。新規ページと供用されていると思われる。

以上BEAR.Sundayをgithubから取得しphpunitテストを実行して確認。実際にビルトインサーバにブラウザからアクセスして表示等を確認した。

PHPはかなり不慣れで戸惑う場面も多かったしまだ納得いってない挙動もあるが同様にUbuntu12.04やDebian Wheezy(現testing)でも環境を構築してみたので随時記事にしたいと思う。

2011年10月3日月曜日

QLife 添付文書Pro リリース

Twitter等でも告知させて頂きましたが情報を集約させるためにブログでまとめたいと思います。

さて本日株式会社QLife様よりiOS上で利用出来る添付文書Proがリリースされました。私いのうえしげゆきもこのアプリの開発に監修という形で参加させて頂いております。



AppStoreはこちらからアクセス出来ます。
http://itunes.apple.com/jp/app/id467352729

上記アプリを継続してご利用頂くにはQLM IDを取得して頂く必要があります。
QLM IDの取得は以下から行なってください。

https://qlifebooks.com/entry/index.php

上記URLは現状iPhoneでの登録について準備中です(10/3現在)。iPhoneでご利用予定の方は事前にIDを取得しておくことをお勧めします。

そしてFacebookページも作成されておりますので是非情報収集等にお役立てください。

このアプリの特徴としては多くの医薬品が画像付きで表示されること。そしてiOS上の添付文書閲覧アプリでは初だと思いますがインタビューフォームを閲覧することが可能です。

またローカルにデータを保持(約1GB)するため回線不通時でも添付文書の閲覧が可能です。
このデータを端末に登録するため初回利用時にちょっと大きめなデータダウンロードが必要となります。サイズの関係上wi-fi接続のみ対応となっておりご自宅や職場等でのインストールをお勧め致します。

添付文書Proに関するご意見等は私 @kaepapa33 もしくは@QLife_JP までお寄せ頂けますようお願い致します。



2011年10月2日日曜日

メディア初登場!?

はてさて拙作添付文書検索ツールが雑誌に掲載されました。

掲載されたのはGetNaviという情報雑誌の11月号。

はじめての事なのでちょっと嬉しい・・・ものの添付文書というものがどういうものなのかちゃんと認識されてない点がちょっと残念です。


でもこの雑誌はとりあえず永久保存ということにしましょう。ちゃんとスキャンして画像としても残したので記念ぱぴこ。

2011年9月20日火曜日

AndroidLibraryプロジェクトの作成とテスト(2)

 ちょっと間があいてしまいましたが前回の続きです。


 今回は実際にテストを作って実行する部分について紹介したいと思います。ではまずはテスト対象となるクラスをライブラリプロジェクトに作成したいと思います。



 ライブラリプロジェクトのパッケージ直下にクラスを作成するためにコンテキストメニューから「新規」-「クラス」を選択し以下の新規Javaクラスダイアログを表示します。


 クラス名はプロジェクトと同じ「AndroidLibrary」としました。他は今回なにも変更をしていません。



 作成されたクラスファイルにとりあえずコンストラクタメソッドのみ作成して続いてテストクラスを作成します。私はQuickJUnitを利用しているのでAndroidLibraryクラスに対するテストクラスが未作成状態ではCtrl+9で以下のようなダイアログが開きます。既にテストクラスが作成されている場合にはテストクラスがパースペクティブに表示されテスト対象クラスとテストクラスをショートカットで行き来することが出来ます。


 ここで「はい」と答えると新規JUnitテスト・ケースダイアログが表示されますのでテストクラスを生成させましょう。



 ソース・フォルダーがLibraryプロジェクトのsrcフォルダがデフォルトで指定されているのでLibraryテストプロジェクトのsrcフォルダに変更しておきます。またスーパークラスをAndroidTestCaseに変更してあります。



 作成したAndroidLibraryTestクラス内にとりあえず簡単なテストを作成しました。AndroidLibraryクラスのインスタンスを生成してそのインスタンスがAndroidLibraryクラスのものであるか確認しているだけです。コンストラクタメソッドは既に作成してあるのでテストファーストじゃありませんが今回は気にしません。



 また「新規作成」-「その他」から「Java」-「JUnit」-「Junitテスト・スイート」からAllTestsクラスを作成して配下のテストクラスを収集するよう実装しました。


 ここで一点注意が必要です。プロジェクトを作成したときに自動ビルドの設定になっている場合空プロジェクトがビルドされて既にapkファイルが作成されています。この状態でテストを実行するとエミュレータにLibraryプロジェクトのパッケージがインストールされてしまいテストが上手く実行出来なくなってしまいます。Libraryプロジェクトフォルダ内のbinフォルダは以下のようになっていることと思います。


 これら3つのファイルは必要ありませんのでテスト実行前に必ず削除しておくようにしてください。そうしないとエミュレータ起動ごとにAndroidライブラリパッケージがエミュレータにインストールされてしまいアンインストールしないとテスト実行が出来なくなってしまいます。もしインストールが実行されてしまった場合は.settingあたりを一度削除する必要があるかもしれませんがそこまでは検証していません。



 さて実際のテストの実行ですが「実行構成」を開き通常のAndroidテストと同じように設定を行います。ここは特に詳しい説明は必要ないことでしょう。


 そしてテストを実行するともちろんコンストラクタクラスが作られているのでグリーンです。


 いかがだったでしょうか。注意点はあるもののお手軽にライブラリプロジェクトのテストを行うことが出来ます。ライブラリなのでandroid.jarに依存しないものであれば普通のJavaプロジェクトとして作成したほうがテスト実行速度も速く優位だと思いますがどうしてもandroid.jarに依存してしまう場合にはこの手法でテストが可能となることと思います。


 さてさてこの手法を用いてライブラリを作成しなくちゃ!

2011年9月17日土曜日

AndroidLibraryプロジェクトの作成とテスト(1)

ADTの機能としてLibraryProjectの作成がある。たしか0.9.7あたりから追加されたものだったと思うが複数のアプリケーションから参照が可能なLibraryプロジェクトが作成可能となっています。


ライブラリであるので当然このLibraryプロジェクトはjarとしてbuildされ複数のAndroidプロジェクトで利用可能ということです。


しかもADTの機能としてLibraryプロジェクトをライブラリとして参照するとそのAndroidプロジェクトの中からもソースコードの添付等を経ずソースコードの参照可能となり使い勝手の面で大きなメリットがあるように思われます。


@ussy00さんのブログで春頃に紹介されていますが画像が無くなってしまっているようなのでATECの一員として後追い記事ではあるものの詳細をまとめてみようと思います。


ということで先ずはLibraryプロジェクトとLibraryテストプロジェクトの作成から行なってみたいと思います。




まずは新規作成より通常のAndroidプロジェクトの作成と同様に「Androidプロジェクト」を選択します。作成仮定としてはAndroidプロジェクトとほぼ同じですが一点だけ違うのはAcitivyを作成しないということです。


上記のようにプロジェクト名を付け内容の部分についてはそれぞれの環境にあわせて設定を行ってください。ビルド・ターゲットとしては今回はAPIレベル7を選択しました。



パッケージ名をを指定した下の部分でAndroidプロジェクトではアクティビティ名を入力してアクティビティクラスを自動生成させていると思いますが今回のライブラリではAcitivityは使わないという設定にしてチェックを外しています。「次へ」ボタンを押してテストプロジェクトも一緒に作成します。


こちらはデフォルトのままでOKです。変更する必要がある場合は随時行なってください。


私はQuickJUnitを使う関係でパッケージ名をAndroidプロジェクトと同じにするようにしています。このあたりも好みかと思いますので自由に設定しましょう。「完了」ボタンを押すと上記ではAndroidLibraryプロジェクトとAndroidLibraryTestプロジェクトの二つがパッケージエクスプローラに表示されるようになったかと思います。続いてそれぞれのパッケージについてLibraryプロジェクトとする設定を行います。


パッケージエクスプローラでAndroidLibraryプロジェクトを右クリックしてコンテキストメニューからプロパティを選択しその中のAndroid項目を開きます。


下の「ライブラリー」という中の「Is Library」にチェックを入れこのプロジェクトがLibraryプロジェクトであることを設定します。続いてAndroiManifest.xmlを開きます。


上記はAndroidManifest.xmlファイルのアプリケーションタブの一部です。上部にある「Define an <application> tag in the AndroidManifest.xml」のチェックが入っていると思うのでこれを外します。


するとAndroidManifest.xmlファイルは上記のようにとても簡単な物になります。


続いては必要のなresフォルダの中身を削除してしまいましょう。リソースを利用する場合は残しておいても構いませんが初期設定で置かれるiconやlayout/main.xmlやvalues/string.xmlは必要ないことが多いのではないかと思います。genフォルダ、assetsフォルダそしてresフォルダ自身は残しておかないとコンパイル時にエラーとなってしまうようです。


続いてはAndroidLibraryTestプロジェクトのプロパティを設定します。先程「Is Library」にチェックを入れた部分で「追加」ボタンを押しAndroidLibraryプロジェクトをライブラリとして登録します。



登録すると上記のように表示されライブラリとして追加されたことが表示されます。



またパッケージエクスプローラ上ではAndroidLibrary_srcとして参照されていることがわかるかと思います。この中身はAndroidLibraryプロジェクトのsrcフォルダがそのままリンクされているような感じで表示されています。こちらからファイルを変更してもAndroidLibraryプロジェクトの内容に反映してくれるようです。


ちょっと長くなってきてしまったのでとりあえずこのあたりでいったん〆め。
次の記事では実際にテストを実行するまでをご紹介します。

2011年8月13日土曜日

AndroidTestCaseを継承したテストケースでテストプロジェクト内のリソースを利用

今日は朝一から外出して若松通商さんで愛用のT61のCPUFan交換後、第一回Androidテスト・ソースコードリーディングに参加してきました。

ちょっと人数が少なめで寂しかったですがデ部とのダブルブッキングやら時期的な事を考えると致し方ないことだったでしょうか。
少人数でスイーツを頂きながらまったりとActivityInstrumentaionTestCase2あたりを眺めてきました。

その中で先日からハマっていたAndroidテストパッケージの中のassets以下のファイルにアクセス出来ない問題についてmike_neckさん例を提示して頂いたのでAssetManagerの呼び出しに@hideされているgetTestContextメソッドを利用してアクセス出来るようになりました。

public class AndroidExtendedTestCase extends AndroidTestCase {

    public Context getExTestContext() {
        Context context = null;
        @SuppressWarnings("unchecked")
        Class clz = (Class) this.getClass();
        try {
            Method method = clz.getMethod("getTestContext");
            context = (Context) method.invoke(this);
        } catch (SecurityException e) {
            fail();
        } catch (NoSuchMethodException e) {
            fail();
        } catch (IllegalArgumentException e) {
            fail();
        } catch (IllegalAccessException e) {
            fail();
        } catch (InvocationTargetException e) {
            fail();
        }
        return context;
    }
}

まんまソースを頂いてループしてしまっているようだったのでメソッド名をちょっと変更しましたがこのAndroidExtendedTestCaseを継承したテストケースクラスで

AssetManager assetMgr = getExTestContext().getAssets();
// filenameはassetsファイル以下相対パスを指定
InputStream is = new InputStream(assetMgr.open(filename));

という感じでばっちりと読み込むことが出来ました。@mike_neckさんいつもありがとうございます。

2011年8月8日月曜日

読まねばならぬもの

だいぶ前に知人の方からMacBookをお借りしてiOS開発にも挑戦しようとぼちぼちと準備中。

第一の目的は添付文書検索ツールのiOSへの移植…メインはiPadをターゲットにしたいと思います。
理由としては今後調剤室で添付文書の閲覧が必要になったときには常に最新のものを見てもらいたいということとなかなか業務中にはパソコンの前に座って最新の添付文書を開くというのが難しいということ。

これをクリアするためにiPadなりAndroidタブレットに最適化した添付文書検索ツールのリリースをと
大分前から考えていたりしますがなかなか形にすることが出来ずにいる訳です。

iPad向け開発もAndroidタブレット向け開発も未経験ではありますがなんとか頑張らないといけません。

とりあえずまったくわかってないiOS開発の勉強をしている訳ですがAppleから膨大なドキュメントが日本語で提供されています。ところがこのページがどこにあるのかDeveloperサイトからなかなか辿ることが出来ないので備忘録的に記事にしておきます。

iOS Developer Library

上記リンク先に日本語化されたドキュメントが沢山あります。
全部じっくりと読めばかなりの知識になりそう。

iOS Developer Library(英語)

上記にはさらに沢山のドキュメントが英語ではありますが用意されています。

とりあえずじっくりと腰を据えてドキュメントを読み込みたいと思っています。

Groovy環境を作ってみる。

さてさて半年以上も放置してしまいまいたがぼちぼち書かないといけませんね。
いろんなことに手をだしていてネタは沢山あるので時間を見つけて書くこととしましょう。

昨日はAndroidテスト部の祭りに参加してきた訳ですが(それも記事にしたらいいのにw)その途中で重いから辞めときゃいいのに本を3冊程買ってしまいました。

そのうちの一冊がこちら



テスト部でご一緒させて頂いているmike_neckさんに触発されておもわず購入です。

ということでWin7とOSX上に環境構築してみました。

公式サイトよりWindows用インストーラをDLして起動
インストールパスをC:\JavaLib\Groovy\Groovy-1.8.1に変更したのみ
環境変数まで設定してくれるので楽チン。

一旦ログアウトしてGroovyConsoleを起動してHello, Groovy!を実行。


Groovyのインストール@OSX
http://groovy.codehaus.org/DownloadよりバイナリzipをDLして
Matsumoto.JSさんのMac OS XでのGroovy等をインストールする場所の記事を参考にさせて頂き/Library/Frameworks/Groovy.frameworks/Versions/1.8.1に展開して/Library/Frameworks/Groovy.frameworks/Versions/Currentにシンボリックリンクを作成

.bash_profileを以下にように変更
export JAVA_HOME=/Library/Java/Home
export ANT_HOME=/usr/share/java/ant-1.8.2
export GROOVY_HOME=/Library/Frameworks/Groovy.framework/Versions/Current
export PATH=$PATH:/Developer/android-sdk-mac_x86/platform-tools
export PATH=$PATH:$GROOVY_HOME/bin
そして確認。
MacBook:~ kaepapa$ source .bash_profile
MacBook:~ kaepapa$ groovy -version
Groovy Version: 1.8.1 JVM: 1.6.0_26

同じくgroovyConsoleを起動してHello, Groovy!を実行


ということで環境構築は完了しました。
徐々にいろいろといじってみたいと思っております。

2010年12月30日木曜日

CodeIgniterでGoogleCalendarAPIのテスト(2)

さてちょっと時間が空いてしまいましたがGoogleCalendarAPIをCI上から利用してカレンダー機能にアクセスはほぼ実装が終わりました。そのあとjsなどをいじっていたので更新が遅くなってしまった訳ですが各機能についてご紹介したいと思います。

前回IBMのサンプルをそのまま用いてカレンダーの予定からタイトルやサマリを取得した訳ですがサマリだとちょっと見づらいので取得する情報をbasicからfullへと変更し各情報の詳細を取得出来るように変更しました。

public function getCalendarEntryList() {

 try{
  $gcal = Zend_Gdata_Calendar::AUTH_SERVICE_NAME;
  $client =Zend_Gdata_ClientLogin::getHttpClient($this->user, $this->pass, $gcal);
  $gcal = new Zend_Gdata_Calendar($client);

  $query = $gcal->newEventQuery();
  $query->setUser('default');
  $query->setVisibility('private');
  $query->setProjection('full');
  $query->setOrderby('starttime');

  $feed=$gcal->getCalendarEventFeed($query);

  $data['title'] = (string)$feed->title;
  $data['totalResults'] = (String)$feed->totalResults;

  $events = array();
  foreach ($feed as $event) {
   $obj = new stdClass;

   $obj->title = $event->title;

   $when = $event->when;
   $obj->when = date('Y/n/j',strtotime($when[0]->getStartTime()));
   $obj->startTime = date('G:i',strtotime($when[0]->getStartTime()));
   $obj->endTime = date('G:i',strtotime($when[0]->getEndTime()));

   $reminders = $when[0]->reminders;
   if (count($reminders)!=0) {
    foreach ($reminders as $reminder) {
     if ($reminder->getMethod()=='email') {
      $obj->reminder_email = $reminder->getMinutes();
     } else {
      $obj->reminder_alert = $reminder->getMinutes();
     }
    }
   }
   $obj->content = $event->content;
   $obj->edit_key = $event->id;

   $where = $event->where;
   $obj->where = $where[0]->getValueString();

   $events[] = $obj;
  }

  $data['events'] = $events;
 }catch(Exception $e){
  echo "エラー:".$e->getMessage();
 }

 return $data;
}

ソースをご覧頂くとEventQuery生成部分でbasicがfullに変更されているのがおわかり頂けるかと思います。またforeach文の中で各種詳細データを取得しているのが前回との変更点です。

詳細データの取得に関して注意が必要なところは「when」「reminders」そして「where」等の情報はイベントデータ内に配列として保持されている点です。基本的にwhenやwhereは1つだけ保持していると思われるので$when[0]等で決め打ちでアクセスしてしまっています。またリマインダについてはGoogleカレンダー上では複数登録することが出来ますが今回はメールとアラートもしくはメール+アラートという3つの選択肢から選択し設定時間は1つだけ保持できる仕様としております。

続いては削除メソッドです。

public function deleteEntry() {
 try {
  $gcal = Zend_Gdata_Calendar::AUTH_SERVICE_NAME;
  $client =Zend_Gdata_ClientLogin::getHttpClient($this->user, $this->pass, $gcal);
  $gcal = new Zend_Gdata_Calendar($client);

  $edit_key = $this->input->post('edit_key', TRUE);
  if (isset($edit_key)) {
   $event = $gcal->getCalendarEventEntry('http://www.google.com/calendar/feeds/default/private/full/'.$edit_key);
   $event->delete();
  }
 }catch(Exception $e){
  echo "エラー:".$e->getMessage();
 }
}

イベントエントリに含まれているidを使って個別EventEntryを取得してdelete()メソッドを実行するだけで削除が可能です。Contactsの時はeditキーを送っていましたがZFの実装がこのようになっています。これはなんなく実装できました。

次は登録メソッドを見てみたいと思います。

public function add_entry() {
 try {
  $gcal = Zend_Gdata_Calendar::AUTH_SERVICE_NAME;
  $client =Zend_Gdata_ClientLogin::getHttpClient($this->user, $this->pass, $gcal);
  $gcal = new Zend_Gdata_Calendar($client);

  $dates = explode('/', $this->input->post('when', TRUE));
  $stimes = explode(':', $this->input->post('start_time', TRUE));
  $etimes = explode(':', $this->input->post('end_time', TRUE));
  date_default_timezone_set('Asia/Tokyo');
  $stime = date(DATE_RFC3339, mktime($stimes[0],$stimes[1],0,$dates[1],$dates[2],$dates[0]));
  $etime = date(DATE_RFC3339, mktime($etimes[0],$etimes[1],0,$dates[1],$dates[2],$dates[0]));

  $event = $gcal->newEventEntry();
  $event->title = $gcal->newTitle($this->input->post('title', TRUE));
  $when = $gcal->newWhen();
  $when->startTime = $stime;
  $when->endTime = $etime;
  if (isset($_POST['reminder_cbox'])) {
   $time = $this->input->post('reminder_time', TRUE);
   $method = $this->input->post('reminder', TRUE);
   $reminders = array();
   switch ($method) {
    case 'm':
     $reminder = $gcal->newReminder();
     $reminder->method = "email";
     $reminder->minutes = $time;
     $reminders[] = $reminder;
     break;
    case 'p':
     $reminder = $gcal->newReminder();
     $reminder->method = "alert";
     $reminder->minutes = $time;
     $reminders[] = $reminder;
     break;
    case 'mp':
     $reminder = $gcal->newReminder();
     $reminder->method = "email";
     $reminder->minutes = $time;
     $reminders[] = $reminder;
     $reminder = $gcal->newReminder();
     $reminder->method = "alert";
     $reminder->minutes = $time;
     $reminders[] = $reminder;
     break;
   }
   $when->reminders = $reminders;
  }
  $event->when = array($when);
  $event->where = array($gcal->newWhere($this->input->post('where', TRUE)));
  $event->content = $gcal->newContent($this->input->post('content', TRUE));

  $newEvent = $gcal->insertEvent($event);
 }catch(Exception $e){
  echo "エラー:".$e->getMessage();
 }
}

この部分はわりと苦労しました。日時をRFC3339形式にして送らないといけませんがこれはphpのdate()メソッドが対応してくれていますので$_POSTから取得した日時データをexplode()メソッドでバラして生成しています。冒頭で書いたようにリマインダについては「メールのみ」「ポップアップのみ」そして「メールとポップアップ併用」という3種類を選択可能とし時間は1つだけ指定できる仕様のためこのような実装になっています。「when」「reminders」そして「where」についてはしっかりと配列で渡してあげることをお忘れなく。これを忘れるとエラーとなります。

また各種データについてZF側でインスタンス生成メソッドが用意されているのでそのメソッドを利用してデータを登録しています。xmlを生成していたContactsに比べてとても楽チンになっているので助かりました。いや…逆にメソッドを調べるのが大変だったかも?(笑

最後にデータ修正です。

public function correct_entry() {
 try {
  $gcal = Zend_Gdata_Calendar::AUTH_SERVICE_NAME;
  $client =Zend_Gdata_ClientLogin::getHttpClient($this->user, $this->pass, $gcal);
  $gcal = new Zend_Gdata_Calendar($client);

  $dates = explode('/', $this->input->post('when', TRUE));
  $stimes = explode(':', $this->input->post('start_time', TRUE));
  $etimes = explode(':', $this->input->post('end_time', TRUE));
  date_default_timezone_set('Asia/Tokyo');
  $stime = date(DATE_RFC3339, mktime($stimes[0],$stimes[1],0,$dates[1],$dates[2],$dates[0]));
  $etime = date(DATE_RFC3339, mktime($etimes[0],$etimes[1],0,$dates[1],$dates[2],$dates[0]));

  $event = $gcal->getCalendarEventEntry($this->input->post('edit_key', TRUE));
  $event->title = $gcal->newTitle($this->input->post('title', TRUE));
  $when = $gcal->newWhen();
  $when->startTime = $stime;
  $when->endTime = $etime;
  if (isset($_POST['reminder_cbox'])) {
   $time = $this->input->post('reminder_time', TRUE);
   $method = $this->input->post('reminder', TRUE);
   $reminders = array();
   switch ($method) {
    case 'm':
     $reminder = $gcal->newReminder();
     $reminder->method = "email";
     $reminder->minutes = $time;
     $reminders[] = $reminder;
     break;
    case 'p':
     $reminder = $gcal->newReminder();
     $reminder->method = "alert";
     $reminder->minutes = $time;
     $reminders[] = $reminder;
     break;
    case 'mp':
     $reminder = $gcal->newReminder();
     $reminder->method = "email";
     $reminder->minutes = $time;
     $reminders[] = $reminder;
     $reminder = $gcal->newReminder();
     $reminder->method = "alert";
     $reminder->minutes = $time;
     $reminders[] = $reminder;
     break;
   }
   $when->reminders = $reminders;
  }
  $event->when = array($when);
  $event->where = array($gcal->newWhere($this->input->post('where', TRUE)));
  $event->content = $gcal->newContent($this->input->post('content', TRUE));

  $event->save();
 }catch(Exception $e){
  echo "エラー:".$e->getMessage();
 }
}

内容については登録時とほぼ同じですがeditキーを使ってEventEntryを取得しその内容を書き換えてsave()メソッドで登録しております。ここで注意が必要なのが削除時や個別イベントデータ取得時に利用するidとデータ修正を行うeditキーは別のデータということです。

IBMのサンプルはbasicでデータを取得しているためこの違いには言及していませんでしたが以下のxmlデータをご覧ください。

<atom:id>http://www.google.com/calendar/feeds/default/private/full/57a84t1b243radd31209erdd9k≷/atom:id>
<atom:link href="http://www.google.com/calendar/event?eid=NTdhODR0MWIyNDNyYWRkMzEyMDllcmRkOWtfMjAxMDEyMjdUMDIzMDAwWiB0ZXN0LmthZXBhcGFAbQ" rel="alternate" type="text/html" title="alternate"/>
<atom:link href="http://www.google.com/calendar/feeds/default/private/full/57a84t1b243radd31209erdd9k" rel="self" type="application/atom+xml"/>
<atom:link href="http://www.google.com/calendar/feeds/default/private/full/57a84t1b243radd31209erdd9k/63429101549" rel="edit" type="application/atom+xml"/>

最後のeditだけ数字のデータが付加されているのがおわかり頂けるかと思います。このデータを渡してあげないとデータ修正は上手くいかずエントリの先頭のデータが書き換えられてしまったりしましたのでご注意ください。

さて実際のブラウザでの表示も掲載しておきたいと思います。


こんな具合にイベントのタイトルと時間、説明と場所が表示されリマインダについてはアイコン表示にしてあります。下の削除と編集リンクからそれぞれの機能が利用できて一番下の「新しいイベントを追加」で新規イベント登録が出来ます。最下段の検索機能についてはまだ実装しておりません。この後実装予定です。


上記はイベントの登録と修正に利用するフォームです。日時と時間の入力はjqueryを利用して入力補助をしています。またリマインダについてはチェックを入れるとセレクトフォームが表示されデータ登録が可能となります。いや…jsで遊んでみたかったんです。jqueryを使うととっても楽にいろいろと出来るようになりますねぇ。もっと勉強しないと!

2010年12月27日月曜日

CodeIgniterでGoogleCalendarAPIのテスト(1)

さてGoogleContactsAPIに続いて今度はCalendarAPIのテストをCodeIgniter上で行なてみたいと思います。

Contactsに比べZendFramework自身が直接対応していますし他の情報もいくつか存在しているので苦労の度合いは少ないように予想してます。今考えたら先にCalendarをテストしておいた方が楽だったのではないかと思わなくもありませんが後の祭りというやつですね。

はてさてGoogleカレンダーは私も利用していますがAjaxがたっぷり使われていてとても使いやすいUIが提供されています。それを今さらならがwebアプリから使えるようにしたところで何になるんだ?という気もしなくもありませんがまあそれはそれとして今回はCodeIgniter上で動作するwebアプリからカレンダーの作成、修正やカレンダーを指定しての予定の追加、修正そして削除を実装してみる予定です。またContactsではClientLoginを使用しましたが実際のwebアプリではwebアプリ製作者である私にもユーザの認証情報の伝わらないAuthSubで実装してみたいと思います。まずはClientLogin認証で機能を実装してから最後にAuthSub認証に変更してみる予定です。

それぞれの詳しい情報については以下のドキュメントをご参照ください。

Zend Framework GDataプログラマーズリファレンスガイド Google Calendar の使用法
Google Calendar APIs and Tools Data API Developer's Guide: Protocol
Google Calendar APIs and Tools API Reference Guide
Authentication and Authorization for Google APIs AuthSub for Web Applications

なおGoogleCalendarAPIのドキュメントには「これは古いバージョンである」旨記載がありますがVer.2のPHPGuideはまだありませんし。ZendFrameworkがまだ対応してないような雰囲気なので古い方のドキュメントを参照してますが詳細はまだわかっていません。
コーディングを進めるうちになにかわかりましたら加筆予定です。

また今回もIBM developerWorksの記事[http://www.ibm.com/developerworks/jp/xml/library/x-googleclndr/index.html]を参考に進めていきたいと思います。

ではdWの記事を熟読した後今回もこのソースをCIに移植していく訳ですがその前にそのままCI上でviewを表示してみたいと思います。

DL可能となっているソースのview.php(idとpassを書き換え)をCIのコントローラからloadするだけです。
CodeIgniterのsystemフォルダやindex.phpファイルを今回もeclipseを利用してコーディングするので作成したPHPプロジェクトフォルダ内にコピーしてconfig.phpやroutes.phpについては適切に設定を行なってください。

まずはコントローラの./system/application/controllers/calendar.phpファイルから見ていきましょう。

<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');

class Calendar extends Controller {

 public function __construct() {

  parent::Controller();

  $this->output->set_header('Content-Type: text/html; charset=UTF-8');
 }

 /*
  * デフォルトメソッド
  */
 public function index() {
  $this->load->view('view');
 }
}

/* End of file calendar.php */
/* Location: ./system/application/controllers/calendar.php */

Controllerクラスを継承したCalendarクラスを作成してコンストラクタで親クラスのコンストラクタを呼び文字化け対策にUTF-8を指定しています。デフォルトメソッドのindex()ではIBMで配布されているview.phpファイルを一部手直ししたものをロードしているだけです。

続いてはview.phpファイルを見てみましょう。

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja">
<head>
 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
 <title>Googleカレンダー予定一覧</title>
 <style type="text/css">
 body {
  font-family: Verdana;
 }
 li {
  border-bottom: solid black 1px;
  margin: 10px;
  padding: 2px;
  width: auto;
  padding-bottom: 20px;
 }
 h2 {
  color: red;
  text-decoration: none;
 }
 span.attr {
  font-weight: bolder;
 }
 </style>
</head>
<body>
 <?php
    require_once 'Zend/Loader.php';
    Zend_Loader::loadClass('Zend_Gdata');
    Zend_Loader::loadClass('Zend_Gdata_ClientLogin');
    Zend_Loader::loadClass('Zend_Gdata_Calendar');
    Zend_Loader::loadClass('Zend_Http_Client');
    
    $gcal = Zend_Gdata_Calendar::AUTH_SERVICE_NAME;
    $user = "username@gmail.com";
    $pass = "password";
    $client = Zend_Gdata_ClientLogin::getHttpClient($user, $pass, $gcal);
    $gcal = new Zend_Gdata_Calendar($client);
    
    $query = $gcal->newEventQuery();
    $query->setUser('default');
    $query->setVisibility('private');
    $query->setProjection('basic');
    $query->setOrderby('starttime');
    if(isset($_GET['q'])) {
      $query->setQuery($_GET['q']);      
    }
    
    try {
      $feed = $gcal->getCalendarEventFeed($query);
    } catch (Zend_Gdata_App_Exception $e) {
      echo "Error: " . $e->getResponse();
    }
    ?>
 <h1><?php echo $title ?></h1>
 <?php echo $totalResults ?> 件の予定があります。
 <p/>
 <ol>
 <?php
  foreach ($feed as $event) {
   echo "<li>\n";
   echo "<h2>" . stripslashes($event->title) . "</h2>\n";
   echo stripslashes($event->summary) . " <br/>\n";
   $id = substr($event->id, strrpos($event->id, '/')+1);
   echo "<a href=\"edit.php?id=$id\">編集</a> | ";
   echo "<a href=\"delete.php?id=$id\">削除</a> <br/>\n";
   echo "</li>\n";
  }
  echo "</ul>";
 ?>
 </ol>
 <p/>
 <a href="add.php">新しいイベントを追加</a><p/>
 <form action="<?php echo $_SERVER['PHP_SELF']; ?>" method="get">
  予定の検索:<br/>
  <input type="text" name="q" size="10"/>
  <input type="submit" name="submit" value="検索"/>
 </form>
</body>
</html>


ヘッダ部分を日本語対応とし表示を日本語化した以外はそのままのコードになっています。

では簡単にphpスクリプト部分を読んでいってみましょう。

最初に必要となるZendFrameworkのクラスをロード後、ClientLogin認証で$gcalインスタンスを生成しZend_Gdata_Calendarクラスを生成しています。さらにdefaultカレンダーのprivate領域のデータをbasicなデータ量で取得するというクエリを作成していますね。$_GET['q']を使っている部分が予定の検索時に利用する条件です。そしてtry~catch文の中でこの作成したクエリを発行してデータを取得しています。得られたfeedをhtml内に埋め込んでいき表示を行なうという実装ですね。ソースコード内の$userと$passについてはご自身のアカウント情報を入力してください。


上記がこのview.phpを表示した状態です。表示されているEventは私が適当に作成したものです。今回もあっさりと表示されて安心しました。

問題なく表示されましたのでCI化していきたいと思います。

まずはモデルの./system/application/models/calendar_model.phpファイルから作成しました。

<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');

class Calendar_model extends Model {

 private $user;
 private $pass;

 public function __construct() {
  parent::Model();

  require_once 'Zend/Loader.php';
  Zend_Loader::loadClass('Zend_Gdata');
  Zend_Loader::loadClass('Zend_Gdata_ClientLogin');
  Zend_Loader::loadClass('Zend_Gdata_Calendar');
  Zend_Loader::loadClass('Zend_Http_Client');

  $this->config->load('calendar_config', TRUE);
  $this->user = $this->config->item('username','calendar_config');
  $this->pass = $this->config->item('password','calendar_config');
 }

 public function getCalendarEntryList() {

  try{
   $gcal = Zend_Gdata_Calendar::AUTH_SERVICE_NAME;
   $client =Zend_Gdata_ClientLogin::getHttpClient($this->user, $this->pass, $gcal);
   $gcal = new Zend_Gdata_Calendar($client);

   $query = $gcal->newEventQuery();
   $query->setUser('default');
   $query->setVisibility('private');
   $query->setProjection('basic');
   $query->setOrderby('starttime');

   $feed=$gcal->getCalendarEventFeed($query);

   $data['title'] = (string)$feed->title;
   $data['totalResults'] = (String)$feed->totalResults;

   $events = array();
   foreach ($feed as $event) {
    $obj = new stdClass;

    $obj->title = $event->title;
    $obj->summary = $event->summary;
    $obj->id = $event->id;

    $events[] = $obj;
   }

   $data['events'] = $events;
  }catch(Zend_Gdata_App_Exception $e){
   echo "エラー:".$e->getResponse();
  }

  return $data;
 }
}

/* End of file calendar_model.php */
/* Location: ./system/application/model/calendar_model.php */

Modelクラスを継承したCalendar_modelクラスを作成してコンストラクタの中でZFの必要なクラスをロード、Contactsの時と同様にユーザ名とパスワードはcalender_config.php内に記述してあり、それをload後取得しています。

getCalendarEntryList()メソッドでGoogleCalendarにアクセスしてfeedを取得し$dataに必要なデータを入れて返すというContactsのときとまったく同じ手法です。

続いては変更を加えたコントローラクラスのcalender.phpを見てみます。

<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');

class Calendar extends Controller {

 public function __construct() {

  parent::Controller();

  $this->load->model('calendar_model');

  $this->output->set_header('Content-Type: text/html; charset=UTF-8');
 }

 /*
  * デフォルトメソッド
  */
 public function index() {
  $data = $this->calendar_model->getCalendarEntryList();
  $this->load->view('calendar_index',$data);
 }
}

/* End of file calendar.php */
/* Location: ./system/application/controllers/calendar.php */

コンストラクタメソッド内でCalendar_modelをロードして先程作成したgetCalendarEntryList()メソッドでデータを取得後view.phpから名称を変更したcalendar_index.phpファイルにデータを渡してロードするよう変更しました。

最後にビューのcalendear_index.phpを見てみましょう。

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja">
<head>
 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
 <title>Googleカレンダー予定一覧</title>
 <style type="text/css">
 body {
  font-family: Verdana;
 }
 li {
  border-bottom: solid black 1px;
  margin: 10px;
  padding: 2px;
  width: auto;
  padding-bottom: 20px;
 }
 h2 {
  color: red;
  text-decoration: none;
 }
 span.attr {
  font-weight: bolder;
 }
 </style>
</head>
<body>
 <h1><?php echo $title ?></h1>
 <?php echo $totalResults ?> 件の予定があります。
 <p/>
 <ol>
 <?php
  foreach ($events as $e) {
   echo "<li>\n";
   echo "<h2>" . stripslashes($e->title) . "</h2>\n";
   echo stripslashes($e->summary) . " <br/>\n";
   $id = substr($e->id, strrpos($e->id, '/')+1);
   echo "<a href=\"edit.php?id=$id\">編集</a> | ";
   echo "<a href=\"delete.php?id=$id\">削除</a> <br/>\n";
   echo "</li>\n";
  }
  echo "</ul>";
 ?>
 </ol>
 <p/>
 <a href="add.php">新しいイベントを追加</a><p/>
 <form action="<?php echo $_SERVER['PHP_SELF']; ?>" method="get">
  予定の検索:<br/>
  <input type="text" name="q" size="10"/>
  <input type="submit" name="submit" value="検索"/>
 </form>
</body>
</html>

実装部分がなくなりかなりすっきりした感がありますね。これぞMVCモデルの利点でしょうか。データ表示部を数カ所実装にある変数名に変更しただけですがこれでview.phpと同じ表示が得られるようになりました。

3ヶ所ほどある他機能へのリンクはまだ実装していないのでそのままになっています。そもそもCIはデフォルトではGETを受け取らないのでこのままでは利用できません。

さてここまででGoogleカレンダーにAPIを使ってアクセスしEventデータを取得する機能が実装できました。実装といってもそのままコピーしたような状態ではありますがまあ良しとしましょう。このあとEventの追加、削除そして修正を実装したりカレンダーの追加や修正等もチャレンジする予定です。

2010年12月26日日曜日

CodeIgniterでGoogleContactsAPIのテスト(8)

さてGoogleContactAPIを叩いての連絡先データをいじいじするテストですが今回で最終回です。連絡先の新規登録、修正及び削除、グループの新規登録、修正及び削除が出来るようになっています。未実装なのは連絡先のデータにグループデータを含める部分ということになりますね。今回の記事では連絡先データの中でのグループデータについて一覧取得、登録そして修正時のxmlデータ扱い方についてご紹介します。

これまでのデータの扱い方が理解できていれば特に問題ない範疇だと思います。

まずは一覧取得時のデータ取得方法です。

$group_data = $this->gdata_groups_model->getGroupsElement();
foreach ($xml->groupMembershipInfo as $g) {
 foreach ($group_data as $key => $val) {
  if ($key == $g['href']) {
   $obj->groups[] = $val;
  }
 }
}

feedデータにはhrefとしてグループのidしか含まれていないのでgroupデータを扱うモデルからElementデータを配列で取得してグループ名を代入していますがやり方としてはおわかり頂けるかと思います。

続いては登録時のxml要素追加方法です。

// add group elements
$groups = $this->input->post('groups', TRUE);
foreach ($groups as $g) {
 $group = $doc->createElement('gContact:groupMembershipInfo');
 $group->setAttribute('deleted','false');
 $group->setAttribute('href',$g);

 $entry->appendChild($group);
}

グループデータはwebサイトデータと同様にgContactに定義されているのでそれで指定してあげます。deleted要素は必要ないような気もしますがデベロッパーガイドに従い加えてあります。

最後に修正時のxml要素追加方法ですが登録時とちょっとだけ違います。

// add group elements
$groups = $this->input->post('groups', TRUE);
foreach ($groups as $g) {
 $group = $doc->createElement('groupMembershipInfo');
 $group->setAttribute('xmlns','http://schemas.google.com/contact/2008');
 $group->setAttribute('deleted','false');
 $group->setAttribute('href',$g);

 $entry->appendChild($group);
}

違いはxmlns要素が追加になっているだけですのでお約束みたいなものでしょうか。
この要素がないとエラーが帰ってしまいますのでご注意ください。

さてCodeIgniterでGoogleContactsAPIを叩きながらwebアプリから連絡先データを操作することが出来るようになりました。まだ連絡先データには様々なデータが扱えるようになっていますがリファレンスガイド等を見ればおそらく操作できるのではないかと思います。

ZendFrameworkを利用したので本当に簡単にデータ操作が出来ました。フレームワークの中でフレームワークを使うような愚はどうかとも思いますがまあ軽量フレームワークのCodeIgniterの利点を活かしつつこのテストを活用したwebアプリをこれから製作する予定です。

しかし連絡先だけでなくカレンダーのデータ操作も実や予定していまして次のシリーズは「CodeIgniterでGoogleCalenderAPIをテスト」シリーズが始まります。(笑

CodeIgniterでGoogleContactsAPIのテスト(7)

さていろいろとハマりまくっているCodeIgniterですがバリデーションにも一癖二癖ある感じです。既に使われている方々にはあたりまえの事かもしれませんがこれから始めて触ってみようという方のために記事にしておきたいと思います。

まずはバリデーションを通らなかったときの対応について。

一般的にはform画面に戻って入力結果をinput等に戻して表示という流れになるかと思います。今回複数のメールアドレスや電話番号登録が可能なように実装しているのでformのnameを配列としてCIに渡しております。

このように配列を利用するとバリデーションに渡した結果を受けるときのset_valueメソッドが上手く動作していないようで値を得ることが出来ませんでした。

仕方がないのでMy_Form_validation.phpを作成してForm_vaidationクラスを継承した独自バリデーションクラスを作成してちょこっとset_validationをいじりました。

function set_value($field = '', $default = '') {
 if ( ! isset($this->_field_data[$field]))
 {
  return $default;
 }

 if ( ! isset($this->_field_data[$field]['postdata'])) {
  return $default;
 } else {
  return $this->_field_data[$field]['postdata'];
 }
}

バリデーション後にviewを呼びだす前に$_POSTに必要データを突っ込んでおいて

set_value('edit', $this->input->post('edit'));

のような感じでデフォルト値として$_POSTからのデータを入れておいてあげると上手く行きました。

また複数formのバリデーションを行なう場合にユーザガイドでは http://codeigniter.jp/user_guide_ja/libraries/form_validation.html#savingtoconfig の「検証ルールのセットを作る」に従ってセット名を付けて呼びだすようになっています。

例えば…

if ($this->form_validation->run('contacts') == TRUE) {
 $this->gdata_model->post();
 $data = $this->gdata_model->getContactsList();
 $this->load->view('contacts_index', $data);
} else {
 $this->load->view('form_contacts_view', $this->gdata_model->getFormData());
}

こんな感じで呼びだすことになる訳です。

このときに利用するルールをApplication/config/form_validation.phpに配列として記載する訳ですがユーザドキュメントでは2010/12/25現在「=>」とすべきところが「=」となってしまっています。
こんな間違いに30分もハマるのは私くらいのものかと思いますが念のため書いておきます。(笑

軽快なCodeIgniterなだけあって痒いところに手が届くという機能は実装されてない感じもありますが個人的にはわりと気に入ったのでもうちょっと深く勉強してみたいと思っています。

2010年12月25日土曜日

CodeIgniterでGoogleContactsAPIのテスト(6)

時間がかかると云ってた割にはすぐに更新がきました。もっと苦労するかと思っていましたがすんなりと実装することが出来ました。ん?すんなり?嘘です。思いきりどハマりしたところが何箇所かありましたねぇ。

ということで今回はGoogleContactsのグループデータの取得、登録、更新そして削除についてまとめてご紹介します。

まずは一覧の取得方法です。

try {
 // perform login and set protocol version to 3.0
 $client = Zend_Gdata_ClientLogin::getHttpClient($this->user, $this->pass, 'cp');
 $gdata = new Zend_Gdata($client);
 $gdata->setMajorProtocolVersion(3);

 // perform query and get feed of all results
 $query = new Zend_Gdata_Query('http://www.google.com/m8/feeds/groups/default/full');
 $query->maxResults = 0;
 $query->setParam('orderby', 'lastmodified');
 $query->setParam('sortorder', 'descending');
 $feed = $gdata->getFeed($query);

 $data['title'] = (string)$feed->id;
 $data['totalResults'] = (String)$feed->totalResults;

 // parse feed and extract contact information
 // into simpler objects
 $groups = array();
 foreach($feed as $entry){
  $obj = new stdClass;
  $xml = simplexml_load_string($entry->getXML());
  if (! isset($xml->systemGroup)) {
   $obj->edit = (string)$entry->getEditLink()->href;
   $obj->group_name = (string) $entry->title;
  } else {
   $obj->edit = 'system_group';
   $val = $this->config->item('system_groups','config_contacts');
   $obj->group_name = $val[(string)$xml->systemGroup['id']];
  }
  $groups[] = $obj;
 }

} catch (Exception $e) {
 die('ERROR:' . $e->getMessage());
}

$data['groups'] = $groups;

return $data;

contactの時と何が違うかというと宛先URLが変っただけですね。グループデータの取得についてはhttp://www.google.com/m8/feeds/groups/default/fullこちらからの取得になります。それ以降はcontactsの時と同じ処理ですのでおわかり頂けるかと思います。

続いては登録部分です。

try {
 // perform login and set protocol version to 3.0
 $client = Zend_Gdata_ClientLogin::getHttpClient($this->user, $this->pass, 'cp');
 $gdata = new Zend_Gdata($client);
 $gdata->setMajorProtocolVersion(3);

 // create new entry
 $doc = new DOMDocument();
 $doc->formatOutput = true;
 $entry = $doc->createElement('atom:entry');
 $entry->setAttributeNS('http://www.w3.org/2000/xmlns/' ,'xmlns:atom', 'http://www.w3.org/2005/Atom');
 $entry->setAttributeNS('http://www.w3.org/2000/xmlns/' ,'xmlns:gd', 'http://schemas.google.com/g/2005');
 $doc->appendChild($entry);

 // add category element
 $category = $doc->createElement('atom:category');
 $category->setAttribute('scheme', 'http://schemas.google.com/g/2005#kind');
 $category->setAttribute('term','http://schemas.google.com/contact/2008#group');
 $entry->appendChild($category);

 // add title element
 $title = $doc->createElement('atom:title', $this->input->post('group_name', TRUE));
 $title->setAttribute('type', 'text');
 $entry->appendChild($title);

 $entryResult = $gdata->insertEntry( $doc->saveXML(), 'https://www.google.com/m8/feeds/groups/default/full');

} catch (Exception $e) {
 die('ERROR:' . $e->getMessage());
}

return $entryResult->id;

登録もputするURLがgroups用になっているだけでそれ以外には項目がグループの方がかなり少ないといったところでしょうか。本来「gd:extendedProperty」という項目にグループの情報を登録することが出来るのですが登録自体は成功するもののデータ更新時にこのinfoデータをxmlから取得するのが上手くいかず登録から断念することになりました。取得した$feedの中に改行が入ってしまっていたりしたのでなにかバグがあるのかも?
作成するxmlはデベロッパーガイドを参考にして作成しています。

続いては削除です。

try {
 // perform login and set protocol version to 3.0
 $client = Zend_Gdata_ClientLogin::getHttpClient($this->user, $this->pass, 'cp');
 // set GData delete headers
 $client->setHeaders('If-Match: *');
 $gdata = new Zend_Gdata($client);
 $gdata->setMajorProtocolVersion(3);

 // delete entry
 $gdata->delete($this->input->post('edit',TRUE));
} catch (Exception $e) {
 die('ERROR:' . $e->getMessage());
}

削除もcontactsと同様editタグに指定されているキーを送ってあげることで簡単に削除が出来ます。今回もIf-Match:*を指定して簡単に削除を行なっています。

最後に更新です。

try {
 // perform login and set protocol version to 3.0
 $client = Zend_Gdata_ClientLogin::getHttpClient($this->user, $this->pass, 'cp');
 $client->setHeaders('If-Match: *');
 $gdata = new Zend_Gdata($client);
 $gdata->setMajorProtocolVersion(3);

 $query = new Zend_Gdata_Query($this->input->post('edit'));
 $entry = $gdata->getEntry($query);
 $xml = simplexml_load_string($entry->getXML());

 $xml->title = $this->input->post('group_name');

 $entryResult = $gdata->updateEntry($xml->saveXML(), $entry->getEditLink()->href);

} catch (Exception $e) {
 die('ERROR:' . $e->getMessage());
}

return $entryResult->id;

更新も項目が少ないのでとても短かなソースになっています。editデータを送りqueryを実行、取得したxmlデータの中でtitleタグに新しいグループ名を代入してZendFrameworkのupdateEntryコマンドで送信して完了です。

contactsの方で苦労したせいかgroupデータについてはわりと簡単に実装することが出来ました。えっ?ハマったところ?データをjavascriptでいじる部分でハマっただけです。(笑

2010年12月24日金曜日

CodeIgniterでGoogleContactsAPIのテスト(5)

さてちょっと時間が空いてしまいましたが今回はContactsの更新です。
これまで一覧取得、登録そして削除ときましたので更新が出来たら一通りデータをいじれるようになる訳です。更新も削除と同じく一覧取得で得ているidをキーに行ないます。

$obj->edit = (string)$entry->getEditLink()->href;

こんな感じで取得してましたね。
ZendFramework様々といったところでしょうか。

今回時間が空いてしまったのはjavascript等をいじっていたせいなんですがこれはまあ他のサイトを参考にしてアレンジしていただけなので省略してContactsデータの更新に焦点をあててご紹介します。

// perform login and set protocol version to 3.0
$client = Zend_Gdata_ClientLogin::getHttpClient($this->user, $this->pass, 'cp');
$client->setHeaders('If-Match: *');
$gdata = new Zend_Gdata($client);
$gdata->setMajorProtocolVersion(3);

$query = new Zend_Gdata_Query($this->input->post('edit'));
$org_entry = $gdata->getEntry($query);
$xml = simplexml_load_string($org_entry->getXML());

上記のような形でアクセスを行ないqueryのデータとしてeditキーを渡しています。
通常はheaderにETagの値を指定して更新を行うらしいですが自分しかデータをいじらないアカウントですのでアスタリスクを指定しETagはデータに含めていません。

このqueryで得られたデータを上書きしていってputすることでデータが更新されるのですがデータ削除を行なうことを考えて一旦メールアドレス等のデータを全削除してからデータ登録を行なったのですがエレメントの順番が大切なようでエラーが出てしまい今回は全てのデータを新たなDOMDocumentに追加してくという手法を取りました。

// create new entry
$doc = new DOMDocument();
$doc->formatOutput = true;
$entry = $doc->createElement('atom:entry');
$entry->setAttributeNS('http://www.w3.org/2000/xmlns/' ,'xmlns:atom', 'http://www.w3.org/2005/Atom');
$doc->appendChild($entry);

// add name element
$name = $doc->createElement('name');
$name->setAttribute('xmlns', 'http://schemas.google.com/g/2005');
$entry->appendChild($name);
//input->post()メソッドを利用してXSS対策
$fullName = $doc->createElement('fullName', $this->input->post('name', TRUE));
$name->appendChild($fullName);

// add org name element
$org = $doc->createElement('organization');
$org->setAttribute('xmlns', 'http://schemas.google.com/g/2005');
$org->setAttribute('rel' ,'http://schemas.google.com/g/2005#work');
$entry->appendChild($org);
$orgName = $doc->createElement('orgName', $this->input->post('org', TRUE));
$org->appendChild($orgName);

// add email elements
$emails = $this->input->post('email', TRUE);
$epleces = $this->input->post('email_place', TRUE);
foreach ($emails as $key => $e) {
 if ($e != '') {
  $email = $doc->createElement('email');
  $email->setAttribute('xmlns','http://schemas.google.com/g/2005');
  if ($epleces[$key] == 'custom') {
   $email->setAttribute('label', $this->input->post('email_custom', TRUE));
  } else {
   $email->setAttribute('rel', 'http://schemas.google.com/g/2005#' . $epleces[$key]);
  }
  $email->setAttribute('address', $e);
  $entry->appendChild($email);
 }
}

// add phone elements
$phones = $this->input->post('phone', TRUE);
$pplaces = $this->input->post('phone_place', TRUE);
foreach ($phones as $key => $p) {
 if ($p != '') {
  $phone = $doc->createElement('phoneNumber', $p);
  $phone->setAttribute('xmlns','http://schemas.google.com/g/2005');
  if ($pplaces[$key] == 'custom') {
   $phone->setAttribute('label', $this->input->post('phone_custom', TRUE));
  } else {
   $phone->setAttribute('rel', 'http://schemas.google.com/g/2005#' . $pplaces[$key]);
  }
  $entry->appendChild($phone);
 }
}

// add web elements
$webs =$this->input->post('web', TRUE);
$wplaces = $this->input->post('web_place', TRUE);
foreach ($webs as $key => $w) {
 if ($w != '') {
  $web = $doc->createElement('website');
  $web->setAttribute('xmlns','http://schemas.google.com/contact/2008');
  $web->setAttribute('href',$w);
  if ($wplaces[$key] == 'custom') {
   $web->setAttribute('label', $this->input->post('web_custom', TRUE));
  } else {
   $web->setAttribute('rel', $wplaces[$key]);
  }
  $entry->appendChild($web);
 }
}

foreach ($org_entry->category as $category) {
 $categorys = $doc->createElement('atom:category');
 $categorys->setAttribute('term', $category->getTerm());
 $categorys->setAttribute('scheme', $category->getScheme());
 $entry->appendChild($categorys);
}


$id = $doc->createElement('atom:id', $org_entry->id);
$entry->appendChild($id);

foreach ($org_entry->link as $link) {
 $links = $doc->createElement('atom:link');
 $links->setAttribute('href', $link->getHref());
 $links->setAttribute('rel', $link->getRel());
 $links->setAttribute('type', $link->getType());
 $entry->appendChild($links);
}

$title = $doc->createElement('atom:title',$this->input->post('name', TRUE));
$entry->appendChild($title);

$updated = $doc->createElement('atom:updated', $org_entry->updated);
$entry->appendChild($updated);

$edited = $doc->createElement('atom:edited', $org_entry->edited);
$entry->appendChild($edited);

更新データ以外のデータについては先程行なったqueryの結果をそのまま入れるようにしてあります。じっくりコードを見るとわかると思いますがGmailではメール等の付随データとして仕事や自宅等が選べるようになっていますがその機能にも対応しています。コード中にcustomの文字が見えるかと思います。カスタムデータの場合にはrel要素ではなくlabel要素としてデータをタグに含めて送ることでデータが登録されます。

$entryResult = $gdata->updateEntry($entry_data, $org_entry->getEditLink()>href);

最後にZendFrameworkGDataAPIのupdateEntryメソッドでデータを送って更新が完了です。一緒に送っているのは冒頭で述べたlinkタグに含まれるeditキーです。

これでContactsについては一通りデータのやりとりが出来るようになりました。次の予定ではこれら連絡先を管理するには大切なグループについて登録や更新、削除を出来るようにしてみたいと思います。この部分についてはIBMのサンプルがないので英語ドキュメントを読みながらの作業になります。さらに時間がかかってしまうかも?

2010年12月18日土曜日

CodeIgniterでGoogleContactsAPIのテスト(4)

IBMのGoogleContactsサンプルのCI化第三弾はデータ削除です。

削除機能実装についてはわりとスムーズに行きました。

formタグで削除する連絡先のデータをhiddenで隠し持ちsubmitボタンで確認ダイアログを出しOKボタンで削除実行という流れです。

確認ダイアログのjavascriptは以下のようになっています。

<script language="JavaScript">
<!--
function check(){
    if(window.confirm('削除して宜しいですか?')){
        return true;
    } else {
        window.alert('削除を中止しました。');
        return false;
    }
}
//-->

一般的なスクリプトですから説明はいらないと思いますがOKボタンを押したときにtrueを返し呼び出し側でそれを検知してsubmitを実行するか判断するというものです。




表示した画像は上記のようになっています。リンククリックで即座に削除はちょっと怖いかな?とこんな機構を取り入れてみました。

<div class="name">
    <?php echo $r->name; ?>
    <span class="links">
        <?php echo form_open('contacts/delete','onSubmit="return check()"'); ?>
        <?php echo form_hidden('id',$r->edit)?>
        <?php echo form_submit('submit', '削除'); ?>
        <?php echo form_close(); ?>
    </span>
</div>

上記がその判断を行なうformタグ部分です。CodeIgniterのformヘルパーを利用しているのでちょっとわかりにくいかもしれませんがsubmit先がcontacts.phpのdeleteメソッドで連絡先のidを送っていることがおわかり頂けますでしょうか。

続いてはコントローラのdelete()メソッドを見てみましょう。

public function delete() {
    $this-<gdata_model-<delete();

    $data = $this-<gdata_model-<getContactsList();
    $this-<load-<view('contacts-index', $data);
}

gdata_modelのdeleteメソッドを呼びだしそちらでデータを削除後一覧表示画面を呼びだしている形になっています。削除すべき連絡先のIDについては$_POSTから受けとるため引数として渡しておりません。このあたりのお作法がよくわかっていませんがmodelでも$_POSTが受けとれるようになっているので問題ないかな?

続いてはmodelのdelete()メソッドです。メソッド名がかぶってしまっていますがまあ気にしない方向で…。(笑

public function delete() {
    try {
        // perform login and set protocol version to 3.0
        $client = Zend_Gdata_ClientLogin::getHttpClient($this-<user, $this-<pass, 'cp');
        // set GData delete headers
        $client-<setHeaders('If-Match: *');
        $gdata = new Zend_Gdata($client);
        $gdata-<setMajorProtocolVersion(3);

        // delete entry
        $gdata-<delete($this-<input-<post('id',TRUE));
    } catch (Exception $e) {
        die('ERROR:' . $e-<getMessage());
    }
}

こちらも登録に比べると非常にシンプルに完了しています。ログインしてプロトコルバージョンの指定およびdelete()メソッドの実行です。たったこれだけで削除については実装できてしまいました。

残すはデータ修正ですがこれは再び苦労しそうな予感がします。CIの使い方の問題とGoogleDataAPIともにまだはっきりと指針を詰められていません。じっくりドキュメントを読んで実装方法を考えてみたいと思います。IBMのサンプルプログラムに修正はないんですよねぇ。(笑