# 画面設計書 60-コードインジェクション

## 概要

本ドキュメントは、Ghost管理画面における「コードインジェクション」画面の設計仕様を記述するものである。

### 本画面の処理概要

コードインジェクション画面は、サイト全体のヘッダー（`{{ghost_head}}`）およびフッター（`{{ghost_foot}}`）にカスタムHTML/CSS/JavaScriptコードを挿入するための設定画面である。これにより、テーマを直接編集することなく、サードパーティのトラッキングコードやカスタムスタイルを追加できる。

**業務上の目的・背景**：ウェブサイト運営において、Google Analytics、Facebook Pixel、その他のマーケティングツールやチャットウィジェットを導入する必要がある。コードインジェクション機能により、テーマファイルを変更せずにこれらのスクリプトやスタイルを安全に追加できる。これはテーマのアップデート時にカスタマイズが失われることを防ぎ、運用の効率化に貢献する。

**画面へのアクセス方法**：管理画面のサイドメニューから「Settings」を選択し、「Advanced」セクション内の「Code injection」項目をクリックしてアクセスする。URLパスは`#/settings/code-injection`となる。「Open」ボタンをクリックするとコードエディタモーダルが開く。

**主要な操作・処理内容**：
1. サイトヘッダー用コードの入力・編集
2. サイトフッター用コードの入力・編集
3. コードの保存

**画面遷移**：設定画面のAdvancedセクションからアクセスする。「Open」ボタンからはコードエディタモーダル（CodeModal）が開く。

**権限による表示制御**：Administrator（管理者）およびOwner（オーナー）ロールのみがこの設定にアクセス・編集可能である。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 32 | コードインジェクション | 主機能 | カスタムHTML/CSS/JavaScriptの挿入設定 |

## 画面種別

設定（モーダルベース編集）

## URL/ルーティング

- 管理画面URL: `/ghost/#/settings/code-injection`
- 内部ルーティング: `navid='code-injection'`
- ReactコンポーネントID: `TopLevelGroup` with `testId='code-injection'`

## 入出力項目

| 項目名 | 項目ID | データ型 | 入力/出力 | 必須 | 最大長 | 説明 |
|--------|--------|----------|-----------|------|--------|------|
| サイトヘッダーコード | codeinjection_head | string | 入力（モーダル） | - | 65535 | `{{ghost_head}}`に挿入されるコード |
| サイトフッターコード | codeinjection_foot | string | 入力（モーダル） | - | 65535 | `{{ghost_foot}}`に挿入されるコード |

## 表示項目

| 項目名 | 表示条件 | 説明 |
|--------|----------|------|
| タイトル | 常時 | 「Code injection」のタイトル |
| 説明文 | 常時 | 「Add custom code to your publication」 |
| Openボタン | 常時 | コードエディタモーダルを開くボタン |

### モーダル表示項目

| 項目名 | 表示条件 | 説明 |
|--------|----------|------|
| Site headerタブ | 常時 | ヘッダーコード編集タブ |
| Site footerタブ | 常時 | フッターコード編集タブ |
| コードエディタ | 常時 | CodeMirror使用のシンタックスハイライト付きエディタ |
| ヒント（ヘッダー） | Site headerタブ | 「Code here will be injected into the {{ghost_head}} tag on every page」 |
| ヒント（フッター） | Site footerタブ | 「Code here will be injected into the {{ghost_foot}} tag on every page」 |
| Closeボタン | 常時 | モーダルを閉じるボタン |
| Saveボタン | 常時 | 変更を保存するボタン |

## イベント仕様

### 1-Openボタン押下

メイン画面のButtonコンポーネントのクリックイベントでNiceModal.show(CodeModal)が呼び出され、コードエディタモーダルが表示される。

### 2-タブ切り替え（モーダル）

TabViewコンポーネントでタブを選択すると、selectedTab状態が更新され（'header' | 'footer'）、対応するコードエディタが表示される。初期値は'header'。

### 3-コード編集

CodeEditorコンポーネントのonChangeイベントでupdateSetting関数が呼び出される。'codeinjection_head'または'codeinjection_foot'の値が更新される。

### 4-保存（Cmd/Ctrl+S）

キーボードショートカット（Cmd+S または Ctrl+S）でonSaveClick関数が呼び出され、設定が保存される。

### 5-Saveボタン押下

onSaveClick関数が呼び出され、handleSave経由でeditSettings APIが呼び出されて設定が保存される。ボタンラベルは保存状態により「Save」→「Saving」→「Saved」と変化する。

### 6-Closeボタン押下

modal.remove()が呼び出され、モーダルが閉じられる。afterCloseコールバックが実行される。

## データベース更新仕様

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 設定保存 | settings | UPDATE | codeinjection_head, codeinjection_footを更新 |

### テーブル別更新項目詳細

#### settings

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | key='codeinjection_head' の value | 入力されたヘッダーコード | |
| UPDATE | key='codeinjection_foot' の value | 入力されたフッターコード | |

## メッセージ仕様

| メッセージID | 種類 | メッセージ内容 | 表示条件 |
|-------------|------|---------------|----------|
| MSG-001 | 説明 | Add custom code to your publication | 説明文 |
| MSG-002 | ヒント | Code here will be injected into the {{ghost_head}} tag on every page of the site | ヘッダータブ |
| MSG-003 | ヒント | Code here will be injected into the {{ghost_foot}} tag on every page of the site | フッタータブ |
| MSG-004 | ラベル | Save | 保存ボタン（初期状態） |
| MSG-005 | ラベル | Saving | 保存ボタン（保存中） |
| MSG-006 | ラベル | Saved | 保存ボタン（保存完了） |

## 例外処理

| 例外条件 | 処理内容 |
|----------|----------|
| API通信エラー | エラートーストを表示 |
| セッション切れ | 認証画面へリダイレクト |

