# 機能設計書 23-編集保存

## 概要

本ドキュメントは、Wikipedia iOSアプリケーションにおける編集保存機能の設計を記載する。この機能により、ユーザーは編集したウィキテキストをWikipediaに公開（保存）できる。

### 本機能の処理概要

編集保存機能は、編集要約の入力、マイナー編集フラグの設定、ウォッチリスト追加設定を行い、編集内容をWikipedia APIに送信して公開する。

**業務上の目的・背景**：Wikipediaへの編集を完了するための最終ステップとして、適切な編集メタデータ（要約、マイナー編集、ウォッチリスト）を付与し、ライセンス同意を確認した上で公開を実行する。編集の品質と追跡可能性を確保する。

**機能の利用シーン**：エディタまたはプレビューから遷移し、編集要約を入力して「公開」ボタンをタップする。CAPTCHA認証が要求された場合は、認証を完了してから再度公開を試みる。

**主要な処理内容**：
1. 編集要約の入力（定型文選択または自由入力）
2. マイナー編集フラグの設定
3. ウォッチリスト追加設定
4. ライセンス同意の表示（CC BY-SA 4.0、GFDL）
5. 非ログインユーザーへの一時アカウント警告
6. ウィキテキストのアップロード（WikiTextSectionUploader）
7. CAPTCHA認証の処理
8. AbuseFilterエラーの処理（警告/禁止）
9. 編集成功・失敗の通知

**関連システム・外部連携**：MediaWiki Action API（edit）、WikiTextSectionUploader、WMFCaptchaViewController

**権限による制御**：ログイン状態に応じてウォッチリスト追加の表示/非表示を制御。ブロックされたユーザーは編集不可。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | エディタ画面 | 呼び出し元 | editorSavePreviewフローでの遷移 |
| - | プレビュー画面 | 呼び出し元 | editorPreviewSaveフローでの遷移 |
| - | 保存画面 | 主画面 | 編集メタデータ入力と公開 |
| - | CAPTCHA画面 | 埋め込み | CAPTCHA認証 |
| - | AbuseFilter警告パネル | モーダル | 編集警告の表示 |
| - | AbuseFilter禁止パネル | モーダル | 編集禁止の表示 |
| - | ブロックパネル | モーダル | ブロック通知の表示 |
| - | 一時アカウント説明シート | ボトムシート | 一時アカウントの説明 |

## 機能種別

データ送信処理、フォーム入力、認証処理、UI制御

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| pageURL | URL? | Yes | 編集対象の記事URL | 有効なWikipedia記事URL |
| sectionID | Int? | No | 編集対象のセクションID | nil（全体）または0以上の整数 |
| languageCode | String? | No | 言語コード | 有効な言語コード |
| wikitext | String | Yes | 保存対象のウィキテキスト | 空文字列以外 |
| dataStore | MWKDataStore? | Yes | データストア | 非nil |
| source | EditorViewController.Source? | No | 編集元種別 | .article または .talk |
| needsWebPreviewButton | Bool | Yes | Webプレビューボタン表示フラグ | true/false |
| needsSuppressPosting | Bool | Yes | 実際の投稿を抑制するフラグ | true/false |
| editTags | [WMFEditTag]? | No | 編集タグ配列 | nil または有効なタグ配列 |
| cannedSummaryTypes | [EditSummaryViewCannedButtonType] | Yes | 定型要約タイプ配列 | デフォルト[.typo, .grammar, .link] |
| savedData | SaveData? | No | 前回入力データ | キャンセル→再開時に復元 |

### 入力データソース

- EditorViewController/EditPreviewViewController（ウィキテキスト、各種設定）
- EditSummaryViewController（編集要約入力）
- WMFCaptchaViewController（CAPTCHA回答）
- WMFArticleDataController（ウォッチリスト状態）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| EditorChanges | struct | 新リビジョンID含む編集結果 |
| SaveData | struct | 保存時のメタデータ（要約、マイナー、ウォッチ） |
| needsNewTempAccountToast | Bool? | 新規一時アカウント作成通知要否 |

