作成したプラグインを国際化対応する(その4)
翻訳可能文字列を選定し、文字列を関数コールで囲みます。
例として
例1 $hello = __("Hello, dear user!"); 例2 _e("TEST!")
のように記載します。
「__」関数で囲んだメッセージが翻訳対象となります。
「_e」関数で囲うと、ブラウザ出力を行います。
翻訳可能文字列を選定し、文字列を関数コールで囲みます。
例として
例1 $hello = __("Hello, dear user!"); 例2 _e("TEST!")
のように記載します。
「__」関数で囲んだメッセージが翻訳対象となります。
「_e」関数で囲うと、ブラウザ出力を行います。
国際化対応するその3は、テキストドメインの決定です。
wordpressがプラグインを識別する為に、ユニークな名前にします。
プラグイン名にすると分かりやすく、運用がしやすいです。
今回は、これまで作ってきたプラグインの「SamplePlugin1」という名前にします。
また、この名前の決定は、後ほど出てくるPOファイルやMOファイルになります。
前回の作成したプラグインを国際化する対応について、
もう少し詳しく落とし込みます。
詳しくは公式ドキュメントのこちらのページに記載されています。
これまでに作成してきたプラグインは、以下のディレクトリになります。
/ワードプレスをインストールしたディレクトリ/wp-content/plugins/SamplePlugin1/
まず、このディレクトリ内に多言語用のディレクトリを新規作成します。
/ワードプレスをインストールしたディレクトリ/wp-content/plugins/SamplePlugin1/languages
WordPressの国際化対応をするには、ライブラリでGNU gettextを使用します。
■POTファイル
Portable Object Templateの略です。
翻訳対象となるメッセージと翻訳結果を保存するファイルのテンプレートとなるものです。
■POファイル
翻訳対象となるメッセージと翻訳結果を保存します。
■MOファイル
Machine Objectの略です。
翻訳結果を表示する為に必要になるファイルです。
実際に国際化する為には、上記の概念を把握した上で作成していきます。
前回まで作成したショートコードをさらに改修します。
ショートコードを利用する側で、次のような記載の場合があります。
[samplelist3]なんらかの記述[/samplelist3]
この場合は、以下のような関数になります。
/** * ショートコード(その3) */ function samplelistFunction3($param, $content = '') { if ($content == "") { return; } else { extract( shortcode_atts( array("class" => "default"), $atts ) ); return '<p class="' . esc_attr($class) . '">' . esc_html($content) . '</p>'; } } add_shortcode('samplelist3', 'samplelistFunction3');
このブログで実装を進めてきたプラグインについて、以前の記事になりますが、ショートコードを実装しました。
その時に実装した内容はこちらの記事になります。
今回はその時に実装したショートコードプラグインを改造してみます。
まず、前回のコードはちなみに下記のようになっています。
/** * ショートコード */ function samplelistFunction() { //データ一覧 $disp_html =<<< EOL <form action="" method="post"> <h2>データ一覧(ショートコードテスト)</h2> <div class="wrap"> <table class="wp-list-table widefat striped posts"> <tr> <th nowrap>ID</th> <th nowrap>名前</th> <th nowrap>登録日時</th> </tr> EOL; global $wpdb; $tbl_name = $wpdb->prefix . 'sample_mst'; $sql = "SELECT * FROM {$tbl_name} ORDER BY id;"; $rows = $wpdb->get_results($sql); foreach($rows as $row) { $disp_html .=<<<EOL <tr> <td>{$row->id}</td> <td>{$row->sample_name}</td> <td>{$row->create_date}</td> <td> </tr> EOL; } $disp_html .=<<< EOL </table> </div> </form> EOL; return $disp_html; } add_shortcode('samplelist', 'samplelistFunction');
上記のコードを投稿側で使用するには、
[samplelist]
のように書きます。
その際に、ショートコードのパラメータ部分を記述し、プログラム側に値として渡す方法を書きます。
[samplelist2 text="TEST"]
プラグイン側の記述も同様にパラメータを受け取るように変更します。
/** * ショートコード(その2) */ function samplelistFunction2($param) { $default_param = array( "text" => "default text" ); $merged_param = shortcode_atts($default_param, $param); extract($merged_param); return esc_html($text); } add_shortcode('samplelist2', 'samplelistFunction2');
上記のように実装し、投稿時には
[samplelist2 text="テスト値"]
のようにショートコードを使うことができます。
実際には値を渡す他に、IDやプラグインの挙動を制御するパラメータを渡す用途があるので、設計によって仕様を決めて実装をします。
作成中のウィジェットは、主にユーザ側への表示でしたが、このウィジェットに対し、管理画面側でなんらかの値を登録&更新する仕組みを実装します。
ウィジェットのソース内に以下のメソッドを追加します。
/** * 管理画面側のパラメータ調整用コード */ public function form($par) { //入力された文字列(title)を取得 if (isset($par["title"])) { $title = esc_attr($par["title"]); } else { $title = ""; } //フォーム用のIDを取得 $title_id = $this->get_field_id('title'); //フォーム用のnameを取得 $title_name = $this->get_field_name('title'); //フォーム部分の表示 echo <<<EOL <label for="{$title_id}"><?php echo _e('Title:'); ?></label> <input type="text" class="widefat" id="{$title_id}" name="{$title_name}" value="{$title}"> EOL; }
こうすることで、管理画面で追加したウィジェットの部分に更新用のフォームが表示されます。
仮に「title」というパラメータ名として話を進めます。
上記のコードを書いた後に管理画面にアクセスすると、次のような画面になります(赤枠部分が追加箇所)。
この表示されたフォーム部分に対し、テキストを入力し保存ボタンを押します。
すると、保存しました。というメッセージになり、値が更新されます。
更新後は
<label for="{$title_id}"><?php echo _e('Title:'); ?></label>
というラベル部分の「echo _e(‘title’);」の箇所が表示され、入力した値が確認できます。
更新した値はウィジェット表示側のメソッド「public function widget」で$parの変数に格納されているので、下記のようにechoすることでユーザ側画面のサイドバーに表示されます。
public function widget($args, $par) { echo $args["before_widget"]; echo $args["before_title"]; echo "これはウィジェットでの表示です"; echo $args["after_title"]; echo "ここは中身です"; echo esc_html($par["title"]); echo $args["after_widget"]; }
前回作成したウィジェットに、ブログ画面側の表示ロジックを入れてみます。
前回のソースコードを基に、public function widgetメソッドに対し、次のように記載します。
public function widget($args, $par) { echo $args["before_widget"]; echo $args["before_title"]; echo "これはウィジェットでの表示です"; echo $args["after_title"]; echo "ここは中身です"; echo $args["after_widget"]; }
次に、管理側の外観→ウィジェットの画面で、ウィジェットエリアに作成したsampleのウィジェットをセットします。
こうすることで、ブログ側のサイドメニューにウィジェットを表示することができます。
前回までは主にプラグインの作成についてやってきましたが、ここからは若干方針を変更して、ウィジェットの作成をやってみます。
まず、ウィジェットを作りにあたり、プラグイン用のフォルダに新規ファイルを作成します。
分かりやすいようにサーバ上の以下のフォルダ「SamplePlugin1」直下に新規ファイルを作成します。
/wordpressがインストールされているフォルダ/wp-content/plugins/SamplePlugin1
上記の場所に「SampleWidget1.php」を作成し、中には次のように記載します。
<?php /* Plugin Name: SampleWidget1 Plugin URI: http:// Description: サンプルウィジェット。 Version: 1.0 Author: sample Author URI: http:// License: GPL2 License URI: https://www.gnu.org/licenses/gpl-2.0.html */ add_action( 'widgets_init', create_function('', 'return register_widget("sample_widget");') ); /** * Widget Class */ class sample_widget extends WP_Widget { function __construct() { $widget_ops = array("description" => "Sample"); $control_ops = array("width" => 400, "height" => 300); parent:: __construct( false, "Sample", $widget_ops, $control_ops ); } public function widget($args, $par) { } }
この記述だけで、管理画面のプラグイン画面には次のように表示されます。
その後「有効化」リンクを押して有効にします。
有効化した後は、管理画面の「外観」の中の「ウィジェット」というリンクが表示されるので、画面を開きます。
次に、管理画面の右側を確認すると、次のようなSampleウィジェットが表示されます。
この時、注意が必要なのは、テーマ側の作りによって、ウィジェットをサポートしていないと、管理メニュー内に「ウィジェット」というメニュー項目そのものが表示されなくなります。
ウィジェットを使う場合は、テーマ側の構造も把握しておくと良いです。
前回に引き続き、HTTP APIの検証をします。
その4としてwp_remote_retrieve_response_code と wp_remote_retrieve_response_messageを試してみます。
//HTTP APIのテスト $url = "http://hogehoge.fuga/"; $response = wp_remote_get($url, $args); $return_code = wp_remote_retrieve_response_code($response); var_dump($return_code); $return_message = wp_remote_retrieve_response_message($response); var_dump($return_message);
上記のコードを実行すると、以下のような結果が返ってきます(状況によります)
200
と
OK
また、wp_remote_get関数を使う場合の注意としては、$urlをesc_url()の関数を使う必要があります。
これはURL内にエスケープが必要な文字列があった場合に、安全に関数を動作させる為に必要になります。
具体的には、以下のように書くと良いです。
//HTTP APIのテスト $url = "http://hogehoge.fuga/"; $response = wp_remote_get(esc_url($url), $args);
前回に引き続き、HTTP APIの検証を行います。
3回目の関数はwp_remote_retrieve_headeresです。
前回同様、wp_remote_getした後に、この関数の戻り値をvar_dumpしてみます。
実際に下記のように書きました(ドメイン部分のみダミー値です)
//HTTP APIのテスト $url = "http://hogehoge.fuga/"; $response = wp_remote_get($url, $args); $return_value3 = wp_remote_retrieve_headers($response); var_dump($return_value3);
var_dumpした結果、次のようになります。
object(Requests_Utility_CaseInsensitiveDictionary)[6275] protected 'data' => array (size=9) 'date' => string 'Sun, 04 Mar 2018 09:23:35 GMT' (length=29) 'server' => string 'Apache' (length=6) 'expires' => string 'Thu, 19 Nov 1981 08:52:00 GMT' (length=29) 'cache-control' => string 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0' (length=62) 'pragma' => string 'no-cache' (length=8) 'set-cookie' => string 'PHPSESSID=547f926e3c6f4ec48bbc4966fb60a386; path=/' (length=50) 'vary' => string 'Accept-Encoding,User-Agent' (length=26) 'content-encoding' => string 'gzip' (length=4) 'content-type' => string 'text/html; charset=UTF-8' (length=24)
結果表示をブラウザで確認したので、上記の値が全てではないかもしれませんが、取得項目はおおよそ把握できます。
詳しい出力結果はログに出力する等の工夫が必要です。
wp_remote_retrieve_headerでは、取得する項目を第二引数で指定する代わりに、wp_remote_retrieve_headersではヘッダー情報の全体を一気に取得してくる。という点が大きく異なります。
それぞれで使用する場面によって適切な使い方をする必要がありそうです。
前回の投稿に引き続き、HTTP APIの検証を行います。
前回は「wp_remote_retrieve_header」の関数を試しましたが、今回は「wp_remote_retrieve_body」を書いてみます。
//HTTP APIのテスト $url = "http://hogehoge.fuga/"; $response = wp_remote_get($url, $args); $return_value = wp_remote_retrieve_body($response);
最後の「$return_value」をvar_dumpすると、相手先サーバから取得してきたサイトのHTMLソースが格納されています。
利用方法は開発場面により応用が必要ですが、手軽に外部サーバの情報を取得できる点は便利な反面、むやみにアクセスしすぎない等の配慮が必要になります。
前回の「HTTP API」について、実際のコードを書いて動作させて検証してみます。
まず、「wp_remote_get」について書いてみます。
プラグイン内の任意の箇所に以下のようなコードを書きました。(URLはダミーに置き換えています)
//HTTP APIのテスト $url = "http://hogefuga.fuga/"; $response = wp_remote_get($url, $args);
このコードを実行し、$responseの値をvar_dumpで内容を見ると、以下のような結果が返ってきます。
長いですが、全て記載します。
Array ( [headers] => Requests_Utility_CaseInsensitiveDictionary Object ( [data:protected] => Array ( [date] => Fri, 02 Mar 2018 15:04:02 GMT [server] => Apache [expires] => Thu, 19 Nov 1981 08:52:00 GMT [cache-control] => no-store, no-cache, must-revalidate, post-check=0, pre-check=0 [pragma] => no-cache [set-cookie] => PHPSESSID=1b6419888d849d81ce51d2cec9ddd499; path=/ [vary] => Accept-Encoding,User-Agent [content-encoding] => gzip [content-type] => text/html; charset=UTF-8 ) ) [body] => [response] => Array ( [code] => 200 [message] => OK ) [cookies] => Array ( [0] => WP_Http_Cookie Object ( [name] => PHPSESSID [value] => 1b6419888d849d81ce51d2cec9ddd499 [expires] => [path] => / [domain] => hogefuga.fuga ) ) [filename] => [http_response] => WP_HTTP_Requests_Response Object ( [response:protected] => Requests_Response Object ( [body] => [raw] => HTTP/1.1 200 OK Date: Fri, 02 Mar 2018 15:04:02 GMT Server: Apache Expires: Thu, 19 Nov 1981 08:52:00 GMT Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0 Pragma: no-cache Set-Cookie: PHPSESSID=1b6419888d849d81ce51d2cec9ddd499; path=/ Vary: Accept-Encoding,User-Agent Content-Encoding: gzip Connection: close Transfer-Encoding: chunked Content-Type: text/html; charset=UTF-8 [headers] => Requests_Response_Headers Object ( [data:protected] => Array ( [date] => Array ( [0] => Fri, 02 Mar 2018 15:04:02 GMT ) [server] => Array ( [0] => Apache ) [expires] => Array ( [0] => Thu, 19 Nov 1981 08:52:00 GMT ) [cache-control] => Array ( [0] => no-store, no-cache, must-revalidate, post-check=0, pre-check=0 ) [pragma] => Array ( [0] => no-cache ) [set-cookie] => Array ( [0] => PHPSESSID=1b6419888d849d81ce51d2cec9ddd499; path=/ ) [vary] => Array ( [0] => Accept-Encoding,User-Agent ) [content-encoding] => Array ( [0] => gzip ) [content-type] => Array ( [0] => text/html; charset=UTF-8 ) ) ) [status_code] => 200 [protocol_version] => 1.1 [success] => 1 [redirects] => 0 [url] => http://hogefuga.fuga/ [history] => Array ( ) [cookies] => Requests_Cookie_Jar Object ( [cookies:protected] => Array ( [PHPSESSID] => Requests_Cookie Object ( [name] => PHPSESSID [value] => 1b6419888d849d81ce51d2cec9ddd499 [attributes] => Requests_Utility_CaseInsensitiveDictionary Object ( [data:protected] => Array ( [path] => / [domain] => hogefuga.fuga ) ) [flags] => Array ( [creation] => 1520003042 [last-access] => 1520003042 [persistent] => [host-only] => 1 ) [reference_time] => 1520003042 ) ) ) ) [filename:protected] => [data] => [headers] => [status] => ) )
これはサーバから返された結果になり、全ての結果情報が格納されているようです。
この中にはレスポンスヘッダやHTMLのbody内の文字列(上記の結果からは削除してあります)、などのコンテンツデータも取得できました。
この結果に対し、wordpressでは結果内容を簡単に解析するヘルパー関数があるようです。
ヘルパー関数の例を記載します。
//本文を取得 wp_remote_retrieve_body() //ヘッダの値を取得 wp_remote_retrieve_header() //全てのヘッダ情報を取得 wp_remote_retrieve_headers() //ステータスコードを取得 wp_remote_retrieve_response_code() //ステータスメッセージを取得 wp_remote_retrieve_response_message()
上記のどの命令を使うかは、場面により異なりますが、
まず、検証の為に「wp_remote_retrieve_header()」を使ってみます。
先ほど冒頭で書いた「wp_remote_get」の結果に対し、使ってみます。
//HTTP APIのテスト $url = "http://hogefuga.fuga/"; $response = wp_remote_get($url, $args); //ここで使用する $header_value = wp_remote_retrieve_header($response, "date");
第二引数の「date」は、取得したいヘッダーの項目名を指定しています。別の項目名でもOKです。
この処理を実行した際、「$header_value」の変数には
Fri, 02 Mar 2018 15:20:49 GMT
等という値が取得されています。
本来であれば、ヘッダーの値を取得する前に、ステータスチェック等を行い、その上で値を取得する。等の対策が必要になってきます。
HTTP APIは、外部サーバに対してHTTP接続を行うAPIのようです。
実際に書いてみないと分からないことが多いので、まずは試しながら動きを確認してみます。
公式ドキュメントはこちらを参考にしています。
HTTP APIの関数は以下のものがあります。
wp_remote_get() wp_remote_post() wp_remote_haed() wp_remote_request()
まずはドキュメントを読み、実際に試していきます。
前回の投稿と似た機能を持つAPIを調査しました。
Transients APIというAPIで、ドキュメントはこちらのサイトになります。
このAPIは、データを一時的にデータベースへ保存するAPIで、
保存期間に有効期限を設定し、有効期限がすぎると削除されることが特徴です。
関数としては、次のものを使います。
それぞれ、データ保存時と、データ取得時です。
データ保存時 set_transient($transient, $value, $expiration); 第1引数は保存名(キー名のようなもの)、第2引数は値を表し、第3引数は有効期限の秒数を指定します。
データ取得時 get_transient($transient);
これらは、短時間のデータ保存が必要な場面で使用することが良さそうです。
実践で必要な場面があったら試す予定です。
wordpressが標準装備していて、プラグインのプログラム内から呼び出して使用できるAPIがあります。
数あるAPIの中から、代表的なAPIを実際にプログラムして、使ってみます。
まずは、Options API という値を保持する為のAPIを使ってみます。
これまで作ってきたプラグインの画面遷移時の動きを利用して、画面遷移の間で値が保持できているかを確認してみます。
まず、データ一覧画面で
update_option("test", "a");
という記述を行い、これを登録画面→確認画面で、取得して表示してみます。
次に、登録確認画面側に、以下のように記述します。
$test_value = get_option("test"); echo $test_value; //内容チェック用に強引に出力する
これで、新規登録後の確認画面で「a」という文字列が表示されることが確認できました(画面キャプチャは省略します)。
保持したデータを削除するには
delete_option("test");
という記述で値を消すことができます。
update_optionと同様のAPIにadd_optionというAPIがあります。
これは画面アクセス時に対し、update_optionはまとめてデータ取得するのに対し、add_optionはget_optionを行う度にデータベースへ問い合わせが発生します。(add_optionの第4引数を使用時)
厳密に言えば、add_optionを多用すると、パフォーマンスが下がる原因になるようです。
これまでの開発ではデータ登録時のエラー処理は、古典的(?)な書き方で入力チェック→エラー内容表示を行っていました。
wordpressには元々エラー処理用のクラスがあるので、それに置き換えてみようと思います。
まずは、入力画面から登録ボタンを押した際の、確認画面の処理を見直します。
//エラー処理を入れる為のオブジェクト生成 $error = new WP_Error(); //入力値チェック(複数のエラーチェックをここで行う) if (!strlen($_POST["sample_name"])) { $error->add("error", "名前を入力してください"); } if (!strlen($_POST["sample_text"])) { $error->add("error", "テキストを入力してください"); } //エラーコードがある場合は、登録画面を再表示する if ($error->get_error_code()) { //ここで改めて登録フォームを表示する }
まず、「$error = new WP_Error();」とすることで、エラー処理用のクラスを呼び、オブジェクトを生成しておきます。
次に、入力値チェックを行います。
入力値のチェック方法は色々あると思いますが、ここでは「strlen」を使って単純に文字列(の長さ)があるか無いかで判定しています。
(チェック用の関数の使い方は、別方法がありますが、ここでは割愛いたします)
最後に、「$error->get_error_code()」でエラーだった場合のコードを取得し、エラー表示を行うか、どうかの判定をします。
エラーだった場合は、改めて登録フォームを表示します。
次に、エラー時の登録フォーム側のプログラムを調整します。
//エラー処理 if ($error->get_error_codes()) { echo "<div class='error'>"; echo "<ul>"; foreach ($error->get_error_messages() as $value) { echo "<li>" . esc_html($value) . "</li>"; } echo "</ul>"; echo "</div>"; }
$errorオブジェクトを渡し、エラーコードの有無を判定します。
エラーだった場合、エラー内容をエラーの個数分表示しています。
具体的な画面は次のようになります。
入力フォームの初期画面
エラーだった場合の再表示画面
エラーメッセージの表示時のクラス名
<div class='error'>
はwordpressに標準搭載されているcssのクラス名なので、画面のエラー文言の表現が統一されて便利です。
また、
<div class='updated'>
というクラス名もあり、その場合は正常に更新されたケースで使用するとよいです。
これまでに作成したプラグインは、データ登録画面で、入力フォームを表示し、次の画面で内容チェック、その後に完了画面を表示する。という一連の動作になっています。
この時、入力フォームから確認画面へ遷移する際のセキュリティとして、CSRF攻撃がされる場面が想定されます。
このCSRF攻撃に対応する為に、対策を入れます。
具体的には、入力画面のform開始タグの直下に、以下の命令を書きます。
<?php wp_nonce_field("my-nonce-key", "my-form"); ?>
また、確認画面側では、次のように書いて、CSRFのチェックを行います。
//CSRF対策用のチェック if (isset($_POST["my-form"]) && $_POST["my-form"]) { if (check_admin_referer("my-nonce-key", "my-form")) { //問題がなければ、ここで処理を続行 } }
ポイントとなる箇所だけを書きましたが、フォーム内に「wp_nonce_field」を、確認画面のチェック処理で「check_admin_referer」を、それぞれ使うということで、覚えておいてよいかと思います。
前回の投稿ではメディアアップローダの画面を利用したファイルのアップロードを書きましたが、今回は単純なプログラムによるファイルのアップロードを書きます。
プラグインを作成する方向性としては、無意味な実装になるかもしれませんが、ここは実験がてらやってみます。
まず、作成中のプラグインのTOP画面にファイルアップロード用のボタンを追加します。
function disp() { echo <<< EOL <form action="" method="post"> <h2>データ一覧</h2> <input type='submit' name='submit[regist]' class='button-primary' value='新規登録' /> <input type='submit' name='submit[regist_file]' class='button-primary' value='新規登録(ファイルアップロード)' /> <div class="wrap"> <table class="wp-list-table widefat striped posts"> <tr> <th nowrap>ID</th> <th nowrap>名前</th> <th nowrap>テキスト</th> <th nowrap>チェック</th> <th nowrap>ラジオ</th> <th nowrap>セレクト</th> <th nowrap>テキストエリア</th> <th nowrap>登録日時</th> <th nowrap>詳細</th> <th nowrap>編集</th> </tr> EOL; 以下、省略します…
すると次のような画面になります。
続いて、ボタンを押下した際の動作を決める関数に、ファイルアップロード用画面と、アップロードボタンを押された際のメソッドを追加します。
function sample_plugin() { if (isset($_REQUEST["submit"]["detail"])) { //他の処理は記述を省略します… } else if (isset($_REQUEST["submit"]["regist_file"])) { //ファイルアップロード用画面 self::regist_file(); } else if (isset($_REQUEST["submit"]["regist_file_up"])) { //ファイルアップロード処理 self::regist_file_up(); } else { self::disp(); } }
次に、画面上の「新規登録(ファイルアップロード)」ボタンを押した時の処理を書きます。
function regist_file($error_message_flg = null) { echo <<< EOL <h2>ファイルアップロード</h2> <div class="wrap"> <div class="wrap"> <table class="wp-list-table widefat striped posts"> <tr> <td>ファイルアップロード</td> <td> <form method="post" action="" enctype="multipart/form-data"> CSVを選択してアップロードボタンを押してください<br /> <input type="file" name="upfilename" /> <input type="submit" value="アップロード"> <input type="hidden" name="submit[regist_file_up]" value="on"> </form> </td> </tr> </table> </div> <form action="" method="post"> <input type='submit' name='submit[file_upload]' class='button-primary' value='戻る' /> <input type="hidden" name="form_id" value="{$form_id}"> <input type="hidden" name="create_date" value="{$create_date}"> </form> </div> EOL; }
今はデザインやレイアウトについては、割愛して書いています。
画面はこのようになります。
画面上から、ファイルを選択してアップロードボタンを押した後の処理は次のように書きました。
function regist_file_up() { //CSVファイルがアップロードされた場合 if (is_uploaded_file($_FILES["upfilename"]["tmp_name"])) { // wordpressが用意している関数 $upload_dir = wp_upload_dir(); // ファイル名を取得してアップロード処理を行う $upload_file_name = $upload_dir['basedir'] . "/" . $_FILES["upfilename"]["name"]; if (move_uploaded_file($_FILES["upfilename"]["tmp_name"], $upload_file_name)) { chmod($upload_file_name, 0777); // アップロード&ファイル権限の変更が完了したら成功とする $message = "ファイルをアップロードいたしました"; } } else { $message = "ファイルのアップロードが失敗しました"; } //完了メッセージの出力 echo <<< EOL <h2>ファイルアップロード完了</h2> <form action="" method="post"> <div class="wrap"> {$message} </div> <input type='submit' name='submit[]' class='button-primary' value='戻る' /> </form> EOL; }
書き方は一般的なファイルアップロードによる処理と、変わらないですが、「wp_upload_dir();」というwordpressが用意している関数を使っている点がポイントになります。
この関数をコールし、「$upload_dir[‘basedir’]」という値をとることにより、wordpressの「uploads」ディレクトリまでのフルパスを参照できます。
そうすることで、uploadsディレクトリ直下にファイルをアップロードすることができています。
完了後は、次のような画面になります。
これで、ファイルをアップロードが完了します。
工夫や改善の余地はありますが、基本的な流れはできているので、あとはどのように応用するかになります。
管理画面側に、wordpressに搭載されているメディアアップローダを組み込んでみます。
そもそも管理画面にファイルをアップロードする機能はphp標準のファイルアップロードの処理を書くことでも実装が可能です。
ただ、その場合はアップロード先ディレクトリをどうするか、仕様を決めておかないといけない為、配慮すべき項目が多くなりがちです。
そこでメディアアップローダを使うことで、アップロードとファイル保存先についてはある程度wordpressまかせにすることができます。
以下、実装手順を記載します。
まず、__construct()等に、必要となるjavascript関連のファイルを読み込みます。
wp_enqueue_script('media-upload'); wp_enqueue_script('thickbox');
次に、スタイルシート関連のファイルも読み込みます。これは同じくコンストラクタ内で問題ありませんが、独自にスタイルシート読み込み用に関数を分けておいてもいいかもしれません。
wp_enqueue_style('thickbox');
続いて、登録画面に対して、ファイルアップロードの為のフォームを記載します。(ここは上記参考サイト様の書き方を引用させていただきました)
<input type="text" id="my_media_1" name="my_media_1" value="" /> <a class="media-upload" href="JavaScript:void(0);" rel="my_media_1">ファイル選択</a>
そして、フォーム内の「ファイル選択」リンクを押下した時に、
メディアアップロードのダイアログを立ち上げる為に、以下のjavascriptを記載します。
<script type="text/javascript"> jQuery("document").ready(function(){ jQuery(".media-upload").each(function(){ var rel = jQuery(this).attr("rel"); jQuery(this).click(function(){ window.send_to_editor = function(html) { imgurl = jQuery("img", html).attr("src"); jQuery("#"+rel).val(imgurl); tb_remove(); } formfield = jQuery("#"+rel).attr("name"); tb_show(null, "media-upload.php?post_id=0&type=image&TB_iframe=true"); return false; }); }); }); </script>
という記載をフォームが表示される画面内のHTMLタグのどこかに記載します。
以上の記述をすることで、フォーム画面からメディアアップロードの画面を出し、そこでファイルを選択して、URL等をフォームにセットすることができます。
以下、これまでに作成してきたサンプルプラグインの「新規登録」画面に対して、ファイルアップロードの項目を追加してみたので、そのキャプチャを貼ります。
赤枠部分が実際に操作をしてみて、ファイルのアップロード(例ではアップロード済みのファイルを選択しました)、ファイル名取得、フォーム項目への設置、という一連の動きになっています。
前回のページャを設置した記事をおさらいを含めて、見直してみます。
前回投稿した記事https://propansystem.net/blog/?p=1022では、登録データ数の無関係にページャ表示をしていました。
この場合、1画面内に10記事のデータ数を超えている場合は問題ありませんが、10記事以内のデータの場合にもページャが1ページ分のみ表示されてしまいます。
ページャをクリックしても何も起きず、ページャを表示していまうことで余計に操作しずらくなります。
そこで、データ一覧を出力する箇所のページャ表示部分に件数制限のロジックを入れて、10件未満の場合にはページャを表示しないようにします。
データ一覧を表示する関数部分を抜粋します。
function disp() { //データ一覧 echo <<< EOL <form action="" method="post"> <h2>データ一覧</h2> <input type='submit' name='submit[regist]' class='button-primary' value='新規登録' /> <div class="wrap"> <table class="wp-list-table widefat striped posts"> <tr> <th nowrap>ID</th> <th nowrap>名前</th> <th nowrap>テキスト</th> <th nowrap>チェック</th> <th nowrap>ラジオ</th> <th nowrap>セレクト</th> <th nowrap>テキストエリア</th> <th nowrap>登録日時</th> <th nowrap>詳細</th> <th nowrap>編集</th> </tr> EOL; //現在ページ取得 $pageid = filter_input(INPUT_GET, 'pageid'); //1ページあたりの件数 $limit = 10; //DBオブジェクトを用意 global $wpdb; //全件数取得 $tbl_name = $wpdb->prefix . 'sample_mst'; $sql = "SELECT count(*) AS CNT FROM {$tbl_name}"; $rows = $wpdb->get_results($sql); $recordcount = $rows[0]->CNT; $offset = $pageid * $limit; //offset と limitによる画面表示用のデータ取得 $sql = "SELECT * FROM {$tbl_name} ORDER BY id limit {$offset}, {$limit};"; //通常の取得方法(SQL実行結果を、オブジェクトとして取得) $rows = $wpdb->get_results($sql); foreach($rows as $row) { echo "<tr>"; echo "<td>" . $row->id . "</td>"; echo "<td>" . $row->sample_name . "</td>"; echo "<td>" . $row->sample_text . "</td>"; echo "<td>" . $row->sample_check . "</td>"; echo "<td>" . $row->sample_radio . "</td>"; echo "<td>" . $row->sample_select . "</td>"; echo "<td>" . $row->sample_textarea . "</td>"; echo "<td>" . $row->create_date . "</td>"; echo "<td>"; echo "<input type='submit' name='submit[detail][" . $row->id . "]'"; echo " class='button-primary' value='詳細' />"; echo "</td>"; echo "<td>"; echo "<input type='submit' name='submit[edit][" . $row->id . "]'"; echo " class='button-primary' value='編集' />"; echo "</td>"; echo "</tr>"; } echo "</table>"; echo "</div>"; echo "</form>"; //データ数が1画面あたりの記事数を超える場合のみ、ページャを表示する if ($recordcount > $limit) { $args = array( 'label' => __('Per Page'), 'default' => 10, 'option' => 'disp' ); $page_html = self::pagination($recordcount); //ページ部分の表示 echo "<div class='admin_pagination'>"; echo "<ul>"; foreach ($page_html as $key => $value) { echo "<li>" . $value . "</li>"; } echo "</ul>"; } }
関数の最後で「if ($recordcount > $limit) {」という分岐を行い、
10件に満たない場合の制御をしています。
こうすることにより、データ数が少ない場合は、以下のような表示になります。
画面表示の書き方はもっとシンプルで、わかりやすい書き方はあると思いますが、今は不恰好ですが動作しているので次に進めていきます。
wordpress流の書き方については、今後の研究課題にして追求していく予定です。
クイックリターン関数について、調べてみます。
下記のようなコードを例にします。
add_filter("test_filter", "sample_func"); function sample_func() { //何らかの処理 return true; }
上記のコードは「test_filter」としてフィルターフックを指定し、sample_func関数をコールバックしています。
sample_func関数内で何らかの処理を行う関数の場合、次のような戻り値の時に簡略化して書くことができます。
「true」「false」「array()」(空の配列を返す)「””」「null」「0」
上記の戻り値の場合、
function sample_func() { //何らかの処理 return true; }
の関数定義を書かずに、
add_filter("test_filter", "__return_true");
のように一行で書くことできます。
下記のそれぞれのケースでは、
「false」「array()」(空の配列を返す)「””」「null」「0」
次のように書けます。
//false add_filter("test_filter", "__return_false"); //array() add_filter("test_filter", "__return_empty_array"); //"" add_filter("test_filter", "__return_empty_string"); //null add_filter("test_filter", "__return_null"); //0 add_filter("test_filter", "__return_zero");
実際にフィルターを使う時に試してみるほうが良さそうです。
前の投稿ではプラグインを有効化した時に実行される関数についてでしたが、今回はプラグインを無効化した時に実行される関数について調べてみます。
無効化する時に実行される関数は以下になります。
register_deactivation_hook($file, $callback);
第一引数は、有効化した時の関数と同じく、実行されるプログラムのファイル名を指定します。
「__FILE__」等と指定しても問題なく動作します。
第二引数は無効化した時に実行されるコールバック関数を指定します。
例として、以下のように書きます。
register_deactivation_hook(__FILE__, 'test_de_action'); function test_de_action() { //ここで何らかの処理 }
関数側がクラスを使っている場合は、次のように書きます。
register_activation_hook(__FILE__, array('test_class', 'test_de_action'); class test_class { function test_de_action() { //ここで何らかの処理 } }
有効化と同じように書けるので、有効化の処理が問題なく動作すれば、そのまま無効化も同じように書くことができます。
プラグインの機能として、プラグインをインストールした後に、「有効化」を行うタイミングの時に1度だけ実行される関数が用意されています。
以前、プラグインを作成した際、過去のブログでも取り上げていましたが、もう少し深く調べてみます。
プラグインの有効化のタイミングで実行される関数は、以下のようになります。
register_activation_hook($file, $callback);
第一引数はプラグインのメインPHPファイルのパスを、
第二引数にはプラグインを有効化した時に実行されるコールバック関数を指定します。
例として、次のようになります。
register_activation_hook(__FILE__, 'test_action'); function test_action() { //ここで何らかの処理 }
関数側がクラスを使っている場合は、第二引数の書き方が次のように変わります。
register_activation_hook(__FILE__, array('test_class', 'test_action'); class test_class { function test_action() { //ここで何らかの処理 } }
実際にプラグイン作成時に書いた処理なので、動作検証については既に完了しています。
あとは処理の中で何をどうプログラムするのかで様々な操作をすることが可能になるので、そこは設計次第になります。
アクションフックにアクションを登録する方法は以下になります。
※wordpressの公式ドキュメントからの抜粋になりますが、若干説明を簡易にしています。
add_action(アクションを適用するフック名, コールバック関数, 優先順位, フックした関数が受け入れられる引数の数);
この登録したアクションの中身は以下のように記述します。
※これもwordpressの公式ドキュメントから参照したコードです。
function email_friends( $post_ID ) { $friends = 'bob@example.org, susie@example.org'; wp_mail( $friends, "sally's blog updated", 'I just put something on my blog: http://blog.example.com' ); return $post_ID; } add_action( 'publish_post', 'email_friends' );
上記のコードは「ブログを投稿しときに、メール通知を行う」という動作のようです。
add_actionの第一引数で「アクションを呼ぶきっかけとなる動作」を決定しています。
この第一引数には、様々な操作を設定できるようで、公式ドキュメントにまとめられています。
上記URLを見ると「一般的リクエスト中に実行されるアクション」や「管理画面リクエスト中に実行されるアクション」等といったアクションのきっかけがまとめれています。
この情報を使って第一引数を決定し、特定の動作を行う関数を用意する。
というような実装の流れになると思われます。(実際の実装は今後の課題予定とします)
do_action()が実行されている場所をアクションフックと呼びます。
アクションフックはフィルターフックと似ています。
どちらもフック(きっかけ)があった場合に処理が動きます。
アクションフックは、管理画面などで、投稿記事の公開、テーマ変更、などが行われた際に始動されます。
また、フィルターフックについてはwordpressのサイト側を表示する際に、特定の処理を通して表示を変更したい場合、等に利用されます。
アクションは特定のイベントが発生した際に実行される処理を言います。
例えば、wp_headアクションはよく利用されるアクションです。
テンプレート内でwp_headが呼び出されたら、cssやjsの読み込みを一括して行う。といった処理で利用される例があります。
apply_filters()にフィルターを追加するにはadd_filter()関数を使います。
これは実際に書いてみないと分からないので、簡単なサンプルプログラムを作ります。
前回の投稿で、簡単なサンプルを作りました。
それをおさらいしてみます。
フィルターフックを設置する function sample_func1() { return apply_filters("sample_filter", "hoge"); } //「sample_filter」フックに対して、コールバック関数(sample_call)を指定する add_filter("sample_filter", "sample_call"); //コールバック関数 function sample_call($value) { return "fuga"; }
applry_filtersはフィルターフックを設置するための関数です。
第一引数がフック名、第二引数がコールバック関数への引数になります。
フィルターの使い方について、学びます。
フィルターフックについて
wordpressはHTMLの出力や、各種設定、環境変数、フォーム入力値などは、apply_filters()という関数を経由します。
例えば、ある関数を定義して、それをフィルターフックを設置する場合は、次のように書きます。
function sample_func1() { return "test"; }
この関数をフィルターフックを設置します。
function sample_func1() { return apply_filters("sample_filter", "hoge"); }
上記のフィルターフックを、プラグインやテーマのfunctions.phpで次のように適用します。
add_filter("sample_filter", "sample_call"); function sample_call($value) { return "fuga"; }
実際にこのフィルターフックを利用したプログラムを作成してみます。
これまでの管理画面の作りは、ほぼphpのみで実装してきました。
次にこの管理画面にjavascirpt(jquery)を動作させるように設定してみます。
まず、プラグインフォルダの中に、jsファイルを設置する為のディレクトリを作成します。
/ワードプレス設置ディレクトリ/wp-content/plugins/SamplePlugin1
上記のディレクトリに対し、jsディレクトリを作成します。
/ワードプレス設置ディレクトリ/wp-content/plugins/SamplePlugin1/js/
次に、そのディレクトリ内に、javascript用のファイルを設置します。
/ワードプレス設置ディレクトリ/wp-content/plugins/SamplePlugin1/js/sample1_script.js
jsの中は、jqueryが使用できるので、以下のようなサンプルコードを書きます。
(function($){ alert("hello jquery !"); })(jQuery);
次に、プラグイン本体のphpのコンストラクタの中に、以下の読み込み命令文を書きます。
wp_register_script('sample1js', plugins_url( './js/sample1_script.js', __FILE__ )); wp_enqueue_script('sample1js');
こうすることで、プラグインの画面にアクセスした時にjsがロードされます。
例では、アラートを表示するだけのシンプルなjsですが、画面にアクセスすると以下のようになります。
アラート表示だけのプログラムなので、あまり意味がないですが、jsが動作することを確認できました。