「第6回WordPressお茶会 WordBench香川」を開催しました

2012/10/06(土)[13:00~18:00]にデザインラボラトリー蒼様をお借りして「第6回WordPressお茶会 WordBench香川」を開催しました。

今回は新規2名を含む計8名の方にご参加での開催となりました。
内容としてはいつも通りノーテーマでのだらだら喋るだけ、土井によるWordPressテーマ構築ハンズオン、ライトニングトークの3本。

ハンズオンの内容としては『WordPress 3.x (速習デザイン)』のCafe Miraggioテーマを参考にトップページに通常の投稿ループに加えカスタム投稿タイプのループを出力、及びカスタム投稿タイプではカスタムフィールドを用いて追加の要素を表示させよう、という内容を考えていました。
Cafe Miraggioテーマの使用をご快諾くださった@jim_0912さんありがとうございました。


photo by: style-design

参加者さんにとって初めてのテーマだったので全体的なテーマの構造について、次いでheader.phpの内容を一通り説明して、という流れになったのですがCafe Miraggioテーマではheader.php内に独自関数を使用していたのでfunctions.phpへ飛んでその説明、更にはその中のWordPress関数の説明のためにCodexに飛んでその説明と、脱線しまくりになってしまい肝心のループまで辿りつけなかったので反省です。
ループメインのハンズオンの場合header.php等は「こういうものですよ」という前提で進めないと駄目ですね。
来月11/03に開催されるWordCamp Osaka 2012に各WordBenchの方もたくさんいらっしゃると思うのでハンズオン含めWordBench運営について意見を頂戴したいですね。

トーク班は今回は技術寄りの方が多かったようでAndroidやらRuby、HTML5の話題で盛り上がっていたようです。
その場でも話題にしましたが、ATNDで参加登録された方が技術者の方の場合、私だと対応できないので「相手できるよ!」って方がいらっしゃったら是非参加してい頂けるとありがたいです。

LTは@_nobinobiruさんによる「jQuery+HTMLで作ったフロントエンドにBackboneを導入」、@styledesignさんによる「逆に考えるんだ!セミナーイベント規模を小さく小さくして個人レッスンに近いかたちにするといいよ」の2本立て。

@_nobinobiruさんのLTはBackbone.jsの説明から入って実演、ソース解説という流れでしたが私が理解できたのはほんの一部ですのでこちらで説明しきれませんので興味のある方は@_nobinobiruへリプライを。
感想としては「知られてないだけでこんなすごい人がまだまだいるんだ、しかも若い!」の一言に尽きます。

@styledesignさんのLTは「逆に考えるんだ!セミナーイベント規模を小さく小さくして個人レッスンに近いかたちにするといいよ | スタイルデザインWeblog」の内容をざっくりと。
イベント・勉強会のスタイルについてですがこちらはとても共感。私がこうしていきたいって考えているWordBench香川コミュニティのあり方がまさに説明されているなと。

というわけでハンズオンはいつも通りグダグダになってしまい反省点も多々ありますが、全体的には楽しく開催できたし参加者のかたにも満足して頂けたのではないかなと。

次回「第7回WordPressお茶会 WordBench香川」は12月中旬くらいに開催予定(未定)です。
が、その前にWordPressのお祭りWordCampが大阪で開催されますので、まずはそちらを楽しみたいですね!!

WordCamp Osaka 2012は2012/11/03(土)天満研修センターにて開催。

WordCamp Osaka 2012

AjaxでWordPressのコンテンツを遷移せず表示させる

WordPressのコンテンツをtwitterやgoogle画像検索のようにあらかじめ一定数のコンテンツを表示させておき「もっと見る」リンクをクリックで画面遷移無くコンテンツを出力させたいと思います。

INDEX

  1. 仕様・デモ
  2. WordPressでAjaxを使う

    1. /wp-admin/admin-ajax.phpを使う
    2. /wp-admin/admin-ajax.phpを使ったデモ
  3. 表示用の固定ページ作成
  4. functions.php周り

    1. JavaScript登録
    2. 全投稿数を取得しクッキーに保存させる
    3. 「もっと見る」クリック時のAjax処理登録
  5. 課題等
  6. 参考サイト

仕様・デモ

