# 画面設計書 45-購読者編集

## 概要

本ドキュメントは、QuickerSite CMSのバックサイト（管理画面）における「購読者編集」画面の設計書です。

### 本画面の処理概要

個別の購読者（サブスクライバー）情報を編集するための画面です。購読者の名前、メールアドレス、アクティブ状態を変更でき、購読者を削除することも可能です。購読者一覧画面から遷移して、1件ずつ詳細な情報を確認・編集する際に使用します。

**業務上の目的・背景**：購読者リストの品質管理において、個別の購読者情報を確認・修正する必要があります。例えば、メールアドレスのタイプミス修正、名前の表記統一、手動での購読解除（bActive=false）などの操作を行います。また、登録キー（sKey）や登録日（dAdded）といった管理情報も確認でき、トラブルシューティング時に役立ちます。

**画面へのアクセス方法**：購読者一覧画面（bs_newsletterSubscribers.asp）の一覧テーブルにある「modify」リンクをクリックしてアクセスします。URLパラメータとしてiSubscriptionIDが必要です。

**主要な操作・処理内容**：
1. 購読者情報の閲覧：名前、メールアドレス、アクティブ状態、カテゴリ、登録キー、登録日を表示
2. 購読者情報の編集：名前、メールアドレス、アクティブ状態を変更
3. 購読者の削除：購読者レコードと関連する配信ログを削除

**画面遷移**：
- 遷移元：購読者一覧画面（bs_newsletterSubscribers.asp）
- 遷移先：購読者一覧画面（bs_newsletterSubscribers.asp）（保存後・削除後）

**権限による表示制御**：secondAdmin.bNewsletter権限がない場合はアクセス不可。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 69 | 購読者管理 | 主機能 | 購読者情報の詳細編集 |

## 画面種別

編集・詳細

## URL/ルーティング

```
/asp/bs_newsletterSubscriber.asp?iSubscriptionID={暗号化されたID}
```

## 入出力項目

| 項目名 | 項目ID | 型 | 必須 | 入力/出力 | 説明 |
|--------|--------|-----|------|-----------|------|
| 購読者ID | iSubscriptionID | hidden | - | 入力 | 暗号化された購読者ID |
| ポストバックフラグ | postBack | hidden | - | 入力 | フォーム送信フラグ |
| セキュリティコード | QS_secCodeHidden | hidden | - | 入力 | CSRF対策用トークン |
| 名前 | sName | text | - | 入力 | 購読者の名前（最大50文字） |
| メールアドレス | sEmail | email | 必須 | 入力 | 購読者のメールアドレス（最大50文字） |
| アクティブ状態 | bActive | checkbox | - | 入力 | 購読状態（チェック=アクティブ） |
| アクションボタン | btnaction | submit | - | 入力 | save/delete |

## 表示項目

| 項目名 | 表示位置 | 説明 |
|--------|---------|------|
| Subscribersリンク | 上部 | 購読者一覧画面へのリンク（bCanImportSubscribers時） |
| Newsletter homeリンク | 上部 | ニュースレター一覧画面へのリンク |
| Name入力欄 | フォーム | 名前入力テキストボックス |
| Email入力欄 | フォーム | メールアドレス入力（type="email"） |
| Activeチェックボックス | フォーム | アクティブ状態チェックボックス |
| Category表示 | フォーム | 所属カテゴリ名（読み取り専用） |
| Key表示 | フォーム | 登録キー（読み取り専用） |
| Date added表示 | フォーム | 登録日（読み取り専用、ヨーロッパ日付形式） |
| 必須項目説明 | フォーム下部 | 「(*) mandatory」の表示 |
| 保存ボタン | フォーム下部 | 「save」ボタン |
| 削除ボタン | フォーム下部 | 「delete」ボタン |

## イベント仕様

### 1-保存ボタン押下

**処理フロー**：
1. フォームデータがPOSTされる（btnaction="save"）
2. sEmail、sName、bActiveをフォームから取得
3. checkEmailSyntax()でメールアドレス形式を検証
4. 検証失敗時はerr_emailエラーを追加して画面再表示
5. 検証成功時はrs.update()でレコード更新
6. 購読者一覧画面にリダイレクト（iCategoryIDパラメータ付き）

### 2-削除ボタン押下

**処理フロー**：
1. JavaScript confirmで削除確認
2. 確認後、btnaction="delete"でPOST
3. tblNewsletterLogから該当購読者のログを削除
4. tblNewsletterCategorySubscriberから購読者を削除
5. 購読者一覧画面にリダイレクト（iCategoryIDパラメータ付き）

## データベース更新仕様

### 操作別データベース影響一覧

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 画面表示 | tblNewsletterCategorySubscriber | SELECT | 購読者情報取得 |
| 保存ボタン押下 | tblNewsletterCategorySubscriber | UPDATE | 購読者情報更新 |
| 削除ボタン押下 | tblNewsletterLog | DELETE | 配信ログ削除 |
| 削除ボタン押下 | tblNewsletterCategorySubscriber | DELETE | 購読者削除 |

### テーブル別更新項目詳細

#### tblNewsletterCategorySubscriber

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | * | iCustomerID=cId AND iId=decrypt(iSubscriptionID) | 購読者取得 |
| UPDATE | sEmail | lcase(trim(request.form("sEmail"))) | 小文字化・トリム処理 |
| UPDATE | sName | trim(request.form("sName")) | トリム処理 |
| UPDATE | bActive | convertBool(request.form("bActive")) | チェック状態 |
| DELETE | - | iCustomerID=cId AND iId=decrypt(iSubscriptionID) | 購読者削除 |

#### tblNewsletterLog

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| DELETE | - | iSubscriberID=decrypt(iSubscriptionID) | 配信ログ削除 |

