# 機能設計書 31-Cloudinary連携

## 概要

Cloudinaryメディアライブラリとの統合機能について記述する。Cloudinaryの外部メディアウィジェットをDecap CMS内で利用可能にし、画像・動画のアップロードおよび管理をCloudinary経由で行えるようにする。

### 本機能の処理概要

Cloudinaryメディアライブラリ連携は、外部サービスであるCloudinaryのMedia Library Widgetを、Decap CMSのメディア管理インターフェースとして統合する機能である。ユーザーはCMS管理画面からCloudinaryに保存されたメディアアセットを選択・挿入できる。

**業務上の目的・背景**：CMSでコンテンツを編集する際、画像や動画などのメディアファイルの管理は重要な課題である。Cloudinaryは画像・動画の最適化・変換・CDN配信を提供するクラウドサービスであり、これと連携することで、静的サイトのメディア管理をCloudinaryに委譲し、高度な画像変換やCDN配信を活用できるようになる。

**機能の利用シーン**：エントリ編集画面でメディアフィールド（画像・ファイルウィジェット）を操作する際、またはグローバルナビのメディアボタンからメディアライブラリを開く際に利用される。Cloudinaryが設定されている場合、Cloudinary独自のメディアライブラリウィジェットが表示され、既存アセットの参照・新規アップロード・画像変換の適用などが可能になる。

**主要な処理内容**：
1. Cloudinaryメディアライブラリスクリプトの動的読み込み（CDN経由）
2. 設定値のマージ（デフォルト設定 + ユーザー設定 + 強制設定）
3. Cloudinaryメディアライブラリインスタンスの生成
4. メディアアセット選択時のURL解決とCMSへの挿入処理
5. ウィジェットの表示／非表示制御

**関連システム・外部連携**：Cloudinary Media Library Widget（外部JavaScriptライブラリ `https://media-library.cloudinary.com/global/all.js`）との連携。Cloudinaryアカウントの設定（cloud_name、api_key等）が必要。

**権限による制御**：特になし。Cloudinaryアカウントの権限はCloudinary側で管理される。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 9 | メディアライブラリ画面 | 参照画面 | Cloudinary連携時のメディアライブラリ表示。外部メディアライブラリとしてCloudinaryウィジェットを表示 |

## 機能種別

外部連携 / メディア管理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| options.config | Object | No | Cloudinary Widget設定オブジェクト（cloud_name、api_key、multiple等） | なし |
| options.use_secure_url | boolean | No | HTTPS URLを使用するか（デフォルト: true） | なし |
| options.use_transformations | boolean | No | 変換後のURLを使用するか（デフォルト: true） | なし |
| options.output_filename_only | boolean | No | ファイル名のみを出力するか（デフォルト: false） | なし |
| handleInsert | Function | Yes | メディア選択時のコールバック関数 | なし |

### 入力データソース

- `config.yml` のmedia_libraryセクション（Cloudinary設定）
- Cloudinary CDN（外部スクリプト）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| url | string / string[] | 選択されたアセットのURL（単一または配列） |

### 出力先

- エントリのメディアフィールド（handleInsertコールバック経由）

## 処理フロー

### 処理シーケンス

```
1. init関数の実行
   └─ config.ymlのmedia_library設定を受け取る
2. 設定値のマージ
   └─ defaultOptions + ユーザー設定 → resolvedOptions
   └─ defaultConfig + ユーザーconfig + enforcedConfig → cloudinaryConfig
3. Cloudinaryスクリプトの読み込み
   └─ loadScript('https://media-library.cloudinary.com/global/all.js')
4. Cloudinaryメディアライブラリインスタンスの生成
   └─ window.cloudinary.createMediaLibrary(config, { insertHandler })
5. APIオブジェクトの返却
   └─ show(), hide(), enableStandalone() メソッドを提供
6. ユーザーがメディアを選択した場合
   └─ insertHandler が呼ばれ、アセットURLを解決
   └─ getAssetUrl でURL形式を決定（secure_url / url / filename_only）
   └─ handleInsert にURLを渡す
```

### フローチャート