### 出力先

- EditSaveViewControllerDelegate（編集結果通知）
- MediaWiki API（ウィキテキスト送信）
- EditAttemptFunnel（ログ記録）

## 処理フロー

### 処理シーケンス

```
1. 保存画面の初期化
   ├─ EditSummaryViewControllerの埋め込み
   ├─ WMFCaptchaViewControllerの埋め込み
   ├─ ウォッチリスト状態の取得
   └─ 一時アカウント状態の確認

2. viewDidAppear
   └─ 非ログインユーザーへの一時アカウント警告表示

3. ユーザー入力
   ├─ 編集要約の入力（定型文/自由入力）
   ├─ マイナー編集トグル
   └─ ウォッチリスト追加トグル

4. 公開ボタンタップ
   ├─ ログ記録（EditAttemptFunnel）
   └─ WikiTextSectionUploader.uploadWikiText()

5. 結果処理
   ├─ 成功時
   │   ├─ EditorChanges生成
   │   ├─ 成功ログ記録
   │   └─ delegate.editSaveViewControllerDidSave()
   │
   └─ エラー時
       ├─ CAPTCHA要求：CAPTCHA画面表示
       ├─ AbuseFilter警告：警告パネル表示（再投稿可能）
       ├─ AbuseFilter禁止：禁止パネル表示
       ├─ ブロック：ブロックパネル表示
       ├─ 保護ページ：エラー表示
       └─ その他：汎用エラー表示
```

### フローチャート

