# 機能設計書 28-メディアインサート

## 概要

本ドキュメントは、Decap CMSのメディアインサート機能について、メディアファイルをエントリのフィールドに挿入する処理の詳細を記述した機能設計書である。

### 本機能の処理概要

メディアライブラリで選択されたファイルをエントリのフィールド（画像/ファイルウィジェット）に挿入する機能である。insertMedia関数がファイルパスを公開パスに変換し、Redux Storeに挿入パスを記録することで、呼び出し元のウィジェットがファイルを参照できるようにする。

**業務上の目的・背景**：エントリ編集中にメディアファイル（画像や添付ファイル）をフィールドに関連付ける必要がある。メディアライブラリで選択したファイルのパスを、コレクション設定やフィールド設定に基づく適切な公開パスに変換してフィールドに設定する。

**機能の利用シーン**：
- メディアライブラリ画面でファイルを選択し「挿入」ボタンを押下した場合
- 画像ウィジェットやファイルウィジェットからメディアライブラリを開いてファイルを選択した場合
- 複数ファイルの同時挿入（配列パスのサポート）

**主要な処理内容**：
1. insertMedia関数がmediaPath（単一またはarray）とfieldを受け取る
2. selectMediaFilePublicPathで各パスを公開パスに変換
3. MEDIA_INSERTアクションをディスパッチ
4. リデューサーでcontrolMedia[controlID]に挿入パスを記録
5. 単一値の場合と配列値（複数選択）の場合でリデューサー処理が分岐
6. replaceIndex指定時は配列の特定インデックスを置換

**関連システム・外部連携**：特になし。フロントエンドのRedux State操作のみ。

**権限による制御**：特になし。メディアライブラリの挿入機能はcanInsert（controlIDの存在）で制御される。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 9 | メディアライブラリ画面 | 主画面 | メディアファイルのエントリフィールドへの挿入処理。insertMediaアクションでファイルパスを保存 |

## 機能種別

データ連携 / UI操作

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| mediaPath | string or string[] | Yes | 挿入するメディアファイルのパス（単一または配列） | ファイルパスとして有効であること |
| field | EntryField or undefined | No | 呼び出し元フィールド定義 | フィールド固有の公開パス変換に使用 |

### 入力データソース

- メディアライブラリのselectedFile.path
- Redux Store: state.config（設定情報）
- Redux Store: state.entryDraft（編集中エントリ）
- Redux Store: state.collections（コレクション定義）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| type | string | MEDIA_INSERT |
| payload.mediaPath | string or string[] | 公開パスに変換されたメディアパス |

### 出力先

- Redux Store: state.mediaLibrary.controlMedia[controlID]（挿入パスの記録）

## 処理フロー

### 処理シーケンス

```
1. insertMedia(mediaPath, field)が呼び出される
   └─ MediaLibrary.jsのhandleInsertから呼び出し
2. Redux Stateからconfig, entry, collectionを取得
3. パスの公開パス変換:
   a. 配列の場合: 各パスに対してselectMediaFilePublicPath()を適用
   b. 文字列の場合: selectMediaFilePublicPath()を適用
4. mediaInserted(mediaPath)でMEDIA_INSERTアクションをディスパッチ
5. リデューサーで処理:
   a. 値が配列でない場合: controlMedia[controlID]にmediaPathを設定
   b. 値が配列の場合:
      - replaceIndexが指定されている場合: valueArray[replaceIndex]を置換
      - 指定されていない場合: valueArrayに追加（push）
      - controlMedia[controlID]にvalueArrayを設定
```

### フローチャート

