# 機能設計書 31-カスタムテーマ設定

## 概要

本ドキュメントは、Ghostのカスタムテーマ設定機能に関する設計仕様を定義する。この機能により、テーマ開発者が定義したカスタム設定項目を管理画面から編集し、サイトの外観をカスタマイズすることができる。

### 本機能の処理概要

カスタムテーマ設定は、テーマの`package.json`に定義されたカスタム設定項目をデータベースに同期し、管理画面からの編集を可能にする機能である。設定値はキャッシュに保存され、テーマのレンダリング時に利用される。

**業務上の目的・背景**：サイト運営者がコードを編集することなく、テーマの外観や動作をカスタマイズできるようにする。テーマ開発者は、色、フォント、レイアウトオプションなどの設定項目を定義でき、サイト運営者はGUI上でこれらを変更できる。これにより、技術的な知識がないユーザーでもサイトのブランディングを容易に調整できる。

**機能の利用シーン**：
- サイトのアクセントカラーを変更したい場合
- ヘッダーやフッターの表示オプションを切り替えたい場合
- テーマ固有のレイアウトスタイルを選択したい場合
- カスタムフォントの設定を変更したい場合

**主要な処理内容**：
1. テーマアクティベーション時に、テーマの`package.json`で定義されたカスタム設定をデータベースに同期
2. 管理画面から設定値の一覧取得（browse API）
3. 管理画面から設定値の更新（edit API）
4. 設定値のバリデーション（型チェック、選択肢の検証、カラーコード形式など）
5. キャッシュへの設定値の反映
6. テーマレンダリング時の設定値提供

**関連システム・外部連携**：
- gscan（テーマ検証ツール）：テーマの`customSettings`定義を解析
- テーマエンジン：設定値をテンプレートで利用可能にする

**権限による制御**：管理者（Administrator）以上のロールを持つスタッフユーザーのみが設定を編集できる。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 40 | デザイン設定 | 主画面 | ロゴ・カラー・ブランディングの設定、テーマ固有のカスタム設定の編集 |

## 機能種別

CRUD操作 / 設定管理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| custom_theme_settings | Array | Yes | 更新する設定の配列 | 各要素にkeyとvalueが必要 |
| key | String | Yes | 設定項目のキー名 | 既知の設定キーであること |
| value | String/Boolean | Yes | 設定値 | 設定タイプに応じたバリデーション |

### 入力データソース

- 管理画面（Admin Settings）からの入力
- テーマの`package.json`内の`customSettings`定義

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| id | String | 設定レコードのID |
| key | String | 設定項目のキー名 |
| value | String/Boolean | 設定値 |
| type | String | 設定タイプ（select/boolean/color/text/image） |
| options | Array | select型の場合の選択肢 |
| default | String/Boolean | デフォルト値 |
| description | String | 設定項目の説明 |
| group | String | 設定グループ（homepage/postなど） |
| visibility | String | 表示条件（NQLクエリ） |

### 出力先

- Admin API レスポンス（JSON）
- 設定キャッシュ（テーマレンダリング用）

## 処理フロー

### 処理シーケンス

```
1. テーマアクティベーション時の同期処理
   └─ テーマの customSettings 定義を取得
   └─ 既存の設定レコードと比較
   └─ 新規設定の追加、削除された設定の削除、型変更の処理
   └─ キャッシュへの反映

2. 設定一覧取得（browse）
   └─ 内部キャッシュから設定オブジェクトを取得
   └─ 配列形式に変換して返却

3. 設定更新（edit）
   └─ 設定キーの存在確認
   └─ 値のバリデーション（型・選択肢・形式チェック）
   └─ データベースへの保存
   └─ 内部キャッシュ・公開キャッシュの更新
   └─ 更新後の設定一覧を返却
```

### フローチャート

