# 画面設計書 14-投稿復元画面

## 概要

本ドキュメントは、Ghost管理画面における投稿復元画面の設計仕様を定義するものである。

### 本画面の処理概要

投稿復元画面は、ローカルストレージに保存された投稿のリビジョン（編集履歴）から、失われた投稿を復元するための画面である。

**業務上の目的・背景**：ブラウザのクラッシュ、誤操作による削除、その他の理由で投稿が失われた場合に、ローカルに保存されたリビジョンから内容を復元できる救済機能を提供する。エディタでの編集中、下書き投稿は定期的にlocalStorageに自動保存されており、この画面からそれらを新規投稿として復元できる。

**画面へのアクセス方法**：投稿一覧画面から「Restore Posts」リンクをクリック、またはURL `/ghost/#/restore-posts` で直接アクセス可能。

**主要な操作・処理内容**：
1. ローカルストレージに保存されたリビジョン一覧の表示
2. 各リビジョンのタイトル、抜粋、保存日時の確認
3. 選択したリビジョンからの投稿復元
4. 復元成功時にエディタ画面への遷移

**画面遷移**：
- 遷移元: 投稿一覧画面
- 遷移先: エディタ画面（復元後）

**権限による表示制御**：
- 認証済みユーザーのみアクセス可能
- 復元はローカルストレージベースのため、そのブラウザで編集した投稿のみ表示される

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 6 | 記事リビジョン | 主機能 | 削除された投稿の復元処理 |

## 画面種別

一覧

## URL/ルーティング

| 項目 | 値 |
|------|-----|
| URL | `/ghost/#/restore-posts` |
| ルート名 | restore-posts |
| ルートファイル | `ghost/admin/app/routes/restore-posts.js` |

## 入出力項目

### 入力項目

本画面には入力項目なし。

### 出力項目（リビジョン一覧）

| 項目名 | フィールド | 説明 |
|--------|-----------|------|
| タイトル | title | リビジョンのタイトル（空の場合は「(no title)」） |
| 抜粋 | excerpt | 本文の抜粋（100文字まで） |
| 作成日時 | revisionTimestamp | リビジョン保存日時（MMM D, YYYY HH:mm形式） |

## 表示項目

### ヘッダー部

| 項目名 | 説明 |
|--------|------|
| タイトル | 「Restore Posts」固定 |

### 説明文

「Posts are regularly saved locally on your device. If you've lost a post, you can restore it from here as long as too much time hasn't passed.」

### リビジョン一覧

| 項目名 | 説明 |
|--------|------|
| リスト項目 | VerticalCollectionによる仮想スクロールリスト |
| 項目高さ | 推定60px |
| バッファサイズ | 20項目 |

### 空状態表示

| 条件 | 表示内容 |
|------|---------|
| リビジョンなし | 「No local revisions found.」メッセージとプレースホルダー |

## イベント仕様

### 1-画面表示時

- 処理: localRevisions.findAll()でローカルストレージからリビジョン取得
- ソート: 新しい順（revisionTimestampの降順）

### 2-復元ボタン押下

- 処理: restorePostTaskを実行
- 復元処理:
  1. 指定キーでlocalStorageからリビジョンを取得
  2. 著者情報をstore.queryRecordで取得
  3. 新規投稿レコードを作成（タイトルに「(Restored)」プレフィックス付与）
  4. post.save()で保存
  5. コンソールにエディタURLをログ出力
- 成功時: 「Post restored successfully」通知表示
- 失敗時: 「Failed to restore post」エラー通知表示

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 復元実行 | posts | INSERT | 新規投稿としてリビジョンを保存 |
| 復元実行 | users | SELECT | 著者情報の取得 |

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

#### posts

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | title | 「(Restored) 」+ 元タイトル | プレフィックス付与 |
| INSERT | lexical | リビジョンのlexical値 | Lexical形式コンテンツ |
| INSERT | slug | リビジョンのslug or 'untitled' | |
| INSERT | status | 'draft' | 必ず下書きとして復元 |
| INSERT | type | リビジョンのtype | post or page |
| INSERT | authors | リビジョンのauthors | 著者リレーション |
| INSERT | tags | リビジョンのtags | タグリレーション |

## メッセージ仕様

| メッセージID | 種別 | 条件 | メッセージ内容 |
|-------------|------|------|---------------|
| MSG-01 | 成功 | 復元成功 | Post restored successfully |
| MSG-02 | エラー | 復元失敗 | Failed to restore post |
| MSG-03 | 情報 | リビジョンなし | No local revisions found. |

## 例外処理

