diff --git a/app/Enums/SpamBlockType.php b/app/Enums/SpamBlockType.php new file mode 100644 index 000000000..f7946ce8e --- /dev/null +++ b/app/Enums/SpamBlockType.php @@ -0,0 +1,26 @@ + + * @copyright OpenSource-WorkShop Co.,Ltd. All Rights Reserved + * @category スパム管理 + * @package Enum + */ +class SpamBlockType extends EnumsBase +{ + // 定数メンバ + const email = 'email'; + const domain = 'domain'; + const ip_address = 'ip_address'; + + // key/valueの連想配列 + const enum = [ + self::email => 'メールアドレス', + self::domain => 'ドメイン', + self::ip_address => 'IPアドレス', + ]; +} diff --git a/app/Models/Common/SpamList.php b/app/Models/Common/SpamList.php new file mode 100644 index 000000000..18550f76c --- /dev/null +++ b/app/Models/Common/SpamList.php @@ -0,0 +1,101 @@ + + * @copyright OpenSource-WorkShop Co.,Ltd. All Rights Reserved + * @category スパム管理 + * @package Model + */ +class SpamList extends Model +{ + // 保存時のユーザー関連データの保持(履歴なしUserable) + use HasFactory; + use UserableNohistory; + use SoftDeletes; + + /** + * create()やupdate()で入力を受け付ける ホワイトリスト + */ + protected $fillable = [ + 'target_plugin_name', + 'target_id', + 'block_type', + 'block_value', + 'memo', + ]; + + /** + * フォームプラグイン用のスパムリストを取得 + * + * @param int|null $forms_id フォームID(nullの場合は全体のみ) + * @return \Illuminate\Database\Eloquent\Collection + */ + public static function getFormsSpamLists($forms_id = null) + { + $query = self::where('target_plugin_name', 'forms'); + + if ($forms_id) { + $query->where(function ($q) use ($forms_id) { + $q->where('target_id', $forms_id) + ->orWhereNull('target_id'); + }); + } else { + $query->whereNull('target_id'); + } + + return $query->orderBy('block_type') + ->orderBy('created_at', 'desc') + ->get(); + } + + /** + * 全体適用かどうか + * + * @return bool + */ + public function isGlobalScope() + { + return is_null($this->target_id); + } + + /** + * 重複チェック付きでスパムリストに追加 + * + * @param string $target_plugin_name 対象プラグイン名 + * @param int|null $target_id 対象ID(nullの場合は全体適用) + * @param string $block_type ブロック種別 + * @param string $block_value ブロック対象の値 + * @param string|null $memo メモ + * @return bool 追加成功時true、重複時false + */ + public static function addIfNotExists($target_plugin_name, $target_id, $block_type, $block_value, $memo = null) + { + $exists = self::where('target_plugin_name', $target_plugin_name) + ->where('target_id', $target_id) + ->where('block_type', $block_type) + ->where('block_value', $block_value) + ->exists(); + + if ($exists) { + return false; + } + + self::create([ + 'target_plugin_name' => $target_plugin_name, + 'target_id' => $target_id, + 'block_type' => $block_type, + 'block_value' => $block_value, + 'memo' => $memo, + ]); + return true; + } +} diff --git a/app/Models/User/Forms/Forms.php b/app/Models/User/Forms/Forms.php index 6735fa456..31293ec81 100644 --- a/app/Models/User/Forms/Forms.php +++ b/app/Models/User/Forms/Forms.php @@ -2,13 +2,14 @@ namespace App\Models\User\Forms; -use Illuminate\Database\Eloquent\Model; - use App\UserableNohistory; +use Illuminate\Database\Eloquent\Factories\HasFactory; +use Illuminate\Database\Eloquent\Model; class Forms extends Model { // 保存時のユーザー関連データの保持(履歴なしUserable) + use HasFactory; use UserableNohistory; /** @@ -45,7 +46,9 @@ class Forms extends Model 'data_save_flag', 'after_message', 'numbering_use_flag', - 'numbering_prefix' + 'numbering_prefix', + 'use_spam_filter_flag', + 'spam_filter_message', ]; /** diff --git a/app/Models/User/Forms/FormsColumns.php b/app/Models/User/Forms/FormsColumns.php index f72038b4f..7b27b806d 100644 --- a/app/Models/User/Forms/FormsColumns.php +++ b/app/Models/User/Forms/FormsColumns.php @@ -2,13 +2,14 @@ namespace App\Models\User\Forms; -use Illuminate\Database\Eloquent\Model; - use App\UserableNohistory; +use Illuminate\Database\Eloquent\Factories\HasFactory; +use Illuminate\Database\Eloquent\Model; class FormsColumns extends Model { // 保存時のユーザー関連データの保持(履歴なしUserable) + use HasFactory; use UserableNohistory; // 更新する項目の定義 diff --git a/app/Models/User/Forms/FormsInputCols.php b/app/Models/User/Forms/FormsInputCols.php index 9c75668b7..a784f5579 100644 --- a/app/Models/User/Forms/FormsInputCols.php +++ b/app/Models/User/Forms/FormsInputCols.php @@ -2,13 +2,14 @@ namespace App\Models\User\Forms; -use Illuminate\Database\Eloquent\Model; - use App\UserableNohistory; +use Illuminate\Database\Eloquent\Factories\HasFactory; +use Illuminate\Database\Eloquent\Model; class FormsInputCols extends Model { // 保存時のユーザー関連データの保持(履歴なしUserable) + use HasFactory; use UserableNohistory; // 更新する項目の定義 diff --git a/app/Models/User/Forms/FormsInputs.php b/app/Models/User/Forms/FormsInputs.php index bd32b962a..b5e09e239 100644 --- a/app/Models/User/Forms/FormsInputs.php +++ b/app/Models/User/Forms/FormsInputs.php @@ -2,13 +2,14 @@ namespace App\Models\User\Forms; -use Illuminate\Database\Eloquent\Model; - use App\UserableNohistory; +use Illuminate\Database\Eloquent\Factories\HasFactory; +use Illuminate\Database\Eloquent\Model; class FormsInputs extends Model { // 保存時のユーザー関連データの保持(履歴なしUserable) + use HasFactory; use UserableNohistory; // 更新する項目の定義 diff --git a/app/Plugins/Manage/SpamManage/SpamManage.php b/app/Plugins/Manage/SpamManage/SpamManage.php new file mode 100644 index 000000000..d8390120b --- /dev/null +++ b/app/Plugins/Manage/SpamManage/SpamManage.php @@ -0,0 +1,308 @@ + + * @copyright OpenSource-WorkShop Co.,Ltd. All Rights Reserved + * @category スパム管理 + * @package Controller + * @plugin_title スパム管理 + * @plugin_desc スパムリストに関する機能が集まった管理機能です。 + */ +class SpamManage extends ManagePluginBase +{ + /** + * 権限定義 + */ + public function declareRole() + { + // 権限チェックテーブル + $role_check_table = array(); + $role_check_table["index"] = array('admin_site'); + $role_check_table["store"] = array('admin_site'); + $role_check_table["edit"] = array('admin_site'); + $role_check_table["update"] = array('admin_site'); + $role_check_table["destroy"] = array('admin_site'); + $role_check_table["downloadCsv"] = array('admin_site'); + return $role_check_table; + } + + /** + * スパムリスト一覧表示 + * + * @return view + * @method_title スパムリスト一覧 + * @method_desc スパムリストを一覧で確認できます。 + * @method_detail メールアドレス、ドメイン、IPアドレスを登録してスパムをブロックできます。 + */ + public function index($request) + { + // ページネートの表示ページを取得 + $page = $this->getPaginatePageFromRequestOrSession($request, 'spam_list_page', 'page'); + + // 検索条件を取得 + $search_block_type = $request->input('search_block_type', ''); + $search_block_value = $request->input('search_block_value', ''); + $search_scope_type = $request->input('search_scope_type', ''); + $search_memo = $request->input('search_memo', ''); + + // スパムリストを取得(検索条件適用) + $query = SpamList::query(); + $query = $this->applySearchConditions($query, $request); + + $spam_lists = $query->orderBy('block_type') + ->orderBy('created_at', 'desc') + ->paginate(20, ['*'], 'page', $page) + ->appends($request->except('page')); + + // フォーム一覧を取得(ID連想配列) + $forms = Forms::orderBy('forms_name')->get()->keyBy('id'); + + // 画面の呼び出し + return view('plugins.manage.spam.index', [ + "function" => __FUNCTION__, + "plugin_name" => "spam", + "spam_lists" => $spam_lists, + "forms" => $forms, + "search_block_type" => $search_block_type, + "search_block_value" => $search_block_value, + "search_scope_type" => $search_scope_type, + "search_memo" => $search_memo, + ]); + } + + /** + * スパムリスト追加処理 + */ + public function store($request) + { + // httpメソッド確認 + if (!$request->isMethod('post')) { + abort(403, '権限がありません。'); + } + + // 項目のエラーチェック + $validator = Validator::make($request->all(), [ + 'block_type' => ['required', 'in:' . implode(',', SpamBlockType::getMemberKeys())], + 'block_value' => ['required', 'max:255'], + 'target_forms_id' => ['required_if:scope_type,form'], + ], [ + 'target_forms_id.required_if' => '適用範囲で特定フォームを選択した場合、フォームを選択してください。', + ]); + $validator->setAttributeNames([ + 'block_type' => '種別', + 'block_value' => '値', + 'target_forms_id' => 'フォーム', + ]); + + // エラーがあった場合は入力画面に戻る。 + if ($validator->fails()) { + return redirect('manage/spam') + ->withErrors($validator) + ->withInput(); + } + + // 適用範囲の処理 + $target_id = null; + if ($request->scope_type === 'form' && $request->filled('target_forms_id')) { + $target_id = $request->target_forms_id; + } + + // スパムリストの追加 + SpamList::create([ + 'target_plugin_name' => 'forms', + 'target_id' => $target_id, + 'block_type' => $request->block_type, + 'block_value' => $request->block_value, + 'memo' => $request->memo, + ]); + + // スパムリスト一覧画面に戻る + return redirect("/manage/spam")->with('flash_message', 'スパムリストに追加しました。'); + } + + /** + * スパムリスト編集画面 + * + * @return view + * @method_title スパムリスト編集 + * @method_desc スパムリストを編集できます。 + * @method_detail + */ + public function edit($request, $id) + { + // スパムリストデータの呼び出し + $spam = SpamList::findOrFail($id); + + // フォーム一覧を取得(ID連想配列) + $forms = Forms::orderBy('forms_name')->get()->keyBy('id'); + + // 画面の呼び出し + return view('plugins.manage.spam.edit', [ + "function" => __FUNCTION__, + "plugin_name" => "spam", + "spam" => $spam, + "forms" => $forms, + ]); + } + + /** + * スパムリスト更新処理 + */ + public function update($request, $id) + { + // httpメソッド確認 + if (!$request->isMethod('post')) { + abort(403, '権限がありません。'); + } + + // 項目のエラーチェック + $validator = Validator::make($request->all(), [ + 'block_value' => ['required', 'max:255'], + 'target_forms_id' => ['required_if:scope_type,form'], + ], [ + 'target_forms_id.required_if' => '適用範囲で特定フォームを選択した場合、フォームを選択してください。', + ]); + $validator->setAttributeNames([ + 'block_value' => '値', + 'target_forms_id' => 'フォーム', + ]); + + // エラーがあった場合は入力画面に戻る。 + if ($validator->fails()) { + return redirect('manage/spam/edit/' . $id) + ->withErrors($validator) + ->withInput(); + } + + // スパムリストデータの呼び出し + $spam = SpamList::findOrFail($id); + + // 適用範囲の処理 + $target_id = null; + if ($request->scope_type === 'form' && $request->filled('target_forms_id')) { + $target_id = $request->target_forms_id; + } + + // 更新 + $spam->target_id = $target_id; + $spam->block_value = $request->block_value; + $spam->memo = $request->memo; + $spam->save(); + + // スパムリスト一覧画面に戻る + return redirect("/manage/spam")->with('flash_message', 'スパムリストを更新しました。'); + } + + /** + * スパムリスト削除処理 + */ + public function destroy($request, $id) + { + // httpメソッド確認 + if (!$request->isMethod('post')) { + abort(403, '権限がありません。'); + } + + // 削除 + SpamList::where('id', $id)->delete(); + + // スパムリスト一覧画面に戻る + return redirect("/manage/spam")->with('flash_message', 'スパムリストから削除しました。'); + } + + /** + * CSVダウンロード + */ + public function downloadCsv($request) + { + // スパムリストを取得(検索条件適用) + $query = SpamList::query(); + $query = $this->applySearchConditions($query, $request); + + $spam_lists = $query->orderBy('block_type') + ->orderBy('created_at', 'desc') + ->get(); + + // フォーム一覧を取得 + $forms = Forms::pluck('forms_name', 'id'); + + // CSVデータの作成 + $csv_data = ''; + + // ヘッダー行 + $csv_data .= '"種別","値","適用範囲","メモ","登録日時"' . "\n"; + + // データ行 + foreach ($spam_lists as $spam) { + $scope_name = is_null($spam->target_id) ? '全体' : ($forms[$spam->target_id] ?? '不明'); + $csv_data .= '"' . SpamBlockType::getDescription($spam->block_type) . '",'; + $csv_data .= '"' . str_replace('"', '""', $spam->block_value) . '",'; + $csv_data .= '"' . $scope_name . '",'; + $csv_data .= '"' . str_replace('"', '""', $spam->memo ?? '') . '",'; + $csv_data .= '"' . $spam->created_at . '"' . "\n"; + } + + // 文字コード変換(UTF-8 BOM付き) + $csv_data = "\xEF\xBB\xBF" . $csv_data; + + // ファイル名 + $filename = 'spam_list_' . date('Ymd_His') . '.csv'; + + // レスポンス + return response($csv_data) + ->header('Content-Type', 'text/csv') + ->header('Content-Disposition', 'attachment; filename="' . $filename . '"'); + } + + /** + * 検索条件をクエリに適用 + * + * @param \Illuminate\Database\Eloquent\Builder $query クエリビルダー + * @param \Illuminate\Http\Request $request リクエスト + * @return \Illuminate\Database\Eloquent\Builder + */ + private function applySearchConditions($query, $request) + { + // 種別 + $search_block_type = $request->input('search_block_type', ''); + if (!empty($search_block_type)) { + $query->where('block_type', $search_block_type); + } + + // 値(部分一致) + $search_block_value = $request->input('search_block_value', ''); + if (!empty($search_block_value)) { + $query->where('block_value', 'like', '%' . $search_block_value . '%'); + } + + // 適用範囲 + $search_scope_type = $request->input('search_scope_type', ''); + if ($search_scope_type === 'global') { + $query->whereNull('target_id'); + } elseif ($search_scope_type === 'form') { + $query->whereNotNull('target_id'); + } + + // メモ(部分一致) + $search_memo = $request->input('search_memo', ''); + if (!empty($search_memo)) { + $query->where('memo', 'like', '%' . $search_memo . '%'); + } + + return $query; + } +} diff --git a/app/Plugins/User/Forms/FormsPlugin.php b/app/Plugins/User/Forms/FormsPlugin.php index a007aa559..0182845a9 100644 --- a/app/Plugins/User/Forms/FormsPlugin.php +++ b/app/Plugins/User/Forms/FormsPlugin.php @@ -20,6 +20,7 @@ use App\Models\User\Forms\FormsColumnsSelects; use App\Models\User\Forms\FormsInputs; use App\Models\User\Forms\FormsInputCols; +use App\Models\Common\SpamList; use App\Rules\CustomValiAlphaNumForMultiByte; use App\Rules\CustomValiCheckWidthForString; @@ -42,6 +43,7 @@ use App\Enums\FormsRegisterTargetPlugin; use App\Enums\FormStatusType; use App\Enums\PluginName; +use App\Enums\SpamBlockType; use App\Enums\Required; use App\Enums\StatusType; use App\Models\User\Bbses\Bbs; @@ -99,6 +101,7 @@ public function getPublicFunctions() 'listInputs', 'editInput', 'thanks', + 'editSpamFilter', ]; $functions['post'] = [ 'index', @@ -115,6 +118,10 @@ public function getPublicFunctions() 'registerOtherPlugins', 'updateSelectSequenceAll', 'updateColumnSequenceAll', + 'saveSpamFilter', + 'addSpamList', + 'deleteSpamList', + 'addToSpamListFromInput', ]; return $functions; } @@ -140,6 +147,11 @@ public function declareRole() $role_check_table["registerOtherPlugins"] = ['role_article']; $role_check_table['updateSelectSequenceAll'] = ['buckets.upColumnSequence', 'buckets.downColumnSequence']; $role_check_table['updateColumnSequenceAll'] = ['buckets.upColumnSequence', 'buckets.downColumnSequence']; + $role_check_table['editSpamFilter'] = ['frames.edit']; + $role_check_table['saveSpamFilter'] = ['frames.create']; + $role_check_table['addSpamList'] = ['frames.create']; + $role_check_table['deleteSpamList'] = ['frames.delete']; + $role_check_table['addToSpamListFromInput'] = ['frames.create']; return $role_check_table; } @@ -335,6 +347,15 @@ public function index($request, $page_id, $frame_id, $errors = null) return false; } + // スパムブロックエラーメッセージの確認 + $spam_blocked_error = session("spam_blocked_error_{$frame_id}"); + if ($spam_blocked_error) { + // エラー画面へ + return $this->commonView('error_messages', [ + 'error_messages' => [$spam_blocked_error], + ]); + } + // 登録期間外か if ($this->isOutOfTermRegist($form)) { // エラー画面へ @@ -800,6 +821,23 @@ public function publicConfirm($request, $page_id, $frame_id, $id = null) ]); } + // スパムフィルタリングチェック + $spam_check = $this->checkSpamFilter($request, $form); + if ($spam_check['blocked']) { + // スパムブロックのログ記録 + Log::info('Spam blocked', [ + 'form_id' => $form->id, + 'ip' => $spam_check['client_ip'], + 'block_type' => $spam_check['matched_spam_list']->block_type ?? null, + 'matched_rule' => $spam_check['matched_spam_list']->id ?? null, + ]); + + $spam_message = $form->spam_filter_message ?: '入力されたメールアドレス、または、IPアドレスからの送信は現在制限されています。'; + return $this->commonView('error_messages', [ + 'error_messages' => [$spam_message], + ]); + } + // フォームのカラムデータ $forms_columns = $this->getFormsColumns($form); @@ -989,6 +1027,25 @@ public function publicStore($request, $page_id, $frame_id, $id = null) return collect(['redirect_path' => url($this->page->permanent_link)]); } + // スパムフィルタリングチェック(二重防御) + $spam_check = $this->checkSpamFilter($request, $form); + if ($spam_check['blocked']) { + // スパムブロックのログ記録 + Log::info('Spam blocked', [ + 'form_id' => $form->id, + 'ip' => $spam_check['client_ip'], + 'block_type' => $spam_check['matched_spam_list']->block_type ?? null, + 'matched_rule' => $spam_check['matched_spam_list']->id ?? null, + ]); + + // エラーメッセージをセッションに保存 + $spam_message = $form->spam_filter_message ?: '入力されたメールアドレス、または、IPアドレスからの送信は現在制限されています。'; + session()->flash("spam_blocked_error_{$frame_id}", $spam_message); + + // 初期表示にリダイレクトして、初期表示処理にまかせる(エラー表示) + return collect(['redirect_path' => url($this->page->permanent_link)]); + } + // forms_inputs 登録 $forms_inputs = new FormsInputs(); $forms_inputs->forms_id = $form->id; @@ -1015,6 +1072,11 @@ public function publicStore($request, $page_id, $frame_id, $id = null) } } + // IPアドレスを記録(スパムフィルタリングが有効な場合のみ) + if ($form->use_spam_filter_flag) { + $forms_inputs->ip_address = $request->ip(); + } + $forms_inputs->save(); // フォームのカラムデータ @@ -2741,6 +2803,21 @@ public function listInputs($request, $page_id, $frame_id, $forms_id = null) ->orderBy('forms_inputs_id', 'asc')->orderBy('forms_columns_id', 'asc') ->get(); + // メールアドレス型カラムのID取得 + $email_column_ids = $columns->where('column_type', FormColumnType::mail)->pluck('id')->toArray(); + + // 各投稿のメールアドレス有無マップを作成 + $has_email_map = []; + foreach ($inputs as $input) { + $has_email = $input_cols->where('forms_inputs_id', $input->id) + ->whereIn('forms_columns_id', $email_column_ids) + ->filter(function ($col) { + return !empty($col->value); + }) + ->isNotEmpty(); + $has_email_map[$input->id] = $has_email; + } + // bucktsで開いていたページの保持 // $frame_page = "frame_{$frame_id}_buckets_page"; @@ -2750,6 +2827,7 @@ public function listInputs($request, $page_id, $frame_id, $forms_id = null) 'columns' => $columns, 'inputs' => $inputs, 'input_cols' => $input_cols, + 'has_email_map' => $has_email_map, ]); } @@ -3111,4 +3189,357 @@ public static function canDownload($request, Uploads $upload): array return [false, '対象ファイルに対する権限なし']; } } + + /** + * スパムフィルタリングチェック + * + * @param \Illuminate\Http\Request $request リクエスト + * @param Forms $form フォームデータ + * @return array ブロック情報の配列 + */ + private function checkSpamFilter($request, $form): array + { + $client_ip = $request->ip(); + + // スパムフィルタリングが無効なら早期リターン + if (!$form->use_spam_filter_flag) { + return [ + 'blocked' => false, + 'matched_spam_list' => null, + 'client_ip' => $client_ip, + 'email' => null, + ]; + } + + // 取得対象のスパムリスト(このフォーム用 + サイト全体用) + $spam_lists = SpamList::getFormsSpamLists($form->id); + + if ($spam_lists->isEmpty()) { + return [ + 'blocked' => false, + 'matched_spam_list' => null, + 'client_ip' => $client_ip, + 'email' => null, + ]; + } + + // IPアドレスチェック + $matched_spam_list = $spam_lists->where('block_type', SpamBlockType::ip_address) + ->where('block_value', $client_ip) + ->first(); + if ($matched_spam_list) { + return [ + 'blocked' => true, + 'matched_spam_list' => $matched_spam_list, + 'client_ip' => $client_ip, + 'email' => null, + ]; + } + + // メールアドレス・ドメインチェック(メール型カラムの値を取得) + $email_columns = FormsColumns::where('forms_id', $form->id) + ->where('column_type', FormColumnType::mail) + ->pluck('id'); + + foreach ($email_columns as $column_id) { + $email = $request->forms_columns_value[$column_id] ?? null; + if (empty($email)) { + continue; + } + + // メールアドレス完全一致チェック + $matched_spam_list = $spam_lists->where('block_type', SpamBlockType::email) + ->where('block_value', $email) + ->first(); + if ($matched_spam_list) { + return [ + 'blocked' => true, + 'matched_spam_list' => $matched_spam_list, + 'client_ip' => $client_ip, + 'email' => $email, + ]; + } + + // ドメインチェック + $domain = substr(strrchr($email, "@"), 1); + if ($domain) { + $matched_spam_list = $spam_lists->where('block_type', SpamBlockType::domain) + ->where('block_value', $domain) + ->first(); + if ($matched_spam_list) { + return [ + 'blocked' => true, + 'matched_spam_list' => $matched_spam_list, + 'client_ip' => $client_ip, + 'email' => $email, + ]; + } + } + } + + return [ + 'blocked' => false, + 'matched_spam_list' => null, + 'client_ip' => $client_ip, + 'email' => null, + ]; + } + + /** + * スパムフィルタリング設定画面 + * + * @method_title スパムフィルタリング + * @method_desc スパムフィルタリングの設定ができます。 + * @method_detail スパムリストの管理や、ブロック時のメッセージを設定できます。 + */ + public function editSpamFilter($request, $page_id, $frame_id) + { + // フォーム&フレームデータ + $form_frame = $this->getFormFrame($frame_id); + + // フォームデータ + $form = null; + if (!empty($form_frame->bucket_id)) { + $form = Forms::where('bucket_id', $form_frame->bucket_id)->first(); + } + + if (empty($form)) { + // ワーニング画面へ + return $this->view('forms_edit_warning_messages', [ + 'warning_messages' => ["フォーム選択から選択するか、フォーム作成で作成してください。"], + ]); + } + + // このフォームに適用されるスパムリスト(フォーム固有 + サイト全体) + $spam_lists = SpamList::getFormsSpamLists($form->id); + + // 表示テンプレートを呼び出す + return $this->view('forms_edit_spam_filter', [ + 'form' => $form, + 'spam_lists' => $spam_lists, + ]); + } + + /** + * スパムフィルタリング設定の保存 + */ + public function saveSpamFilter($request, $page_id, $frame_id, $forms_id) + { + // フォームデータ取得 + $form = Forms::find($forms_id); + if (empty($form)) { + abort(404, 'フォームが見つかりません。'); + } + + // 更新 + $form->use_spam_filter_flag = $request->input('use_spam_filter_flag', 0); + $form->spam_filter_message = $request->spam_filter_message; + $form->save(); + + // リダイレクト + return redirect("/plugin/forms/editSpamFilter/{$page_id}/{$frame_id}#frame-{$frame_id}") + ->with('flash_message', 'スパムフィルタリング設定を保存しました。'); + } + + /** + * スパムリストへの追加 + */ + public function addSpamList($request, $page_id, $frame_id, $forms_id) + { + // 項目のエラーチェック + $validator = Validator::make($request->all(), [ + 'block_type' => ['required', 'in:' . implode(',', SpamBlockType::getMemberKeys())], + 'block_value' => ['required', 'max:255'], + ]); + $validator->setAttributeNames([ + 'block_type' => '種別', + 'block_value' => '値', + ]); + + // エラーがあった場合は入力画面に戻る + if ($validator->fails()) { + return redirect("/plugin/forms/editSpamFilter/{$page_id}/{$frame_id}#frame-{$frame_id}") + ->withErrors($validator) + ->withInput(); + } + + // スパムリストの追加 + SpamList::create([ + 'target_plugin_name' => 'forms', + 'target_id' => $forms_id, + 'block_type' => $request->block_type, + 'block_value' => $request->block_value, + 'memo' => $request->memo, + ]); + + // リダイレクト + return redirect("/plugin/forms/editSpamFilter/{$page_id}/{$frame_id}#frame-{$frame_id}") + ->with('flash_message', 'スパムリストに追加しました。'); + } + + /** + * スパムリストからの削除 + */ + public function deleteSpamList($request, $page_id, $frame_id, $spam_id) + { + // スパムリストデータの取得 + $spam = SpamList::find($spam_id); + + // このフォーム専用のスパムリストのみ削除可能 + if ($spam && !$spam->isGlobalScope()) { + $spam->delete(); + } + + // リダイレクト + return redirect("/plugin/forms/editSpamFilter/{$page_id}/{$frame_id}#frame-{$frame_id}") + ->with('flash_message', 'スパムリストから削除しました。'); + } + + /** + * メールアドレス型カラムの入力値を取得 + * + * @param int $forms_id フォームID + * @param int $inputs_id 投稿ID + * @return \Illuminate\Database\Eloquent\Collection + */ + private function getEmailInputCols($forms_id, $inputs_id) + { + $email_columns = FormsColumns::where('forms_id', $forms_id) + ->where('column_type', FormColumnType::mail) + ->pluck('id'); + + return FormsInputCols::where('forms_inputs_id', $inputs_id) + ->whereIn('forms_columns_id', $email_columns) + ->get(); + } + + /** + * スパムリストへの追加(重複チェック付き) + * + * @param int|null $target_id 対象ID(nullの場合は全体適用) + * @param string $block_type ブロック種別 + * @param string $block_value ブロック対象の値 + * @param string|null $memo メモ + * @return string 'added'(追加成功)または'duplicate'(重複のためスキップ) + */ + private function addToSpamListWithDuplicateCheck($target_id, $block_type, $block_value, $memo) + { + $added = SpamList::addIfNotExists('forms', $target_id, $block_type, $block_value, $memo); + return $added ? 'added' : 'duplicate'; + } + + /** + * 投稿一覧からスパムリストへ追加 + */ + public function addToSpamListFromInput($request, $page_id, $frame_id, $inputs_id) + { + // 投稿データの取得 + $input = FormsInputs::find($inputs_id); + if (empty($input)) { + abort(404, '投稿データが見つかりません。'); + } + + $forms_id = $input->forms_id; + $added_count = 0; + $skipped_duplicate_count = 0; + $skipped_no_data_count = 0; + + // メモ + $memo = $request->input('memo'); + + // チェックボックスが1つも選択されていない場合 + $has_selection = $request->filled('add_ip_address') || $request->filled('add_email') || $request->filled('add_domain'); + if (!$has_selection) { + return redirect("/plugin/forms/listInputs/{$page_id}/{$frame_id}/{$forms_id}#frame-{$frame_id}") + ->with('flash_message', '追加する項目(IPアドレス、メールアドレス、ドメイン)を1つ以上選択してください。'); + } + + // 適用範囲の決定 + $scope_type = $request->input('scope_type', 'form'); + $target_id = ($scope_type === 'global') ? null : $forms_id; + + // IPアドレスをスパムリストに追加 + if ($request->filled('add_ip_address')) { + if (empty($input->ip_address)) { + $skipped_no_data_count++; + } else { + $result = $this->addToSpamListWithDuplicateCheck($target_id, SpamBlockType::ip_address, $input->ip_address, $memo); + if ($result === 'duplicate') { + $skipped_duplicate_count++; + } else { + $added_count++; + } + } + } + + // メールアドレスをスパムリストに追加 + if ($request->filled('add_email')) { + $input_cols = $this->getEmailInputCols($forms_id, $inputs_id); + + $has_email_value = false; + foreach ($input_cols as $col) { + if (empty($col->value)) { + continue; + } + $has_email_value = true; + + $result = $this->addToSpamListWithDuplicateCheck($target_id, SpamBlockType::email, $col->value, $memo); + if ($result === 'duplicate') { + $skipped_duplicate_count++; + } else { + $added_count++; + } + } + if (!$has_email_value) { + $skipped_no_data_count++; + } + } + + // ドメインをスパムリストに追加 + if ($request->filled('add_domain')) { + $input_cols = $this->getEmailInputCols($forms_id, $inputs_id); + + $has_domain_value = false; + foreach ($input_cols as $col) { + if (empty($col->value)) { + continue; + } + + $domain = substr(strrchr($col->value, "@"), 1); + if (empty($domain)) { + continue; + } + $has_domain_value = true; + + $result = $this->addToSpamListWithDuplicateCheck($target_id, SpamBlockType::domain, $domain, $memo); + if ($result === 'duplicate') { + $skipped_duplicate_count++; + } else { + $added_count++; + } + } + if (!$has_domain_value) { + $skipped_no_data_count++; + } + } + + // メッセージの組み立て + $messages = []; + if ($added_count > 0) { + $messages[] = "{$added_count}件をスパムリストに追加しました。"; + } + if ($skipped_duplicate_count > 0) { + $messages[] = "{$skipped_duplicate_count}件は既に登録済みのためスキップしました。"; + } + if ($skipped_no_data_count > 0) { + $messages[] = "{$skipped_no_data_count}件は該当データがないためスキップしました。"; + } + if (empty($messages)) { + $messages[] = 'スパムリストへの追加はありませんでした。'; + } + + // リダイレクト + return redirect("/plugin/forms/listInputs/{$page_id}/{$frame_id}/{$forms_id}#frame-{$frame_id}") + ->with('flash_message', implode(' ', $messages)); + } } diff --git a/database/factories/Common/SpamListFactory.php b/database/factories/Common/SpamListFactory.php new file mode 100644 index 000000000..b75511ff8 --- /dev/null +++ b/database/factories/Common/SpamListFactory.php @@ -0,0 +1,95 @@ + 'forms', + 'target_id' => null, + 'block_type' => SpamBlockType::email, + 'block_value' => $this->faker->email, + 'memo' => $this->faker->sentence, + ]; + } + + /** + * グローバルスコープ(全体適用)のスパムリスト + */ + public function global() + { + return $this->state(function (array $attributes) { + return [ + 'target_id' => null, + ]; + }); + } + + /** + * 特定フォーム用のスパムリスト + */ + public function forForm($forms_id) + { + return $this->state(function (array $attributes) use ($forms_id) { + return [ + 'target_id' => $forms_id, + ]; + }); + } + + /** + * メールアドレス型のスパムリスト + */ + public function emailType() + { + return $this->state(function (array $attributes) { + return [ + 'block_type' => SpamBlockType::email, + 'block_value' => $this->faker->email, + ]; + }); + } + + /** + * ドメイン型のスパムリスト + */ + public function domainType() + { + return $this->state(function (array $attributes) { + return [ + 'block_type' => SpamBlockType::domain, + 'block_value' => $this->faker->domainName, + ]; + }); + } + + /** + * IPアドレス型のスパムリスト + */ + public function ipAddressType() + { + return $this->state(function (array $attributes) { + return [ + 'block_type' => SpamBlockType::ip_address, + 'block_value' => $this->faker->ipv4, + ]; + }); + } +} diff --git a/database/factories/User/Forms/FormsColumnsFactory.php b/database/factories/User/Forms/FormsColumnsFactory.php new file mode 100644 index 000000000..525ad8abf --- /dev/null +++ b/database/factories/User/Forms/FormsColumnsFactory.php @@ -0,0 +1,71 @@ + null, + 'column_type' => FormColumnType::text, + 'column_name' => $this->faker->word, + 'required' => 0, + 'frame_col' => 12, + 'caption' => null, + 'caption_color' => 'text-dark', + 'place_holder' => null, + 'minutes_increments' => 10, + 'minutes_increments_from' => 10, + 'minutes_increments_to' => 10, + 'rule_allowed_numeric' => 0, + 'rule_allowed_alpha_numeric' => 0, + 'rule_digits_or_less' => null, + 'rule_max' => null, + 'rule_min' => null, + 'rule_word_count' => null, + 'rule_date_after_equal' => null, + 'display_sequence' => 0, + ]; + } + + /** + * メールアドレス型のカラム + */ + public function emailType() + { + return $this->state(function (array $attributes) { + return [ + 'column_type' => FormColumnType::mail, + 'column_name' => 'メールアドレス', + ]; + }); + } + + /** + * テキスト型のカラム + */ + public function textType() + { + return $this->state(function (array $attributes) { + return [ + 'column_type' => FormColumnType::text, + ]; + }); + } +} diff --git a/database/factories/User/Forms/FormsFactory.php b/database/factories/User/Forms/FormsFactory.php new file mode 100644 index 000000000..13b6ae5ec --- /dev/null +++ b/database/factories/User/Forms/FormsFactory.php @@ -0,0 +1,64 @@ + null, + 'forms_name' => $this->faker->sentence(3), + 'form_mode' => 'form', + 'access_limit_type' => 0, + 'form_password' => null, + 'entry_limit' => null, + 'entry_limit_over_message' => null, + 'display_control_flag' => 0, + 'display_from' => null, + 'display_to' => null, + 'regist_control_flag' => 0, + 'regist_from' => null, + 'regist_to' => null, + 'can_view_inputs_moderator' => 0, + 'mail_send_flag' => 0, + 'mail_send_address' => null, + 'user_mail_send_flag' => 0, + 'mail_subject' => null, + 'mail_format' => null, + 'data_save_flag' => 1, + 'after_message' => null, + 'numbering_use_flag' => 0, + 'numbering_prefix' => null, + 'use_spam_filter_flag' => 0, + 'spam_filter_message' => null, + ]; + } + + /** + * スパムフィルタリングを有効にする + */ + public function withSpamFilter() + { + return $this->state(function (array $attributes) { + return [ + 'use_spam_filter_flag' => 1, + 'spam_filter_message' => $this->faker->sentence, + ]; + }); + } +} diff --git a/database/factories/User/Forms/FormsInputColsFactory.php b/database/factories/User/Forms/FormsInputColsFactory.php new file mode 100644 index 000000000..a1217e97b --- /dev/null +++ b/database/factories/User/Forms/FormsInputColsFactory.php @@ -0,0 +1,29 @@ + null, + 'forms_columns_id' => null, + 'value' => $this->faker->word, + ]; + } +} diff --git a/database/factories/User/Forms/FormsInputsFactory.php b/database/factories/User/Forms/FormsInputsFactory.php new file mode 100644 index 000000000..b8bb6b0ff --- /dev/null +++ b/database/factories/User/Forms/FormsInputsFactory.php @@ -0,0 +1,41 @@ + null, + 'status' => 0, // 本登録 + 'ip_address' => null, + ]; + } + + /** + * IPアドレスを記録 + */ + public function withIpAddress($ip_address = null) + { + return $this->state(function (array $attributes) use ($ip_address) { + return [ + 'ip_address' => $ip_address ?? $this->faker->ipv4, + ]; + }); + } +} diff --git a/database/migrations/2026_01_18_143634_create_spam_lists_table.php b/database/migrations/2026_01_18_143634_create_spam_lists_table.php new file mode 100644 index 000000000..cd3929939 --- /dev/null +++ b/database/migrations/2026_01_18_143634_create_spam_lists_table.php @@ -0,0 +1,45 @@ +increments('id'); + $table->string('target_plugin_name', 255)->comment('対象プラグイン名(forms等)'); + $table->integer('target_id')->nullable()->comment('対象ID(フォーム毎の場合はforms_id、全体の場合はnull)'); + $table->string('block_type', 50)->comment('ブロック種別: email, domain, ip_address'); + $table->string('block_value', 255)->comment('ブロック対象の値'); + $table->text('memo')->nullable()->comment('メモ'); + $table->integer('created_id')->nullable(); + $table->string('created_name', 255)->nullable(); + $table->timestamp('created_at')->nullable(); + $table->integer('updated_id')->nullable(); + $table->string('updated_name', 255)->nullable(); + $table->timestamp('updated_at')->nullable(); + $table->softDeletes(); + + $table->index(['target_plugin_name', 'target_id', 'block_type'], 'spam_lists_target_index'); + $table->index(['block_type', 'block_value'], 'spam_lists_block_index'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('spam_lists'); + } +} diff --git a/database/migrations/2026_01_18_143635_add_spam_filter_to_forms.php b/database/migrations/2026_01_18_143635_add_spam_filter_to_forms.php new file mode 100644 index 000000000..ed9af4903 --- /dev/null +++ b/database/migrations/2026_01_18_143635_add_spam_filter_to_forms.php @@ -0,0 +1,34 @@ +integer('use_spam_filter_flag')->default(0)->comment('スパムフィルタリング使用フラグ'); + $table->text('spam_filter_message')->nullable()->comment('スパムブロック時のメッセージ'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('forms', function (Blueprint $table) { + $table->dropColumn('use_spam_filter_flag'); + $table->dropColumn('spam_filter_message'); + }); + } +} diff --git a/database/migrations/2026_01_18_143636_add_ip_address_to_forms_inputs.php b/database/migrations/2026_01_18_143636_add_ip_address_to_forms_inputs.php new file mode 100644 index 000000000..acb787e2b --- /dev/null +++ b/database/migrations/2026_01_18_143636_add_ip_address_to_forms_inputs.php @@ -0,0 +1,32 @@ +string('ip_address', 255)->nullable()->comment('投稿者のIPアドレス'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('forms_inputs', function (Blueprint $table) { + $table->dropColumn('ip_address'); + }); + } +} diff --git a/resources/views/plugins/manage/menus_list.blade.php b/resources/views/plugins/manage/menus_list.blade.php index c2dbe5697..0a2b90eee 100644 --- a/resources/views/plugins/manage/menus_list.blade.php +++ b/resources/views/plugins/manage/menus_list.blade.php @@ -45,6 +45,13 @@ セキュリティ管理 @endif @endif + @if (Auth::user()->can('admin_site')) + @if (isset($plugin_name) && $plugin_name == 'spam') + スパム管理 + @else + スパム管理 + @endif + @endif @if (Auth::user()->can('admin_system')) @if (isset($plugin_name) && $plugin_name == 'plugin') プラグイン管理 diff --git a/resources/views/plugins/manage/spam/edit.blade.php b/resources/views/plugins/manage/spam/edit.blade.php new file mode 100644 index 000000000..a2d72cbb9 --- /dev/null +++ b/resources/views/plugins/manage/spam/edit.blade.php @@ -0,0 +1,100 @@ +{{-- + * スパム管理の編集テンプレート + * + * @author 井上 雅人 + * @copyright OpenSource-WorkShop Co.,Ltd. All Rights Reserved + * @category スパム管理 +--}} +@php +use App\Enums\SpamBlockType; +@endphp +{{-- 管理画面ベース画面 --}} +@extends('plugins.manage.manage') + +{{-- 管理画面メイン部分のコンテンツ section:manage_content で作ること --}} +@section('manage_content') + +
+
+ {{-- 機能選択タブ --}} + @include('plugins.manage.spam.spam_tab') +
+ +
+ + @include('plugins.common.errors_form_line') + +
+ {{ csrf_field() }} + +
+ +
+
+ {{ SpamBlockType::getDescription($spam->block_type) }} +
+ 種別は変更できません。 +
+
+ +
+ +
+ + @include('plugins.common.errors_inline', ['name' => 'block_value']) +
+
+ +
+ +
+
+ target_id) ? 'global' : 'form') == 'global') checked @endif> + +
+
+ target_id) ? 'global' : 'form') == 'form') checked @endif> + +
+ + @include('plugins.common.errors_inline', ['name' => 'target_forms_id']) +
+
+ +
+ +
+ +
+
+ +
+ +
+
+ {{ $spam->created_at->format('Y/m/d H:i:s') }} +
+
+
+ +
+
+ + キャンセル + + +
+
+
+ +
+
+ +@endsection diff --git a/resources/views/plugins/manage/spam/index.blade.php b/resources/views/plugins/manage/spam/index.blade.php new file mode 100644 index 000000000..5500baaea --- /dev/null +++ b/resources/views/plugins/manage/spam/index.blade.php @@ -0,0 +1,221 @@ +{{-- + * スパム管理のメインテンプレート + * + * @author 井上 雅人 + * @copyright OpenSource-WorkShop Co.,Ltd. All Rights Reserved + * @category スパム管理 +--}} +@php +use App\Enums\SpamBlockType; +@endphp +{{-- 管理画面ベース画面 --}} +@extends('plugins.manage.manage') + +{{-- 管理画面メイン部分のコンテンツ section:manage_content で作ること --}} +@section('manage_content') + +
+
+ {{-- 機能選択タブ --}} + @include('plugins.manage.spam.spam_tab') +
+ +
+ + {{-- 登録後メッセージ表示 --}} + @include('plugins.common.flash_message') + + @include('plugins.common.errors_form_line') + +
+ サイト全体で適用されるスパムリストを管理します。 +
+ + {{-- 検索フォーム --}} +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + クリア +
+
+
+ + {{-- スパムリスト一覧 --}} +
+ スパムリスト一覧 +
+ {{ csrf_field() }} + + + + + +
+
+ +
+ + + + + + + + + + + + + @forelse($spam_lists as $spam) + + + + + + + + + @empty + + + + @endforelse + +
種別適用範囲メモ登録日時操作
+ @if ($spam->block_type == SpamBlockType::email) + メールアドレス + @elseif ($spam->block_type == SpamBlockType::domain) + ドメイン + @else + IPアドレス + @endif + {{ $spam->block_value }} + @if (is_null($spam->target_id)) + 全体 + @else + @php + $form_name = $forms[$spam->target_id]->forms_name ?? '不明'; + @endphp + {{ $form_name }} + @endif + {{ Str::limit($spam->memo, 30) }}{{ $spam->created_at->format('Y/m/d H:i') }} + + 編集 + +
+ {{ csrf_field() }} + +
+
スパムリストは登録されていません。
+
+ + {{-- ページング処理 --}} + {{ $spam_lists->links() }} + +
+ + {{-- スパムリスト追加フォーム --}} +
スパムリストへ追加
+ +
+ {{ csrf_field() }} + +
+ +
+ @foreach (SpamBlockType::getMembers() as $key => $value) +
+ + +
+ @endforeach + @include('plugins.common.errors_inline', ['name' => 'block_type']) + + ※ メールアドレス:完全一致でブロックします。
+ ※ ドメイン:メールアドレスの@以降と一致する場合にブロックします。
+ ※ メールアドレス・ドメインはフォームに「メールアドレス」型項目がある場合に有効です。
+ ※ IPアドレス:送信元IPアドレスと一致する場合にブロックします。 +
+
+
+ +
+ +
+ + @include('plugins.common.errors_inline', ['name' => 'block_value']) +
+
+ +
+ +
+
+ + +
+
+ + +
+ + @include('plugins.common.errors_inline', ['name' => 'target_forms_id']) + ※「全体」を選択すると、スパムフィルタリングを有効にしているすべてのフォームに適用されます。 +
+
+ +
+ +
+ +
+
+ +
+
+ +
+
+
+ +
+
+ +@endsection diff --git a/resources/views/plugins/manage/spam/spam_tab.blade.php b/resources/views/plugins/manage/spam/spam_tab.blade.php new file mode 100644 index 000000000..3c2e6ae32 --- /dev/null +++ b/resources/views/plugins/manage/spam/spam_tab.blade.php @@ -0,0 +1,31 @@ +{{-- + * スパム管理のタブ + * + * @author 井上 雅人 + * @copyright OpenSource-WorkShop Co.,Ltd. All Rights Reserved + * @category スパム管理 +--}} +
+ +
diff --git a/resources/views/plugins/user/forms/default/forms_edit_spam_filter.blade.php b/resources/views/plugins/user/forms/default/forms_edit_spam_filter.blade.php new file mode 100644 index 000000000..322d56392 --- /dev/null +++ b/resources/views/plugins/user/forms/default/forms_edit_spam_filter.blade.php @@ -0,0 +1,191 @@ +{{-- + * スパムフィルタリング設定画面テンプレート + * + * @author 井上 雅人 + * @copyright OpenSource-WorkShop Co.,Ltd. All Rights Reserved + * @category フォームプラグイン +--}} +@php +use App\Enums\SpamBlockType; +@endphp +@extends('core.cms_frame_base_setting') + +@section("core.cms_frame_edit_tab_$frame->id") + {{-- プラグイン側のフレームメニュー --}} + @include('plugins.user.forms.forms_frame_edit_tab') +@endsection + +@section("plugin_setting_$frame->id") + +{{-- 登録後メッセージ表示 --}} +@include('plugins.common.flash_message') + +@include('plugins.common.errors_form_line') + +
+ スパムフィルタリングの設定を行います。 +
+ +{{-- スパムフィルタリング設定フォーム --}} +
+ {{ csrf_field() }} + + +
+ +
+
+ + use_spam_filter_flag)) checked @endif> + +
+ + 本機能を有効にすると、スパムフィルタリングのために送信元のIPアドレスをフォーム投稿時に収集します。サイトのプライバシーポリシーにその旨を記載されることを推奨いたします。 + +
+
+ +
use_spam_filter_flag)) style="display: none;" @endif> +
+ +
+ + ※ 未入力の場合、デフォルトメッセージ「入力されたメールアドレス、または、IPアドレスからの送信は現在制限されています。」が表示されます。 +
+
+
+ + {{-- Submitボタン --}} +
+ +
+
+ +
use_spam_filter_flag)) style="display: none;" @endif> +
+ +{{-- 適用されるスパムリスト --}} +
適用されるスパムリスト
+ +
+ + + + + + + + + + + + @forelse($spam_lists as $spam) + + + + + + + + @empty + + + + @endforelse + +
種別適用範囲メモ操作
+ @if ($spam->block_type == SpamBlockType::email) + メールアドレス + @elseif ($spam->block_type == SpamBlockType::domain) + ドメイン + @else + IPアドレス + @endif + {{ $spam->block_value }} + @if ($spam->isGlobalScope()) + 全体 + @else + このフォーム + @endif + {{ $spam->memo }} + @if ($spam->isGlobalScope()) + + @else +
+ {{ csrf_field() }} + + +
+ @endif +
スパムリストは登録されていません。
+
+ +※ 適用範囲が「全体」のスパムリストはスパム管理から編集できます。 + +
+ +{{-- スパムリスト追加フォーム --}} +
スパムリストへ追加(このフォーム用)
+ +
+ {{ csrf_field() }} + + +
+ +
+ @foreach (SpamBlockType::getMembers() as $key => $value) +
+ + +
+ @endforeach + @include('plugins.common.errors_inline', ['name' => 'block_type']) + + ※ メールアドレス:完全一致でブロックします。
+ ※ ドメイン:メールアドレスの@以降と一致する場合にブロックします。
+ ※ メールアドレス・ドメインはフォームに「メールアドレス」型項目がある場合に有効です。
+ ※ IPアドレス:送信元IPアドレスと一致する場合にブロックします。 +
+
+
+ +
+ +
+ + @include('plugins.common.errors_inline', ['name' => 'block_value']) +
+
+ +
+ +
+ +
+
+ +
+ +
+
+
+ + + +@endsection diff --git a/resources/views/plugins/user/forms/default/forms_list_inputs.blade.php b/resources/views/plugins/user/forms/default/forms_list_inputs.blade.php index c9f4749f8..a6d81a8ef 100644 --- a/resources/views/plugins/user/forms/default/forms_list_inputs.blade.php +++ b/resources/views/plugins/user/forms/default/forms_list_inputs.blade.php @@ -100,6 +100,8 @@ function submit_register_other_plugins(id) { @endif 登録ユーザ 登録日時 + IPアドレス + 操作 @@ -151,6 +153,58 @@ function submit_register_other_plugins(id) { {{$input->created_at}} + + {{$input->ip_address}} + + + +
+ + +
+ + @endforeach @@ -180,4 +234,18 @@ function submit_register_other_plugins(id) { + + + @endsection diff --git a/resources/views/plugins/user/forms/forms_frame_edit_tab.blade.php b/resources/views/plugins/user/forms/forms_frame_edit_tab.blade.php index 3e7b62461..230ad4738 100644 --- a/resources/views/plugins/user/forms/forms_frame_edit_tab.blade.php +++ b/resources/views/plugins/user/forms/forms_frame_edit_tab.blade.php @@ -46,6 +46,15 @@ 登録一覧 @endif +@if ($action == 'editSpamFilter') + +@else + +@endif @if ($action == 'listBuckets')