```mermaid
flowchart TD
    A[テーマアクティベーション] --> B[customSettings定義を解析]
    B --> C{既存設定あり?}
    C -->|Yes| D[設定の同期処理]
    C -->|No| E[新規設定の追加]
    D --> F[キャッシュ更新]
    E --> F
    F --> G[完了]

    H[設定更新リクエスト] --> I{設定キー存在?}
    I -->|No| J[ValidationError]
    I -->|Yes| K{値のバリデーション}
    K -->|失敗| J
    K -->|成功| L[DBに保存]
    L --> M[キャッシュ更新]
    M --> N[設定一覧返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-31-01 | 設定タイプ制限 | 設定タイプは select/boolean/color/text/image のいずれかに限定 | 設定作成・更新時 |
| BR-31-02 | select型バリデーション | select型の値は定義されたoptionsに含まれる必要がある | select型の設定更新時 |
| BR-31-03 | boolean型バリデーション | boolean型の値はtrue/falseのみ | boolean型の設定更新時 |
| BR-31-04 | color型バリデーション | color型の値は#で始まる6桁の16進数（例：#FF1A75） | color型の設定更新時 |
| BR-31-05 | 型変更時の再作成 | 設定の型が変更された場合、既存レコードを削除して新規作成 | テーマ更新時 |
| BR-31-06 | visibility条件 | visibility条件を満たさない設定はキャッシュ値がnullになる | キャッシュ計算時 |

### 計算ロジック

visibility条件の評価:
- NQLクエリとして設定値のマップに対して評価
- 条件を満たさない場合、その設定のキャッシュ値は`null`（非表示）

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| テーマ同期 | custom_theme_settings | SELECT/INSERT/UPDATE/DELETE | テーマ定義との同期 |
| 設定取得 | custom_theme_settings | SELECT | アクティブテーマの設定取得 |
| 設定更新 | custom_theme_settings | UPDATE | 設定値の更新 |

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

#### custom_theme_settings

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | * | theme = {アクティブテーマ名} | テーマ別に設定を管理 |
| INSERT | id, theme, key, type, value | 新規設定のデフォルト値 | テーマ定義から追加 |
| UPDATE | value | ユーザー入力値 | バリデーション後に更新 |
| DELETE | - | theme = {テーマ名} AND key = {削除対象キー} | テーマ定義から削除された設定 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | ValidationError | 不明な設定キーを指定 | 「Unknown setting: {key}」エラーを返却 |
| - | ValidationError | select型で許可されていない値 | 「Unallowed value for '{key}'. Allowed values: {options}」エラーを返却 |
| - | ValidationError | color型で不正な形式 | 「Invalid value for '{key}'. The value must follow this format: #1234AF」エラーを返却 |

### リトライ仕様

リトライは不要。バリデーションエラーの場合はユーザーに正しい値を入力してもらう。

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

- 設定更新は各設定項目ごとに個別にコミット
- テーマ同期時の一括処理もレコードごとに処理

## パフォーマンス要件

- 設定取得は内部キャッシュから行うため、DBアクセスなしで即座に応答
- テーマレンダリング時の設定参照もキャッシュから取得

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

- 管理者権限を持つユーザーのみが設定を編集可能
- image型の値はURLTransform処理を適用してセキュリティを確保
- 設定値のサニタイズはテーマレンダリング側で実施

## 備考

- テーマの`package.json`内の`custom`キーでカスタム設定を定義
- 設定グループ（homepage, post等）はテーマ開発者が自由に定義可能
- Ghost公式テーマではカスタムフォント設定が非表示になる場合がある（Ghost 6.0での段階的廃止対応）

---

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

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

### 推奨読解順序

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

まず、カスタムテーマ設定のデータ構造と型定義を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | schema.js | `ghost/core/core/server/data/schema/schema.js` | **945-964行目**: custom_theme_settingsテーブルのスキーマ定義（id, theme, key, type, value） |
| 1-2 | custom-theme-setting.js | `ghost/core/core/server/models/custom-theme-setting.js` | Bookshelfモデル定義、parse/formatメソッドでの型変換処理 |
| 1-3 | custom-theme-settings.ts | `apps/admin-x-framework/src/api/custom-theme-settings.ts` | **4-23行目**: フロントエンド側の型定義（CustomThemeSettingData, CustomThemeSetting） |

**読解のコツ**:
- schema.jsの`type`フィールドのvalidationsで許可される設定タイプを確認
- モデルのparse/formatでboolean型やimage型の値変換ロジックを確認

#### Step 2: サービス層を理解する

設定の同期・取得・更新を行うサービスクラスを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | custom-theme-settings-service.js | `ghost/core/core/shared/custom-theme-settings-cache/custom-theme-settings-service.js` | **16-299行目**: メインのサービスクラス |

**主要処理フロー**:
1. **46-84行目**: `activateTheme()` - テーマアクティベーション時の同期処理
2. **89-95行目**: `listSettings()` - 設定一覧の取得
3. **100-165行目**: `updateSettings()` - 設定値の更新とバリデーション
4. **175-231行目**: `_syncRepositoryWithTheme()` - テーマ定義とDBの同期
5. **285-298行目**: `_computeCachedSettings()` - visibility条件の評価

#### Step 3: キャッシュ層を理解する

設定値のキャッシュ管理を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | custom-theme-settings-cache.js | `ghost/core/core/shared/custom-theme-settings-cache/custom-theme-settings-cache.js` | **1-27行目**: シンプルなkey-valueキャッシュ実装 |
| 3-2 | custom-theme-settings-bread-service.js | `ghost/core/core/shared/custom-theme-settings-cache/custom-theme-settings-bread-service.js` | **1-29行目**: DBアクセスを抽象化するBREADサービス |

#### Step 4: API エンドポイントを理解する

管理APIのエンドポイント定義を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | custom-theme-settings.js | `ghost/core/core/server/api/endpoints/custom-theme-settings.js` | **4-28行目**: browse/editエンドポイントの定義 |
| 4-2 | custom-theme-settings.js | `ghost/core/core/server/services/custom-theme-settings.js` | **1-14行目**: サービスのラッパー初期化 |

#### Step 5: フロントエンドUIを理解する

管理画面でのUI実装を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | theme-settings.tsx | `apps/admin-x-settings/src/components/settings/site/design-and-branding/theme-settings.tsx` | **43-92行目**: 設定フォームのレンダリング、Ghost公式テーマでのフォント設定非表示処理 |

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

```
API Request (browse/edit)
    │
    ├─ custom-theme-settings.js (endpoint)
    │      └─ customThemeSettingsService.api
    │             │
    │             ├─ listSettings()
    │             │      └─ _activeThemeSettings (internal cache)
    │             │
    │             └─ updateSettings(settings)
    │                    ├─ バリデーション処理
    │                    ├─ _repository.read/save (BREAD Service)
    │                    │      └─ CustomThemeSetting Model
    │                    │             └─ custom_theme_settings table
    │                    └─ _valueCache.populate() (public cache)
    │
