まだプログラマーですが何か?

プログラマーネタとアスリートネタ中心。たまに作成したウェブサービス関連の話も http://twitter.com/dotnsf

タグ:php

データのマイグレーションなどで WordPress を使う場合、元データのユーザーデータごと移行するケースがあると思います。そんなユーザー情報を WordPress の機能で追加するのではなく、外部アプリケーションから直接データベースを操作して追加する方法を紹介します。

なお、今回紹介するケースでは WordPress 4.8.2 を使いました。また WordPress のテーブルプリフィクス値はデフォルトの 'wp_' であると仮定して以下を紹介します。異なる設定で利用している場合は適宜読み替えてください。加えて、今回紹介する PHP ファイルを MySQL サーバーとは異なるホストから実行する場合は、このファイル内で指定したユーザーが MySQL にリモートログインできるような設定があらかじめされている必要がある点をご注意ください。


まず WordPress を普通にセットアップすると、いくつかのテーブルが自動的に生成されます。その中でユーザー情報を管理するのは wp_users と wp_usermeta の2つです:


wp_users はその名の通り WordPress のユーザーテーブルとなっていて、1レコードが1ユーザーの情報を意味します。詳しくは後述しますが、単に「ユーザーを登録する」だけであればこのテーブルに1レコード追加するだけで事は足ります。

一方、wp_usermeta はユーザーの属性に関わる値を管理するテーブルです。例えばユーザーのニックネームや権限といった wp_users だけでは管理できない一部の情報や、システムが内部的に利用するセッション情報、最終アクション日時といった情報を wp_usermeta テーブルで管理します。以下では「管理画面にアクセスできる権限を持ったユーザーを追加する」例を紹介しますが、この場合は wp_usermeta も操作する必要があります。


まず wp_users テーブルの属性を desc コマンドで調べた結果がこちらです:
mysql> desc wp_users;
+---------------------+---------------------+------+-----+---------------------+----------------+
| Field               | Type                | Null | Key | Default             | Extra          |
+---------------------+---------------------+------+-----+---------------------+----------------+
| ID                  | bigint(20) unsigned | NO   | PRI | NULL                | auto_increment |
| user_login          | varchar(60)         | NO   | MUL |                     |                |
| user_pass           | varchar(255)        | NO   |     |                     |                |
| user_nicename       | varchar(50)         | NO   | MUL |                     |                |
| user_email          | varchar(100)        | NO   | MUL |                     |                |
| user_url            | varchar(100)        | NO   |     |                     |                |
| user_registered     | datetime            | NO   |     | 0000-00-00 00:00:00 |                |
| user_activation_key | varchar(255)        | NO   |     |                     |                |
| user_status         | int(11)             | NO   |     | 0                   |                |
| display_name        | varchar(250)        | NO   |     |                     |                |
+---------------------+---------------------+------+-----+---------------------+----------------+
10 rows in set (0.01 sec)

プライマリキーは ID(auto_increment)で、他のフィールドは全て Not Null 属性です。ただ user_registered と user_status にはデフォルト値が設定されています。ということはこれらを除いた user_login, user_pass, user_nicename, user_email, user_url, user_activation_key, display_name を指定すればレコードは作れそうです。

というわけで、とりあえずは以下のような関数(createWpUser)およびファイル(wp-user-import.php(改良前))を PHP で作ってみました:
<?php

$wp_db_name = 'wpdb';            //. WordPress DB
$wp_db_host = 'localhost';       //. WordPress ホスト名
$wp_db_username = 'adminuser';   //. WordPress DB の管理者ユーザー名
$wp_db_password = 'adminpass';   //. WordPress DB の管理者パスワード
$wp_table_prefix = 'wp_';        //. WordPress DB のテーブルプリフィクス


function createWpUser( $u_email, $u_password ){
  global $wp_db_name, $wp_db_host, $wp_db_username, $wp_db_password, $wp_table_prefix;

  $pdo = new PDO( 'mysql:dbname='.$wp_db_name.';host='.$wp_db_host.';charset=utf8', $wp_db_username, $wp_db_password );
  if( $pdo != null ){
     $pdo->query( 'SET NAMES utf8' );

     $sql1 = 'insert into ' . $wp_table_prefix . 'users(user_login,user_pass,user_nicename,user_email,user_url,user_activation_key,display_name) values( :user_login, MD5(:user_pass), :user_nicename, :user_email, "", "", :display_name )';
     $stmt1 = $pdo->prepare( $sql1 );
     $stmt1->bindParam( ':user_login', $u_email );
     $stmt1->bindParam( ':user_pass', $u_password );
     $stmt1->bindParam( ':user_nicename', $u_email );
     $stmt1->bindParam( ':user_email', $u_email );
     $stmt1->bindParam( ':display_name', $u_email );
     $r1 = $stmt1->execute();
  }
}

