WordpressでR-18コンテンツ表示に年齢認証機能を実装したよ

## なんで年齢認証なんかを？
自前のサイトはWordpressで既存テーマを改造して使っていて、主にDIYやゲーム系、プログラム系のコンテンツでAmazonなどのアフィリエイトプログラムに登録しています。
かたや、アダルトグッズのアフィリエイトを一部に仕掛けたネタ的なFC2ブログもあって、管理が面倒なので自前ブログに統合したかったけど、そのまま自サイトにR-18コンテンツを統合するとAmazonとかから万一ボットとかで巡回チェックされてアフィリエイト登録解除されたら困るので、統合するならR-18コンテンツはページ本体も投稿一覧のサムネ画像もボットを回避する年齢認証の仕掛けを挟めばいいかなと思っていました。
(※正直、アフィリエイト登録審査を一度パスしたサイトを、ボットを使ってアフィリエイトリンクのリンク元をチェックしてるのか、無作為に人力でやってるのか、そもそもやってないのかの実際の所は私は判りません(^0^;) 以前、どこかのサイトで審査通った後で規約違反コンテンツ登録してたら登録解除されたみたいな話を見かけたので必要かな～と思い込んでるだけなんですw 誰か実態を知っている方がいたらお教え下さい！)

## 年齢認証するプラグインがない・・・だと
んで、お気楽に年齢認証を実現するプラグインとかないものかと探しましたが、、、求める機能に合致したのがないんですよねww 会員認証やサイト全体の認証とかならいくらでもありますが、要認証コンテンツとそうでないものが混在していて登録ユーザーでない不特定多数の来訪者に年齢認証かけるものがない…。それで、機能を自作するしかないと1年前に以下の内容で1度チャレンジしました。

