Home > blog > wordpressで非公開コミュニティサイトを作る(3)続・ユーザーレベルで表示内容を変更

wordpressで非公開コミュニティサイトを作る(3)続・ユーザーレベルで表示内容を変更

  • 2009-04-26 (日) 15:50
  • blog
  • |
Google Adsense

wordpress!

前回ユーザーレベルでの振り分けをpluginとphpやwordpressAPIを使い分けて行う方法を模索したが、pluginが古いせいもあり挙動が不安定なので、やはり全てを自力で行う方法を考えていく。

と言うわけでどうすればplugin無しで出来るか考えていく。
ここまでのやり方で全部代替出来るんじゃねーかなぁという感触があり、pluginが行っている閲覧制限機能を1つづつ代替出来るか試してみる。

pluginが行っているのは表面的には以下の内容。

  • 閲覧権限がないカテゴリーの記事自体のタイトル・本文・コメント本文を非表示化、もしくはメッセージとして登録した文字列に置換
  • 各種ループで閲覧権限がないカテゴリーの記事のタイトル・本文・コメント本文を非表示化(トップページ・カテゴリーアーカイブ・最新の記事等)
  • 閲覧権限がないカテゴリーへのリンクを非表示化(カテゴリー一覧等)、ただしループ内に表示される記事のカテゴリー表示は非表示化されない
  • Feedを各記事のタイトルと記事へのリンクのみに

こんなかな。
なんかいけそ~な気がする~。
注:過程を追って書いてるんで、一度書いたコードが徐々に変わったりしてるんで注意><

前提条件

ユーザーレベル0の人にも表示していいカテゴリー名は『体験用』、カテゴリーIDは『3』。
閲覧制限したいのは主に本文、タイトルは別に出てても問題なし。
あとコメント本文も閲覧制限したい。
コメント非表示の場合は入力フォームもか。

記事自体の閲覧制限

閲覧OKのカテゴリー以外の記事(post)の本文を隠す。
やり方はテーマの中のfunctions.phpに、テーマ内で利用するプログラムを含める事が出来るので、ここにその処理を作っていく。
テーマの柔軟性や保守性のため、出来る限り他のテンプレートであれこれせず、処理をfunctions.phpに集めるのがいいと思う。
wordpressは様々なフックのAPIが用意されているので、データベースからデータ取得→表示、の間に取得したデータを加工する処理をフィルターフックで割り込む形でやってみる。
pluginでもよく使われるこの方法だと、テンプレートのほうから関数呼び出しすら必要がなくなるので、綺麗に表示との分離が出来る。

詳しい記述方法はググってもらうとして、フィルターのフックを使うにはこんな感じで書く。

add_filter('the_content', 'op_hide_content');

add_filterでフィルタを設定する、1つ目の引数がフィルターフック名、2つ目がフックが起動した際に処理させる関数名。
この場合the_contentで記事本文が表示される際にフックが起動し、op_hide_content関数を実行する、となる。
で、op_hide_content関数はこんな感じで書いてみた。

function op_hide_content( $content ) {
  global $user_level;
  if ( 0 == $user_level && !in_category(3) ) {
    $content = "<p>みせないよ</p>";
    return $content;
  } else {
    return $content;
  }
}

引数の$contentはフックにあわせた内容だったりpost_IDだったりを渡せるようだ、このケースだと記事本文を取得できる。
前回の記事のユーザーレベル取得の方法でglobalから$user_levelを持ってくる。
さらに条件にin_category()を利用し、引数で指定したカテゴリーIDに表示中の記事が含まれているかを判別。

注意点は、フックで呼び出す関数の引数は必ず1つ、ってのと必ず引数を返す、って点。
引数はオプションで数を変更できるが。

これで無事本文を隠す事が出来るようになった。
記事自体を表示した時はもちろん、トップページ等で記事の概要のみ表示している場合でも、その部分は表示されないようにする事が出来た。

コメントの閲覧制限

コメントの非表示は記事本文非表示とフックが変わるだけでやる事はほぼ同じだ。
なので同じ関数で処理できるようにフィルタだけ追加してみる。