?>

createWpUser 関数のパラメータはメールアドレスとパスワードの2つだけです。メールアドレスは user_login, user_nicename, user_email, display_name に設定します。パスワードは MySQL の MD5 関数を使ってハッシュ化したものを指定します。また user_url および user_activation_key には '' を指定して insert を実行しています。

このファイルを使って、1ユーザーを作成するサンプルがこちらです(test1.php):
<?php

require_once './wp-user-import.php';

$r1 = createWpUser( 'user1@xxx.com', 'P@ssw0rd' );  //. ID: user1@xxx.com , パスワード: P@ssword のユーザーを新規作成する

?>

試しにこの test1.php を実行すると、WordPress に user1@xxx.com という ID のユーザーが登録されます:
$ php -f test1.php


管理者権限でログインして管理ページを開くと、たしかに指定したユーザーが追加されていることが確認できます(この画面で「権限グループ」が「なし」になっていることがわかります):
2017103001


この時点で作成したユーザーは有効なので、指定した ID とパスワードでログインすることはできます:
2017103002


ただ管理機能については権限を一切持っていないユーザーなので、無理やり URL を指定して管理ページにアクセスしてもエラー画面となります:
2017103003


とりあえずはここまで。wp_users に1レコード追加するだけで(管理や投稿の権限を持たない)ユーザーを作ることができることがわかりました。とりあえずこの状態になれば、後から管理者がこれらのユーザーの権限を(手動で)変える、ということも可能ではあります。


次に管理者や投稿者の権限を持ったユーザーを同様に外部から作成する方法を紹介します。先程と同様に wp_usermeta テーブルの属性を同様に調べた結果がこちらです:
mysql> desc wp_usermeta;
+------------+---------------------+------+-----+---------+----------------+
| Field      | Type                | Null | Key | Default | Extra          |
+------------+---------------------+------+-----+---------+----------------+
| umeta_id   | bigint(20) unsigned | NO   | PRI | NULL    | auto_increment |
| user_id    | bigint(20) unsigned | NO   | MUL | 0       |                |
| meta_key   | varchar(255)        | YES  | MUL | NULL    |                |
| meta_value | longtext            | YES  |     | NULL    |                |
+------------+---------------------+------+-----+---------+----------------+
4 rows in set (0.00 sec)

umeta_id がプライマリキー(auto_increment)で、user_id は wp_users.ID を示す外部キーです。そしてこの user_id で指定したユーザーに対して、meta_key と meta_value で属性を加えることができるようになっています。

ユーザーの権限に関わる設定をするには、meta_key の値が 'wp_user_level' 及び 'wp_capabilities' となる2つのレコードを追加して指定することになります。具体的には対象のユーザーの権限に応じて、以下のような値を設定します:
ユーザー権限wp_user_level の設定値wp_capabilities の設定値
購読者0a:1:{s:10:"subscriber";b:1;}
寄稿者1a:1:{s:11:"contributor";s:1:"1";}
投稿者2~4a:1:{s:6:"author";b:1;}
編集者5~7a:1:{s:6:"editor";b:1;}
管理者8~10a:1:{s:13:"administrator";b:1;}


というわけで wp-user-import.php を少し改良します。createWpUser 関数のパラメータを2つから3つに変更し、3つ目のパラメータで上記の wp_user_level を指定するものとします:
<?php

$wp_db_name = 'wpdb';            //. WordPress DB
$wp_db_host = 'localhost';       //. WordPress ホスト名
$wp_db_username = 'adminuser';   //. WordPress DB の管理者ユーザー名
$wp_db_password = 'adminpass';   //. WordPress DB の管理者パスワード
$wp_table_prefix = 'wp_';        //. WordPress DB のテーブルプリフィクス


