# 機能設計書 51-ファイルインポート

## 概要

本ドキュメントは、Etherpadにおけるファイルインポート機能の設計仕様を記載する。外部ファイルをパッドにインポートし、テキストコンテンツとして取り込む機能について詳細に解説する。

### 本機能の処理概要

ファイルインポート機能は、ユーザーがローカルファイルをアップロードし、パッドのコンテンツとして取り込む処理を提供する。テキストファイル、HTMLファイル、Microsoft Word形式、PDF、ODT、Etherpad独自形式など、多様なファイル形式に対応している。

**業務上の目的・背景**：
既存のドキュメントをEtherpadで共同編集したいケースは多く存在する。ユーザーが既に作成済みのWord文書やテキストファイルをパッドに取り込むことで、新規に内容を入力し直す手間を省き、スムーズに共同編集作業を開始できる。また、異なるプラットフォーム間でのドキュメント移行も容易になる。

**機能の利用シーン**：
- 会議資料のWord文書をパッドに取り込んでリアルタイム編集する場合
- 既存のHTMLページをパッドにインポートして編集する場合
- 他のEtherpadインスタンスからエクスポートしたパッドを復元する場合
- PDFの内容をテキストとして取り込む場合

**主要な処理内容**：
1. マルチパートフォームデータとしてアップロードされたファイルを受信
2. ファイル形式を判定し、対応可能な形式かを検証
3. 必要に応じてLibreOffice/AbiWordで中間形式（HTML）に変換
4. HTMLまたはテキストをパッドのデータ構造に変換
5. 変更を全クライアントにブロードキャスト

**関連システム・外部連携**：
- LibreOffice（soffice）: doc/docx/pdf/odt形式の変換に使用
- AbiWord: LibreOfficeが利用不可の場合の代替変換ツール
- Formidable: ファイルアップロードのパース処理

**権限による制御**：
インポート実行にはパッドへの書き込み権限が必要。読み取り専用アクセスのユーザーはインポートを実行できない。セッションIDおよびトークンによる認証チェックが実行される。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 2 | パッド編集画面 | 補助機能 | インポートポップアップでファイルを選択してパッドにインポート |

## 機能種別

データ連携 / ファイル処理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| file | File | Yes | インポート対象のファイル（マルチパートフォーム） | 最大ファイルサイズ設定による制限（settings.importMaxFileSize） |
| pad | string | Yes | URLパスパラメータとして指定されるパッドID | 存在するパッドIDである必要あり |

### 入力データソース

- ブラウザからのマルチパートフォームデータ（POST /p/:pad/import）
- 対応ファイル形式: .txt, .doc, .docx, .pdf, .odt, .html, .htm, .etherpad, .rtf

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| code | number | 処理結果コード（0: 成功, 1: 既知のエラー, 2: 内部エラー） |
| message | string | 処理結果メッセージ |
| data.directDatabaseAccess | boolean | Etherpad形式で直接DB書き込みが行われたかどうか |

### 出力先

- HTTPレスポンス（JSON形式）
- パッドデータ（データベース更新）
- Socket.IO経由で接続中クライアントへブロードキャスト

## 処理フロー

### 処理シーケンス

```
1. HTTPリクエスト受信（POST /p/:pad/import）
   └─ express-rate-limitによるレート制限チェック
2. アクセス権限検証
   └─ securityManager.checkAccessでセッション・トークン検証
3. Formidableでファイルパース
   └─ 一時ディレクトリにファイル保存
4. ファイル形式判定
   └─ 拡張子から対応形式か確認
5. 変換処理実行（形式別分岐）
   ├─ .etherpad: 直接データベースインポート
   ├─ .html/.htm: HTML解析してパッドに設定
   ├─ .txt: テキストとしてパッドに設定
   └─ その他: LibreOffice/AbiWordでHTMLに変換後パッドに設定
6. パッドデータ更新
   └─ padManager経由でパッドに変更を適用
7. クライアントへブロードキャスト
   └─ padMessageHandler.updatePadClientsで全クライアントに通知
8. 一時ファイルクリーンアップ
```

### フローチャート