function op_hide_content( $content ) {
  global $user_level;
  if ( 0 == $user_level && !in_category(3) ) {
    $content = "<p>みせないよ</p>";
    return $content;
  } else {
    return $content;
  }
}

add_filter('the_content', 'op_hide_content');
add_filter('comment_text', 'op_hide_content');

最後の行に追加されているフィルターがコメント本文のフックだ。
どのフィルターから関数が呼び出されたかを判別する方法がわからないが、もし無いなら呼び出す関数を別にしないと置き換えメッセージを本文とコメントで別々に設定できないなぁ。
add_filterを使う前にあれこれしてあげればいいけど。

とりあえずこれでコメントも隠す事ができた。
しかし記事もコメントも見れないのに、コメント入力フォームが表示されているのはスマートじゃない。
なのでコメント入力フォームも隠す方法を考える。

単一記事(post)の閲覧制限

アクションフックにcomment_formなるものも用意されてはいるものの、どうも動かない、というかこれであってるのかすらよくわからないのだが。
なんかテーマによっては動かないよって事なので、ここは仕方なくコメント部分のテンプレートをいじろうかなと思い立つ。

けどよく考えてみると、記事単体を表示する際は本文とコメントとコメント入力フォームを隠すわけだから、単一記事の投稿テンプレートをいじってまとめて隠したほうがいいな。
さっきのフィルターで本文は隠れるようになっているので機能重複するけど、あれはトップページとかでも使えたりするんでまぁいいか。
というわけで記事単体のテンプレートを修正する。

今回俺のケースだとタイトルは別に表示してもかまわないので、使用してるテーマにもよるが、本文呼び出しのテンプレートタグ(the_content())やコメントテンプレート呼び出しタグ(comments_template())をラップしてるblock要素を丸ごとを、分岐で変更させる。

//single.php ヘッダー部分省略
  <h1><?php the_title(); ?></h1>
<?php

global $user_level; //ユーザーレベル取得
if ( 0 == $user_level && !in_category(3) ) { //体験中のユーザーで、かつ閲覧制限のかかったカテゴリーの場合

?>
  <div class="entry">
    <div class="textBody"><p>だめー</p></div>
  </div><!--end entry-->
<?php

} else {//通常表示する場合

?>
  <div class="entry">
    <div class="textBody"><?php the_content(); ?></div>//本文呼び出し
    <?php comments_template(); ?> //comments.php呼び出し
  </div><!--end entry-->
<?php

}

?>
//single.php フッター部分省略

これでおk。
閲覧できない場合のメッセージや表示はもっと細かく書くなり、functions.phpのほうにおいたりも可能だが、書き出された際のhtmlに矛盾が無いようにする点だけ注意。

しかしこれ、functions.phpのように関数だけ集めてあるならともかく、テンプレートを直接いじってるのでカテゴリーIDを生で入れてるのが気になる。どう考えても変更の際面倒な事になる。
なので指定カテゴリーIDをfunctions.phpに格納し利用したいなー、と思い定数にでもするかと思ったが、カテゴリーが複数ある場合困るなぁ。
定数じゃ配列入れれないし、そもそも複数ある場合はカテゴリーIDだけひっぱってきても分岐部分の書き換えが必要になってしまう。
なのでfunctions.phpのほうでその辺を判別する処理を、グローバル変数を置くのも気が引けるんでもろもろclass化したり色々書き換え。

functions.php
//非表示カテゴリーに関連するクラス
class DivergenceView {
  var $hide_cat = array(3); //表示OKなカテゴリーIDの配列
  var $hide_mess = "<p>この内容は今の権限では見れません!<p>"; //非表示化した際置き換える文字列
  function lv() { //ユーザーレベルの確認結果を格納する
    global $user_level;
    return ( 0 != $user_level )?true:false;
  }
  function in_cat() { //現在表示中の場所が除外カテゴリーかをチェックし、非表示にする必要がないならtrueを返す
    if ( $this->lv() ) return true; //ユーザーレベルもチェック、閲覧制限のないユーザーなら何もせずtrue返す
    foreach ( $this->hide_cat as $k => $v ) {
      if ( in_category( $v ) ) return true;
    }
    return false;
  }
}