function createWpUser( $u_email, $u_password, $wp_user_level = 0 ){
  global $wp_db_name, $wp_db_host, $wp_db_username, $wp_db_password, $wp_table_prefix;

  $pdo = new PDO( 'mysql:dbname='.$wp_db_name.';host='.$wp_db_host.';charset=utf8', $wp_db_username, $wp_db_password );
  if( $pdo != null ){
    $pdo->query( 'SET NAMES utf8' );

    $sql1 = 'insert into ' . $wp_table_prefix . 'users(user_login,user_pass,user_nicename,user_email,user_url,user_activation_key,display_name) values( :user_login, MD5(:user_pass), :user_nicename, :user_email, "", "", :display_name )';
    $stmt1 = $pdo->prepare( $sql1 );
    $stmt1->bindParam( ':user_login', $u_email );
    $stmt1->bindParam( ':user_pass', $u_password );
    $stmt1->bindParam( ':user_nicename', $u_email );
    $stmt1->bindParam( ':user_email', $u_email );
    $stmt1->bindParam( ':display_name', $u_email );
    $r1 = $stmt1->execute();

    $user_id = null;
    $sql2 = 'select last_insert_id() as user_id from ' . $wp_table_prefix . 'users';
    $stmt2 = $pdo->query( $sql2 );
    if( $row = $stmt2->fetch( PDO::FETCH_ASSOC ) ){
      $user_id = $row['user_id'];
    }

    if( $user_id ){
      //. wp_user_level
      $sql3 = 'insert into ' . $wp_table_prefix . 'usermeta(user_id,meta_key,meta_value) values( :user_id, "wp_user_level", :wp_user_level )';
      $stmt3 = $pdo->prepare( $sql3 );
      $stmt3->bindParam( ':user_id', $user_id );
      $stmt3->bindParam( ':wp_user_level', $wp_user_level );
      $r3 = $stmt3->execute();

      //. wp_capabilities
      $wp_capabilities = null;
      if( $wp_user_level == 0 ){
        $wp_capabilities = 'a:1:{s:10:"subscriber";b:1;}';
      }else if( $wp_user_level == 1 ){
        $wp_capabilities = 'a:1:{s:11:"contributor";s:1:"1";}';
      }else if( 2 <= $wp_user_level && $wp_user_level <= 4 ){
        $wp_capabilities = 'a:1:{s:6:"author";b:1;}';
      }else if( 5 <= $wp_user_level && $wp_user_level <= 7 ){
        $wp_capabilities = 'a:1:{s:6:"editor";b:1;}';
      }else if( 8 <= $wp_user_level && $wp_user_level <= 10 ){
        $wp_capabilities = 'a:1:{s:13:"administrator";b:1;}';
      }
      if( $wp_capabilities != null ){
        $sql4 = 'insert into ' . $wp_table_prefix . 'usermeta(user_id,meta_key,meta_value) values( :user_id, "wp_capabilities", :wp_capabilities )';
        $stmt4 = $pdo->prepare( $sql4 );
        $stmt4->bindParam( ':user_id', $user_id );
        $stmt4->bindParam( ':wp_capabilities', $wp_capabilities );
        $r4 = $stmt4->execute();
      }
    }
  }
}
?>

createWpUser の3つ目のパラメータ(無指定時は 0)の値を上記の wp_user_level と見なして、wp_usermeta テーブルにユーザー権限に合わせた値を作成するように変更しました。また wp_usermeta テーブルにレコードを作成する際に wp_users.ID の値が必要になるので、MySQL の last_insert_id() 関数を使って作成したユーザーの ID を取得しています。

そして、例えば管理者権限を持ったユーザーを作成したい場合は以下(test2.php)のようなコードを用意します:
<?php

require_once './wp-user-import.php';

$r2 = createWpUser( 'user2@xxx.com', 'P@ssw0rd', 10 );  //. ID: user2@xxx.com , パスワード: P@ssword のユーザーを管理者権限で新規作成する

?>

試しにこの test2.php を実行すると、WordPress に user2@xxx.com という ID の管理者ユーザーが登録されます:
$ php -f test2.php

2017103004


このユーザーの ID とパスワードでログインすると、管理者ダッシュボードにアクセスして、管理者権限でのオペレーションが可能です:
2017103005



というわけで、PHP から WordPress の MySQL データベースを直接操作してユーザーを登録することができました!


(参考) WordPress Codex 日本語版:ユーザーの種類と権限

無料のグループウェアを SaaS で提供していたサイボウズ Live が 2019 年4月15日をもってサービスを終了する、というアナウンスがありました:
サイボウズLiveサービス終了のお知らせ

個人的にも仲間内サークル活動の中で使っていたりしていたもので、このニュース自体はとても残念なものでした。が、終了まで1年半も猶予を持ったアナウンスであり、また(上記リンクによると)今後データのエクスポート(CSVファイル?機能?)などを提供する予定もあるらしいです。データを他のサービスに移行するための準備期間としては充分にあるようにも感じました。

