# 機能設計書 53-HTMLエクスポート

## 概要

本ドキュメントは、EtherpadにおけるHTMLエクスポート機能の設計仕様を記載する。パッドのコンテンツをHTML形式でダウンロードする機能について詳細に解説する。

### 本機能の処理概要

HTMLエクスポート機能は、パッドの編集内容をHTML形式でエクスポートし、ユーザーがローカルにダウンロードできるようにする機能を提供する。テキストの書式（太字、斜体、下線、取り消し線）、見出し、リスト構造などがHTMLタグとして適切に変換される。

**業務上の目的・背景**：
共同編集で作成したリッチテキストドキュメントを、Webページとして公開したり、他のHTMLエディタで編集したりする必要がある。HTML形式は書式情報を保持したまま汎用的に利用できるフォーマットであり、ブラウザで直接表示可能である。

**機能の利用シーン**：
- ドキュメントをWebサイトに掲載する場合
- 書式付きでメールに貼り付ける場合
- CMSへのコンテンツ投稿の下書きとして使用する場合
- 印刷用のフォーマット済みドキュメントを作成する場合

**主要な処理内容**：
1. エクスポートリクエストを受信（GET /p/:pad/export/html）
2. パッドからAText（属性付きテキスト）を取得
3. 属性を解析し、HTMLタグに変換
4. HTMLテンプレートでラップ
5. Content-Dispositionヘッダーでダウンロードファイル名を設定
6. HTMLデータをHTTPレスポンスとして返却

**関連システム・外部連携**：
- プラグインフック: exportHtmlAdditionalTags, exportHTMLSend, stylesForExportなど

**権限による制御**：
パッドへの読み取りアクセス権があれば実行可能。読み取り専用パッドからもエクスポート可能。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 2 | パッド編集画面 | 補助機能 | エクスポートポップアップからhtml形式でダウンロード |
| 3 | タイムスライダー画面 | 補助機能 | エクスポートポップアップからhtml形式でダウンロード |

## 機能種別

帳票出力 / データエクスポート

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| pad | string | Yes | URLパスパラメータとして指定されるパッドID | 存在するパッドID |
| rev | string | No | エクスポート対象のリビジョン番号 | 数値であること |

### 入力データソース

- HTTPリクエスト（GET /p/:pad/export/html または GET /p/:pad/:rev/export/html）
- パッドデータベース

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| content | string | 完全なHTMLドキュメント（DOCTYPE、head、body含む） |

### 出力先

- HTTPレスポンス（text/html形式のダウンロードファイル）
- Content-Disposition: attachment; filename="{padId}.html"

## 処理フロー

### 処理シーケンス

```
1. HTTPリクエスト受信（GET /p/:pad/export/html）
   └─ express-rate-limitによるレート制限チェック
2. パッドアクセス権限検証
   └─ hasPadAccessでセッション・トークン検証
3. パッドID解決
   └─ 読み取り専用IDの場合は実パッドIDを取得
4. パッド存在確認
   └─ padManager.doesPadExistsで確認
5. リビジョン指定時はリビジョン検証
   └─ checkValidRevで数値検証
6. HTML生成
   └─ exporthtml.getPadHTMLDocumentでHTML生成
7. プラグインフック呼び出し
   └─ exportHTMLSendで最終調整
8. レスポンス送信
   └─ res.attachmentでファイル名設定、res.sendでHTML送信
```

### フローチャート

