目安箱機能 仕様書¶
作成日: 2025-12-29
更新日: 2025-12-29
ステータス: 仕様確定
プラグイン名: inportal-suggestion-box
1. 概要¶
1.1 目的¶
社員が匿名または実名で意見・相談・改善提案などを投稿できる目安箱機能。投稿内容はデータベースに保存され、管理者にメール通知される。
1.2 主な特徴¶
| 特徴 | 説明 |
|---|---|
| 匿名/実名選択 | 投稿者が選択可能 |
| カテゴリ分類 | 意見・相談・改善提案・その他 |
| 返信希望 | 実名投稿時のみ選択可能 |
| 添付ファイル | 画像・PDFを添付可能(最大2件) |
| メール通知 | 新規投稿時に管理者へ通知 |
| 全員利用可 | 会社を問わず全ユーザーが利用可能 |
1.3 匿名投稿に関する注意事項¶
[!IMPORTANT] 匿名性の技術的限界について
本システムはアプリケーション層で投稿者のユーザーIDを保存しないことで「匿名」を実現していますが、以下の点に注意が必要です:
- Webサーバー(Apache/Nginx)のアクセスログには、投稿時のIPアドレス、User-Agent、タイムスタンプが記録されます
- 社内ネットワークからのアクセスの場合、DHCPログ等と突合することで技術的には個人の特定が可能な場合があります
運用ルール(推奨): - 重大な犯罪行為等が疑われる場合を除き、サーバーログの解析は行わない - 投稿フォームに以下の免責文言を表示する
2. 投稿フォーム¶
2.1 フォームUI¶
┌──────────────────────────────────────────────────────────┐
│ 📮 目安箱 │
│ │
│ ご意見・ご相談・改善提案などをお寄せください。 │
│ 匿名での投稿も可能です。 │
│ │
│ ※ システム上は個人情報を記録しませんが、ネットワーク │
│ ログ等は会社の規定に基づき管理されます。 │
├──────────────────────────────────────────────────────────┤
│ │
│ カテゴリ * │
│ ┌────────────────────────────────────────────────────┐ │
│ │ ▼ 選択してください │ │
│ │ ・意見・感想 │ │
│ │ ・相談 │ │
│ │ ・改善提案 │ │
│ │ ・その他 │ │
│ └────────────────────────────────────────────────────┘ │
│ │
│ 投稿種別 * │
│ ┌────────────────────────────────────────────────────┐ │
│ │ ○ 匿名で投稿 │ │
│ │ ※ 投稿者情報は記録されません │ │
│ │ │ │
│ │ ○ 実名で投稿 │ │
│ │ ※ 管理者のみが投稿者情報を確認できます │ │
│ └────────────────────────────────────────────────────┘ │
│ │
│ ┌─ 実名選択時のみ表示 ─────────────────────────────────┐ │
│ │ │ │
│ │ □ 返信を希望する │ │
│ │ │ │
│ │ ┌────────────────────────────────────────────────┐ │ │
│ │ │ 投稿者情報(自動入力・変更不可) │ │ │
│ │ │ │ │ │
│ │ │ お名前: 山田 太郎 │ │ │
│ │ │ 社員番号: 001234 │ │ │
│ │ │ 会社名: 北日本造船㈱ │ │ │
│ │ │ メール: yamada@kitanihonship.com │ │ │
│ │ └────────────────────────────────────────────────┘ │ │
│ └──────────────────────────────────────────────────────┘ │
│ │
│ 件名 * │
│ ┌────────────────────────────────────────────────────┐ │
│ │ │ │
│ └────────────────────────────────────────────────────┘ │
│ │
│ 内容 * │
│ ┌────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ │ │
│ │ │ │
│ │ │ │
│ └────────────────────────────────────────────────────┘ │
│ │
│ 添付ファイル(任意) │
│ ┌────────────────────────────────────────────────────┐ │
│ │ 📎 ファイルを選択 │ │
│ │ │ │
│ │ ・最大2ファイルまで │ │
│ │ ・1ファイル10MBまで │ │
│ │ ・対応形式: JPG, PNG, GIF, PDF │ │
│ └────────────────────────────────────────────────────┘ │
│ │
│ ┌────────────────────┐ │
│ │ 送信内容を確認 │ │
│ └────────────────────┘ │
│ │
└──────────────────────────────────────────────────────────┘
2.2 送信確認モーダル¶
[!NOTE] 誤送信防止のため、送信前に確認画面を表示する
┌──────────────────────────────────────────────────────────┐
│ ⚠️ 送信確認 │
├──────────────────────────────────────────────────────────┤
│ │
│ 以下の内容で送信してよろしいですか? │
│ ⚠️ 送信後は修正できません │
│ │
│ ┌────────────────────────────────────────────────────┐ │
│ │ カテゴリ: 改善提案 │ │
│ │ 投稿種別: 実名 │ │
│ │ 返信希望: あり │ │
│ │ 件名: 社食のメニューについて │ │
│ │ │ │
│ │ 内容: │ │
│ │ 最近、社食のメニューがマンネリ化しているように │ │
│ │ 感じます。月に一度でも特別メニューの日があると... │ │
│ │ │ │
│ │ 添付ファイル: │ │
│ │ ・参考資料.pdf (1.2MB) │ │
│ └────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ 戻る │ │ 送信する │ │
│ └──────────────┘ └──────────────┘ │
│ │
└──────────────────────────────────────────────────────────┘
2.3 フォーム項目¶
| 項目 | 必須 | 型 | 説明 |
|---|---|---|---|
| カテゴリ | ✅ | select | 意見・感想 / 相談 / 改善提案 / その他 |
| 投稿種別 | ✅ | radio | 匿名 / 実名 |
| 返信希望 | - | checkbox | 実名時のみ表示 |
| 件名 | ✅ | text | 最大100文字 |
| 内容 | ✅ | textarea | 最大5000文字 |
| 添付ファイル | - | file | 最大2件、各10MB |
2.4 カテゴリ一覧¶
| キー | 表示名 | 説明 |
|---|---|---|
opinion |
意見・感想 | 会社や業務に対する意見・感想 |
consultation |
相談 | 個人的な相談事項 |
improvement |
改善提案 | 業務改善の提案 |
other |
その他 | 上記以外 |
3. データベース設計¶
3.1 投稿テーブル¶
CREATE TABLE wp_suggestion_box (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
-- 投稿内容
category VARCHAR(50) NOT NULL, -- 'opinion', 'consultation', 'improvement', 'other'
subject VARCHAR(255) NOT NULL, -- 件名
content TEXT NOT NULL, -- 本文
-- 投稿種別
is_anonymous TINYINT(1) DEFAULT 1, -- 1:匿名, 0:実名
wants_reply TINYINT(1) DEFAULT 0, -- 返信希望(実名時のみ有効)
-- 投稿者情報(実名投稿時のみ記録)
user_id BIGINT UNSIGNED NULL, -- WordPress ユーザーID
employee_id VARCHAR(20) NULL, -- 社員番号
employee_name VARCHAR(100) NULL, -- 氏名
email VARCHAR(100) NULL, -- メールアドレス
company_name VARCHAR(100) NULL, -- 会社名
-- 管理情報
status VARCHAR(20) DEFAULT 'new', -- 'new', 'read', 'resolved'
admin_note TEXT NULL, -- 管理者メモ
-- タイムスタンプ
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
-- インデックス
INDEX idx_category (category),
INDEX idx_status (status),
INDEX idx_is_anonymous (is_anonymous),
INDEX idx_created_at (created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
3.2 添付ファイルテーブル¶
CREATE TABLE wp_suggestion_box_attachments (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
suggestion_id BIGINT UNSIGNED NOT NULL, -- 投稿ID
file_name VARCHAR(255) NOT NULL, -- 元のファイル名
file_path VARCHAR(500) NOT NULL, -- 保存パス(相対パス)
file_type VARCHAR(50) NOT NULL, -- MIMEタイプ
file_size BIGINT UNSIGNED NOT NULL, -- ファイルサイズ(バイト)
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (suggestion_id) REFERENCES wp_suggestion_box(id) ON DELETE CASCADE,
INDEX idx_suggestion_id (suggestion_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
[!NOTE] COLLATE について
utf8mb4_unicode_ciを使用することで、MySQL 5.7以降の幅広い環境で互換性を確保します。utf8mb4_unicode_520_ciは一部の古いサーバーでサポートされていない可能性があります。
4. 添付ファイル¶
4.1 仕様¶
| 項目 | 値 |
|---|---|
| 最大ファイル数 | 2件/投稿 |
| 最大ファイルサイズ | 10MB/ファイル |
| 許可形式 | jpg, jpeg, png, gif, pdf |
| 保存先 | wp-content/uploads/private/suggestion-box/{年}/{月}/ |
| ファイル名 | {suggestion_id}_{タイムスタンプ}_{ランダム16文字}.{拡張子} |
4.2 セキュリティ(強化版)¶
[!CAUTION] 添付ファイルの直接アクセス禁止
内部告発やハラスメント相談などの機密性の高いファイルを保護するため、以下の対策を必須とします。
4.2.1 保存ディレクトリの保護¶
- 保存ディレクトリは
wp-content/uploads/private/suggestion-box/とし、.htaccessで外部からの直接アクセスを完全に禁止 - ディレクトリリスティングも禁止
4.2.2 ファイルダウンロードエンドポイント¶
添付ファイルは直接URLではなく、PHPを介した専用エンドポイント経由でのみダウンロード可能とする:
権限チェック:
- WordPressにログイン済みであること
- 管理者権限(manage_options)を持っていること
- 有効なnonceであること
実装例:
add_action('admin_post_download_suggestion_file', function() {
// 権限チェック
if (!current_user_can('manage_options')) {
wp_die('権限がありません', 403);
}
// nonceチェック
if (!wp_verify_nonce($_GET['nonce'], 'download_suggestion_file')) {
wp_die('無効なリクエストです', 403);
}
// ファイル取得・送信処理
$attachment_id = intval($_GET['id']);
$attachment = get_suggestion_attachment($attachment_id);
if (!$attachment) {
wp_die('ファイルが見つかりません', 404);
}
$file_path = WP_CONTENT_DIR . '/uploads/private/suggestion-box/' . $attachment->file_path;
if (!file_exists($file_path)) {
wp_die('ファイルが見つかりません', 404);
}
// ファイル送信
header('Content-Type: ' . $attachment->file_type);
header('Content-Disposition: attachment; filename="' . $attachment->file_name . '"');
header('Content-Length: ' . filesize($file_path));
readfile($file_path);
exit;
});
4.2.3 その他のセキュリティ対策¶
- ファイル名は16文字のランダム文字列を含める(推測困難化)
- MIMEタイプ検証(
finfo_file()で実際のファイル内容を確認) - ファイルサイズ検証
- 元のファイル名はDBに保存(ダウンロード時に復元)
4.3 ファイルクリーンアップ¶
[!IMPORTANT] 投稿削除時のファイル自動削除
投稿が削除された際、関連する添付ファイルも自動的に削除すること。
// 投稿削除時のフック
add_action('suggestion_box_before_delete', function($suggestion_id) {
global $wpdb;
// 関連する添付ファイルを取得
$attachments = $wpdb->get_results($wpdb->prepare(
"SELECT file_path FROM {$wpdb->prefix}suggestion_box_attachments WHERE suggestion_id = %d",
$suggestion_id
));
// ファイルを削除
$base_path = WP_CONTENT_DIR . '/uploads/private/suggestion-box/';
foreach ($attachments as $attachment) {
$file_path = $base_path . $attachment->file_path;
if (file_exists($file_path)) {
unlink($file_path);
}
}
// DBレコードはFOREIGN KEY ON DELETE CASCADEで自動削除
});
5. レート制限(連投対策)¶
5.1 仕様¶
| 項目 | 値 |
|---|---|
| 制限方式 | IPアドレスベース + Cookieベース(併用) |
| 投稿間隔 | 5分間に1回まで |
| 実装 | WordPress Transient API |
5.2 実装¶
function can_submit_suggestion() {
$user_ip = $_SERVER['REMOTE_ADDR'];
$transient_key = 'suggestion_limit_' . md5($user_ip);
if (get_transient($transient_key)) {
return false; // 制限中
}
return true;
}
function set_submission_limit() {
$user_ip = $_SERVER['REMOTE_ADDR'];
$transient_key = 'suggestion_limit_' . md5($user_ip);
set_transient($transient_key, time(), 5 * MINUTE_IN_SECONDS);
}
5.3 制限時のメッセージ¶
6. 通知メール¶
6.1 送信条件¶
- 新規投稿時に管理者へメール送信
6.2 送信先設定¶
- 管理画面で設定可能
- デフォルト:
y_iga@kitanihonship.com - 複数アドレス対応(カンマ区切り)
6.3 メールテンプレート¶
[!NOTE] 件名にカテゴリと投稿種別を含めることで、優先度判断を容易にする
件名: 【目安箱:改善提案】社食のメニューについて(実名・返信希望あり)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
📮 目安箱に新しい投稿がありました
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
■ 基本情報
投稿日時: 2025年12月29日 10:30
カテゴリ: 改善提案
投稿種別: 実名
返信希望: あり
■ 投稿者情報
氏名: 山田 太郎
社員番号: 001234
会社名: 北日本造船㈱
メール: yamada@kitanihonship.com
※ 匿名投稿の場合、投稿者情報は表示されません
■ 投稿内容
件名: 社食のメニューについて
本文:
─────────────────────────────────────────────
最近、社食のメニューがマンネリ化しているように
感じます。月に一度でも特別メニューの日があると
嬉しいです。
例えば...
─────────────────────────────────────────────
■ 添付ファイル
・参考資料.pdf (1.2MB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
管理画面で確認:
https://portal.example.com/wp-admin/admin.php?page=suggestion-box
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
6.4 件名フォーマット¶
例:
- 【目安箱:改善提案】社食のメニューについて(実名・返信希望あり)
- 【目安箱:相談】人間関係について(匿名)
- 【目安箱:意見・感想】新システムの使い勝手(実名)
7. 管理画面¶
7.1 メニュー構成¶
7.2 投稿一覧画面¶
目安箱 - 投稿一覧
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
フィルター:
ステータス: [すべて ▼] カテゴリ: [すべて ▼] 種別: [すべて ▼] [絞り込み]
┌────┬──────────┬────────────┬────────┬──────────┬────────┬──────┐
│ ID │ ステータス│ カテゴリ │ 種別 │ 件名 │ 投稿者 │ 日時 │
├────┼──────────┼────────────┼────────┼──────────┼────────┼──────┤
│ 5 │ 🆕新規 │ 改善提案 │ 実名📩 │ 社食の... │ 山田太郎│ 12/29 │
│ 4 │ 📖確認済み│ 相談 │ 匿名 │ 人間関... │ (匿名)│ 12/28 │
│ 3 │ ✅対応済み│ 意見・感想 │ 実名 │ 新しい... │ 鈴木一郎│ 12/27 │
└────┴──────────┴────────────┴────────┴──────────┴────────┴──────┘
📩 = 返信希望あり
7.3 投稿詳細画面¶
目安箱 - 投稿詳細 #5
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
■ ステータス
┌──────────────────────────────────────────────────────────┐
│ ○ 新規 ● 確認済み ○ 対応済み [更新] │
└──────────────────────────────────────────────────────────┘
■ 基本情報
┌──────────────────────────────────────────────────────────┐
│ 投稿日時: 2025年12月29日 10:30 │
│ カテゴリ: 改善提案 │
│ 投稿種別: 実名 │
│ 返信希望: あり │
└──────────────────────────────────────────────────────────┘
■ 投稿者情報
┌──────────────────────────────────────────────────────────┐
│ 氏名: 山田 太郎 │
│ 社員番号: 001234 │
│ 会社名: 北日本造船㈱ │
│ メール: yamada@kitanihonship.com │
│ │
│ [📧 メールを送信] │
└──────────────────────────────────────────────────────────┘
■ 投稿内容
┌──────────────────────────────────────────────────────────┐
│ 件名: 社食のメニューについて │
├──────────────────────────────────────────────────────────┤
│ 最近、社食のメニューがマンネリ化しているように │
│ 感じます。月に一度でも特別メニューの日があると │
│ 嬉しいです。 │
│ │
│ 例えば、ご当地グルメフェアや季節限定メニューなど... │
└──────────────────────────────────────────────────────────┘
■ 添付ファイル
┌──────────────────────────────────────────────────────────┐
│ 📎 参考資料.pdf (1.2MB) [ダウンロード] │
└──────────────────────────────────────────────────────────┘
※ 添付ファイルは管理者のみダウンロード可能です
■ 管理者メモ(非公開)
┌──────────────────────────────────────────────────────────┐
│ 総務部に転送済み。来月から検討予定。 │
│ │
│ │
└──────────────────────────────────────────────────────────┘
[メモを保存]
[一覧に戻る] [この投稿を削除]
7.4 設定画面¶
目安箱 - 設定
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
■ 通知設定
通知先メールアドレス
┌──────────────────────────────────────────────────────────┐
│ y_iga@kitanihonship.com │
└──────────────────────────────────────────────────────────┘
※ 複数の場合はカンマ区切りで入力
☑ 新規投稿時にメール通知を送信する
■ カテゴリ設定
┌──────────────────────────────────────────────────────────┐
│ ≡ 意見・感想 [編集] [削除] │
│ ≡ 相談 [編集] [削除] │
│ ≡ 改善提案 [編集] [削除] │
│ ≡ その他 [編集] [削除] │
└──────────────────────────────────────────────────────────┘
[+ カテゴリを追加]
■ ファイル設定
最大ファイル数: [ 2 ] 件
最大ファイルサイズ: [ 10 ] MB
■ レート制限設定
投稿間隔: [ 5 ] 分
※ 同一IPからの連続投稿を制限します
[変更を保存]
8. フロントエンド実装¶
8.1 アクセスURL¶
| 方式 | URL |
|---|---|
| クエリパラメータ | https://portal.example.com/?suggestion_box=1 |
8.2 ドロワーメニュー追加¶
// template-parts/app-header.php に追加
<li class="app-drawer__item">
<a href="<?php echo esc_url(home_url('/?suggestion_box=1')); ?>"
class="app-drawer__link<?php echo $app_id === 'suggestion' ? ' app-drawer__link--active' : ''; ?>">
<span class="material-symbols-rounded app-drawer__icon">inbox</span>
<span><?php echo esc_html(_t('目安箱')); ?></span>
</a>
</li>
8.3 投稿完了画面¶
┌──────────────────────────────────────────────────────────┐
│ │
│ ✅ 送信完了 │
│ │
│ ご投稿ありがとうございました。 │
│ 内容を確認のうえ、対応いたします。 │
│ │
│ ※ 実名投稿で返信希望の場合は、 │
│ 担当者よりご連絡いたします。 │
│ │
│ ┌────────────────────┐ │
│ │ ホームに戻る │ │
│ └────────────────────┘ │
│ │
└──────────────────────────────────────────────────────────┘
9. セキュリティ考慮事項¶
| リスク | 対策 |
|---|---|
| CSRF | WordPress nonce 使用 |
| XSS | esc_html(), wp_kses_post() でサニタイズ |
| SQLインジェクション | $wpdb->prepare() 使用 |
| ファイルアップロード攻撃 | MIMEタイプ・拡張子検証、ファイルサイズ制限 |
| 添付ファイル直接アクセス | Deny from all + PHPエンドポイント経由のダウンロード |
| スパム投稿 | ログインユーザーのみ利用可、レート制限(5分/1投稿) |
| 匿名性の保証 | 匿名投稿時はIPアドレス等も記録しない(ログ免責表示あり) |
10. REST API(将来拡張用)¶
現時点ではAJAXハンドラで実装しますが、将来的にREST API化する場合:
11. テスト項目¶
投稿フォーム¶
- [ ] カテゴリ選択が動作する
- [ ] 匿名/実名の切り替えが動作する
- [ ] 実名選択時に返信希望チェックボックスが表示される
- [ ] 実名選択時にユーザー情報が自動入力される
- [ ] 必須項目未入力でエラー表示
- [ ] 添付ファイルが2件までアップロードできる
- [ ] 10MB超のファイルでエラー表示
- [ ] 許可外形式のファイルでエラー表示
- [ ] 送信確認モーダルが表示される
- [ ] 送信完了画面が表示される
レート制限¶
- [ ] 5分以内の連続投稿がブロックされる
- [ ] 5分経過後は投稿可能になる
データ保存¶
- [ ] 匿名投稿で投稿者情報が保存されない
- [ ] 実名投稿で投稿者情報が正しく保存される
- [ ] 添付ファイルが正しく保存される
- [ ] 添付ファイルが直接URLでアクセスできない
通知メール¶
- [ ] 新規投稿時にメールが送信される
- [ ] 件名にカテゴリと投稿種別が含まれる
- [ ] メール内容が正しい
管理画面¶
- [ ] 投稿一覧が表示される
- [ ] フィルターが動作する
- [ ] 投稿詳細が表示される
- [ ] ステータス変更が動作する
- [ ] 管理者メモが保存される
- [ ] 添付ファイルがダウンロードできる(管理者のみ)
- [ ] 設定画面で通知先を変更できる
ファイルクリーンアップ¶
- [ ] 投稿削除時に添付ファイルも削除される