データ移行に関しては時間的な猶予もあるので、しばらくは謹製のデータエクスポートが出されるのを待ってもいいとは思っています。が、個人的/技術的な興味もあって自分でも PHP で作ってみました。詳しくは後述しますが、もしご利用になる場合、現時点ではまだ不完全なものだと理解の上で使ってください。なおこのツールではデータを XML ファイルでエクスポートします(掲示板データなど、改行情報を含むデータは CSV に向かないと判断したので):
https://github.com/dotnsf/cbl_export/blob/master/cbl_export.php


実際に使うには、まず PHP 実行環境が必要です(自分自身がテストした範囲では PHP 5.3.3 でも動きました、かなり古いバージョンでも大丈夫だと思います):
(例 CentOS の場合)
# yum install php php-mbstring php-xml php-pear

加えてプログラムの中で OAuth などの外部ライブラリモジュールを使うため、これらのモジュールをあらかじめインストールしておいてください:
(例 pear を使う場合)
# pear install Net_URL2
# pear install HTTP_Request2
# pear install HTTP_OAuth

またこのツールを使う準備としてサイボウズ Developer Center のアカウントが必要です。同アカウントをお持ちでない場合はリンク先の「デベロッパー登録」ボタンから申請してアカウントを作成してください:
2017102901


アカウント作成後にログインし、「Myアプリケーション」の一覧で「アプリケーションを登録する」を選択し、今から動かすアプリケーションの登録を行います:
2017102902


アプリケーションを登録する際のアプリケーション名などは任意に設定いただいていいのですが、「アプリケーションの種類」は「クライアント」、そして「アクセスレベル」は「レベルZ」を選択する点に注意してください(ここを間違えると正しく動かなくなります。なおアプリケーション種類をクライアントにしないとレベルZのアクセスレベルは選択できないはずです):
2017102903


こうしてアプリケーションを登録し、登録後の画面に表示される Consumer Key と、Consumer Secret をメモしておきます(他人に教えてはいけません)。またアプリケーションの種類とアクセスレベルが正しく登録されていることを確認してください:
20171026



ここまでの準備ができたところで、改めて今回作成したエクスポートアプリを使います。こちらのサイトから git clone するなどして cbl_export.php をコピーします:
https://github.com/dotnsf/cbl_export/blob/master/cbl_export.php


cbl_export.php をテキストエディタで開き、一部を編集します:
<?php
//. cbl_export.php
//. Referer: https://developer.cybozulive.com/doc/current/#id1
//. For XAuth(Ones of Level Z)
$consumer_key           = '(CybozuLive Developer Center Consumer Key)';
$consumer_secret        = '(CybozuLive Developer Center Consumer Secret)';
$xauth_access_token_url = 'https://api.cybozulive.com/oauth/token';
 
$params = array(
    'x_auth_username' =--> '(CybozuLive Email)',
    'x_auth_password' => '(CybozuLive Password)',
    'x_auth_mode'     => 'client_auth',
);

:
:

上記の青字部分を編集します。$consumer_key には上記サイボウズ Developer Center でのアプリケーション登録時に確認した Consumer Key を、$consumer_secret には同 Consumer Secret を代入します。また $params 'x_auth_username' には自分のサイボウズ Live ログイン時のメールアドレスを、そして 'x_auth_password' には同パスワードを指定します。


ここまでの準備ができたら php コマンドで cbl_export.php を実行します:
# php -f cbl_export.php

実行が成功するとカレントディレクトリに自分のメールアドレスと同じ名前のフォルダと、自分がサイボウズ Live で所属しているグループごとの ID(X:XXXXX みたいなフォーマットです)のフォルダが作成され、各データがそれぞれのフォルダ内にエクスポートされます。例えば1つのグループにしか所属していない場合であれば、自分のメールアドレスのフォルダが1つと、そのグループのIDのフォルダが1つ生成されます:
2017102904
(↑自分が実行した場合の結果サンプル)


そして個人のデータはメールアドレスのフォルダ内に、要素毎に XML ファイルでエクスポートされています(現在の仕様ではスケジュール、チャット、ToDo、コネクションのデータがエクスポートされます):
2017102905


またグループ ID のフォルダには各グループ毎のメンバー、掲示板、イベント、ToDo、ファイル共有の情報が XML ファイルでエクスポートされています:
2017102906


なお、このツールを開発するにあたり、サイボウズ Live API を使っています。その仕様はこちらです:
サイボウズLive データ API ドキュメント