```mermaid
flowchart TD
    A[エクスポートリクエスト受信] --> B{レート制限チェック}
    B -->|制限内| C{アクセス権限チェック}
    B -->|制限超過| Z1[429エラー]
    C -->|許可| D{読み取り専用ID?}
    C -->|拒否| Z2[403エラー]
    D -->|Yes| E[実パッドID取得]
    D -->|No| F{パッド存在確認}
    E --> F
    F -->|存在| G{リビジョン指定あり?}
    F -->|不存在| Z3[404エラー]
    G -->|Yes| H[リビジョン検証]
    G -->|No| I[最新リビジョン使用]
    H --> I
    I --> J[exporthtml.getPadHTMLDocument]
    J --> K[AText取得]
    K --> L[HTML変換処理]
    L --> M[プラグインフック実行]
    M --> N[HTMLテンプレート適用]
    N --> O[res.attachment設定]
    O --> P[HTMLレスポンス送信]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-53-01 | 書式タグ変換 | bold→strong, italic→em, underline→u, strikethrough→s | 書式属性がある場合 |
| BR-53-02 | 見出しタグ変換 | heading1→h1, heading2→h2 | 見出し属性がある場合 |
| BR-53-03 | リスト変換 | 箇条書き→ul/li, 番号付き→ol/li | リスト要素がある場合 |
| BR-53-04 | リンク変換 | URL文字列をaタグに変換、rel="noreferrer noopener"付与 | URLパターン検出時 |
| BR-53-05 | 空白処理 | 連続スペースはnbsp変換 | 常時 |
| BR-53-06 | セキュリティエスケープ | HTML特殊文字をエスケープ | 常時 |

### 計算ロジック

リストのネスト処理:
- リストレベルが上がる場合は新しいul/olを開始
- リストレベルが下がる場合は対応するul/olを閉じる
- リスト種別が変わる場合は現在のリストを閉じて新しいリストを開始

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| パッドデータ取得 | pad:{padId} | SELECT | パッドのATextを取得 |
| リビジョンデータ取得 | pad:{padId}:revs:{n} | SELECT | 特定リビジョンのATextを取得（rev指定時） |

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

#### pad:{padId}

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | atext | 最新の属性付きテキスト | HTML変換の入力データ |
| SELECT | pool | 属性プール | 属性解析に使用 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 404 | Not Found | パッドが存在しない | 正しいパッドIDを指定 |
| 403 | Forbidden | アクセス権限がない | 適切な認証を行う |
| 429 | Too Many Requests | レート制限超過 | 時間をおいて再試行 |

### リトライ仕様

自動リトライは実装されていない。クライアント側で手動リトライが必要。

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

読み取り専用操作のためトランザクション管理は不要。

## パフォーマンス要件

- HTML変換はメモリ上で実行
- 大きなパッドでも数秒以内にレスポンス
- レート制限により過負荷を防止

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

- レート制限によるDoS攻撃対策
- hasPadAccessによる認証・認可チェック
- 読み取り専用IDでアクセス時は実パッドIDをレスポンスに含めない
- HTML特殊文字のエスケープによるXSS対策
- リンクにrel="noreferrer noopener"付与によるリファラ漏洩防止
- CORSヘッダー設定（Access-Control-Allow-Origin: *）

## 備考

- 特殊文字（Unicode 12：フォームフィード）は除去される
- プラグインでカスタムタグを追加可能（exportHtmlAdditionalTagsフック）

---

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

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

### 推奨読解順序

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

エクスポート処理で扱うAText（属性付きテキスト）と属性プールの構造を理解することが重要。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | PadType.ts | `src/node/types/PadType.ts` | AText、APoolの型定義 |
| 1-2 | AttributePool.ts | `src/static/js/AttributePool.ts` | 属性プールの実装 |

**読解のコツ**: 属性プールは属性名と値のペアを番号で管理する。anumMapは属性番号からprops配列のインデックスへのマッピング。

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

HTTPリクエストのルーティングからエクスポートハンドラへの流れを確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | importexport.ts | `src/node/hooks/express/importexport.ts` | GETルーティング設定 |

**主要処理フロー**:
1. **28-29行目**: GET /p/:pad{/:rev}/export/:type ルーティング定義
2. **31行目**: 対応形式チェック（html含む）
3. **52行目**: hasPadAccessによるアクセス権チェック
4. **68行目**: exportHandler.doExportの呼び出し

#### Step 3: エクスポートハンドラを理解する

形式別の分岐とhtml形式の処理を確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | ExportHandler.ts | `src/node/handler/ExportHandler.ts` | メインエクスポート処理 |

**主要処理フロー**:
- **46-48行目**: ファイル名の決定
- **59行目**: res.attachmentでダウンロード設定
- **77行目**: exporthtml.getPadHTMLDocumentの呼び出し
- **82-88行目**: html形式の場合の処理
- **84-85行目**: exportHTMLSendフックで最終調整

#### Step 4: HTML変換ユーティリティを理解する

実際のHTML変換ロジックを確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | ExportHtml.ts | `src/node/utils/ExportHtml.ts` | HTML変換処理 |

**主要処理フロー**:
- **34-44行目**: getPadHTML - メイン処理
- **46-477行目**: getHTMLFromAtext - 変換処理本体
- **51-52行目**: タグとプロパティの定義
- **56-70行目**: プラグインフックで追加タグ取得
- **128-294行目**: getLineHTML - 行単位変換
- **170-183行目**: emitOpenTag - タグ開始出力
- **187-200行目**: emitCloseTag - タグ終了出力
- **270-289行目**: リンク変換処理
- **312-474行目**: リスト処理（ul/ol/li）
- **479-500行目**: getPadHTMLDocument - 完全なHTMLドキュメント生成
- **503-549行目**: _processSpaces - 空白処理

**読解のコツ**: openTagsスタックでネストを管理。deserializeOpsで属性を解析し、anumMapで対応するHTMLタグを特定。

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

```
expressCreateServer (importexport.ts)
    │
    └─ GET /p/:pad{/:rev}/export/html
           │
           ├─ hasPadAccess
           │
           ├─ readOnlyManager.isReadOnlyId
           │      └─ readOnlyManager.getPadId
           │
           ├─ padManager.doesPadExists
           │
           └─ exportHandler.doExport (ExportHandler.ts)
                  │
                  ├─ hooks.aCallFirst('exportFileName')
                  │
                  ├─ res.attachment (ファイル名設定)
                  │
                  └─ exporthtml.getPadHTMLDocument (ExportHtml.ts)
                         │
                         ├─ padManager.getPad
                         │
                         ├─ hooks.aCallAll('stylesForExport')
                         │
                         ├─ getPadHTML
                         │      │
                         │      └─ pad.getInternalRevisionAText (rev指定時)
                         │
                         ├─ getHTMLFromAtext
                         │      │
                         │      ├─ hooks.aCallAll('exportHtmlAdditionalTags')
                         │      │
                         │      ├─ splitAttributionLines
                         │      │
                         │      ├─ _analyzeLine (各行解析)
                         │      │
                         │      └─ getLineHTML (HTML変換)
                         │             │
                         │             ├─ padutils.findURLs
                         │             │
                         │             └─ Security.escapeHTML
                         │
                         ├─ hooks.aCallAll('exportHTMLAdditionalContent')
                         │
                         └─ eejs.require (テンプレート適用)