```mermaid
flowchart TD
    A[ファイルアップロード受信] --> B{レート制限チェック}
    B -->|制限内| C{アクセス権限チェック}
    B -->|制限超過| Z1[429エラー]
    C -->|許可| D[Formidableでファイルパース]
    C -->|拒否| Z2[403エラー]
    D --> E{ファイルサイズチェック}
    E -->|超過| Z3[maxFileSizeエラー]
    E -->|OK| F{ファイル形式判定}
    F -->|.etherpad| G[直接DBインポート]
    F -->|.html/.htm| H[HTML解析]
    F -->|.txt| I[テキスト読込]
    F -->|.doc/.docx/.pdf/.odt| J[LibreOffice変換]
    F -->|不明形式| K{allowUnknownFileEnds?}
    K -->|Yes| L[.txtとして処理]
    K -->|No| Z4[uploadFailedエラー]
    G --> M{パッド既存データ確認}
    M -->|10リビジョン以上| Z5[padHasDataエラー]
    M -->|OK| N[importEtherpad.setPadRaw]
    H --> O[importHtml.setPadHTML]
    I --> P[pad.setText]
    J --> O
    L --> I
    N --> Q[パッド再読込]
    O --> Q
    P --> Q
    Q --> R[updatePadClients]
    R --> S[一時ファイル削除]
    S --> T[成功レスポンス返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-51-01 | ファイルサイズ制限 | settings.importMaxFileSizeを超えるファイルは拒否 | 常時 |
| BR-51-02 | Etherpadインポート制限 | .etherpad形式は既存パッドに10リビジョン以上ある場合は拒否 | .etherpad形式インポート時 |
| BR-51-03 | 未知形式の扱い | 未知の拡張子はsettings.allowUnknownFileEndsがtrueの場合のみ.txtとして処理 | 未知拡張子の場合 |
| BR-51-04 | ASCII検証 | コンバータ未使用時、非ASCII文字のみのファイルは拒否 | コンバータ不使用時 |

### 計算ロジック

特になし

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| パッドコンテンツ更新 | pad:{padId} | UPDATE | パッドのテキストと属性を更新 |
| リビジョン追加 | pad:{padId}:revs:{n} | INSERT | 新しいリビジョンを作成 |
| 著者データ更新 | globalAuthor:{authorId} | UPDATE | .etherpad形式インポート時に著者情報を復元 |

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

#### pad:{padId}

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | atext | インポートしたコンテンツから生成 | 属性付きテキスト |
| UPDATE | pool | インポートしたコンテンツの属性プール | |
| UPDATE | head | 最新リビジョン番号 | インクリメント |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| maxFileSize | バリデーション | ファイルサイズが上限超過 | ファイルを分割または設定値を変更 |
| uploadFailed | アップロード | ファイルアップロード失敗 | 再試行またはファイル形式確認 |
| padHasData | ビジネスルール | .etherpadインポート先パッドに既存データあり | 新規パッドにインポート |
| convertFailed | 変換処理 | LibreOffice/AbiWord変換失敗 | ファイル形式・内容を確認 |
| internalError | システム | 予期しないエラー | ログ確認・管理者へ連絡 |

### リトライ仕様

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

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

パッドデータの更新はアトミックに実行される。.etherpad形式のインポート時は複数レコードの一括書き込みが行われるが、検証フェーズで問題が検出された場合はコミット前にロールバックされる。

## パフォーマンス要件

- ファイル変換はキュー処理され、同時実行数は1に制限
- LibreOffice変換のタイムアウトは120秒
- 大きなファイルの処理中はクライアントへの応答が遅延する可能性あり

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

- レート制限によるDoS攻撃対策（settings.importExportRateLimiting）
- ファイルサイズ制限による資源枯渇攻撃対策
- セッション・トークンによる認証チェック
- webaccess.userCanModifyによる書き込み権限チェック
- 一時ファイルは処理完了後に削除

## 備考

- Windows環境ではファイルロック解放の遅延に対応するため100msの待機処理あり
- LibreOfficeがハングした場合の強制終了処理を実装

---

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

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

### 推奨読解順序

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

インポート処理で扱うデータ構造として、パッドのAText（属性付きテキスト）とChangeset（変更セット）の概念を理解することが重要。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | PadType.ts | `src/node/types/PadType.ts` | AText、APoolの型定義を確認 |
| 1-2 | Changeset.ts | `src/static/js/Changeset.ts` | Changesetの構造と操作を理解 |

**読解のコツ**: ATextはtext（プレーンテキスト）とattribs（属性情報）の組み合わせ。属性はAttributePoolで管理される番号付き属性として表現される。

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

HTTPリクエストの受け取りからインポート処理への橋渡しを行うルーティング部分を確認。

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

**主要処理フロー**:
1. **15-25行目**: レート制限の設定
2. **74-86行目**: POST /p/:pad/import エンドポイントの定義
3. **79-80行目**: セキュリティチェック（checkAccess、userCanModify）
4. **84行目**: importHandler.doImportの呼び出し

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

実際のインポート処理ロジックを確認。

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

**主要処理フロー**:
- **39-49行目**: ImportErrorクラスの定義
- **59-71行目**: コンバータ（AbiWord/LibreOffice）の選択
- **82-239行目**: doImport関数 - 実際のインポート処理
- **91-95行目**: Formidableでのファイルパース設定
- **119-121行目**: 対応ファイル形式の定義
- **146-157行目**: .etherpad形式の直接DBインポート
- **160-178行目**: ファイル変換処理
- **211-219行目**: HTML/テキストのパッドへの設定
- **232行目**: クライアントへのブロードキャスト

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

各ファイル形式の変換処理を確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | ImportHtml.ts | `src/node/utils/ImportHtml.ts` | HTML→パッド変換 |
| 4-2 | ImportEtherpad.ts | `src/node/utils/ImportEtherpad.ts` | Etherpad形式の復元 |
| 4-3 | LibreOffice.ts | `src/node/utils/LibreOffice.ts` | LibreOffice変換 |

**読解のコツ**: ImportHtml.tsはjsdomとcontentcollectorを使用してHTML DOMをパッドの内部データ構造に変換する。ImportEtherpad.tsはJSON形式でエクスポートされた完全なパッドデータを復元する。

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

```
expressCreateServer (importexport.ts)
    │
    ├─ POST /p/:pad/import
    │      │
    │      ├─ securityManager.checkAccess
    │      ├─ webaccess.userCanModify
    │      │
    │      └─ importHandler.doImport (ImportHandler.ts)
    │             │
    │             ├─ Formidable.parse (ファイルパース)
    │             │
    │             ├─ [.etherpad] importEtherpad.setPadRaw
    │             │       └─ Pad.init / db.set
    │             │
    │             ├─ [.html/.htm] importHtml.setPadHTML
    │             │       ├─ jsdom (DOM解析)
    │             │       ├─ contentcollector (属性収集)
    │             │       └─ pad.appendRevision
    │             │
    │             ├─ [変換必要] converter.convertFile
    │             │       └─ LibreOffice.convertFile / Abiword.convertFile
    │             │
    │             ├─ pad.setText / importHtml.setPadHTML
    │             │
    │             └─ padMessageHandler.updatePadClients