## メッセージ仕様

| メッセージID | 種別 | 表示条件 | メッセージ内容 |
|-------------|------|---------|--------------|
| err_email | エラー | メールアドレス形式不正時 | メール形式エラー |
| (JavaScript confirm) | 確認 | 削除ボタン押下時 | "Are you sure to delete this subscription?" |

## 例外処理

| 例外条件 | 処理内容 |
|---------|---------|
| アクセス権限なし | secondAdmin.bNewsletter権限チェックに失敗した場合、エラー表示 |
| 購読者が存在しない | rs.recordcount=0の場合、購読者一覧画面にリダイレクト |
| iSubscriptionIDが不正 | decrypt処理失敗時、エラーまたは空結果 |
| メールアドレス形式不正 | err_emailエラーメッセージを表示し、更新をスキップ |

## 備考

- カテゴリ（Category）は読み取り専用で、別カテゴリへの移動は不可
- 登録キー（sKey）は購読解除リンクの認証に使用される
- 登録日（dAdded）はconvertEurodate()でヨーロッパ形式（DD/MM/YYYY）で表示
- メールアドレスは保存時に小文字に変換される
- リダイレクト時にiCategoryIDを引き継ぎ、一覧画面で同じカテゴリが選択される

---

## コードリーディングガイド

本画面を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | newsletterCategory.asp | `asp/includes/newsletterCategory.asp` | cls_newsletterCategoryクラス。sNameプロパティを確認 |

**読解のコツ**: tblNewsletterCategorySubscriberテーブルのカラム構成（sName, sEmail, bActive, sKey, dAdded, iCategoryID, iCustomerID）を理解することが重要。

#### Step 2: エントリーポイントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | bs_newsletterSubscriber.asp | `asp/bs_newsletterSubscriber.asp` | 画面表示のメインファイル。db.getDynamicRS()による動的レコードセット操作、保存・削除処理の分岐を確認 |

**主要処理フロー**:
1. **4行目**: 権限チェック（logon.hasaccess secondAdmin.bNewsletter）
2. **7-8行目**: db.getDynamicRS()でレコードセット取得
3. **9行目**: recordcount=0で存在チェック、なければリダイレクト
4. **10行目**: iCategoryIDを保持（リダイレクト用）
5. **11-23行目**: 保存処理（btnaction="save"）
   - checkEmailSyntax()でメール検証
   - rs.update()で更新
6. **24-28行目**: 削除処理（btnaction="delete"）
   - tblNewsletterLog DELETE
   - tblNewsletterCategorySubscriber DELETE
7. **28行目以降**: フォーム出力

### プログラム呼び出し階層図

```
bs_newsletterSubscriber.asp（画面表示）
    │
    ├─ begin.asp（初期化）
    │
    ├─ bs_security.asp（認証チェック）
    │      └─ logon.hasaccess(secondAdmin.bNewsletter)
    │
    ├─ db.getDynamicRS()
    │      └─ SELECT * FROM tblNewsletterCategorySubscriber
    │
    ├─ 保存処理（btnaction="save"）
    │      ├─ checkEmailSyntax(sEmail)
    │      │      └─ 失敗時 → message.AddError("err_email")
    │      ├─ rs("sEmail") = lcase(trim(...))
    │      ├─ rs("sName") = trim(...)
    │      ├─ rs("bActive") = convertBool(...)
    │      ├─ rs.update()
    │      └─ Response.Redirect → bs_newsletterSubscribers.asp
    │
    ├─ 削除処理（btnaction="delete"）
    │      ├─ db.execute("delete from tblNewsletterLog...")
    │      ├─ db.execute("delete from tblNewsletterCategorySubscriber...")
    │      └─ Response.Redirect → bs_newsletterSubscribers.asp
    │
    ├─ NewsletterCategories(iCategoryID).sName
    │      └─ カテゴリ名表示
    │
    └─ includes/footer.asp
```

### データフロー図

```
[入力]                      [処理]                           [出力]

iSubscriptionID ──────────▶ decrypt() ──────────────────────▶ SELECT購読者
（URLパラメータ）                └─ db.getDynamicRS()

フォーム入力
├─ sEmail ────────────────▶ checkEmailSyntax() ─────────────▶ rs.update()
│                                │                                │
│                                └─ 失敗 → err_email              │
├─ sName                                                          │
└─ bActive ───────────────▶ lcase/trim/convertBool ─────────▶ UPDATE実行

btnaction="delete" ───────▶ db.execute(DELETE) ─────────────▶ tblNewsletterLog
                                 │                                tblNewsletterCategorySubscriber
                                 └─ Response.Redirect

保存/削除完了 ────────────▶ Response.Redirect ──────────────▶ bs_newsletterSubscribers.asp
                                 └─ iCategoryID引き継ぎ
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| bs_newsletterSubscriber.asp | `asp/bs_newsletterSubscriber.asp` | ソース | 購読者編集画面 |
| bs_newsletterSubscribers.asp | `asp/bs_newsletterSubscribers.asp` | ソース | 購読者一覧画面 |
| bs_newsletterList.asp | `asp/bs_newsletterList.asp` | ソース | ニュースレター一覧画面 |
| newsletterCategory.asp | `asp/includes/newsletterCategory.asp` | ソース | カテゴリクラス定義 |
| customer.asp | `asp/includes/customer.asp` | ソース | 顧客クラス（NewsletterCategories） |
| bs_security.asp | `asp/bs_security.asp` | ソース | 認証・認可処理 |
| begin.asp | `asp/begin.asp` | ソース | 共通初期化処理 |
| header.asp | `asp/includes/header.asp` | テンプレート | 共通ヘッダー |
| footer.asp | `asp/includes/footer.asp` | テンプレート | 共通フッター |