```mermaid
flowchart TD
    A[保存画面開始] --> B[要約・オプション入力]
    B --> C[公開ボタンタップ]
    C --> D{needsSuppressPosting?}
    D -->|Yes| E[ダミー成功]
    D -->|No| F[WikiTextSectionUploader]
    F --> G{結果}
    G -->|成功| H[EditorChanges生成]
    H --> I[delegate通知]
    G -->|CAPTCHA| J[CAPTCHA表示]
    J --> K[CAPTCHA入力]
    K --> C
    G -->|AbuseFilter警告| L[警告パネル]
    L --> M{続行?}
    M -->|Yes| C
    M -->|No| N[キャンセル]
    G -->|AbuseFilter禁止| O[禁止パネル]
    O --> N
    G -->|ブロック| P[ブロックパネル]
    P --> N
    G -->|その他エラー| Q[エラー表示]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-001 | ウォッチリスト表示制御 | 非ログインユーザーにはウォッチリスト追加オプションを非表示 | authStateIsPermanent = false時 |
| BR-002 | ログインリンク表示 | 非ログインユーザーにログインリンクを表示 | authStateIsPermanent = false時 |
| BR-003 | ライセンス同意 | 公開により CC BY-SA 4.0 と GFDL への同意とみなす | 常時 |
| BR-004 | 一時アカウント警告 | 一時アカウント対応Wikiで非ログイン時に警告表示 | wikiHasTempAccounts = true かつ未ログイン時 |
| BR-005 | CAPTCHA再入力 | CAPTCHA失敗時は再入力を求める | needsCaptchaエラー発生時 |
| BR-006 | AbuseFilter警告許可 | AbuseFilter警告は再投稿を許可 | abuseFilterWarningエラー発生時 |
| BR-007 | AbuseFilter禁止 | AbuseFilter禁止は編集不可 | abuseFilterDisallowedエラー発生時 |

### 計算ロジック

該当なし

## データベース操作仕様

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| ウォッチリスト状態取得 | WMFArticleDataController | SELECT | 現在の記事のウォッチ状態取得 |

### テーブル別操作詳細

#### WMFArticleDataController

| 操作 | 項目 | 更新値・取得条件 | 備考 |
|-----|------|-----------------|------|
| SELECT | watched | 記事タイトルに基づく | ウォッチリストトグルの初期値設定 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| needsCaptcha | CAPTCHA要求 | CAPTCHA認証が必要 | CAPTCHA画面表示、認証後に再送信 |
| abuseFilterWarning | AbuseFilter警告 | 編集が警告対象 | 警告パネル表示、ユーザー判断で再送信可能 |
| abuseFilterDisallowed | AbuseFilter禁止 | 編集が禁止対象 | 禁止パネル表示、編集不可 |
| abuseFilterOther | AbuseFilterその他 | その他のAbuseFilterエラー | 警告パネル表示 |
| blocked | ブロック | ユーザーがブロックされている | ブロックパネル表示 |
| protectedPage | 保護ページ | ページが保護されている | エラーアラート表示 |
| server | サーバーエラー | サーバー側でエラー発生 | エラーアラート表示 |
| unknown | 不明エラー | 上記以外のエラー | 汎用エラーアラート表示 |
| connectionError | 接続エラー | ネットワーク接続なし | エラーアラート表示 |

### リトライ仕様

- CAPTCHA: ユーザー入力後に自動リトライ
- AbuseFilter警告: ユーザー確認後に手動リトライ可能
- その他: 自動リトライなし、ユーザーが再操作

## トランザクション仕様

MediaWiki APIへの編集リクエストは原子的に処理される。途中失敗時はロールバックされ、成功時のみ新リビジョンが生成される。

## パフォーマンス要件

- 編集アップロード: 通常10秒以内（ネットワーク状況依存）
- 「Publishing...」アラートを表示してユーザーに待機を通知

## セキュリティ考慮事項

- ライセンス同意の明示的表示
- CAPTCHA認証によるボット対策
- AbuseFilterによる悪意ある編集の検出と阻止
- ブロックユーザーの編集防止
- 一時アカウントに関する警告表示（プライバシー配慮）

## 備考

- needsSuppressPostingはテスト/プレビュー用で実際の投稿を行わない
- SaveData構造体でキャンセル→再開時の入力データを保持
- EditSummaryViewControllerは編集要約入力UIを担当する子ViewControllerとして埋め込まれる

---

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

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

### 推奨読解順序

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

保存画面で使用される構造体とデリゲートプロトコルを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | EditSaveViewController.swift | `Wikipedia/Code/EditSaveViewController.swift` | EditorChanges, SaveData, デリゲートプロトコル（L6-33） |

**読解のコツ**: EditorChangesは編集成功時の結果、SaveDataはキャンセル時の入力保持に使用される。

#### Step 2: 初期化とライフサイクルを理解する

保存画面の初期化処理を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | EditSaveViewController.swift | `Wikipedia/Code/EditSaveViewController.swift` | viewDidLoad, viewDidAppear（L187-300） |

**主要処理フロー**:
- **L187-234**: viewDidLoad - ボタン設定、EditSummaryVC/CaptchaVC埋め込み、ウォッチリスト状態取得
- **L242-300**: viewDidAppear - 一時アカウント警告表示

#### Step 3: 保存処理を理解する

ウィキテキストのアップロード処理を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | EditSaveViewController.swift | `Wikipedia/Code/EditSaveViewController.swift` | save()（L435-498） |

**主要処理フロー**:
- **L436-443**: 認証状態の確認とauthState設定
- **L444**: 「Publishing...」アラート表示
- **L451-452**: マイナー編集/ウォッチリスト設定取得
- **L474-478**: needsSuppressPosting時のダミー成功処理
- **L482-496**: WikiTextSectionUploader.uploadWikiText()呼び出し

#### Step 4: 成功/エラー処理を理解する

編集結果のハンドリングを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | EditSaveViewController.swift | `Wikipedia/Code/EditSaveViewController.swift` | handleEditSuccess, handleEditFailure（L500-659） |

**主要処理フロー**:
- **L500-549**: handleEditSuccess - 新規一時アカウントチェック、EditorChanges生成、delegate通知
- **L551-659**: handleEditFailure - エラータイプ別の処理（CAPTCHA、AbuseFilter、ブロック等）

#### Step 5: 編集要約入力を理解する

EditSummaryViewControllerとの連携を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | EditSummaryViewController.swift | `Wikipedia/Code/EditSummaryViewController.swift` | プロトコル、定型ボタン（L4-26） |
| 5-2 | EditSaveViewController.swift | `Wikipedia/Code/EditSaveViewController.swift` | EditSummaryViewDelegate実装（L738-741） |

**主要処理フロー**:
- **EditSummaryViewController L4-7**: EditSummaryViewDelegateプロトコル定義
- **EditSummaryViewController L9-26**: EditSummaryViewCannedButtonType定型要約タイプ
- **EditSaveViewController L738-741**: summaryChanged - 要約テキスト更新

#### Step 6: CAPTCHA処理を理解する

CAPTCHA認証フローを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 6-1 | EditSaveViewController.swift | `Wikipedia/Code/EditSaveViewController.swift` | WMFCaptchaViewControllerDelegate（L670-687） |

**主要処理フロー**:
- **L558-572**: needsCaptchaエラー時のCAPTCHA画面表示
- **L681-683**: captchaKeyboardReturnKeyTapped - CAPTCHA入力後の再save()

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

```
EditorViewController / EditPreviewViewController
    │
    └─ showEditSave() / editPreviewViewControllerDidTapNext()
           │
           └─ EditSaveViewController
                  │
                  ├─ viewDidLoad()
                  │      ├─ setupButtonsAndTitle()
                  │      ├─ EditSummaryViewController（子VC）
                  │      ├─ WMFCaptchaViewController（子VC）
                  │      └─ fetchWatchlistStatusAndUpdateToggle()
                  │
                  ├─ viewDidAppear()
                  │      └─ 一時アカウント警告表示
                  │
                  └─ goForward() / save()
                         │
                         └─ WikiTextSectionUploader.uploadWikiText()
                                │
                                ├─ handleEditSuccess()
                                │      └─ delegate.editSaveViewControllerDidSave()
                                │
                                └─ handleEditFailure()
                                       ├─ CAPTCHA → mode = .captcha
                                       ├─ AbuseFilter → wmf_showAbuseFilterPanel()
                                       └─ Blocked → wmf_showBlockedPanel()