//コンテンツの非表示化
function op_hide_content( $content ) {
  $DView = new DivergenceView();
  if ( !$DView->in_cat() ) $content = $DView->hide_mess;
  return $content;
}
add_filter('the_content', 'op_hide_content');
add_filter('comment_text', 'op_hide_content');
single.php(逆にした)
//single.php ヘッダー部分省略
  <h1><?php the_title(); ?></h1>
<?php

$DView = new DivergenceView(); //オブジェクト生成
if ( $DView->in_cat() ) { //体験中のユーザーではない、もしくは閲覧制限のかかっていないカテゴリーの場合
  <div class="entry">
    <div class="textBody"><?php the_content(); ?></div>//本文呼び出し
    <?php comments_template(); ?> //comments.php呼び出し
  </div><!--end entry-->
?>

<?php

} else {//通常表示する場合

?>
  <div class="entry">
    <div class="textBody"><?php echo $DView->hide_mess; ?></div> //class内にある置換メッセージ表示
  </div><!--end entry-->
<?php

}

?>

本当はフィルターで呼び出す関数もメソッド化したかったが、add_filterからメソッドを呼ぶ方法が分からなかったのでそのままに。

ページ(page)の閲覧制限

テンプレート自体は記事(post)と似たようなものなため基本的なやり方は同様だが、ページの場合はカテゴリーがないので別の判別方法が必要になる。
ページにもカテゴリーをつけるpluginとか昔あった気がするが、経験上こういうトリッキーな代物は後々大変になったりするんで、今回は素直にカスタムフィールドを利用する。1

カスタムフィールドに、名前を“provisional”、値にtrueを入力し追加しておく。(勿論名前は英数字なら何でもい)
ここでtrueを値にしたのは、今後カスタムフィールドの拡張pluginを利用する場合、チェックボックスのみで公開非公開を選べるといいなぁという淡い期待からである。
これをページ描画の際に取得して振り分けを行う。

ページのテンプレートpage.phpの作りは、これもテーマによるが基本的に記事用のテンプレートと大差ないはずなので、ここでは条件の部分のみ。

//page.php ヘッダー部分省略
<h1><?php the_title(); ?></h1>
<?php
$cfv = get_post_custom_values("provisional");//カスタムフィールドの値取得
$cfv = $cfv[0];//配列なので代入しなおし
global $user_level; //ユーザーレベル取得
if ( !$cfv  && 0 == $user_level ) {//体験中のユーザーで、カスタムフィールドがtrueを返す場合、制限表示
	$DView = new DivergenceView();
?>
<div class="entry">
	<div class="textBody">
		<p><?php echo $DView->hide_mess; ?></p>
	</div>
</div><!--end entry-->
<?php
} else {
?>
<div class="entry">
	<div class="textBody"><?php the_content(); ?></div>
<?php comments_template(); ?>
</div><!--end entry-->
<?php
}
?>
//page.php フッター部分省略

カスタムフィールドの情報を取得するにはthe_meta()というテンプレートタグを利用すればいい、がこれそのまま使うとそのwordpressにあるカスタムフィールドをすべて、しかもご丁寧にリストで表示してくれちゃったりする。
これじゃ分岐には使えないので引数で取得する情報を指定する、出力じゃなく値で取得するにはいくつかの方法があるのだが、今回は今見てるページのカスタムフィールドが欲しいのでpost_IDを指定しなくて済むget_post_custom_values()を使う。
ここではget_post_custom_values("provisional")の部分。
さらに返り値は配列なので、key0を代入しなおしている。2

さて、このままだと分岐がちゃんと行われるものの、先ほど作ったフィルターが全てのページの本文だけブロックしてしまう。ページはカテゴリーがないからね。
なのでフィルターに少し手を加える。

function op_hide_content( $content ) {
  $DView = new DivergenceView();
  if ( !$DView->in_cat() && !is_page() ) $content = $DView->hide_mess;
  return $content;
}

if文の条件に!is_page()を加えた、これによって今の表示がページではなく、の条件が加えられる。