## 備考

- コードエディタにはCodeMirrorが使用され、HTMLシンタックスハイライトが適用される
- ヘッダーコードは`<head>`タグ内、フッターコードは`</body>`タグ直前に挿入される
- コードインジェクションはすべてのページに適用される（個別ページには投稿/ページエディタでのcode injection設定も存在）
- Cmd+S / Ctrl+S のキーボードショートカットで素早く保存可能
- モーダルはフルスクリーンサイズで表示され、コード編集に十分なスペースを確保

---

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

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

### 推奨読解順序

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

コードインジェクション設定で使用される設定値を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | settings.ts | `apps/admin-x-framework/src/api/settings.ts` | Setting型の定義、getSettingValues関数 |

**読解のコツ**: codeinjection_headとcodeinjection_footはstring型で、HTML/CSS/JavaScriptを含む任意のテキストを保存できる。

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

コードインジェクション画面のメインファイルを読み解く。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | code-injection.tsx | `apps/admin-x-settings/src/components/settings/advanced/code-injection.tsx` | コンポーネント全体の構造 |

**主要処理フロー**:
1. **7行目**: CodeInjectionコンポーネント定義
2. **9-16行目**: TopLevelGroupでラップ、customHeaderでタイトルとOpenボタンを配置
3. **13-15行目**: Openボタンのクリックで NiceModal.show(CodeModal) を呼び出し

#### Step 3: コードエディタモーダルを理解する

CodeModalコンポーネントの実装を読み解く。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | code-modal.tsx | `apps/admin-x-settings/src/components/settings/advanced/code/code-modal.tsx` | モーダルの実装 |

**主要処理フロー**:
- **16-21行目**: useSettingGroupでフォーム状態管理を初期化
- **24行目**: getSettingValuesでcodeinjection_head, codeinjection_footを取得
- **26行目**: selectedTab状態の管理（'header' | 'footer'）
- **33-45行目**: headerPropsとfooterPropsの定義（onChange, valueなど）
- **47-62行目**: tabs配列の定義（Site header, Site footer）
- **64行目**: useSaveButtonフックで保存ボタンの状態管理
- **66-77行目**: Cmd+S / Ctrl+S ショートカットの実装

#### Step 4: 保存ボタンロジックを理解する

useSaveButtonカスタムフックの実装を読み解く。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | use-save-button.ts | `apps/admin-x-settings/src/hooks/use-save-button.ts` | 保存ボタンの状態管理ロジック |

**主要処理フロー**:
- savingTitle: 'Save' | 'Saving' | 'Saved' の状態遷移
- isSaving: 保存中フラグ
- onSaveClick: 保存実行関数

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

```
CodeInjection (code-injection.tsx)
    │
    └─ TopLevelGroup
           ├─ customHeader
           │      ├─ SettingGroupHeader
           │      └─ Button 'Open'
           │             └─ NiceModal.show(CodeModal)
           │
           └─ navid: 'code-injection'

CodeModal (code-modal.tsx)
    │
    ├─ useSettingGroup()
    │      └─ handleSave, updateSetting
    │
    ├─ getSettingValues()
    │      ├─ codeinjection_head
    │      └─ codeinjection_foot
    │
    ├─ useSaveButton()
    │      └─ savingTitle, isSaving, onSaveClick
    │
    ├─ useEffect (keydown: Cmd+S / Ctrl+S)
    │      └─ onSaveClick()
    │
    └─ Modal (fullscreen)
           ├─ ButtonGroup
           │      ├─ Close → modal.remove()
           │      └─ Save → onSaveClick()
           │
           └─ TabView
                  ├─ Tab: Site header
                  │      └─ CodeEditor (codeinjection_head)
                  │             ├─ extensions: html()
                  │             ├─ onChange → updateSetting('codeinjection_head')
                  │             └─ hint: ghost_head説明
                  │
                  └─ Tab: Site footer
                         └─ CodeEditor (codeinjection_foot)
                                ├─ extensions: html()
                                ├─ onChange → updateSetting('codeinjection_foot')
                                └─ hint: ghost_foot説明
```

### データフロー図

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

初期表示
settings ───▶ getSettingValues ───▶ codeinjection_head, codeinjection_foot
    │
    └─ CodeEditorに表示

コード編集
ユーザー入力 ───▶ CodeEditor onChange
    │
    ▼
updateSetting('codeinjection_head' or 'codeinjection_foot', value)
    │
    ▼
localSettings更新

保存
Save click / Cmd+S ───▶ onSaveClick()
    │
    ▼
handleSave() ───▶ PUT /settings/ ───▶ settingsテーブル更新
    │
    ▼
savingTitle: 'Saving' → 'Saved'

テーマ出力
{{ghost_head}} ───▶ codeinjection_head ───▶ <head>内に挿入
{{ghost_foot}} ───▶ codeinjection_foot ───▶ </body>直前に挿入
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| code-injection.tsx | `apps/admin-x-settings/src/components/settings/advanced/code-injection.tsx` | ソース | コードインジェクション画面のエントリーポイント |
| code-modal.tsx | `apps/admin-x-settings/src/components/settings/advanced/code/code-modal.tsx` | ソース | コードエディタモーダル |
| use-setting-group.tsx | `apps/admin-x-settings/src/hooks/use-setting-group.tsx` | ソース | 設定グループ共通フック |
| use-save-button.ts | `apps/admin-x-settings/src/hooks/use-save-button.ts` | ソース | 保存ボタン状態管理フック |
| settings.ts | `apps/admin-x-framework/src/api/settings.ts` | ソース | Settings API定義 |
| CodeEditor | `@tryghost/admin-x-design-system` | 外部 | CodeMirrorベースのコードエディタコンポーネント |