```mermaid
flowchart TD
    A[init呼び出し] --> B[設定値マージ]
    B --> C[Cloudinaryスクリプト読み込み]
    C --> D[createMediaLibrary実行]
    D --> E[APIオブジェクト返却]
    E --> F{show呼び出し?}
    F -->|Yes| G[Cloudinaryウィジェット表示]
    G --> H{アセット選択?}
    H -->|Yes| I[insertHandler実行]
    I --> J{output_filename_only?}
    J -->|Yes| K[public_id.format を返却]
    J -->|No| L{use_transformations && derived?}
    L -->|Yes| M[derived URLを使用]
    L -->|No| N[元のURLを使用]
    M --> O{use_secure_url?}
    N --> O
    K --> P[handleInsert呼び出し]
    O -->|Yes| Q[secure_urlを使用]
    O -->|No| R[urlを使用]
    Q --> P
    R --> P
    F -->|hide| S[Cloudinaryウィジェット非表示]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-31-01 | 強制設定の上書き禁止 | button_class, inline_container, insert_transformation, z_indexはユーザー設定で上書きできない | 常時 |
| BR-31-02 | 複数選択制御 | allowMultipleがfalseの場合、instanceConfig.multipleをfalseに設定する | showメソッド呼び出し時 |
| BR-31-03 | URL形式の決定 | output_filename_only=trueの場合はpublic_id.format形式、そうでない場合はuse_secure_urlとuse_transformationsに基づきURLを選択 | アセットURL解決時 |
| BR-31-04 | 単一/配列の判定 | config.multipleがtrueまたはアセット数が2以上の場合は配列として返却、それ以外は単一URLとして返却 | insertHandler実行時 |

### 計算ロジック

特になし

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

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

データベース操作なし。Cloudinary側のアセット管理はCloudinaryの外部サービスで行われる。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | スクリプト読み込みエラー | Cloudinary CDNスクリプトの読み込みに失敗した場合 | loadScript内でエラーハンドリング。ネットワーク接続を確認 |
| - | 設定エラー | cloud_nameやapi_keyが未設定の場合 | Cloudinaryウィジェット側でエラー表示 |

### リトライ仕様

特にリトライ機構はない。スクリプトの読み込みが失敗した場合はページの再読み込みが必要。

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

トランザクション管理なし（外部サービス連携のため）

## パフォーマンス要件

- Cloudinaryスクリプトの初回読み込みはネットワーク速度に依存
- スクリプトはinit時に一度だけ読み込まれる

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

- Cloudinaryの認証情報（api_key等）はconfig.ymlに記載されるため、公開リポジトリでは注意が必要
- use_secure_urlがデフォルトでtrueのため、HTTPSが推奨される
- 外部スクリプト（Cloudinary CDN）を読み込むため、CSPポリシーの考慮が必要

## 備考

- enableStandalone()がtrueを返すため、グローバルナビの「Media」ボタンからも利用可能
- Cloudinaryの画像変換（リサイズ、フィルター等）はCloudinary側で管理される

---

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

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

### 推奨読解順序

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

Cloudinary連携は比較的シンプルなモジュールで、主要なデータ構造は設定オブジェクトとアセットオブジェクトである。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | index.js | `packages/decap-cms-media-library-cloudinary/src/index.js` | defaultOptions（4-8行目）: 統合オプションのデフォルト値。enforcedConfig（13-18行目）: 上書き禁止の設定値。defaultConfig（20-22行目）: Cloudinaryウィジェットのデフォルト設定 |

**読解のコツ**: `options`はDecap CMS側の統合設定（use_secure_url等）、`config`はCloudinaryウィジェット自体の設定（multiple、cloud_name等）という区別が重要。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | index.js | `packages/decap-cms-media-library-cloudinary/src/index.js` | init関数（48-82行目）がエントリーポイント。外部メディアライブラリのライフサイクル管理 |

**主要処理フロー**:
1. **48行目**: init関数の定義。optionsとhandleInsertを受け取る
2. **53-57行目**: 設定のマージ処理。enforcedConfigは最後に適用され上書き不可
3. **59行目**: loadScriptでCloudinaryスクリプトをCDNから読み込み
4. **61-64行目**: insertHandler定義。アセット選択時のコールバック
5. **66行目**: window.cloudinary.createMediaLibraryでインスタンス生成
6. **68-81行目**: show/hide/enableStandaloneメソッドを持つAPIオブジェクト返却

#### Step 3: アセットURL解決ロジック

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | index.js | `packages/decap-cms-media-library-cloudinary/src/index.js` | getAssetUrl関数（24-46行目）がURL解決の中核ロジック |

**主要処理フロー**:
- **29-31行目**: output_filename_only=trueの場合はpublic_id.format形式で返却
- **37行目**: derivedプロパティとuse_transformationsに基づくURLオブジェクト選択
- **43行目**: use_secure_urlに基づきsecure_urlかurlを選択

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

```
CMS MediaLibrary制御
    │
    ├─ cloudinaryMediaLibrary.init({ options, handleInsert })
    │      ├─ loadScript('https://media-library.cloudinary.com/global/all.js')
    │      ├─ window.cloudinary.createMediaLibrary(config, { insertHandler })
    │      └─ return { show, hide, enableStandalone }
    │
    ├─ show({ config, allowMultiple })
    │      └─ mediaLibrary.show(mergedConfig)
    │
    ├─ hide()
    │      └─ mediaLibrary.hide()
    │
    └─ insertHandler(data)
           ├─ data.assets.map(asset => getAssetUrl(asset, options))
           └─ handleInsert(urls)
```

### データフロー図

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

config.yml            ─▶ init()                      ─▶ APIオブジェクト
  media_library:          ├─ 設定マージ                    { show, hide,
    name: cloudinary      ├─ スクリプト読み込み               enableStandalone }
    config:               └─ インスタンス生成
      cloud_name: xxx

ユーザー操作           ─▶ show()                     ─▶ Cloudinaryウィジェット表示
  (メディア選択)

Cloudinaryアセット     ─▶ insertHandler()            ─▶ handleInsert(url)
  { secure_url,           ├─ getAssetUrl()               ─▶ エントリフィールドに挿入
    public_id,            └─ 単一/配列判定
    format, ... }
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| index.js | `packages/decap-cms-media-library-cloudinary/src/index.js` | ソース | Cloudinary連携のメイン実装。init/show/hide/enableStandaloneの定義 |
| index.spec.js | `packages/decap-cms-media-library-cloudinary/src/__tests__/index.spec.js` | テスト | 設定マージ、insertHandler、show/hideの動作テスト |
| mediaLibrary.ts | `packages/decap-cms-core/src/mediaLibrary.ts` | ソース | 外部メディアライブラリの初期化・表示制御の上位モジュール |
| registry.js | `packages/decap-cms-core/src/lib/registry.js` | ソース | メディアライブラリの登録管理（registerMediaLibrary） |