| 例外ケース | 処理内容 |
|-----------|---------|
| リビジョンなし | 空状態メッセージとプレースホルダー表示 |
| 復元失敗 | エラー通知表示、コンソールにエラーログ |
| 著者取得失敗 | 著者なしで投稿を作成 |

## 備考

- リビジョンはlocalStorageに保存されるため、同一ブラウザでのみ利用可能
- リビジョンは下書き（status='draft'）の投稿のみ自動保存される
- 各投稿につき最新5件のリビジョンが保持される
- 最小リビジョン間隔: 60秒（MIN_REVISION_TIME）
- localStorage容量超過時は古いリビジョンから自動削除
- 復元された投稿は必ず下書きステータスで作成される
- 復元後のエディタURLはコンソールに出力される（自動遷移はしない）
- VerticalCollectionによる仮想スクロールでパフォーマンスを最適化

---

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

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

### 推奨読解順序

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

ローカルリビジョンのデータ構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | local-revisions.js | `ghost/admin/app/services/local-revisions.js` | リビジョンのデータ構造とlocalStorage操作 |

**読解のコツ**: localStorageのキー構造とリビジョンデータの形式を理解する。

**主要処理フロー**:
- **23-33行目**: キー生成（`post-revision-{id}-{timestamp}`）
- **68-99行目**: performSave - localStorageへの保存
- **126-140行目**: findAll - 全リビジョンの取得とソート
- **231-261行目**: restore - リビジョンから投稿を復元
- **267-279行目**: filterRevisions - 5件制限の適用

#### Step 2: ルートを理解する

ルートでモデルを取得する処理を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | restore-posts.js | `ghost/admin/app/routes/restore-posts.js` | シンプルなモデルフック |

**主要処理フロー**:
- **7-9行目**: model() - localRevisions.findAll()を呼び出し

#### Step 3: コントローラーを理解する

復元タスクの実装を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | restore-posts.js | `ghost/admin/app/controllers/restore-posts.js` | 復元タスクの定義 |

**主要処理フロー**:
- **9-21行目**: restorePostTask - localRevisions.restore()呼び出しと通知

#### Step 4: テンプレートを理解する

画面構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | restore-posts.hbs | `ghost/admin/app/templates/restore-posts.hbs` | 画面レイアウトとリスト表示 |

**主要処理フロー**:
- **1行目**: did-insert modifierでloadDataを呼び出し（実際はmodelから自動取得）
- **11-48行目**: リビジョン一覧の条件分岐表示
- **18-39行目**: VerticalCollectionによる仮想スクロールリスト
- **28-36行目**: GhTaskButtonによる復元ボタン
- **40-47行目**: 空状態表示

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

```
RestorePostsRoute (ghost/admin/app/routes/restore-posts.js)
    │
    └─ model()
           └─ localRevisions.findAll()
                  └─ localStorage全キースキャン
                  └─ revisionTimestamp降順ソート

RestorePostsController (ghost/admin/app/controllers/restore-posts.js)
    │
    └─ restorePostTask(revision)
           └─ localRevisions.restore(key)
                  ├─ find(key) - localStorage取得
                  ├─ store.queryRecord('user') - 著者取得
                  ├─ store.createRecord('post') - 新規投稿作成
                  └─ post.save() - API保存

restore-posts.hbs (テンプレート)
    │
    ├─ GhCanvasHeader - ヘッダー
    │
    └─ VerticalCollection
           └─ li × N (リビジョン項目)
                  ├─ タイトル
                  ├─ 抜粋
                  ├─ 日時
                  └─ GhTaskButton (復元ボタン)
```

### データフロー図

```
[データソース]                    [処理]                         [出力]

localStorage ─────────────▶ localRevisions.findAll() ───────▶ リビジョン配列
(post-revision-*)                 │
                                  └─ ソート（新しい順）

復元ボタン押下 ───────────▶ restorePostTask ─────────────────▶ 新規投稿
                                  │
                                  ├─ localRevisions.restore()
                                  │      ├─ localStorage取得
                                  │      ├─ 著者解決
                                  │      └─ store.createRecord()
                                  │
                                  ├─ post.save()
                                  │      └─ API POST /posts
                                  │
                                  └─ 通知表示
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| restore-posts.js | `ghost/admin/app/routes/restore-posts.js` | ルート | モデル取得 |
| restore-posts.js | `ghost/admin/app/controllers/restore-posts.js` | コントローラー | 復元タスク |
| restore-posts.hbs | `ghost/admin/app/templates/restore-posts.hbs` | テンプレート | 画面レイアウト |
| local-revisions.js | `ghost/admin/app/services/local-revisions.js` | サービス | localStorageリビジョン管理 |
| post.js | `ghost/admin/app/models/post.js` | モデル | 投稿データ構造 |
