# 機能設計書 27-メディア削除

## 概要

本ドキュメントは、Decap CMSのメディア削除機能について、ドラフトファイルと公開済みファイルの両方に対応した削除処理の詳細を記述した機能設計書である。

### 本機能の処理概要

メディアファイルの削除処理を担当する機能である。deleteMedia関数がファイルの種別（ドラフト/公開済み）やバックエンド種別（Integration/通常）に応じて削除処理を分岐し、Redux Storeとバックエンドの両方からファイルを除去する。

**業務上の目的・背景**：不要になったメディアファイルをリポジトリから削除し、ストレージの整理を行うための機能。ドラフト編集中のファイルはメモリ上からのみ削除し、公開済みファイルはバックエンドのGitリポジトリまたはアセットストアからも削除する。

**機能の利用シーン**：
- メディアライブラリ画面でファイルを選択して削除ボタンを押下した場合
- エントリ編集中にドラフトファイルを除去する場合
- 重複ファイルのアップロード時に既存ファイルを上書き削除する場合

**主要な処理内容**：
1. アセットストアIntegration使用時: provider.delete(file.id)でAPI経由削除
2. ドラフトファイルの場合: removeAsset + removeDraftEntryMediaFileでメモリから除去
3. 公開済みファイルの場合: removeAsset + backend.deleteMedia + Redux Store更新
4. 編集中ドラフトがある場合、公開済みファイル削除後にドラフトのmediaFilesからも除去

**関連システム・外部連携**：Git系バックエンド、アセットストアIntegration。

**権限による制御**：認証済みユーザーのみが削除可能。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 9 | メディアライブラリ画面 | 主画面 | 選択したメディアファイルの削除処理。確認ダイアログ表示後にdeleteMediaアクションを実行 |

## 機能種別

CRUD操作（Delete）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| file | MediaFile | Yes | 削除対象のメディアファイル | key, id, path, draftプロパティを含む |
| privateUpload | boolean | No | プライベートアップロードフラグ | アセットストアIntegration使用時のみ |

### 入力データソース

- メディアライブラリ画面で選択されたファイル
- Redux Store: state.config（バックエンド設定）
- Redux Store: state.entryDraft（編集中エントリ）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| type | string | MEDIA_DELETE_REQUEST / SUCCESS / FAILURE |
| payload.file | MediaFile | 削除されたファイル情報 |
| payload.privateUpload | boolean | プライベートアップロードフラグ |

### 出力先

- Redux Store: state.medias（AssetProxy除去: removeAsset）
- Redux Store: state.mediaLibrary.files（ファイル一覧から除去: mediaDeleted）
- Redux Store: state.entryDraft.entry.mediaFiles（ドラフト時: removeDraftEntryMediaFile）
- Git バックエンド: リポジトリからファイルを削除
- Redux Store: state.mediaLibrary.displayURLs（該当IDのdisplayURLを削除）

## 処理フロー

### 処理シーケンス

```
1. deleteMedia(file, opts)が呼び出される
   └─ MediaLibrary.jsのhandleDeleteまたはpersistMedia内の重複上書き時
2. 処理パスの分岐:
   a. アセットストアIntegration使用時:
      └─ MEDIA_DELETE_REQUESTディスパッチ
      └─ provider.delete(file.id)
      └─ 成功: mediaDeleted(file, {privateUpload})
      └─ 失敗: エラー通知 + mediaDeleteFailed({privateUpload})
   b. ドラフトファイルの場合（file.draft === true）:
      └─ removeAsset(file.path)でAssetProxyをメモリから除去
      └─ removeDraftEntryMediaFile({id: file.id})でドラフトから除去
   c. 公開済みファイルの場合:
      └─ MEDIA_DELETE_REQUESTディスパッチ
      └─ removeAsset(file.path)
      └─ backend.deleteMedia(state.config, file.path)
      └─ mediaDeleted(file)
      └─ editingDraftがある場合: removeDraftEntryMediaFile({id: file.id})
```

### フローチャート

