# 機能設計書 52-テキストエクスポート

## 概要

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

### 本機能の処理概要

テキストエクスポート機能は、パッドの編集内容をプレーンテキスト（.txt）形式でエクスポートし、ユーザーがローカルにダウンロードできるようにする機能を提供する。リストのインデントや番号付けなども適切にテキスト表現に変換される。

**業務上の目的・背景**：
共同編集で作成したドキュメントを、他のアプリケーションで利用したり、バックアップとして保存したりする必要がある。テキスト形式は最も汎用的なフォーマットであり、あらゆるテキストエディタやワードプロセッサで開くことができる。シンプルなドキュメント共有やアーカイブに最適である。

**機能の利用シーン**：
- 会議メモをローカルに保存する場合
- パッドの内容をメールに貼り付けて共有する場合
- シンプルなバックアップを作成する場合
- 他のシステムへのテキストデータ移行を行う場合

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

**関連システム・外部連携**：
特に外部システムとの連携はなし。Etherpad内部で完結する処理。

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

## 関連画面

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

## 機能種別

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

## 入力仕様

### 入力パラメータ

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

### 入力データソース

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

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| content | string | パッドのプレーンテキスト内容 |

### 出力先

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

## 処理フロー

### 処理シーケンス

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

### フローチャート

```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[exporttxt.getPadTXTDocument]
    J --> K[AText取得]
    K --> L[テキスト変換処理]
    L --> M[res.attachment設定]
    M --> N[テキストレスポンス送信]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-52-01 | リスト表現変換 | 箇条書きは「* 」プレフィックス、番号リストは「N. 」形式で出力 | リスト要素がある場合 |
| BR-52-02 | インデント表現 | リストのネストはタブ文字でインデント | ネストしたリストの場合 |
| BR-52-03 | 階層番号表現 | ネストした番号リストは「1.1. 」のような階層表現 | ネストした番号リストの場合 |
| BR-52-04 | 書式除去 | 太字・斜体などの書式属性は無視 | 常時 |

### 計算ロジック

リスト番号の計算:
- 同一階層レベルでリスト種別が変わらない限りインクリメント
- 階層が下がった場合は上位階層の番号をリセット
- 階層が上がった場合は下位階層の番号は維持

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

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

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

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

#### pad:{padId}

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

## エラー処理

### エラーケース一覧

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

### リトライ仕様

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

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

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

## パフォーマンス要件

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

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

- レート制限によるDoS攻撃対策
- hasPadAccessによる認証・認可チェック
- 読み取り専用IDでアクセス時は実パッドIDをレスポンスに含めない
- CORSヘッダー設定（Access-Control-Allow-Origin: *）

## 備考

- 特殊文字（Unicode 12：フォームフィード）は除去される
- 改行コードはLF（\n）で統一

---

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

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

### 推奨読解順序

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

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | PadType.ts | `src/node/types/PadType.ts` | AText、APoolの型定義 |
| 1-2 | Changeset.ts | `src/static/js/Changeset.ts` | splitAttributionLinesなどの関数 |

**読解のコツ**: ATextはtextとattribsの組み合わせ。attribsは属性の開始位置と長さを表すシリアライズされた文字列。

#### 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-35行目**: 対応形式チェック（txt含む）
3. **52行目**: hasPadAccessによるアクセス権チェック
4. **68行目**: exportHandler.doExportの呼び出し

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

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

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

**主要処理フロー**:
- **46-48行目**: ファイル名の決定（readOnlyId優先）
- **51-56行目**: hookによるファイル名カスタマイズ
- **59行目**: res.attachmentでダウンロード設定
- **61-65行目**: リビジョン番号の検証
- **72-74行目**: txt形式の場合の処理
- **73行目**: exporttxt.getPadTXTDocumentの呼び出し

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

実際のテキスト変換ロジックを確認。

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

**主要処理フロー**:
- **33-43行目**: getPadTXT - メイン処理
- **47-264行目**: getTXTFromAtext - 変換処理本体
- **64-185行目**: getLineTXT - 行単位変換
- **201-261行目**: リスト処理（箇条書き・番号付け・インデント）
- **205-206行目**: 箇条書きの「* 」プレフィックス付与
- **224-254行目**: 番号リストの階層番号計算

**読解のコツ**: _analyzeLineでリスト種別とレベルを判定し、getLineTXTでテキスト変換、その後リスト表現を付加する流れ。

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

```
expressCreateServer (importexport.ts)
    │
    └─ GET /p/:pad{/:rev}/export/txt
           │
           ├─ hasPadAccess
           │
           ├─ readOnlyManager.isReadOnlyId
           │      └─ readOnlyManager.getPadId
           │
           ├─ padManager.doesPadExists
           │
           └─ exportHandler.doExport (ExportHandler.ts)
                  │
                  ├─ hooks.aCallFirst('exportFileName')
                  │
                  ├─ res.attachment (ファイル名設定)
                  │
                  └─ exporttxt.getPadTXTDocument (ExportTxt.ts)
                         │
                         ├─ padManager.getPad
                         │
                         ├─ getPadTXT
                         │      │
                         │      └─ pad.getInternalRevisionAText (rev指定時)
                         │
                         └─ getTXTFromAtext
                                │
                                ├─ splitAttributionLines
                                │
                                ├─ _analyzeLine (各行解析)
                                │
                                └─ getLineTXT (テキスト変換)
```

### データフロー図

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

HTTPリクエスト       ┌─────────────────┐
GET /p/:pad/        │  ルーティング    │
export/txt ────────▶│  importexport   │
                    └────────┬────────┘
                             │
                             ▼
                    ┌─────────────────┐
                    │  権限チェック    │
                    │  hasPadAccess   │
                    └────────┬────────┘
                             │
                             ▼
                    ┌─────────────────┐
パッドDB ─────────▶ │  パッドデータ    │
pad:{padId}         │  取得           │
                    └────────┬────────┘
                             │
                             ▼
                    ┌─────────────────┐
                    │  AText取得      │
                    │  (rev指定時は   │
                    │   特定rev)      │
                    └────────┬────────┘
                             │
                             ▼
                    ┌─────────────────┐
                    │  テキスト変換   │
                    │  ExportTxt      │
                    └────────┬────────┘
                             │
                             ▼
                    ┌─────────────────┐
                    │  res.send       │ ───────▶ .txtファイル
                    │  テキスト返却   │          ダウンロード
                    └─────────────────┘
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| importexport.ts | `src/node/hooks/express/importexport.ts` | ソース | Expressルーティング定義 |
| ExportHandler.ts | `src/node/handler/ExportHandler.ts` | ソース | エクスポートハンドラ |
| ExportTxt.ts | `src/node/utils/ExportTxt.ts` | ソース | テキスト変換処理 |
| ExportHelper.js | `src/node/utils/ExportHelper.js` | ソース | 行解析ヘルパー |
| Changeset.ts | `src/static/js/Changeset.ts` | ソース | 属性操作ユーティリティ |
| PadManager.ts | `src/node/db/PadManager.ts` | ソース | パッド管理 |
| ReadOnlyManager.ts | `src/node/db/ReadOnlyManager.ts` | ソース | 読み取り専用ID管理 |
| padaccess.js | `src/node/padaccess.js` | ソース | アクセス権チェック |
| checkValidRev.ts | `src/node/utils/checkValidRev.ts` | ソース | リビジョン検証 |