いくつか課題はありますが、とりあえずの仕様としては以下の通り。

  • 投稿のタイトル及びリンクをあらかじめ一定数表示(今回は10件)
  • 「もっと見る」をクリックする度にAjaxでコンテンツを取得し10件ずつ追加
  • 固定ページ「AjaxでWordPressのコンテンツを遷移無く表示サンプル(page-wp-content-ajax-viewer.php)」を作成しそこに出力
  • すべてのコンテンツを表示し終えたら「もっと見る」リンクは無効化にする
  • テーマはTwenty Elevenをベースに

実際のデモは以下よりご覧ください。
AjaxでWordPressのコンテンツを遷移無く表示デモ

WordPressでAjaxを扱う

実際の処理部分の説明の前にWordPressでAjaxを扱う方法を。

/wp-admin/admin-ajax.phpを使う

AJAX in Plugins – WordPress Codex 日本語版が本家マニュアルだけど複雑で理解できなかったのでgoogle先生に頼りました。
分かりやすかったのは次のサイト。
Wp Ajax – WordPress Hook to Handle Ajax Request — W4dev.com
仕組みとしては次のようなイメージ(自己解釈)。

  • JavaScriptで/wp-admin/admin-ajax.phpへリスエストを送信することで任意のPHP関数を呼び出し、そこから送り返された情報をもとにJavaScriptで結果を出力する。
  • 任意のPHP関数はアクションフック「wp_ajax_アクション名」「wp_ajax_nopriv_アクション名」で呼び出すことができ、これらアクションフックを実行するには/wp-admin/admin-ajax.phpリクエスト時のactionパラメータに「アクション名」を渡しおく。
    「wp_ajax_アクション名」「wp_ajax_nopriv_アクション名」はログインユーザか否かで、公開部分でAjax処理を行いたい場合はともにフックする必要がある。

    add_action( 'wp_ajax_my_func', 'my_func' );
    add_action( 'wp_ajax_nopriv_my_func', 'my_func' );
    
    function my_func() {
    	// JavaScriptに値を返す処理
    }
  • my_funcにはJavaScript側に渡す情報を出力する処理を。

/wp-admin/admin-ajax.phpを使ったデモ

/wp-admin/admin-ajax.phpを使ってAjax処理を行うデモ

ソースは以下の通り。

page-wp-ajax-demo.php

// ヘッダとか省略
<div class="entry-content">
	<a href="#" id="show-json">JSON出力</a>
	<div id="json-data"></div>
</div><!-- .entry-content -->
// フッタとか省略

ヘッダ、フッタ領域は省略しています。
jQueryのclick()イベント用のアンカーとJSONデータ出力用のdivを用意。

functions.php

// JavaScript登録
add_action( 'wp_enqueue_scripts', 'sh_add_scripts' );

function sh_add_scripts() {
	if ( is_page( 'wp-ajax-demo' ) ) {
		wp_enqueue_script( 'jquery' );
	}
}

// admin-ajax.phpへリクエストを送信し返ってきた情報をもとにページ情報を出力
add_action( 'wp_head', 'sh_show_json' );

function sh_show_json() {
	if ( is_page( 'wp-ajax-demo' ) ) {
		?>
		<script>
			//<![CDATA[
			ajaxurl = '<?php echo admin_url( 'admin-ajax.php' ); ?>';
			jQuery(function($){
				$('#show-json').one('click', function(){
					jQuery.ajax({
						type: 'POST',
						url: ajaxurl,
						data: {
							"action": "sh_get_json"
						},
						success: function(data){
							var json_str = JSON.stringify(data);
							$('#json-data').append(json_str);
						},
						error: function(){
							alert('error');
						}
					});
					$(this).css({
						'pointerEvents': 'none',
						'color': '#ccc'
					});
					return false;
				});
				return false;
			});
			//]]>
		</script>
		<?php
	} // endif
}

// json出力
add_action( 'wp_ajax_sh_get_json', 'sh_get_json' );
add_action( 'wp_ajax_nopriv_sh_get_json', 'sh_get_json' );

function sh_get_json() {
	$charset = get_bloginfo( 'charset' );
	$array = array( 'foo' => 'bar', 'hoge' => 'fuga' );
	$json = json_encode( $array );
	nocache_headers();
	header( "Content-Type: application/json; charset=$charset" );
	echo $json;
	die();
}