Theme Activation
    │
    └─ activateTheme(name, theme)
           ├─ _syncRepositoryWithTheme()
           │      ├─ _repository.browse/add/edit/destroy
           │      └─ CustomThemeSetting Model
           ├─ _populateValueCacheForTheme()
           │      └─ _valueCache.populate()
           └─ _populateInternalCacheForTheme()
                  └─ _activeThemeSettings
```

### データフロー図

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

テーマpackage.json ───▶ gscan解析 ───▶ customSettings定義
                              │
                              ▼
                      activateTheme()
                              │
                              ▼
                      _syncRepositoryWithTheme()
                              │
                              ▼
                      custom_theme_settings DB
                              │
                              ▼
                      キャッシュ更新 ───▶ テーマレンダリング
                              │
管理画面入力 ───────▶ updateSettings() ───▶ API レスポンス
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| custom-theme-settings-service.js | `ghost/core/core/shared/custom-theme-settings-cache/custom-theme-settings-service.js` | ソース | メインサービスロジック |
| custom-theme-settings-cache.js | `ghost/core/core/shared/custom-theme-settings-cache/custom-theme-settings-cache.js` | ソース | 値キャッシュ管理 |
| custom-theme-settings-bread-service.js | `ghost/core/core/shared/custom-theme-settings-cache/custom-theme-settings-bread-service.js` | ソース | DBアクセス抽象化 |
| custom-theme-setting.js | `ghost/core/core/server/models/custom-theme-setting.js` | ソース | Bookshelfモデル |
| custom-theme-settings.js | `ghost/core/core/server/api/endpoints/custom-theme-settings.js` | ソース | APIエンドポイント |
| custom-theme-settings.js | `ghost/core/core/server/services/custom-theme-settings.js` | ソース | サービスラッパー |
| custom-theme-settings.ts | `apps/admin-x-framework/src/api/custom-theme-settings.ts` | ソース | フロントエンドAPI hooks |
| theme-settings.tsx | `apps/admin-x-settings/src/components/settings/site/design-and-branding/theme-settings.tsx` | ソース | 設定UIコンポーネント |
| schema.js | `ghost/core/core/server/data/schema/schema.js` | 設定 | DBスキーマ定義 |