(↓2017/Oct/29 時点での制限事項)
上記のように一応動くものが作れたつもりなのですが、2017/Oct/29 時点ではファイルのダウンロード機能が正しく動いていません。例えばサイボウズ Live のファイル共有機能や掲示板の中でファイルを添付しているケースは珍しくないと思っています。その「どんなファイルが添付されているか?」といった情報は取得できて、その情報は現在取得できるファイルの中にも正しくエクスポートできているのですが、肝心のファイルそのもののダウンロードができていません。具体的にはこの API の実行時にだけ想定外のエラーになってしまい、まだその理由や解決策が分からずにいます:
https://developer.cybozulive.com/doc/current/pub/fileDownload.html


この部分については私のミスなのか、API 側の問題なのかの判別ができず、現在は問い合わせ中です。分かり次第に対応するつもりです。
(↑2017/Oct/29 時点での制限事項)


(↓2017/Nov/06 追記)
サイボウズより返答あり。サイボウズ側では問題なく動いているとのこと。むむむ・・ (--;
(↑2017/Nov/06 追記)


添付ファイルのダウンロード以外についてはある程度動くようになっているつもりです。早めのデータ移行を検討していたり、移行先候補へのテストを早めに実行したい場合に活用ください。

なおサイボウズから謹製のエクスポートデータやエクスポートツールが提供された場合は、(きっと面倒なアカウント登録なども不要になると思うので)そちらの利用をオススメします。 (^^;


久しぶりに PHP を使っていて気付いたことをブログネタにしました。

最近の Web API/REST API は JSON フォーマットで結果を取得できるものが多いですが、中には XML のみサポートされているものもあります(RSS とかね)。そんな XML を PHP で扱う方法について調べました。
2017102700


まず、PHP には simplexml_load_file および simplexml_load_string という便利な関数が用意されています。これらは XML ファイル(のURL)や XML 文字列を指定して、PHP のオブジェクト化するというものです。例えば以下のような ATOM フィードフォーマットの XML ファイル(atom.xml)があったとします:
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="ja">
  <id>tag:phpxml/</id>
  <title>サンプルタイトル</title>
  <link rel="alternate" type="text/html" href="http://sample.xml.jp/feed/"/>
  <link rel="self" type="application/atom+xml" href="http://sample.xml.jp/feed/atom10.xml"/>
  <entry>
    <id>http://sample.xml.jp/test1.html</id>
    <title>紹介タイトルその1</title>
    <link rel="alternate" type="text/html" href="http://sample.xml.jp/test1.html"/>
    <summary>紹介するエントリの1つです。</summary>
    <dc:updated>Thu, 26 Oct 2017 07:00:00 +0900</dc:updated>
  </entry>
  <entry>
    <id>http://sample.xml.jp/test2.html</id>
    <title>紹介タイトルその2</title>
    <link rel="alternate" type="text/html" href="http://sample.xml.jp/test2.html"/>
    <summary>紹介するエントリの2つめです。</summary>
    <dc:updated>Fri, 27 Oct 2017 07:00:00 +0900</dc:updated>
  </entry>
</feed>

この atom.xml ファイルに対して、例えば以下のようなコード(test1.php)を用意して実行します:
<?php
$feed = simplexml_load_file( "atom.xml" ); echo "id: " . ( $feed->id ) . "\n"; echo "title: " . ( $feed->title ) . "\n";
foreach( $feed->entry as $entry ){ echo "entry_title: " . ( $entry->title ) . "\n"; echo "link_url: " . ( $entry->link->attributes()->href ) . "\n"; }
?>

その実行結果(赤字部分)はこちら:
$ php -f test1.php

id: tag:phpxml/
title: サンプルタイトル
entry_title: 紹介タイトルその1
link_url: http://sample.xml.jp/test1.html
entry_title: 紹介タイトルその2
link_url: http://sample.xml.jp/test2.html

これだけ。simplexml_load_file 関数や simplexml_load_string 関数は XML ファイルや XML 文字列を簡単に PHP のオブジェクト化して、XML 内の各データや属性値にアクセスできるようになるものです。

※上記のように属性値は $entry->link->attributes()->属性名 で取得できます。


さてここまでは普通にできるのですが、実はこの方法では取得が難しいデータがあります。各 entry 内にある <dc:updated> という特殊な名前のデータです。 
      :
      :
    <title>紹介タイトルその1</title>
    <link rel="alternate" type="text/html" href="http://sample.xml.jp/test1.html"/>
    <summary>紹介するエントリの1つです。</summary>
    <dc:updated>Thu, 26 Oct 2017 07:00:00 +0900</dc:updated>
      :
      :

この部分が少し厄介なのです。test1.php と全く同じ要領でこんな test2.php ファイルを作ってみます:
<?php
$feed = simplexml_load_file( "atom.xml" ); echo "id: " . ( $feed->id ) . "\n"; echo "title: " . ( $feed->title ) . "\n";
foreach( $feed->entry as $entry ){ echo "entry_title: " . ( $entry->title ) . "\n"; echo "link_url: " . ( $entry->link->attributes()->href ) . "\n"; echo "dc:updated: " . ( $entry->dc:updated ) . "\n"; }
?>

この test2.php を実行すると・・・ Parse error となってしまいます。PHP の文法ではここに ":" を記述してはいけないんですね。。
$ php -f test2.php

Parse error: syntax error, unexpected ':' in test2.php on line 8

なるほど。これでは文法的にNGということはわかった。が、問題はどうやって回避すればいいのか、です。simplexml_load_file 関数を使わずに別の方法で XML をパースする方法もありますが、それはそれで面倒だったりします。なんとか simplexml_load_file の便利な機能をそのままに回避できないか・・

そこでググって見つけたがこの方法でした。簡単にいうと
 (1) タグ(<から>まで)内の ":" を "_" とかに一括変換する
 (2) ただしプロトコル( http:// とか https:// とか)で使われている部分だけは元に戻す
という方法です。この方法で作り直したのがこちら(test3.php)です:
<?php
//. XML ファイルを文字列変数へ読み込む
$txt = file_get_contents( "atom.xml" );
//. <***:****> -> <***_****>
$txt  = preg_replace( "/<([^>]+?):(.+?)>/", "<$1_$2>", $txt );
//. プロトコルは元に戻す
$txt  = preg_replace( "/_\/\//", "://", $txt );

$feed = simplexml_load_string( $txt );
echo "id: " . ( $feed->id ) . "\n";
echo "title: " . ( $feed->title ) . "\n";
foreach( $feed->entry as $entry ){
  echo "entry_title: " . ( $entry->title ) . "\n";
  echo "link_url: " . ( $entry->link->attributes()->href ) . "\n";
  echo "dc:updated: " . ( $entry->dc_updated ) . "\n";
}
?>

青字部分ではまず普通に atom.xml ファイルを文字列として読み込み、上記 (1) および (2) の処理を行います。そしてその結果を simplexml_load_string 関数でオブジェクト化しました。このようにすると、各 entry 内の <dc:updated> 要素は <dc_updated> と変わっているので、そのまま扱えるようになります。

この test3.php を実行すると、期待通りの結果を得ることができました:
$ php -f test3.php

id: tag:phpxml/
title: サンプルタイトル
entry_title: 紹介タイトルその1
link_url: http://sample.xml.jp/test1.html
dc:updated: Thu, 26 Oct 2017 07:00:00 +0900
entry_title: 紹介タイトルその2
link_url: http://sample.xml.jp/test2.html
dc:updated: Fri, 27 Oct 2017 07:00:00 +0900

とりあえず動くことは動きました。でももうちょっと上手い方法がないもんかな。。

超有名なコンテンツ管理システムであるワードプレスは数多くのプラグインも提供されており、プラグインを組み合わせるだけで色々な目的のサイトが構築できてしまいます。

ふとした事情でクラウドファンディング的なサイトを試験的に用意する必要が生じたのですが、「ワードプレスだけでサクッとクラウドファンディングサイトが作れたりしないだろうか?」と思い立って調べてみました。結論としては「色々あるけど、とりあえずクラウドファンディングが作れるプラグインは見つけた」ので、調べて構築した内容を以下に紹介します。


まず、クラウドファンディング用のワードプレスプラグインは有料/無料あわせて数多く存在しています。その中で以下に紹介するのは IgnitionDeck という団体(?)から提供されている IDCF(IgnitionDeck CrowdFunding) (及び IDCF 対応のテーマ)です:
2017040812



IDCF をワードプレスに導入するにはワードプレス管理画面のプラグイン導入から "IgnitionDeck" を検索し、"IgnitionDeck Crowdfunding & Commerce" を見つけて「今すぐインストール」します:
2017040801


他のプラグイン同様に、インストール後「有効化」をクリックすると、このプラグインが有効になります:
2017040802


IDCF プラグインを有効にしても、この段階では IgnitionDeck 未登録のためまだ使えません。管理画面に "IgnitionDeck" というメニューが追加されているので、メニューから IDCF を選び、 "Activate Now" ボタンをクリックして登録画面に遷移します:
2017040803


登録といっても名前、メールアドレス、そしてパスワードを入力するだけです。簡単:
2017040804


登録が完了すると次に行うべき手順が紹介された画面が表示されます。↓下図の3ステップです:
2017040805


1ステップ目、(ここまでの作業でいつの間にか導入されていた)"500 Framework" テーマを有効にします:
2017040806


2ステップ目、IDCF の設定を行います(そのまま Save でも可):
2017040807


そして3ステップ目、というかクラウドファンディングサイトとしてのスタート地点です。クラウドファンディングのプロジェクトが開始できるようになったので、何か1つ追加してみます:
2017040808


プロジェクトを保存すると、ワードプレスのトップ画面のプロジェクト一覧から確認できるようになります:
2017040802


プロジェクトを選択して開くと、クラウドファンディングの説明や目標額が確認でき、支援表明もできるようになっていることが確認できます:
2017040803


IDCF には有料のオプションもあり、サポートや追加で使えるツールキットが用意されているようです。が、まだあまり調べていません。こちらは気が向いたらということで。。


軽く使ってみた限りでの感想ですが、管理画面が英語なのはともかくとして、目標額などを入力すると自動的に米ドル扱いになってしまうことに気付きました(要は金額のデータとして "100" と入力すると、自動的に「100 米ドル」扱いになってしまう)。これをカスタマイズするには IDC メニューの CURRENCY SETTINGS から "JPY" を選択して更新しておくことで変更できました:
2017040801


もう一点、ファンディングの支援レベル(例えばレベル1はステッカーのみ、レベル2は商品そのもの1つ、レベル3は商品を10個まとめて、・・・みたいなもの)を複数追加しようとしたのですが、うまくできませんでした。ググってみると、同じ現象で悩んでるっぽい人向けの FAQ っぽいページを見つけたのですが「会員専用ページです」みたいなメッセージから先に進めず、未だに原因やワークアラウンドが分かっていません。会員としてログインしてるはずなんだけどな・・・ もしかすると、ここが無料版の制限なのかもしれません:
2017040801


もう一点。実際にユーザーがこのプロジェクトを支援しようと "Support Now" して、"Next Step" に進もうとすると、、:
2017040901


こんな画面に推移します:
2017040902


ここで何らかの入力フォーム(project_purchase_form ?)が出て来る想定なんだろうけど、ここをどうすると表示できるようになるのか? カスタマイズするにはどうするのか? といった情報を探していたのですが、どうやらここから先は無料版では提供されていない機能のようでした:
Ignitiondeck does not redirect me to Purchase Page


というわけで、まだ IDCF のフル機能を試せたわけではないのですが、ここまでは超簡単に作れてしまったことも事実。便利なプラグインをまた1つ発見してしまった。。


IBM Bluemix で Context Path Route(以下、「コンテキストルーティング」)を行う方法が紹介されていました:
Context path routes for your Bluemix Cloud Foundry apps
https://www.ibm.com/blogs/bluemix/2017/01/context-path-routes-hour-bluemix-cloud-foundry-apps/



コンテキストルーティング」とは URL のパス部分を使ったルーティングの仕組みです。以下の例を使って紹介します。 "myapp.yourbluemix.net" というホストが稼働しており、ここでは色々な機能が稼働しています。例えばメインのポータル機能は http://myapp.yourbluemix.net/ で、ドキュメント管理機能は http://myapp.yourbluemix.net/docs/ 以下で、サーバー管理機能は http://myapp.yourbluemix.net/admin/ 以下で実装されている、といった具合です。ユーザーからは同じホスト名(myapp.yourbluemix.net)にアクセスしており、同一のサーバーを使っているように見えています:
2017020901


コンテキストルーティングを使うと、実際にはより強固&柔軟な構成で実現できます。例えば上記で紹介した3つの機能を全て別々のサーバー上に実装した上で、ユーザーにそれぞれのホストに(異なるホスト名へ)アクセスさせるのではなく、同じホスト名でのアクセスを可能にします。そして URL のパス部分でどのサーバーを使うかのルーティングを定義します。以下の例では /docs 以下にアクセスする場合は mydocs.yourbluemix.net へ、/admin 以下にアクセスする場合は myadmin.yourbluemix.net へ、それ以外は myapp.yourbluemix.net へ、といった具合です:
2017020902


これがコンテキストルーティングです。これを IBM Bluemix 環境のアプリケーションでも行う方法が上記ブログにて紹介されていました。ブログで紹介されていたのは Node.js ランタイムと Python ランタイムを使った方法でした。自分は Node.js と PHP で試してみたところ少し挙動が変わっていたようでした。それがランタイムの種類の違いに起因しているのかどうかは分からないのですが、、自分で動作確認した方法を以下に紹介します。なお実際に以下の手順を実行するには Bluemix アカウントと cf コマンドラインツールが必要になるので、事前に自分の環境にあった cf をダウンロードしてインストールしておいてください。

まずシステム構成、およびルーティングルールを以下のようなものとします:
2017020903

- 2台のランタイムおよびアプリケーションを用意する:
  - dotnsf-cr.mybluemix.net (Node.js)
  - dotnsf-cr-php.mybluemix.net (PHP)
- ユーザーがアクセスする URL は http://dotnsf-cr.mybluemix.net/ 以下のみ
- dotnsf-cr.myblutemix.net/phpapp/ 以下へのアクセスを dotnsf-cr-php.mybluemix.net/phpapp 以下の PHP アプリケーションにルーティングする。それ以外は dotnsf-cr.mybluemix.net の Node.js アプリケーションが処理する
- PHP アプリケーションが稼働する dotnsf-cr-php.mybluemix.net への直接アクセスはエラーとして処理する


Bluemix らしく、2つの異なるランタイムで仮想的な1つのシステムを作ってみることにしました。で、こんなソースコードを用意しました:
https://github.com/dotnsf/BluemixContextRoutingSample


上記サイトよりソースコードをダウンロード&展開するか git clone して入手してください。展開後に manifest.yml をテキストエディタで開き、以下の赤字部分を自分用のものに変更してください:
applications:
# Node.js app
- name: dotnsf-cr
  memory: 256M
  routes:
  - route: dotnsf-cr.mybluemix.net
  path: ./node_app/
# PHP app
- name: dotnsf-cr-php
  memory: 256M
  routes:
  - route: dotnsf-cr.mybluemix.net/phpapp
  path: ./php_app/

Node.js ランタイムには dotnsf-cr 、PHP ランタイムには dotnsf-cr-php という名前を付けており、最終的にはどちらも dotnsf-cr.myblutemix.net という名前でアクセスできるようにコンテキストルーティングを設定する、という内容です(つまりこの1つの manifest.yml で2つのランタイムの定義をしています)。上記の赤字部分を皆様のアプリケーション名に合わせて変えて使ってください。また US-SOUTH 以外のデータセンターを使う場合はドメインを(例えば eu-gb.mybluemix.net などに)適宜変更してください。


こうして作成したソースコードを cf ツールでプッシュします。manifest.yml 内に(2つのランタイムそれぞれを作るための)必要な情報が全て記述されているので1回の "cf push" だけで実行されます:
2017020901

↑まずは Node.js アプリケーションである dotnsf-cr のプッシュから始まります


2017020902

↑Node.js アプリケーションのビルドが行われている様子です


2017020903

↑ Node.js アプリケーションのプッシュが完了した様子です。そして間髪をいれずそのまま PHP アプリケーションである dotnsf-cr-php のプッシュが始まります。


2017020904

↑途中でルーティング処理が行われている様子が確認できます。


2017020905

↑PHP アプリケーションのプッシュも終わりました。dotnsf-cr-php という名前のアプリケーションですが、その URL が dotnsf-cr.myblutemix.net/phpapp に設定されていることがわかります。


この状態でダッシュボードを確認するとこのようになっているはずです。Node.js と PHP 2つのランタイムが追加されて実行中になっています。これらのルーティング先はどちらも同じものになっていることを確認してください。
2017020909


この状態で http://dotnsf-cr.mybluemix.net/ にアクセスすると Node.js 内の public/index.html が表示されます:
2017020906


また https://dotnsf-cr.mybluemix.net/about にアクセスすると、Node.js の app.js 内で定義された内容に従ったメッセージが表示されます:
2017020907


一方、http://dotnsf-cr.mybluemix.net/phpapp/ にアクセスすると、このパスの場合は PHP ランタイムにルーティングされ、PHP ランタイム内の index.php が表示されるはずです:
2017020908


というわけで、Bluemix でもコンテキストルーティングが実現できることが確認できました。他のランタイム環境でも、3種類以上であってもルーティングの設定だけで同様に実現できると思います。

このページのトップヘ