```

### データフロー図

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

wikitext (String)     ───▶ EditSaveViewController          ───▶ MediaWiki API
                              │
                              ├─ summaryText (要約)
                              ├─ isMinorEdit (マイナー編集)
                              └─ addToWatchlist (ウォッチリスト)
                                      │
                                      ▼
                           WikiTextSectionUploader
                                      │
                              ┌───────┴───────┐
                              ▼               ▼
                           成功            エラー
                              │               │
                              ▼               ▼
                    EditorChanges    エラーハンドリング
                    (newRevisionID)        │
                              │       ┌────┼────┬────┐
                              ▼       ▼    ▼    ▼    ▼
                       delegate   CAPTCHA ABF  Block Other
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| EditSaveViewController.swift | `Wikipedia/Code/EditSaveViewController.swift` | ソース | 保存画面メインコントローラー |
| EditSummaryViewController.swift | `Wikipedia/Code/EditSummaryViewController.swift` | ソース | 編集要約入力UI |
| EditorViewController.swift | `Wikipedia/Code/EditorViewController.swift` | ソース | 保存画面への遷移とデリゲート実装 |
| EditPreviewViewController.swift | `Wikipedia/Code/EditPreviewViewController.swift` | ソース | 保存画面への遷移 |
| WikiTextSectionUploader.swift | `WMF Framework/` | ソース | ウィキテキストアップロード |
| WMFCaptchaViewController.swift | `Wikipedia/Code/WMFCaptchaViewController.swift` | ソース | CAPTCHA認証UI |
| WMFArticleDataController.swift | `WMFData/` | ソース | ウォッチリスト状態取得 |
| TempAccountSheetCoordinator.swift | `Wikipedia/Code/TempAccountSheetCoordinator.swift` | ソース | 一時アカウント説明シート |