## とりあえずの実装
とりあえず実装したのは、**テーマのloop.php**を書き換えて(これだけは外部設定に置き換えれなかったorz オリジナルを書き換えないように子テーマとして作ってやるぐらいですかね…。)「R-18」タグが付いたコンテンツは、ホームのページ一覧でサムネを閲覧禁止と書かれた画像に置き換える機能。
![f412bbd507a932bd2467fc8110508d8a.png](https://qiita-image-store.s3.amazonaws.com/0/57876/adf656e4-7ba7-bbe4-f6a6-a72a4f8c5dc2.png)


具体的な修正箇所は使用するテーマによって変わるでしょうが、私が使用しているテーマは「Graphene」で、修正はホーム画面の投稿一覧を表示する役割の「loop.php」のサムネ表示部分の`<a>`タグに挟まれた部分だけを以下の様に変更。
(認証画面を通った場合はクッキーとして"confirm_cookie"がセットされている前提。最終的な認証画面のスクリプトコードの詳細については後述)

original:

```loop.php
<?php /* The post thumbnail */
if ( has_post_thumbnail( get_the_ID() ) ) { ?>
    <div class="excerpt-thumb">
    <a href="<?php the_permalink() ?>" rel="bookmark" title="<?php printf( esc_attr__( 'Permalink to %s', 'graphene' ), the_title_attribute( 'echo=0' ) ); ?>">

        <?php the_post_thumbnail( apply_filters( 'graphene_excerpt_thumbnail_size', 'thumbnail' ) ); ?>
    </a>
    </div>
    <?php
} else {
    echo graphene_get_post_image( get_the_ID(), apply_filters( 'graphene_excerpt_thumbnail_size', 'thumbnail' ), 'excerpt' );	
}
?>
```

modified:

```loop.php
<?php /* The post thumbnail */
if ( has_post_thumbnail( get_the_ID() ) ) { ?>
    <div class="excerpt-thumb">
    <a href="<?php the_permalink() ?>" rel="bookmark" title="<?php printf( esc_attr__( 'Permalink to %s', 'graphene' ), the_title_attribute( 'echo=0' ) ); ?>">

        <?php /* exchange thumbnail image if has R-18 tag  and not set cookie*/
        if ( has_tag('R-18', get_the_ID()) && ( !isset( $_COOKIE['confirm_cookie'] ) || ( $_COOKIE['confirm_cookie'] ) == -1) ) {
            echo '<img src="https://サイト名/wp-content/uploads/登録済み差し換え用画像のパス.png" alt="R-18" width="240" height="180" class="attachment-thumbnail size-thumbnail wp-post-image" />';
        } else {
            the_post_thumbnail( apply_filters( 'graphene_excerpt_thumbnail_size', 'thumbnail' ) );
        }
        ?>
    </a>
    </div>
    <?php
} else {
    echo graphene_get_post_image( get_the_ID(), apply_filters( 'graphene_excerpt_thumbnail_size', 'thumbnail' ), 'excerpt' );	
}
?>
```

そして、「**[CSS & JavaScript Toolbox](https://ja.wordpress.org/plugins/css-javascript-toolbox/)**」というプラグインを使ってphpやjavascriptを任意のページのヘッダーやボディ末尾に埋め込めるようにして、ページに飛んだ際にCSSのレイヤー機能でページを隠してモーダルな年齢認証ダイアログを表示して「18歳以上」ボタンをクリックしたらレイヤーを解除してページを表示、「18歳未満」ボタンをクリックしたら前のページに戻るという仕掛けでした。
![f27e0f046f65c8fb672692308893d3a2.png](https://qiita-image-store.s3.amazonaws.com/0/57876/ac7d1a06-dab6-2895-0288-c2e0ccaba9f8.png)

でも、よく考えたらこれってR-18ページのhtmlが読み込まれてからモーダルダイアログ表示して隠してるだけで、**ボットからは中身丸見え**だったんですよね(^0^;)
これじゃ、見た目はクリック操作を挟むことによる年齢認証してるっぽいけど、そもそもの目的であるボットのクロール回避ができてない・・・。
そこで、一旦実装はペンディングして放置していました orz

## やはりクッションページとリダイレクトが必要
で、1年ほど放置していましたが、先日思い立って再挑戦！
認証の段階でコンテンツのhtmlが暴露されないためには、やはり認証画面は独立したページにするしかない。
そうなるとこれから表示しようとするページと認証通らなかった時に前のページに戻すためにページジャンプやリダイレクトを用いてページ遷移を制御しなければいけない。認証ページを間に挟むので、認証を通った場合に飛ばす先のURLや、元のページに戻るURLをcookieに保持させなくてはいけない。色々面倒そう…。

## 実装に向けてのロジック
というわけで、今回の実装での必須機能要件は以下の2点。
- **ページ単位で認証有無の指定ができる** (昨年実装済みで「R-18」タグの有無で切替)
- **htmlを見せないようにしてボットによるクローリングを回避** (今回追加の要件)

認証をパスしたら設定するcookieによってサムネのすり替えをする機能と、モーダルダイアログを表示する部分は1年前に実装したままで問題ないとして、今回ページ遷移を実現するために以下の様な流れを考えました。
1. 全てのページにおいて表示する際にcookieに現在のページのURLと1つ前のページ(リファラー)のURLを保存しておく
2. R-18タグが設定されたページが呼び出された場合かつ認証用cookieが設定されていなければ、年齢認証ページにリダイレクト
3. 認証用のモーダルダイアログを表示
4-1. 認証通過の場合はcookieに保存した本来表示するページにジャンプ
4-2. 認証不通過の場合は同じくcookieに保存した1つ前のページにジャンプ

## 実装編 ～認証用ページ作成～
まずは、クッションページを固定ページとして作成します。作成する際には、以下の要領で作ります。
・CSSのレイヤーによりテーマのテンプレートが生成するページは後ろに隠れるのでローディングを軽くするため、コンテンツの中身は無しで、固定ページの属性でサイドバー無しのテンプレートを選択。
・スラッグ名を固定化するためにパーマリンクを任意の名前で指定します。私は"check_age"としています。このスラッグ名は後で使います。
![c7e4d5f615c79b2786909fe98ea23381.png](https://qiita-image-store.s3.amazonaws.com/0/57876/23229877-c4ad-ed53-a345-4822f2b18870.png)
保存したら次の作業へ。

## 実装編 ～function.phpにコードを追加～
ページに飛んで表示する前、HTTPリクエストを受けて処理してリダイレクトするためにはget_headerの時点で処理する必要があるようなので、そのためにfunction.phpに処理を追加しなくてはいけません。

function.phpを直接変更するのは嫌なので、今回は「**[PHP code snippets](https://ja.wordpress.org/plugins/insert-php/)**」というプラグインを採用しました。

PHP code snippetsで新規のsnippetを作成して編集します。
![e1b35069097ea6a30e2cb1b4e1c6245f.png](https://qiita-image-store.s3.amazonaws.com/0/57876/5ccacc95-98db-131d-728e-822be8a3198e.png)

具体的には以下のコードを記述します。関数名は任意でOKですが、認証ページがリクエストされた場合はcookie設定されると前後のページURLがおかしくなりますから、is_pageの判定で先に作成した認証用固定ページのスラッグ名を記述します。
リクエストされた飛び先のURLをcookieの"link"に、元のページURLをcookieの"back"に設定。
(ページURLのcookieは認証処理内で削除していますが、念のため24時間でexpireするようにしています。)
そして、飛び先ページに設定されたタグ名が「R-18」だったら、認証ページに302リダイレクトさせます。

「Where to execute the code?」の部分は「Run everywhre」を選択し全てのページで呼ばれるようにします。

```php:check_age
add_action( 'get_header', '任意の関数名');
 
function 任意の関数名() {
    if ( !is_page('認証用固定ページのスラッグ名') ) {
        $link_page =  (is_ssl() ? 'https' : 'http') . '://' . $_SERVER["HTTP_HOST"] . $_SERVER["REQUEST_URI"];
        setcookie( 'link', $link_page, time() + 60*60*24, '/' );
        $back_page = $_SERVER['HTTP_REFERER'];
        setcookie( 'back', $back_page, time() + 60*60*24, '/' );
        if ( has_tag('R-18') && ( !isset( $_COOKIE['confirm_cookie'] ) || ( $_COOKIE['confirm_cookie'] ) == -1)){
            $check_page =  (is_ssl() ? 'https' : 'http') . '://' . $_SERVER["HTTP_HOST"] . '/check_age';
            wp_redirect( $check_page, 302 );
            exit;
        }
    }
}
```

必要な設定を終えたら、更新ボタンで反映します。

## 実装編 ～認証用モーダルダイアログ周りを実装～
この部分は「**[CSS & JavaScript Toolbox](https://ja.wordpress.org/plugins/css-javascript-toolbox/)**」でCSSも含めてPHPを記述して認証ページなどに埋め込みます。

### ・全ページ用の「jquery.cookie.js」のローディングと認証cookie解除用関数の埋め込み
「CSS & JavaScript Toolbox」で、「New code block」から新しいブロックを作成します。
![85ad08b390fc7b2a56c7bb0dd69bcb1b.png](https://qiita-image-store.s3.amazonaws.com/0/57876/6b1cd74e-bf52-4d1b-e30d-92c9aa121aa4.png)
・ブロック名は任意で。ブロック名末尾に「.php」を付けると記入したコードをデフォルトでPHPとしてチェックしてくれます。
・「Location Hook」はコードをhtmlに埋め込む位置をHederかFooterか選択。今回は**Footer**で。
・「Block Position」は「CSS & JavaScript Toolbox」管理画面上のブロックの並び順で、後からドラッグ＆ドロップで変更もできるのでデフォルトのままでOK。
・記述するコードは以下の内容。

```load_modal_js.php
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-cookie/1.4.1/jquery.cookie.js"></script>
<script type="text/javascript">
    function clear_cookie() {
        jQuery.removeCookie("confirm_cookie", {expires:-1, path:"/"});
        location.reload();
    };
</script>
```

JavaScript関数のclear_cookie()は、動作確認時に認証用のcookie"confirm_cookie"を削除するための関数で、必要な場合はウィジェットエリアにボタンを設置して呼びだすようにするといいでしょう。

コードを記入したら、呼びだされる場所を指定します。このコードは全ページで呼びだす必要があるので、
右側のタブの「AUX」で「Entire Website」と「All Posts」をチェックしてSaveボタンをクリックします。
![9220725466ce987fd2d4fa246e73850d.png](https://qiita-image-store.s3.amazonaws.com/0/57876/597a58cf-fb1f-148f-7ff2-af14391c5c53.png)

### ・認証ページ表示用・ページジャンプ用のコードの埋め込み
先程と同様に「CSS & JavaScript Toolbox」で、「New code block」から新しいブロックを作成します。
・「Location Hook」などは先程と同じく**Footer**で。
・記述するコードは以下の内容。

```do_modal_page.php
<style>
#confirm_modal {
	visibility: hidden;
	display: none;
	position: fixed;
	top: 50%;
	left: 50%;
	transform: translate(-50%, -50%);
	margin: auto;
	z-index: 2147483647 !important;
	padding: 5px !important;
	border: solid;
	border-color: #F7FF00;
	border-radius: 20px;
	border-width: 4px;
	width: 300px;
	height: 300px;
	background-color: #5C6CFF;
}

#confirm_overlay {
	display: block;
	position: fixed;
	background-color:  #2F064F;
	width: 100%;
	height: 100%;
	z-index: 2147483646 !important;
	left: 0px;
	right: 0px;
	top: 0px;
	bottom: 0px;
	opacity: 1;
	filter:alpha(opacity = 100); /* For IE8 and earlier */
}

/* POP-UP */
#confirm_content {
	overflow-y: auto;
	overflow-x: auto;
	height: 100%;
	width: 100%;
	font-size: 12px;
	color: #FFFFFF;
	text-align: center;
}

#confirm_content h3 {
	margin-bottom: 6px;
}

#confirm_content p {
	color: #FFFFFF;
}
</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-cookie/1.4.1/jquery.cookie.js"></script>
<script type="text/javascript">
	var limit = 10;	//Cookieの有効期限(日)
	var cookie_check = jQuery.cookie("confirm_cookie");
	var cookie_link = jQuery.cookie("link");
	var cookie_back = jQuery.cookie("back");

	function confirm_yes() {
		var clearTime = new Date();
        clearTime.setTime(clearTime.getTime() + (limit*24*60*60*1000));
		jQuery.cookie("confirm_cookie", "off", {expires:clearTime, path:"/"});
		jQuery.removeCookie("link", {expires:-1, path:"/"});
		jQuery.removeCookie("back", {expires:-1, path:"/"});
        location.href = cookie_link;
	};

	function confirm_no() {
		jQuery.removeCookie("confirm_cookie", {expires:-1, path:"/"});
		jQuery.removeCookie("link", {expires:-1, path:"/"});
		jQuery.removeCookie("back", {expires:-1, path:"/"});
        location.href = cookie_back;
	};
</script>

<?php if (( !isset( $_COOKIE['confirm_cookie'] ) || ( $_COOKIE['confirm_cookie'] ) == -1)) : ?>
<div id="confirm_modal">
	<div id="confirm_content">
		<h3>年齢確認 (Confirmation your age)</h3>
		<p>
		このページは１８歳未満のかたは閲覧できません。<br />
		( This page is only for over 18 years old. )<br />
		<br />
		あなたは１８歳以上ですか？<br />
		( Are you sure as over 18 years old? )<br />
		<br />
		</p>
    	<input type="image" src="/wp-content/uploads/Yesのボタン画像ファイルパス名" onClick="confirm_yes();" alt="はい(Yes)" /><br /><br />
	    <input type="image" src="/wp-content/uploads/Noのボタン画像ファイルパス名" onClick="confirm_no(); return false;" alt="いいえ(No)" /><br />
	</div>
</div>

<div id="confirm_overlay"></div>
<?php endif; ?>

<script type="text/javascript">
if ( document.cookie.indexOf("confirm_cookie") == -1 ) {
		jQuery("#confirm_modal").css('display', 'block');
		jQuery("#confirm_modal").css('opacity', 1);
		jQuery("#confirm_modal").css('visibility', 'visible');

		jQuery("#confirm_overlay").fadeIn(function()
		{
			jQuery("#confirm_modal").fadeIn();
		});
}
</script>
```

・先頭はCSSレイヤーを用いたダイアログ表示のスタイルシート定義
・その下が、各cookieの取得とYes/Noボタンでの処理とページジャンプ。Yesの場合のみ認証通過のcookie"confirm_cookie"を設定。また、URL保存用のcookieはここで削除しています。
・PHPブロックではダイアログを表示するためのhtmlを記述しています。
・最後のブロックは、認証用cookieが未設定の場合にモーダルダイアログの表示を有効化する部分です。

コードを記入したら、呼びだされる場所を指定します。このコードは認証ページでのみ呼びだすので、
右側のタブの「Page」で認証用ページとして作成した固定ページをチェックしてSaveボタンをクリックします。
![b708667b971a0ab7a88987ecde31174f.png](https://qiita-image-store.s3.amazonaws.com/0/57876/cf796109-02b4-84cb-9d9f-dbf7404434b7.png)

# あとは投稿に「R-18」タグを設定するだけ
事前にダッシュボードの「投稿」メニュー内の「タグ」から**「R-18」タグ**を作成しておきます。
![a0b1e1fb14f838d0903067133181e50a.png](https://qiita-image-store.s3.amazonaws.com/0/57876/94197565-ab2b-8391-5928-510be4c1d076.png)
loop.phpの改造ではタグのスラッグ名ではなく「R-18」の名前で判定しているので、スラッグ名は適当で構いません。

ここまで、終えたらあとはコンテンツを作成してタグ名に「R-18」を加えるだけです。

# 判明している問題点
こうして実装したニッチな機能ですが、現状問題点(未対応?)が1つ。
ブラウザでURLを直接叩いた場合はリファラーが取得できないので、「18歳未満」をクリックした時に戻るページURLのcookieが設定されていないため、ページURLが"undefined"になりサイト上で「ページは存在しません」のエラーページが表示されます(^0^;)

# 1日のやっつけ作業を終えて・・・
1年前の作業をrevisedする形でしたので、CSSの調整とかcookie設定とかは済んでいたのもあり、今回の作業は丸1日で済みました。

私のようなサイトの使い方をする人がどれだけいるのか甚だ疑問ですがw 実践的なノウハウとして残した次第です。

もっと上手い方法を編み出した方、コードへの突っ込みなどありましたらお教え下さい！