```mermaid
flowchart TD
    A[deleteMedia呼び出し] --> B{Integration使用?}
    B -->|Yes| C[provider.delete実行]
    C --> D{成功?}
    D -->|Yes| E[mediaDeleted dispatch]
    D -->|No| F[エラー通知 + mediaDeleteFailed]
    B -->|No| G{file.draft?}
    G -->|Yes| H[removeAsset + removeDraftEntryMediaFile]
    G -->|No| I[DELETE_REQUEST dispatch]
    I --> J[removeAsset]
    J --> K[backend.deleteMedia]
    K --> L[mediaDeleted dispatch]
    L --> M{editingDraft?}
    M -->|Yes| N[removeDraftEntryMediaFile]
    M -->|No| O[終了]
    N --> O
    H --> O
    E --> O
    F --> O
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-27-01 | ドラフトファイルのメモリ削除 | ドラフトファイルはバックエンドへの削除リクエストを行わず、メモリからのみ除去 | file.draft === true |
| BR-27-02 | 公開済みファイルのバックエンド削除 | 公開済みファイルはバックエンドAPIを通じてGitリポジトリから削除 | file.draft !== true かつ Integration非使用 |
| BR-27-03 | 編集中ドラフトへの反映 | 公開済みファイル削除後、現在編集中のドラフトがある場合はドラフトのmediaFilesからも除去 | editingDraft === true |
| BR-27-04 | displayURL同時削除 | ファイル削除時、該当ファイルIDのdisplayURLもRedux Storeから削除 | MEDIA_DELETE_SUCCESS時 |
| BR-27-05 | 確認ダイアログ | メディアライブラリからの削除ではwindow.confirm()で確認ダイアログを表示 | MediaLibrary.jsのhandleDelete経由時 |

### 計算ロジック

特になし。

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| アセット除去 | Redux Store: state.medias | DELETE | removeAssetでAssetProxyを除去 |
| ドラフトファイル除去 | Redux Store: state.entryDraft.entry.mediaFiles | DELETE | removeDraftEntryMediaFileでドラフトから除去 |
| ファイル一覧更新 | Redux Store: state.mediaLibrary.files | DELETE | keyまたはidでフィルタして除去 |
| displayURL削除 | Redux Store: state.mediaLibrary.displayURLs | DELETE | 該当IDのdisplayURLを削除 |
| バックエンド削除 | Git Repository | DELETE | backend.deleteMediaでファイルを削除 |

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

#### Redux Store: state.mediaLibrary

| 操作 | 項目（キー） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | isDeleting | true -> false | DELETE_REQUEST -> DELETE_SUCCESS |
| DELETE | files | keyまたはidでフィルタ | MEDIA_DELETE_SUCCESS時 |
| DELETE | displayURLs[id] | 該当IDを削除 | MEDIA_DELETE_SUCCESS時 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | Integration削除エラー | アセットストアAPIの通信失敗 | エラー通知 + MEDIA_DELETE_FAILUREディスパッチ |
| - | バックエンド削除エラー | Git APIの通信失敗 | エラー通知 + MEDIA_DELETE_FAILUREディスパッチ |

### リトライ仕様

自動リトライは実装されていない。

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

ドラフトファイルの削除はメモリ上のみの操作。公開済みファイルの削除ではremoveAsset（メモリ）とbackend.deleteMedia（バックエンド）が順次実行されるが、途中失敗時のロールバックは行われない。

## パフォーマンス要件

特別なパフォーマンス要件はなし。

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

- 認証済みユーザーのみが削除可能
- 確認ダイアログによるミスオペレーション防止

## 備考

- privateUpload変更時のリデューサーガード: privateUploadの値が変わった場合、削除リクエスト結果を無視する保護ロジックがリデューサーに実装されている
- ドラフトファイルの削除ではMEDIA_DELETE_REQUESTアクションはディスパッチされない

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | mediaLibrary.ts (reducer) | `packages/decap-cms-core/src/reducers/mediaLibrary.ts` | **205-230行目**: MEDIA_DELETE_REQUEST/SUCCESS/FAILUREリデューサー処理。filesのフィルタとdisplayURLs削除 |

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | mediaLibrary.ts (actions) | `packages/decap-cms-core/src/actions/mediaLibrary.ts` | **311-366行目**: deleteMedia関数。3パス分岐の削除ロジック |

**主要処理フロー**:
1. **316行目**: selectIntegrationでアセットストア連携を確認
2. **317-335行目**: Integration使用時のprovider.delete()
3. **338-340行目**: ドラフトファイル時のremoveAsset + removeDraftEntryMediaFile
4. **341-352行目**: 公開済みファイル時のbackend.deleteMedia
5. **350-352行目**: editingDraft時のremoveDraftEntryMediaFile追加呼び出し

#### Step 3: リデューサーの状態更新を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | mediaLibrary.ts (reducer) | `packages/decap-cms-core/src/reducers/mediaLibrary.ts` | **208-222行目**: MEDIA_DELETE_SUCCESS時のfiles更新。keyまたはidでフィルタしてファイルを除去し、displayURLsから該当IDを削除 |

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

```
deleteMedia(file, opts)                    [actions/mediaLibrary.ts:311]
    |
    +-- selectIntegration(state, null, 'assetStore')
    |
    +-- [Integration] provider.delete(file.id)
    |   +-- mediaDeleted(file, {privateUpload})
    |
    +-- [ドラフト] file.draft === true
    |   +-- removeAsset(file.path)           [actions/media.ts:29]
    |   +-- removeDraftEntryMediaFile({id})   [actions/entries.ts]
    |
    +-- [公開済み]
        +-- mediaDeleting()
        +-- removeAsset(file.path)
        +-- backend.deleteMedia(config, file.path)
        +-- mediaDeleted(file)
        +-- [editingDraft] removeDraftEntryMediaFile({id})
```

### データフロー図

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

MediaFile             分岐判定                           Redux Store更新
(file)                    |                               (medias, mediaLibrary)
        |                 v
        +---------> [Integration] -> provider.delete      アセットストア削除
        |
        +---------> [ドラフト] -> メモリ除去              ドラフト更新
        |
        +---------> [公開済み] -> backend.deleteMedia     Git Repository削除
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| mediaLibrary.ts | `packages/decap-cms-core/src/actions/mediaLibrary.ts` | ソース | deleteMedia関数（メインロジック） |
| media.ts | `packages/decap-cms-core/src/actions/media.ts` | ソース | removeAssetアクション |
| entries.ts | `packages/decap-cms-core/src/actions/entries.ts` | ソース | removeDraftEntryMediaFile関数 |
| mediaLibrary.ts | `packages/decap-cms-core/src/reducers/mediaLibrary.ts` | ソース | 削除関連リデューサー処理 |
| entries.ts | `packages/decap-cms-core/src/reducers/entries.ts` | ソース | selectEditingDraft関数 |