さらに、pageテンプレート自体の修正を減らしたいのと、このカスタムフィールドからの判別は他でも使うため(後述)、これも先ほどのclassのメソッド化しておく。

class DivergenceView {
  var $hide_cat = array(3); //表示OKなカテゴリーIDの配列
  var $hide_mess = "<p>この内容は今の権限では見れません!<p>"; //非表示化した際置き換える文字列
  function lv() { //ユーザーレベルの確認結果を格納する
    global $user_level;
    return ( 0 != $user_level )?true:false;
  }
  function in_cat() { //現在表示中の場所が除外カテゴリーかをチェックし、非表示にする必要がないならtrueを返す
    if ( $this->lv() ) return true; //ユーザーレベルもチェック、閲覧制限のないユーザーなら何もせずtrue返す
    foreach ( $this->hide_cat as $k => $v ) {
      if ( in_category( $v ) ) return true;
    }
    return false;
  }
  function page_cf() { //ページのカスタムフィールドをチェックし表示すべきかを返す
    if ( $this->lv() ) return true; //ユーザーレベルもチェック、閲覧制限のないユーザーなら何もせずtrue返す
    $cfv = get_post_custom_values("provisional");//カスタムフィールドの値取得
    $cfv = $cfv[0];//配列なので代入しなおし
    return ( $cfv )?true:false;
  }
}

さらに先ほどのpage.phpテンプレートもあわせて変更。
ちなみに分岐内容に重複するタグがあるのでもっとコンパクトに出来るんだけど、視認性のためにそのままにしてる。
アホなんで次の日見ただけでもう忘れてる可能性がある。俺流のリスクヘッジ2009春。

//page.php ヘッダー部分省略
	<h1><?php the_title(); ?></h1>
<?php

$DView = new DivergenceView();
if ( $DView->page_cf() ) {

?>
<div class="entry">
  <div class="textBody"><?php the_content(); ?></div>
<?php comments_template(); ?>
</div><!--end entry-->
<?php

} else {

?>
<div class="entry">
  <div class="textBody">
    <p><?php echo $DView->hide_mess; ?></p>
  </div>
</div><!--end entry-->
<?php

}

?>
//page.php フッター部分省略

これで、ページを作る際はカスタムフィールドを設定してあげれば『体験中の人にも公開するページ』、を簡単に作成する事が出来るようになった。
カスタムフィールド名を選択し、値にtrueと入力するだけの簡単なお仕事だが、これは技術的な話と無縁の人にはちょっと抵抗感のある作業だ。
しかし公開する内容を書く、しかも記事でなくページ、という作業を管理者になるであろう俺以外がやるケースはあまりなさそうなので、これでいくことに。
(本来はエディタにチェックボックスでも表示させて、チェックするかどうかだけで選択出来ると初心者でも抵抗ないんで、そのうちそんな感じのpluginを探す事に。どこかで見た気がする。)

アーカイブの閲覧制限

そもそも見る事の出来ない記事をアーカイブやトップに表示する必要はあまりない、にぎやかし的に「正式加入するとこんな記事が見れるのか~」という心理効果はありそうだけど。
というわけで次はトップページからそもそも閲覧権限のない記事を書き出さない方法を。
あれ、すると本文をフィルターで隠した意味がいよいよ無くなる気がするな…まぁいいか。

if (have_posts()) :
  while (have_posts()) : the_post();

こんな感じのループの開始部分を、以下のようにしてあげる。

if (have_posts()) :
  $DView = new DivergenceView(); //オブジェクト生成
  while (have_posts()) : the_post();
    if ( !$DView->in_cat() ) continue;

ユーザーレベルとカテゴリーをチェックし、そのループ回の記事が公開してはいけないものならcontinueでループを飛ばすだけ。
これを行うのは主にcategory.php・index.php・search.php・archive.php・tag.php等になる、勿論使用しているテーマの構成によって変わる場合はある。
これによってカテゴリー、トップページ、検索結果、月ごとの投稿、同じタグの付いた投稿などの一覧表示が最適化された事になる。

サイドバーの修正