```mermaid
flowchart TD
    A[insertMedia呼び出し] --> B[config, entry, collection取得]
    B --> C{mediaPathは配列?}
    C -->|Yes| D[各パスにselectMediaFilePublicPath適用]
    C -->|No| E[単一パスにselectMediaFilePublicPath適用]
    D --> F[MEDIA_INSERTディスパッチ]
    E --> F
    F --> G{value は配列?}
    G -->|No| H[controlMedia に mediaPath を設定]
    G -->|Yes| I{replaceIndex指定?}
    I -->|Yes| J[配列の指定インデックスを置換]
    I -->|No| K[配列にmediaPathを追加]
    J --> L[controlMedia に配列を設定]
    K --> L
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-28-01 | 公開パス変換 | メディアファイルの内部パスをselectMediaFilePublicPathで公開パスに変換する | 常時 |
| BR-28-02 | 配列挿入サポート | mediaPathが配列の場合、各要素を個別に公開パス変換する | Array.isArray(mediaPath) |
| BR-28-03 | replaceIndex置換 | replaceIndexが数値の場合、配列の特定位置の値を置換する | typeof replaceIndex === 'number' |
| BR-28-04 | 配列追加 | replaceIndex未指定時は配列末尾にmediaPathを追加する | replaceIndex未指定かつ値が配列 |
| BR-28-05 | 挿入後モーダル閉じ | handleInsert実行後にhandleCloseが呼ばれてモーダルが閉じられる | MediaLibrary.jsのhandleInsert |

### 計算ロジック

- 公開パス: `selectMediaFilePublicPath(config, collection, path, entry, field)`

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| パス記録 | Redux Store: state.mediaLibrary.controlMedia | UPDATE | controlID配下に挿入パスを記録 |

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

#### Redux Store: state.mediaLibrary

| 操作 | 項目（キー） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | controlMedia[controlID] | string or string[] | MEDIA_INSERT時に挿入パスを記録 |

## エラー処理

### エラーケース一覧

特になし。公開パス変換とRedux State更新のみのため、エラーは基本的に発生しない。

### リトライ仕様

該当なし。

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

単一のRedux Store更新で完結する。

## パフォーマンス要件

即座のUI応答が期待される（フロントエンドのみの処理）。

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

特になし。

## 備考

- MEDIA_REMOVE_INSERTEDアクションでcontrolMedia[controlID]を空文字に戻すことが可能
- 外部メディアライブラリ（Cloudinary/Uploadcare）からの挿入はhandleInsert経由で同じMEDIA_INSERTアクションを使用
- removeInsertedMedia(controlID)で挿入された値をクリアできる

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | mediaLibrary.ts (reducer) | `packages/decap-cms-core/src/reducers/mediaLibrary.ts` | **102-125行目**: MEDIA_INSERTリデューサー処理。単一値と配列値の分岐、replaceIndex処理 |

**読解のコツ**: controlMediaはMap<string, string | string[]>構造。controlIDをキーとして挿入されたパスを保持する。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | mediaLibrary.ts (actions) | `packages/decap-cms-core/src/actions/mediaLibrary.ts` | **114-130行目**: insertMedia関数。mediaPathの公開パス変換とMEDIA_INSERTアクションのディスパッチ |

**主要処理フロー**:
1. **116-120行目**: state からconfig, entry, collectionNameを取得
2. **121-127行目**: 配列/文字列の分岐とselectMediaFilePublicPath適用
3. **128行目**: mediaInserted(mediaPath)でアクションディスパッチ

#### Step 3: 呼び出し元を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | MediaLibrary.js | `packages/decap-cms-core/src/components/MediaLibrary/MediaLibrary.js` | **220-226行目**: handleInsert関数。selectedFile.pathを取得してinsertMediaを呼び出し、handleCloseでモーダルを閉じる |

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

```
MediaLibrary.js (handleInsert)
    |
    +-- selectedFile.path 取得
    |
    +-- insertMedia(path, field)          [actions/mediaLibrary.ts:114]
    |       |
    |       +-- selectMediaFilePublicPath  [reducers/entries.ts]
    |       |
    |       +-- mediaInserted(mediaPath)   [actions/mediaLibrary.ts:426]
    |               |
    |               +-- reducer: MEDIA_INSERT [reducers/mediaLibrary.ts:102]
    |                       +-- controlMedia[controlID] = mediaPath
    |
    +-- handleClose()
        +-- closeMediaLibrary()
```

### データフロー図

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

selectedFile.path     selectMediaFilePublicPath         Redux Store
(内部パス)                |                               (controlMedia)
        |                 v
        +---------> 公開パス変換                          ウィジェット参照
                         |                               (画像/ファイルフィールド)
field定義                v
(メディアフォルダ)    MEDIA_INSERT dispatch
                         |
                         v
                    controlMedia[controlID] = publicPath
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| mediaLibrary.ts | `packages/decap-cms-core/src/actions/mediaLibrary.ts` | ソース | insertMedia関数、mediaInsertedアクションクリエーター |
| mediaLibrary.ts | `packages/decap-cms-core/src/reducers/mediaLibrary.ts` | ソース | MEDIA_INSERTリデューサー、MEDIA_REMOVE_INSERTED処理 |
| MediaLibrary.js | `packages/decap-cms-core/src/components/MediaLibrary/MediaLibrary.js` | ソース | handleInsert関数（呼び出し元） |
| entries.ts | `packages/decap-cms-core/src/reducers/entries.ts` | ソース | selectMediaFilePublicPath関数 |