jQuery.ajax()を使用するので#01~08でjQueryの登録。
デモページスラッグ名がwp-ajax-demoなのでis_page( ‘wp-ajax-demo’ )でデモページのみ読み込まれるように。

#10~47でデモページにJavaScriptを出力。処理内容は以下の通り。

  • jQuery.ajax()のリクエスト先URLを設定(#18)
  • 「JSON出力」アンカー(#show-json)クリックイベント(#20)
  • jQuery.ajax()実行。(#21~)
    urlは”/wp-admin/wp-ajax.php”、dataキー(サーバに送信するデータ)のactionパラメータとして”sh_get_json”を渡す。後述のsh_get_json()が実行されJSONオブジェクトを返す。
    通信成功時には渡されたJSONオブジェクトをJSON文字列に変換し出力。(#27~30)

#49~61でJSON出力処理。

  • /wp-admin/admin-ajax.php?action=sh_get_jsonへのリクエストでアクションフック”wp_ajax_sh_get_json”、”wp_ajax_nopriv_sh_get_json”がフックされ、sh_get_json()が実行される。(#51,52)
  • sh_get_json()では適当な配列$arrayを作成し、json_encode()で配列をJSON形式にして出力。
  • JSONを出力したままだとエラーが発生するのでdie()でプログラムを終了させる。(#60)
  • 出力されたJSONが先のsh_show_json()のjQuery.ajax()のsuccess時の引数”data”として渡され適宜処理される。

というわけで/wp-admin/admin-ajax.phpについて一応の理解はできたところで本題に戻って実際の処理を行なっていきます。

表示用の固定ページ作成

まず固定ページ「AjaxでWordPressのコンテンツを遷移無く表示サンプル(page-wp-content-ajax-viewer.php)」を作成して投稿の最新10件を表示します。

page-wp-content-ajax-viewer.php

<?php
/**
 * AjaxでWordPressのコンテンツを遷移無く表示デモ
 *
 * @package WordPress
 * @subpackage Twenty_Eleven
 * @since Twenty Eleven 1.0
 */
get_header();
?>
<div id="primary">
	<div id="content" role="main">
		<article id="wp-content-ajax-viewer" <?php post_class(); ?>>
			<header class="entry-header">
				<h1 class="entry-title"><?php the_title(); ?></h1>
			</header><!-- .entry-header -->
			<div class="entry-content">
				<?php
				$args = array(
					'posts_per_page' => 5
				);
				$my_posts = get_posts( $args );
				if ( $my_posts ):
					$output = '';
					$output .= '<ul id="content-list">';
					foreach ( $my_posts as $post ):
						$output .= '<li><a href="' . get_permalink() . '">' . get_the_title() . '</a></li>';
					endforeach;
					$output .= '</ul>';
					echo $output;
				endif;
				?>
				<a id="show-more" href="#">もっと見る</a>
			</div><!-- .entry-content -->
		</article>
	</div><!-- #content -->
</div><!-- #primary -->

<?php get_footer(); ?>
	

get_posts()で最新10件を取得し、リスト形式でタイトル・パーマリンクを出力しています。

functions.php周り

JavaScript登録

jQuery.ajax()を使用するのでjQueryを、全投稿数保持の為にjquery.cookieプラグインを利用するので登録しておきます。

functions.php

// JavaScript登録
add_action( 'wp_enqueue_scripts', 'sh_add_scripts' );

function sh_add_scripts() {
	if ( is_page( 'wp-content-ajax-viewer' ) ) {
		wp_enqueue_script( 'jquery' );
		wp_enqueue_script( 'jquery-cookie', get_template_directory_uri() . '/js/jquery.cookie.js', array( 'jquery' ), true );
	}
}

全投稿数を取得しクッキーに保存させる

投稿を全て表示し終えたら「もっと見る」アンカーを無効化する、を実現するために全投稿の件数が必要になります。
ページ読み込み時に全投稿数をカウントしてクッキーに保存しておきます。

2012/10/17修正
wp_headにフックさせるとhtmlを出力した後setcookie()が実行され”Warning: Cannot modify header information – headers already sent by”が発生するのでinitにフックするよう修正しています。
PHP: setcookie – Manual

2012/10/17更に修正
initにフックさせるとsh_get_query_count()関数内のis_page()が効かないのでwpフックに修正。

functions.php

// 全投稿数をカウントしクッキーに保存
add_action( 'wp', 'sh_get_query_count' );

function sh_get_query_count() {
	if ( is_page( 'wp-content-ajax-viewer' ) ) {
		$args = array( 'posts_per_page' => -1 );
		$my_posts = get_posts( $args );
		$query_count = count( $my_posts );
		setcookie( 'count', $query_count, time() + 3600 );
	}
}

「もっと見る」クリック時のajax処理登録

functions.php

// admin-ajax.phpへリクエストを送信し返ってきた情報をもとにページ情報を出力
add_action( 'wp_head', 'sh_add_ajax_script' );

function sh_add_ajax_script() {
	if ( is_page( 'wp-content-ajax-viewer' ) ) {
		?>
		<script>
			//<![CDATA[
			ajaxurl = '<?php echo admin_url( 'admin-ajax.php' ); ?>';
			jQuery(function($){
				$('#show-more').click(function(){
					var paged = $('#content-list li').length;
					jQuery.ajax({
						type: 'POST',
						url: ajaxurl,
						data: {
							"action": "sh_show_more",
							"paged": paged
						},
						success: function(data){
							var html = generateHtml(data);
							var target = $('#content-list');
							$(target).append(html);
							if ( $('#content-list li').length == $.cookie('count')) {
								$('#show-more').css({
									'pointerEvents': 'none',
									'color': '#ccc'
								});
							}
						},
						error: function(){
							alert('error');
						}
					});
					return false;
				});
				function generateHtml(data){
					var dataLength = data.length;
					var li = [];
					for ( i = 0; i < dataLength; i++ ){
						li.push('<li><a href="' + data[i].url + '">' + data[i].title + '</a></li>');
					}
					li = li.join("");
					return li;
				}
			})
			//]]>
		</script>
		<?php
	} // endif
}

// json出力
add_action( 'wp_ajax_sh_show_more', 'sh_show_more' );
add_action( 'wp_ajax_nopriv_sh_show_more', 'sh_show_more' );

function sh_show_more() {
	global $post;
	$charset = get_bloginfo( 'charset' );
	$paged = $_POST['paged'];
	$json = array( );
	$args = array(
		'posts_per_page' => 10,
		'offset' => $paged
	);

	$my_posts = get_posts( $args );
	foreach ( $my_posts as $key => $post ) :
		$json[$key]['title'] = get_the_title();
		$json[$key]['url'] = get_permalink();
	endforeach;
	
	$json = json_encode( $json );
	nocache_headers();
	header( "Content-Type: application/json; charset=$charset" );
	echo $json;
	die();
}

基本は/wp-admin/admin-ajax.phpを使ったデモと同じなのでそちらを参照。

大まかな流れとしては以下の通り。

  1. wp_headアクションフックでsh_add_ajax_script()を実行し(#02)、「AjaxでWordPressのコンテンツを遷移無く表示デモ」ページのみJavaScriptを出力(#05)
  2. 「もっと見る」アンカー(#show-more)クリックイベント(#11)
  3. 現在表示されている投稿件数を取得。(#12)
  4. jQuery.ajax()実行。(#13~)
    今回はdataキー(サーバに送信するデータ)にactionパラメータ他にpagedパラメータを登録。pagedパラメータは#12で取得した現在表示されている投稿件数の値。
  5. /wp-admin/admin-ajax.php?action=sh_show_moreへのリクエストでアクションフック”wp_ajax_sh_show_more”、”wp_ajax_nopriv_sh_show_more”がフックされ、sh_show_more()が実行される。(#54,55)
  6. PHP関数側で$pagedにjQuery.ajax()より送信されたpagedの値を代入。(#60)
  7. get_posts()用のパラメータを設定。posts_per_page(表示件数)は10件。offset(取得開始位置)$pagedの値を。
    他パラメータは今回はデフォルトで。画像で同様の処理を行いたいなら’post_type’ => ‘attachment’とかを指定。
  8. get_posts()で投稿を取得してきて(#67)、foreachで配列$jsonにタイトルとパーマリンクを代入。(#68~71)
  9. 先のデモ同様JSONを出力してdie()でプログラムを終了させる。(#73~77)
  10. jQuery.ajax()にJSONオブジェクトが返されsuccess時の処理実行。(#20~)
    出力するhtmlを生成し(#21及び#37~45)、指定した要素へ出力。(#22,23)
  11. もし投稿件数がcookieに保存されている全投稿数と同じになれば「もっと見る」アンカー無効化処理。(#24~29)

ちなみにjQuery.ajax()でsuccess時に返ってくるJSONオブジェクトは以下の様な形式。(JSON文字列化及び整形済み)

[
    {
        "title": "Mac OS X Lion環境NetBeans IDEにプログラミング用フォントRicty導入", 
        "url": "http://show-web.jp/2012/01/31/mac-os-x-lion%E7%92%B0%E5%A2%83netbeans-ide%E3%81%AB%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0%E7%94%A8%E3%83%95%E3%82%A9%E3%83%B3%E3%83%88ricty%E5%B0%8E%E5%85%A5/"
    }, 
    {
        "title": "WordCrab Fukui 2012に参加してきました", 
        "url": "http://show-web.jp/2012/01/31/wordcrab-fukui-2012%E3%81%AB%E5%8F%82%E5%8A%A0%E3%81%97%E3%81%A6%E3%81%8D%E3%81%BE%E3%81%97%E3%81%9F/"
    }, 
    {
        "title": "Windows7でSCSS+Compass(導入編)", 
        "url": "http://show-web.jp/2012/01/17/windows7%E3%81%A7scss-compass%EF%BC%88%E5%B0%8E%E5%85%A5%E7%B7%A8%EF%BC%89/"
    }, 
    {
        "title": "Retinaディスプレイ時に読み込む画像を切り替えるjQueryその2", 
        "url": "http://show-web.jp/2012/01/12/retina%E3%83%87%E3%82%A3%E3%82%B9%E3%83%97%E3%83%AC%E3%82%A4%E6%99%82%E3%81%AB%E8%AA%AD%E3%81%BF%E8%BE%BC%E3%82%80%E7%94%BB%E5%83%8F%E3%82%92%E5%88%87%E3%82%8A%E6%9B%BF%E3%81%88%E3%82%8Bjquery-2/"
    }, 
    {
        "title": "Retinaディスプレイ時に読み込む画像を切り替えるjQuery", 
        "url": "http://show-web.jp/2011/12/31/retina%E3%83%87%E3%82%A3%E3%82%B9%E3%83%97%E3%83%AC%E3%82%A4%E6%99%82%E3%81%AB%E8%AA%AD%E3%81%BF%E8%BE%BC%E3%82%80%E7%94%BB%E5%83%8F%E3%82%92%E5%88%87%E3%82%8A%E6%9B%BF%E3%81%88%E3%82%8Bjquery/"
    }, 
    {
        "title": "第1回WordPressお茶会@WordBench香川を開催しました", 
        "url": "http://show-web.jp/2011/11/21/%E7%AC%AC1%E5%9B%9Ewordpress%E3%81%8A%E8%8C%B6%E4%BC%9A%EF%BC%A0wordbench%E9%A6%99%E5%B7%9D%E3%82%92%E9%96%8B%E5%82%AC%E3%81%97%E3%81%BE%E3%81%97%E3%81%9F/"
    }, 
    {
        "title": "第1回WordPressお茶会 &#8211; WordBench香川 &#8211; WordPressのインストール", 
        "url": "http://show-web.jp/2011/11/16/%E7%AC%AC1%E5%9B%9Ewordpress%E3%81%8A%E8%8C%B6%E4%BC%9A-wordbench%E9%A6%99%E5%B7%9D-wordpress%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB/"
    }, 
    {
        "title": "第1回WordPressお茶会 &#8211; WordBench香川 &#8211; PHP環境設定", 
        "url": "http://show-web.jp/2011/11/10/%E7%AC%AC1%E5%9B%9Ewordpress%E3%81%8A%E8%8C%B6%E4%BC%9A-wordbench%E9%A6%99%E5%B7%9D-php%E7%92%B0%E5%A2%83%E8%A8%AD%E5%AE%9A/"
    }, 
    {
        "title": "第1回WordPressお茶会 &#8211; WordBench香川 &#8211; MAMPの環境設定(外部からのアクセス制限設定)", 
        "url": "http://show-web.jp/2011/11/05/%E7%AC%AC1%E5%9B%9Ewordpress%E3%81%8A%E8%8C%B6%E4%BC%9A-wordbench%E9%A6%99%E5%B7%9D-mamp%E3%81%AE%E7%92%B0%E5%A2%83%E8%A8%AD%E5%AE%9A%EF%BC%88%E5%A4%96%E9%83%A8%E3%81%8B%E3%82%89%E3%81%AE/"
    }, 
    {
        "title": "第1回WordPressお茶会 &#8211; WordBench香川 &#8211; XAMPPの環境設定(ドキュメントルートの確認)", 
        "url": "http://show-web.jp/2011/11/05/%E7%AC%AC1%E5%9B%9Ewordpress%E3%81%8A%E8%8C%B6%E4%BC%9A-wordbench%E9%A6%99%E5%B7%9D-xampp%E3%81%AE%E7%92%B0%E5%A2%83%E8%A8%AD%E5%AE%9A%EF%BC%88%E3%83%89%E3%82%AD%E3%83%A5%E3%83%A1%E3%83%B3/"
    }
]

課題等

以上でタイトル通りの処理は実現できましたがいくつか課題が。

  • ソースが全体的に冗長的。(get_posts()の条件を変更したい場合、例えば出力件数等を変更したい場合、page-wp-content-ajax-viewer.phpとfunctions.phpともに変更する必要がある)
    Class化とかである程度対応出来そう?
  • 拡張性が無い。(出力内容を変更したい場合ソースを結構変更する必要があるので面倒)
    これもClass化で対応出来そう?
  • セキュリティ的にどうか。(まだそこまで検証出来てません)
  • プラグイン化して、WP-PageNaviプラグインみたいにindex.phpやarchive.phpの所定の箇所に関数を書くと、「もっと読む」クリックで投稿が出力されたら面白いかなと。(既にあるか?)

参考サイト

以下サイトを参考にさせて頂きました。

ハッスルサーバーでBackWPupプラグインが動作しない対処法

ファーストサーバーの件もあってか自社でバックアップをきちんと取りましょう、という風潮(正しくはリスク管理をきちんとしましょうですね、バックアップ然りバージョンアップ然り)というわけでうちもこれまでのバックアップ方法から最近注目されているBackWPupプラグインを導入してみました。
BackWPupプラグイン導入については以下参考サイト参照。

で、本題ですが以下記事が目に止まったので原因、解決方法を調べてみました。

記事を読むとローカルXAMPP環境では問題なく動作するけどハッスルサーバーでは動作しないとのこと。プラグインのダウングレード(2.1.11→2.1.9)や他プラグインとの競合、パーミッション変更等試されたようですがやはり駄目な様子。

というわけで端折りますがハッスルサーバーを試用していろいろと調べてみましたがどれをやっても駄目。(PHPがCGIモード?、セーフモード?、PHPのメモリーが足りてない?、.htaccess設定?)

更に調べてようやく辿り着いたのがBackWPupオフィシャルフォーラムの以下トピック。

filter_input関数が未定義のため、/wp-content/plugins/backwpup/job/job_run.phpでエラーが発生するとの記述が。実際に当ハッスルサーバー環境でjob_run.phpにアクセスしてみるとこちらの環境でも同様のエラーが発生。というわけでこれが原因のようです…

Fatal error: Call to undefined function filter_input() in /home/showweb/public/www/wp/wp-content/plugins/backwpup/job/job_run.php on line 7

フォーラムにある解決方法としてはとりあえずBackWPup2.1.8にダウングレードしましょうとの事だったのでダウングレードで正常に動作するようになりました。

※BackWPupプラグインはいくつかバックアップ方法(サーバ内に保存、メール送信、オンラインストレージに保存)等ありますがgmailへのバックアップテストの際にMAILER-DAEMONが帰ってきたのは以下ツイートが関係あるのかな。