これまたテーマによるが、多くのテーマでサイドバーが1つか2つほど採用されており、そこにはページ一覧やカテゴリー・タグ一覧・最近の記事10件、といった内容があったりする。
ここまでの作業でなるべく閲覧権限の無いものは目に触れないように手を加えてきたので、当然ここもやっておく。

ページ一覧

サイドバーにページの一覧を載せる場合、wp_list_pages()っていうテンプレートタグを使う場合が多いと思うので、これの修正方法。
ちなみにwordpress2.7からwp_page_menu()というテンプレートタグも追加されたが、上記タグと比較してホームへのリンクを追加できる点とulも書き出す、くらいしか差異が見つからない、よくわからないのでこっちは今回は考えない事に。
wp_list_pages()にはオプションでincludeパラメータがあり、これに表示したいページのIDを渡すと他を除外してくれるので、閲覧してもよいページのIDを全部調べて返すメソッドを先ほど作ったclassに追加する事にする。

class DivergenceView {
  var $hide_cat = array(3); //表示OKなカテゴリーIDの配列
  var $hide_mess = "<p>この内容は今の権限では見れません!<p>"; //非表示化した際置き換える文字列
  function lv() { //ユーザーレベルの確認結果を格納する
    global $user_level;
    return ( 0 != $user_level )?true:false;
  }
  function in_cat() { //現在表示中の場所が除外カテゴリーかをチェックし、非表示にする必要がないならtrueを返す
    if ( $this->lv() ) return true; //ユーザーレベルもチェック、閲覧制限のないユーザーなら何もせずtrue返す
    foreach ( $this->hide_cat as $k => $v ) {
      if ( in_category( $v ) ) return true;
    }
    return false;
  }
  function page_cf() { //ページのカスタムフィールドをチェックし表示すべきかを返す
    if ( $this->lv() ) return true; //ユーザーレベルもチェック、閲覧制限のないユーザーなら何もせずtrue返す
    $cfv = get_post_custom_values("provisional");//カスタムフィールドの値取得
    $cfv = $cfv[0];//配列なので代入しなおし
    return ( $cfv )?true:false;
  }
  function hide_page_lis() { //表示可能なページのみサイドバーなどのリストに表示する
    if ( $this->lv() ) return; //ユーザーレベルもチェック、閲覧制限のないユーザーなら何もせず返す
    $r = '&include=';
    $pages = get_pages(); //全ページの情報取得
    for ( $i = 0,$c = count( $pages ); $i < $c; ++$i ) { //取得したページの数だけループ
      $cfv = get_post_meta( $pages[$i]->ID, 'provisional', true ); //ページのIDを取得し、そのIDのページのカスタムフィールドの値を取得
      if ( $cfv ) $r .= $pages[$i]->ID . ","; //カスタムフィールドの値がtrueの場合、ページIDとカンマを連結
    }
    $r = substr( $r, 0, -1); //余分に付いたカンマを除去
    return $r;
  }
}

一番下に追加したhide_page_lis()がそれにあたる。
流れとしてはまずいつもどおり準備としてユーザーレベルをチェックし、返す値を格納する変数に“&include=”を先にいれる。
全ページの情報を取得したらループでまわし、get_post_meta()を利用してページのIDを指定しカスタムフィールドの値を取得する。
あとは取得した値がtrueならカンマを付けて格納変数に継ぎ足していき、ループ後に最後の余分なカンマを除去し返す。
これを利用するSidebar.phpの該当部分はこんなかんじ。

<?php
$DView = new DivergenceView();
if ($pages = &get_pages('')) : ?>
    <dt>ページ</dt>
    <dd>
      <ul class="pages">
<?php wp_list_pages( 'sort_column=menu_order&title_li=0' . $DView->hide_page_lis() ); ?>
      </ul>
    </dd>
<?php
endif;
?>

これで閲覧制限のかかった記事は、サイドバーのページ一覧に顔を出さなくなる。
ところでメソッドのほうで全ページの情報取得、なんかしてるけどこれを格納した変数って開放しなくていいのかな…よくわからないんでやってないけど。

最新の記事一覧

