-
Notifications
You must be signed in to change notification settings - Fork 101
Issue #819: RFC 8058対応のワンクリックメルマガ登録解除機能 #1316
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 7 commits
5e572b4
c40baa5
bd51b9a
ac98461
b16549a
34e441d
ca3f2fa
40cba44
5047096
2cc3e18
8f53d91
5a7f1e3
121a1a0
8878646
f021870
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| <!--{* | ||
| * This file is part of EC-CUBE | ||
| * | ||
| * Copyright(c) EC-CUBE CO.,LTD. All Rights Reserved. | ||
| * | ||
| * http://www.ec-cube.co.jp/ | ||
| * | ||
| * This program is free software; you can redistribute it and/or | ||
| * modify it under the terms of the GNU General Public License | ||
| * as published by the Free Software Foundation; either version 2 | ||
| * of the License, or (at your option) any later version. | ||
| * | ||
| * This program is distributed in the hope that it will be useful, | ||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| * GNU General Public License for more details. | ||
| * | ||
| * You should have received a copy of the GNU General Public License | ||
| * along with this program; if not, write to the Free Software | ||
| * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
| *}--> | ||
|
|
||
| <div id="undercolumn"> | ||
| <h2 class="title"><!--{$tpl_title|h}--></h2> | ||
| <div id="undercolumn_mailmaga_unsubscribe"> | ||
| <div id="complete_area"> | ||
| <!--{if $tpl_success}--> | ||
| <p class="message success"><!--{$tpl_message|h}--></p> | ||
| <p>今後、メールマガジンは配信されません。</p> | ||
| <div class="btn_area"> | ||
| <ul> | ||
| <li> | ||
| <a href="<!--{$smarty.const.TOP_URL}-->"><img class="hover_change_image" src="<!--{$TPL_URLPATH}-->img/button/btn_toppage.jpg" alt="トップページへ" /></a> | ||
| </li> | ||
| </ul> | ||
| </div> | ||
| <!--{elseif $tpl_message}--> | ||
| <p class="message error"><!--{$tpl_message|h}--></p> | ||
| <div class="btn_area"> | ||
| <ul> | ||
| <li> | ||
| <a href="<!--{$smarty.const.TOP_URL}-->"><img class="hover_change_image" src="<!--{$TPL_URLPATH}-->img/button/btn_toppage.jpg" alt="トップページへ" /></a> | ||
| </li> | ||
| </ul> | ||
| </div> | ||
| <!--{else}--> | ||
| <p class="message">メールアドレス: <strong><!--{$tpl_email|h}--></strong></p> | ||
| <p>メールマガジンの登録を解除しますか?</p> | ||
| <form method="post"> | ||
| <input type="hidden" name="mode" value="confirm" /> | ||
| <div class="btn_area"> | ||
| <ul> | ||
| <li> | ||
| <button type="submit" class="btn">登録を解除する</button> | ||
| </li> | ||
| </ul> | ||
| </div> | ||
| </form> | ||
| <!--{/if}--> | ||
| </div> | ||
| </div> | ||
| </div> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -531,6 +531,16 @@ public static function sfSendMailmagazine($send_id) | |
| $subjectBody = preg_replace('/{name}/', $customerName, $arrMail['subject']); | ||
| $mailBody = preg_replace('/{name}/', $customerName, $arrMail['body']); | ||
|
|
||
| // ワンクリック登録解除トークンの生成 | ||
| $token = SC_Helper_Mailmaga_Ex::generateUnsubscribeToken( | ||
| $arrDestination['customer_id'], | ||
| $send_id, | ||
| $arrDestination['email'] | ||
| ); | ||
|
Comment on lines
+534
to
+539
|
||
|
|
||
| // ワンクリック登録解除URLの生成 | ||
| $unsubscribeUrl = SC_Helper_Mailmaga_Ex::getUnsubscribeUrl($token); | ||
|
|
||
| $objMail->setItem( | ||
| $arrDestination['email'], | ||
| $subjectBody, | ||
|
|
@@ -542,16 +552,23 @@ public static function sfSendMailmagazine($send_id) | |
| $objSite['email04'] // errors_to | ||
| ); | ||
|
|
||
| // RFC 8058 ヘッダーの追加 | ||
| $objMail->addCustomHeader('List-Unsubscribe', '<'.$unsubscribeUrl.'>'); | ||
| $objMail->addCustomHeader('List-Unsubscribe-Post', 'List-Unsubscribe=One-Click'); | ||
|
|
||
| // テキストメール配信の場合 | ||
| if ($arrMail['mail_method'] == 2) { | ||
| $sendResut = $objMail->sendMail(); | ||
| $sendResult = $objMail->sendMail(); | ||
| // HTMLメール配信の場合 | ||
| } else { | ||
| $sendResut = $objMail->sendHtmlMail(); | ||
| $sendResult = $objMail->sendHtmlMail(); | ||
| } | ||
|
|
||
| // カスタムヘッダーをクリア(次の送信のため) | ||
| $objMail->clearCustomHeaders(); | ||
|
|
||
| // 送信完了なら1、失敗なら2をメール送信結果フラグとしてDBに挿入 | ||
| if (!$sendResut) { | ||
| if (!$sendResult) { | ||
| $sendFlag = '2'; | ||
| } else { | ||
| // 完了を 1 増やす | ||
|
|
@@ -584,10 +601,10 @@ public static function sfSendMailmagazine($send_id) | |
|
|
||
| // テキストメール配信の場合 | ||
| if ($arrMail['mail_method'] == 2) { | ||
| $sendResut = $objMail->sendMail(); | ||
| $sendResult = $objMail->sendMail(); | ||
| // HTMLメール配信の場合 | ||
| } else { | ||
| $sendResut = $objMail->sendHtmlMail(); | ||
| $sendResult = $objMail->sendHtmlMail(); | ||
| } | ||
|
|
||
| return; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,173 @@ | ||
| <?php | ||
| /* | ||
| * This file is part of EC-CUBE | ||
| * | ||
| * Copyright(c) EC-CUBE CO.,LTD. All Rights Reserved. | ||
| * | ||
| * http://www.ec-cube.co.jp/ | ||
| * | ||
| * This program is free software; you can redistribute it and/or | ||
| * modify it under the terms of the GNU General Public License | ||
| * as published by the Free Software Foundation; either version 2 | ||
| * of the License, or (at your option) any later version. | ||
| * | ||
| * This program is distributed in the hope that it will be useful, | ||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| * GNU General Public License for more details. | ||
| * | ||
| * You should have received a copy of the GNU General Public License | ||
| * along with this program; if not, write to the Free Software | ||
| * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
| */ | ||
|
|
||
| /** | ||
| * メールマガジン関連のヘルパークラス. | ||
| * | ||
| * RFC 8058 対応のワンクリック登録解除機能を含む | ||
| * | ||
| * @author EC-CUBE CO.,LTD. | ||
| * | ||
| * @version $Id$ | ||
| */ | ||
| class SC_Helper_Mailmaga | ||
| { | ||
| /** トークンの有効期限(日数) */ | ||
| public const TOKEN_EXPIRE_DAYS = 90; | ||
|
|
||
| /** トークンの長さ */ | ||
| public const TOKEN_LENGTH = 64; | ||
|
|
||
| /** | ||
| * ワンクリック登録解除用トークンを生成 | ||
| * | ||
| * @param int $customer_id 会員ID | ||
| * @param int $send_id 配信ID | ||
| * @param string $email メールアドレス | ||
| * | ||
| * @return string トークン文字列 | ||
| */ | ||
| public static function generateUnsubscribeToken($customer_id, $send_id, $email) | ||
| { | ||
| // セキュアなトークン生成 | ||
| $token = SC_Utils_Ex::sfGetRandomString(self::TOKEN_LENGTH); | ||
|
||
|
|
||
| // データベースに保存 | ||
| $objQuery = SC_Query_Ex::getSingletonInstance(); | ||
|
|
||
| $sqlval = []; | ||
| $sqlval['customer_id'] = $customer_id; | ||
| $sqlval['send_id'] = $send_id; | ||
| $sqlval['token'] = $token; | ||
| $sqlval['email'] = $email; | ||
| $sqlval['used_flg'] = 0; | ||
| $sqlval['expire_date'] = date('Y-m-d H:i:s', strtotime('+'.self::TOKEN_EXPIRE_DAYS.' days')); | ||
| $sqlval['create_date'] = date('Y-m-d H:i:s'); | ||
| $sqlval['mailmaga_unsubscribe_token_id'] = $objQuery->nextVal('dtb_mailmaga_unsubscribe_token_mailmaga_unsubscribe_token_id'); | ||
|
|
||
| $objQuery->insert('dtb_mailmaga_unsubscribe_token', $sqlval); | ||
|
|
||
| return $token; | ||
| } | ||
|
|
||
| /** | ||
| * ワンクリック登録解除URLを生成 | ||
| * | ||
| * @param string $token トークン文字列 | ||
| * | ||
| * @return string 完全なURL | ||
| */ | ||
| public static function getUnsubscribeUrl($token) | ||
| { | ||
| return HTTPS_URL.'mailmaga/unsubscribe/index.php?token='.urlencode($token); | ||
| } | ||
|
|
||
| /** | ||
| * トークンの検証と取得 | ||
| * | ||
| * @param string $token トークン文字列 | ||
| * | ||
| * @return array|false トークン情報の配列 or false | ||
| */ | ||
| public static function validateToken($token) | ||
| { | ||
| $objQuery = SC_Query_Ex::getSingletonInstance(); | ||
|
|
||
| $now = date('Y-m-d H:i:s'); | ||
| $where = 'token = ? AND used_flg = 0 AND expire_date > ?'; | ||
| $arrToken = $objQuery->getRow('*', 'dtb_mailmaga_unsubscribe_token', $where, [$token, $now]); | ||
|
|
||
| if (empty($arrToken)) { | ||
| return false; | ||
| } | ||
|
|
||
| return $arrToken; | ||
| } | ||
|
|
||
| /** | ||
| * トークンを使用済みにマーク | ||
| * | ||
| * @param string $token トークン文字列 | ||
| * | ||
| * @return bool 成功した場合 true | ||
| */ | ||
| public static function markTokenAsUsed($token) | ||
| { | ||
| $objQuery = SC_Query_Ex::getSingletonInstance(); | ||
|
|
||
| $sqlval = []; | ||
| $sqlval['used_flg'] = 1; | ||
| $sqlval['used_date'] = date('Y-m-d H:i:s'); | ||
|
|
||
| $ret = $objQuery->update( | ||
| 'dtb_mailmaga_unsubscribe_token', | ||
| $sqlval, | ||
| 'token = ?', | ||
| [$token] | ||
| ); | ||
|
|
||
| return $ret > 0; | ||
| } | ||
|
|
||
| /** | ||
| * メルマガ配信を解除(mailmaga_flg を 3 に設定) | ||
| * | ||
| * @param int $customer_id 会員ID | ||
| * | ||
| * @return bool 成功した場合 true | ||
| */ | ||
| public static function unsubscribeMailmaga($customer_id) | ||
| { | ||
| $objQuery = SC_Query_Ex::getSingletonInstance(); | ||
|
|
||
| $sqlval = []; | ||
| $sqlval['mailmaga_flg'] = 3; // 配信拒否 | ||
| $sqlval['update_date'] = date('Y-m-d H:i:s'); | ||
|
|
||
| $ret = $objQuery->update( | ||
| 'dtb_customer', | ||
| $sqlval, | ||
| 'customer_id = ?', | ||
| [$customer_id] | ||
| ); | ||
|
|
||
| return $ret > 0; | ||
| } | ||
|
|
||
| /** | ||
| * 期限切れトークンのクリーンアップ | ||
| * (バッチ処理として定期実行を想定) | ||
| * | ||
| * @return int 削除件数 | ||
| */ | ||
| public static function cleanupExpiredTokens() | ||
| { | ||
| $objQuery = SC_Query_Ex::getSingletonInstance(); | ||
|
|
||
| $now = date('Y-m-d H:i:s'); | ||
| $where = 'expire_date < ? OR used_flg = 1'; | ||
| $ret = $objQuery->delete('dtb_mailmaga_unsubscribe_token', $where, [$now]); | ||
|
|
||
| return $ret; | ||
| } | ||
nobuhiko marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.