```

### データフロー図

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

ファイル              ┌─────────────────┐
(multipart) ───────▶ │  Formidable     │
                     │  ファイル解析    │
                     └────────┬────────┘
                              │
                              ▼
                     ┌─────────────────┐
一時ファイル ◀────── │  ファイル保存   │
                     └────────┬────────┘
                              │
                              ▼
                     ┌─────────────────┐
                     │  形式判定       │
                     └────────┬────────┘
                              │
        ┌─────────────────────┼─────────────────────┐
        ▼                     ▼                     ▼
┌───────────────┐    ┌───────────────┐    ┌───────────────┐
│ .etherpad     │    │ .html/.txt    │    │ 要変換形式    │
│ 直接復元      │    │ 直接解析      │    │ LibreOffice   │
└───────┬───────┘    └───────┬───────┘    └───────┬───────┘
        │                    │                    │
        ▼                    ▼                    ▼
        └─────────────┬──────┴────────────────────┘
                      │
                      ▼
             ┌─────────────────┐
             │  パッド更新     │ ───────▶ Database
             └────────┬────────┘
                      │
                      ▼
             ┌─────────────────┐
             │ updatePadClients│ ───────▶ Socket.IO
             └─────────────────┘            全クライアント
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| importexport.ts | `src/node/hooks/express/importexport.ts` | ソース | Expressルーティング定義 |
| ImportHandler.ts | `src/node/handler/ImportHandler.ts` | ソース | メインインポート処理 |
| ImportHtml.ts | `src/node/utils/ImportHtml.ts` | ソース | HTML解析・変換 |
| ImportEtherpad.ts | `src/node/utils/ImportEtherpad.ts` | ソース | Etherpad形式復元 |
| LibreOffice.ts | `src/node/utils/LibreOffice.ts` | ソース | LibreOffice変換制御 |
| Abiword.ts | `src/node/utils/Abiword.ts` | ソース | AbiWord変換制御 |
| PadManager.ts | `src/node/db/PadManager.ts` | ソース | パッド管理 |
| PadMessageHandler.ts | `src/node/handler/PadMessageHandler.ts` | ソース | クライアント通知 |
| contentcollector.js | `src/static/js/contentcollector.js` | ソース | HTML→属性変換 |
| Settings.ts | `src/node/utils/Settings.ts` | ソース | 設定値管理 |
