WordPressで会員制ページを作る際に有名なのがプラグイン「Simple WP Membership」ですが、一覧表示時に、閲覧権限がない記事も下図のように表示されてしまうのが、ちょっと気に入らずカスタマイズしてみることにしました。
環境
WordPress 4.6.1
Simple WP Membership 3.3.2
調査
まずはプラグインの中を調べましたが、一覧表示した後にその記事が表示可能かどうかを判断する仕組みのようです。それで一覧表示されてしまうのですね、納得です。
ではアクションフックのpre_get_postsを追加するように対応します。
Simple WP Membershipのデータベースの中を見てみると、swpm_membership_tblというテーブルのpost_listと言うフィールドに、その会員の種類ごとに閲覧可能な記事IDが格納されているようです。
調査の結果から、ログインしている会員の種類を取得して、この閲覧可能な記事IDを使ってpre_get_postsで表示する記事を絞り込む方針でカスタマイズして行くことにします。
テーマのfunction.phpに追加
プラグイン側に書けるんじゃないの?とも思いつつ、慣れているやり方として、テーマのfunction.phpにアクションフックを記載することにします。
[php]
function use_membership($query) {
}
add_action(‘pre_get_posts’, ‘use_membership’);
[/php]
おなじみですね。後はこのuse_membershipの中に絞り込み処理を追加していきます。
会員の種類の取得
Access Controlあたりかな~と目星をつけてlevelで検索してみたところ、
class.swpm-access-control.php
に、
[php]
$permission = SwpmPermission::get_instance($auth->get(‘membership_level’));
[/php]
こんなコードを発見。
[php]
$nt_auth = SwpmAuth::get_instance();
$nt_level = $nt_auth->get(‘membership_level’);
[/php]
SwpmAuthを使えるようにして、会員の種類を取得しました。
記事の絞り込み
今回の方針だと、
[php]
//post__inに設定
$query->set(‘post__in’, array(1,2,3….));
[/php]
最終的にはこんな感じのコードになればいいはずです。
post_listをキーワードにプラグインのソースの中を見て回ると、
class.swpm-protection-base.php
の
init
に、
[php]
$this->posts = isset($result->post_list) ? (array) unserialize($result->post_list) : array();
[/php]
という行があって、その上の方に
[php]
$query = $wpdb->prepare(“SELECT * FROM {$wpdb->prefix}swpm_membership_tbl WHERE ” . (is_numeric($level_id) ? ‘id = %d’ : ‘md5(id) = %s’ ), $level_id);
[/php]
levelを使って抽出していることが見て取れました。
しかも、さっきのSwpmAuthに
[php]
$this->protected = SwpmProtection::get_instance();
[/php]
という記述があるので、そのまま使えるんじゃないかと思ったのですが、initはpublicじゃないので使えません。
ですので、initの中のコードをそのまま流用して、
[php]
global $wpdb;
//閲覧可能なpost_idを取得
$nt_query = $wpdb->prepare(“SELECT * FROM {$wpdb->prefix}swpm_membership_tbl WHERE ” . (is_numeric($nt_level) ? ‘id = %d’ : ‘md5(id) = %s’ ), $nt_level);
$nt_result = $wpdb->get_row($nt_query);
$nt_posts = isset($nt_result->post_list) ? (array) unserialize($nt_result->post_list) : array();
[/php]
として取得しました。
このときの結果は
[0][id1]
[1][id1]
[2][id1]
・
・
のように二次元配列になっているので、いったん文字列としてのカンマ区切りにしてしまいました。
[php]
//post_idをカンマ区切り文字列に整形
$nt_post_id = ”;
for ($p = 0; $p < count($nt_posts); $p ++) {
if ($nt_post_id != '') $nt_post_id .= ',';
$nt_post_id .= $nt_posts[$p];
}
[/php]
でもって、先ほどのquery->setにexplodeで配列に変換して引数として渡します。
[php]
//post__inに設定
$query->set(‘post__in’, explode(‘,’, $nt_post_id));
[/php]
絞り込みする画面の制御
このままですと管理画面(wp-admin)での一覧表示でちゃんと全件表示されなくなってしまうので、
[php]
if (!is_admin() && ($query->is_main_query() || $query->is_search())) {
[/php]
というif文を追加します。管理画面以外で、一覧表示と検索結果の場合に絞り込むようにします。
プラグインが有効かどうかをチェック
すでにSimple WP Membership前提で構築してはいるのですが、念のため、プラグインが有効かどうかをチェックします。
最初のアクションフック追加部分をちょっと工夫します。
[php]
//プラグインの有効確認用
include_once(ABSPATH . ‘wp-admin/includes/plugin.php’);
//プラグイン「Simple WP Membership」が有効ならアクション追加
if (is_plugin_active(‘simple-membership/simple-wp-membership.php’)) {
add_action(‘pre_get_posts’, ‘use_membership’);
}
[/php]
これで完成です!
テーマを変更したとしても、function.phpに追加すれば動作するので、十分に管理できるようになりました。
最後に全体を記載しておきます。
ちなみにところどころの変数のプレフィックスのnt_は他の変数と重複しないようにしているだけで、意味はありません。
[php]
//プラグインの有効確認用
include_once(ABSPATH . ‘wp-admin/includes/plugin.php’);
function use_membership($query) {
//メインクエリーか検索クエリーの場合
if (!is_admin() && ($query->is_main_query() || $query->is_search())) {
global $wpdb;
//ログインしているユーザーの会員の種類を取得
$nt_auth = SwpmAuth::get_instance();
$nt_level = $nt_auth->get(‘membership_level’);
//閲覧可能なpost_idを取得
$nt_query = $wpdb->prepare(“SELECT * FROM {$wpdb->prefix}swpm_membership_tbl WHERE ” . (is_numeric($nt_level) ? ‘id = %d’ : ‘md5(id) = %s’ ), $nt_level);
$nt_result = $wpdb->get_row($nt_query);
$nt_posts = isset($nt_result->post_list) ? (array) unserialize($nt_result->post_list) : array();
//post_idをカンマ区切り文字列に整形
$nt_post_id = ”;
for ($p = 0; $p < count($nt_posts); $p ++) {
if ($nt_post_id != '') $nt_post_id .= ',';
$nt_post_id .= $nt_posts[$p];
}
//post__inに設定
$query->set(‘post__in’, explode(‘,’, $nt_post_id));
}
}
//プラグイン「Simple WP Membership」が有効ならアクション追加
if (is_plugin_active(‘simple-membership/simple-wp-membership.php’)) {
add_action(‘pre_get_posts’, ‘use_membership’);
}
[/php]