いわゆるRecent Entriesとかの事、最新投稿記事を5~20くらい表示されてる、これもサイドバーの定番。
何かこういうの表示するためのpluginとかもあった気がするけど、とりあえずwp_get_archives(()ってテンプレートタグで出力してるケースが多いと思うんで、それへの対応。

最新5件の投稿記事を表示する場合、以下のようなテンプレートタグを出力したい部分に記述する形になると思う。

<?php wp_get_archives('type=postbypost&limit=5'); ?>

このテンプレートタグでは指定や除外が出来ないので、頑張れば可能だけどちょっと面倒臭い。
公開可能か不可能かはどのカテゴリーに属しているかで決めているので、この際このテンプレートタグではなく普通にループを利用して書き出していく事に。
やり方はアーカイブ系のループをやった時と同じ、今回はリストとしてタイトルとリンクを書き出せばいい。

<dt>Recent Entries</dt>
<dd>
  <ul class="recentEntries">
<?php
if (have_posts()) :
  while (have_posts()) : the_post();
    if ( !$DView->in_cat() ) continue;
?>
    <li><a href='<?php the_permalink() ?>' title='<?php the_title(); ?>'><?php the_title(); ?></a></li>
<?php
    endwhile;
  endif;
?>
  </ul>
</dd>

classからオブジェクト作るのは一回でいいんで、さっきやったページ一覧でオブジェクト作ってたら別にやらなくておk。
こんな感じで大概のものは専用のテンプレートタグを諦めればループか、生データ持って来て自力加工でどうにでもなる。

カテゴリー一覧

これを表示させるかが非常に迷う…。
たった一つのカテゴリーのみ閲覧可能なら、そもそも見れないカテゴリーばかり並ぶ一覧を表示する必要はない、と前提に立てれるなら単純にユーザーレベルでカテゴリー一覧の表示非表示を切り替えればいい。
ただ1つの投稿記事に複数のカテゴリーを付けれるので、決して閲覧可不可を決定するカテゴリー以外役に立たない訳でもない。
逆に体験者にとって体験者カテゴリー内の記事しか見れないのなら、体験者カテゴリーというもの自体が見えている必要がない。なにせ目に付く記事全部がそのカテゴリー内なわけだし、そもそもこのカテゴリーの役目が本来のものではなくむしろ管理制御用なのだから。
というわけで、体験者権限のみカテゴリーリストから体験者カテゴリーを取り除く形にする。3
(カテゴリーが複雑にネストしだすと結構面倒な事になりそうだが、今回はそれは考慮外とした。)

カテゴリーの表示にはwp_list_categories()テンプレートタグが利用される。(たまにドロップダウンを使う所もあるが基本は同じ)
このテンプレートタグには指定・除外が出来るオプションがあるので、ページ一覧と同様に引数を作って渡してあげればいい。

functions.php
class DivergenceView {
  var $hide_cat = array(3); //表示OKなカテゴリーIDの配列
  var $hide_mess = "<p>この内容は今の権限では見れません!<p>"; //非表示化した際置き換える文字列
  function lv() { //ユーザーレベルの確認結果を格納する
    global $user_level;
    return ( 0 != $user_level )?true:false;
  }
  function in_cat() { //現在表示中の場所が除外カテゴリーかをチェックし、非表示にする必要がないならtrueを返す
    if ( $this->lv() ) return true; //ユーザーレベルもチェック、閲覧制限のないユーザーなら何もせずtrue返す
    foreach ( $this->hide_cat as $k => $v ) {
      if ( in_category( $v ) ) return true;
    }
    return false;
  }
  function page_cf() { //ページのカスタムフィールドをチェックし表示すべきかを返す
    if ( $this->lv() ) return true; //ユーザーレベルもチェック、閲覧制限のないユーザーなら何もせずtrue返す
    $cfv = get_post_custom_values("provisional");//カスタムフィールドの値取得
    $cfv = $cfv[0];//配列なので代入しなおし
    return ( $cfv )?true:false;
  }
  function hide_page_lis() { //表示可能なページのみサイドバーなどのリストに表示する
    if ( $this->lv() ) return; //ユーザーレベルもチェック、閲覧制限のないユーザーなら何もせず返す
    $r = '&include=';
    $pages = get_pages(); //全ページの情報取得
    for ( $i = 0,$c = count( $pages ); $i < $c; ++$i ) { //取得したページの数だけループ
      $cfv = get_post_meta( $pages[$i]->ID, 'provisional', true ); //ページのIDを取得し、そのIDのページのカスタムフィールドの値を取得
      if ( $cfv ) $r .= $pages[$i]->ID . ","; //カスタムフィールドの値がtrueの場合、ページIDとカンマを連結
    }
    $r = substr( $r, 0, -1); //余分に付いたカンマを除去
    return $r;
  }
  function hide_cats_lis() {
    if ( $this->lv() ) return; //ユーザーレベルもチェック、閲覧制限のないユーザーなら何もせず返す
    $r = '&exclude=';
    foreach ( $this->hide_cat as $k => $v ) $r .= $v . ',';
    $r = substr( $r, 0, -1); //余分に付いたカンマを除去
    return $r
  }
}
sidebar.php
<dt>Categories</dt>
<dd>
  <ul class="category">
<?php wp_list_categories( 'title_li=' . $DView->hide_cats_lis() ); ?>
  </ul>
</dd>

やってる事はページ一覧と大差ない。
classが長大になってきたな…大差ない事やってるならもう少しメソッドを抽象化できりゃいいんだけどうーん。

feedの閲覧制限

feedへのリンクやheadタグ内のメタタグは分岐してやりゃいいだけなのですぐ終わる。
分岐させて非表示にした所でfeedは存在するという点で、画像への直アクセスに似ているが、これは対処可能だった。

参考:wordpressで非公開コミュニティサイトを作る(1)認証機能をつける – atl*weblog

上記記事内ではheader.phpに直接処理を書いていたが、その後偶然send_headersというアクションフックがある事を知った。
このフックを利用する事で、header.phpに直接かかなくて済む上に、feedへのアクセスにもログインが必要になる。

function op_auth() {
  if ( !is_user_logged_in() ) auth_redirect();
}
add_action('send_headers', 'op_auth');

これをfunction.phpにでも書いておけばOK。
ちなみにこの方法でも画像への直アクセスは防げないので注意。

とりあえずここまでやったが、大事な問題がある。
このfeed、権限があるとして、どうやって見るのって話なのだw
…wordpressのログイン管理は当然cookieを利用してるんで、普通に考えたらcookieを利用出来ないfeedreaderじゃ見れないよなぁ。
今後解決策を考えていくけど、場合によってはタイトルと概要のみのfeedを認証が必要ない状態で吐き出すとか、妥協案も考える必要がありそうな雰囲気。
BASIC認証みたくhttp://username:password@example.com/feed/とか、こんな感じの方法ないかなぁ、なんか難しそうで解決しなそうな雰囲気を感じるな…;;

記事単位での所属カテゴリー表示の加工

もーないよな!と思いつつ色々見てたら、記事ごとにその記事がどのカテゴリーに属するかを表示するカテゴリー一覧があったんだった。
別に記事がどのカテゴリーのものかを表示する事自体はいいのだが、さっきサイドバーのカテゴリー一覧で体験用カテゴリー表示のみ消したのと同様に、こちらも消しておかないといけないだろう。
しょうがない、やるか!

記事ごとの所属カテゴリー表示はテンプレートタグthe_category()を利用する、引数にはセパレートを指定できる。
この所属カテゴリー表示は記事単体表示のみならず、トップページやらアーカイブ系やら、ループでいくつも記事を表示する際にも必ず出てくる。
なのでテンプレートを直接いじるのではなく、functions.phpに処理を書いてフィルターする事に。

the_categoryというフィルターフックを使う事で、テンプレートタグthe_category()が吐き出すhtmlを表示する前に受け取って加工できる。
これを利用してこんな感じで書いてみた。

function cat_fill( $htm ){
  $DView = new DivergenceView();
  if ( $DView->lv() ) return $htm; //ユーザーレベルもチェック、閲覧制限のないユーザーなら何もせず返す

  $r = "";
  $sp = "\|"; //セパレート
  $lis = split( $sp, $htm ); //セパレートで分割し配列に格納
  for ( $i = 0,$l = count($lis); $i < $l; ++$i ) {
    $FLG = true;
    //urlのパラメータの位置を探し、文字分を足した位置から後を切り出す
    $p = substr( $lis[$i], strpos( $lis[$i], "?cat=") + 5);

    //切り出した文字列の頭からurl終りの位置までを格納(カテゴリーID)
    $p = substr( $p, 0, strpos( $p, "\"") );

    foreach ( $DView->hide_cat as $k => $v ) { //取り出したカテゴリーIDが閲覧禁止かチェック
      if ( $p == $v ) $FLG = false;
    }

    $r .= $FLG?$lis[$i].$sp:"";

  }
  if ( strpos( $r, $sp ) ) $r = substr( $r, 0, -4); //カテゴリーが1つしかない場合、セパレート除去

  $htm = $r;
  return $htm;
}
add_filter('the_category', 'cat_fill');

フィルターから呼び出す関数の処理の流れは以下。

  1. 関数スタート、引数$htmはテンプレートタグthe_category()が書き出すはずだったhtml
  2. オブジェクトを生成しユーザーレベルチェック、制限かからない権限だった場合そのまま返して終了
  3. 各変数初期化、$rは一時格納、$spはセパレート、$lisには引数で渡されたhtmlをセパレートで分割し配列を格納
  4. $lisの配列をループ回し、始めにフラグ
  5. html内のurlのパラメータから後ろを切り出す(urlのカテゴリーIDの前部分を切捨て)
  6. 切り出した文字列頭からurlの最後までを切り出す(カテゴリーIDから後ろ部分を切捨て)
  7. こうやって取り出したカテゴリーIDが閲覧制限のあるものかをチェック
  8. 問題なければ格納変数$rに文字列連結
  9. ループ終了、複数項目があった場合、ケツに余計なセパレートが付いてるはずなんで切り捨て
  10. いじくりまわしたhtmlを返す(引数と同じ名前じゃないとダメだった気がして入れなおしてる、記憶おぼろげ)

絶対もっとうまい方法があった気がする…カテゴリーIDは正規表現で取り出せるだろ、とかw
文字列いじりをするって個人的に最終手段というか、大概の場合悪手で他にいい手があったりするイメージなので気が引けるが、functions.phpのほうだけで済んだからまぁいいか。
(アクセス数が多い場合はちゃんと効率のいい方法でやらないときっとサーバに負担かかるんだと推測)

  1. 単にDBに情報を格納するだけでなく、デフォのwordpressのデータベースをいじったりするものは、面倒事の元になったりするという経験から [戻る]
  2. カスタムフィールドの名前を明示しているのだから返り値は1つ確定なので配列で返す事ないのに、と思ったがなんと同じ名前のカスタムフィールドをいくつも設定出来るらしい。ただしその場合の取得順などのコントロールは出来ないというなんともかんともな仕様らしい… [戻る]
  3. Aカテゴリーにはいくつか体験者カテゴリーにも属する記事があるのでAカテゴリーアーカイブを見る事に意味が出るが、一つも体験者カテゴリーに同時に属する記事がないBカテゴリーを閲覧制限された人が見る意味がない。という事で本来各カテゴリーに含まれる記事を全て精査すべきだが、面倒なので今回はパスした。 [戻る]

Trackbacks (Close):1

pingback from WordPressでベーシック認証させる方法まとめ 11-04-15 (金) 19:22

[...] イトを作る(3)続・ユーザーレベルで表示内容を変更 http://weblog.atl-r.net/blog/t……nitysite3/ [...]

Home > blog > wordpressで非公開コミュニティサイトを作る(3)続・ユーザーレベルで表示内容を変更

Calendar
« 2017 年 2月 »
M T W T F S S
    1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28          
Search
ABM | AnotherBookmark™
heteml
heteml
amadana
amadana(アマダナ)
feed
Meta

Return to page top