```

### データフロー図

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

HTTPリクエスト       ┌─────────────────┐
GET /p/:pad/        │  ルーティング    │
export/html ───────▶│  importexport   │
                    └────────┬────────┘
                             │
                             ▼
                    ┌─────────────────┐
                    │  権限チェック    │
                    │  hasPadAccess   │
                    └────────┬────────┘
                             │
                             ▼
                    ┌─────────────────┐
パッドDB ─────────▶ │  パッドデータ    │
pad:{padId}         │  取得           │
                    └────────┬────────┘
                             │
                             ▼
                    ┌─────────────────┐
                    │  AText取得      │
                    └────────┬────────┘
                             │
                             ▼
                    ┌─────────────────┐
                    │  HTML変換       │
                    │  ExportHtml     │
                    │  ・書式タグ     │
                    │  ・リスト       │
                    │  ・リンク       │
                    └────────┬────────┘
                             │
                             ▼
                    ┌─────────────────┐
プラグイン ────────▶│  フック処理     │
                    │  ・追加タグ     │
                    │  ・追加CSS      │
                    └────────┬────────┘
                             │
                             ▼
                    ┌─────────────────┐
                    │  テンプレート   │
                    │  export_html    │
                    └────────┬────────┘
                             │
                             ▼
                    ┌─────────────────┐
                    │  res.send       │ ───────▶ .htmlファイル
                    │  HTML返却       │          ダウンロード
                    └─────────────────┘
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| importexport.ts | `src/node/hooks/express/importexport.ts` | ソース | Expressルーティング定義 |
| ExportHandler.ts | `src/node/handler/ExportHandler.ts` | ソース | エクスポートハンドラ |
| ExportHtml.ts | `src/node/utils/ExportHtml.ts` | ソース | HTML変換処理 |
| ExportHelper.js | `src/node/utils/ExportHelper.js` | ソース | 行解析ヘルパー |
| export_html.html | `src/templates/export_html.html` | テンプレート | HTMLドキュメントテンプレート |
| Changeset.ts | `src/static/js/Changeset.ts` | ソース | 属性操作ユーティリティ |
| AttributePool.ts | `src/static/js/AttributePool.ts` | ソース | 属性プール管理 |
| security.js | `src/static/js/security.js` | ソース | HTMLエスケープ処理 |
| pad_utils.ts | `src/static/js/pad_utils.ts` | ソース | URL検出処理 |
| PadManager.ts | `src/node/db/PadManager.ts` | ソース | パッド管理 |
