Contact Form 7で入力値を加工した文字列を自動返信メールに付与する

WordPressのContact Form 7で、例えばログインのための仮パスワードを自動返信メールにくっつけたいみたいなことありますよね。
そういうときに例えばhiddenなフィールドにJSでランダム文字列を生成しておいてそれを使うみたいなことをやろうとすると、

- その文字列をスプレッドシートなりデータベースなりに覚えとく必要がある
- 一定期間後に消すみたいな操作をしないと無限に溜まっていく
- ログイン時に照合をしなきゃいけないのが面倒
- クライアント側に文字列の生成方法が見えてしまうのが気持ち悪い

というのがなんだかなあ、、、と思い、

今回はそれを回避するために、例えばメールアドレスのような、アカウントに固有の情報のハッシュ値を用いる方法を紹介します。
想定しているフローは以下です。

1. メールアドレスのみで仮登録 -> 本登録用URLがメールで返信される
1. 本登録用URLで追加の情報（例えばアカウント名）を入力してもらう

（門外漢なので他にベストプラクティスがあったら教えて欲しいです）
:snail: :snail::snail:

```:仮登録フォーム(ContactForm7)
メールアドレス:
[email* email]
[submit "仮登録する"]
```
:snail::snail::snail:

```:仮登録への返信メール本文(ContactForm7)
仮登録フォームからの自動返信メールです
本登録URLはこちら
https://example.com/application?secret-id=[secret-id]
※URL流出時のリスクが増えるが、ユーザー的にはemailも自動で入力されていてくれた方が嬉しいかもしれない
 -> https://example.com/application?secret-id=[secret-id]&email=[email]
```
:snail::snail::snail:

```php:function.php
// ハッシュ生成関数
// $srcに応じて一意に決まるものならなんでも良い
function wpcf7_make_hash($src) {
	// 単にハッシュするだけだと推測されるかもしれないので適当に何か固定のデータをくっつけとく
	return sha1("some-unguessable-string".$src);
}
// 仮登録用メール送信前フック
add_action("wpcf7_before_send_mail", "wpcf7_replace_secret_id");
function wpcf7_replace_secret_id($WPCF7_ContactForm) {
	// もしフォーム毎に処理を変えたいならid()をチェック
	if(your-form-id == $WPCF7_ContactForm->id()) {
		$wpcf7 = WPCF7_ContactForm::get_current();
		$submission = WPCF7_Submission::get_instance();
		if ($submission) {
			$data = $submission->get_posted_data();
			$secretID = wpcf7_make_hash($data['email']);

			$mail = $wpcf7->prop('mail'); // もし「メール2」の方を使うなら$mail = $wpcf7->prop('mail_2');
			// メール本文中の[secret-id]を$secretIDで置換
			$mail['body'] = str_replace('[secret-id]', $secretID, $mail['body']);
			$wpcf7->set_properties(array(
				"mail" => $mail // "mail_2" => $mail
			));
			return $wpcf7;
		}
	}
}
```
:snail::snail::snail:

```:本登録フォーム(ContactForm7)
[hidden secret-id default:get readonly] // ここにURLクエリパラメータのsecret-idが入力される
アカウント名:
[text* account-name]
メールアドレス:
[email* email]
; [email* email default:get] // URLクエリパラメータにemailというデータがあれば自動入力される
; [hidden* email default:get] // 自動入力するなら、場合によってはhiddenでも良いかもしれない
[submit "本登録する"]
```
:snail::snail::snail:

```php:function.php
// 本登録用バリデーション
add_filter( 'wpcf7_validate', 'wpcf7_validation_login_with_tmp_secret_id', 11, 2 );
function wpcf7_validation_login_with_tmp_secret_id( $result, $tags ) {
	// secret-idという値が存在しないなら関係ないフォーム
	if(is_null($_POST['secret-id'])) {
		return $result;
	}
	foreach( $tags as $tag ) {
		$name = $tag['name'];
		if($name == 'email') {
			// メールアドレスからハッシュを再生成して一致をチェック
			if(wpcf7_make_hash($_POST[$name]) != $_POST['secret-id']) {
				$result->invalidate( $tag, '仮登録の返信メールに記載のURL以外からアクセスしているか、またはメールアドレスが間違っています。');
			}
			// 実用的には、登録済みメールアドレスかどうかのチェックもこのあたりに入れておきたい
		}
	}
	return $result;
}
```

### 参考にしたページ
ハッシュ文字列の生成 : https://www.php.net/manual/ja/function.sha1.php
メール本文の一部置換 : https://www.366service.com/jp/qa/580a78a79e16c3193f02179822fb799d
メール2はmail_2 : https://www.reddit.com/r/Wordpress/comments/3hljb8/contact_form_7_modify_mail_of_second_recipient/
URLパラメータをフォーム項目に自動入力 : https://se.ekaki-j.com/wp-contact-form-7-get-parameter/
フォームデータのカスタムバリデーション : https://qiita.com/kd9951/items/1717fc384c16e00d6458