# 通知設計書 19-convertFailed

## 概要

本ドキュメントは、Etherpadにおけるファイル変換失敗通知「convertFailed」の設計仕様を定義する。この通知は、インポートしようとしたファイルの形式変換処理（LibreOffice/AbiWord）が失敗した際に表示されるエラーメッセージである。

### 本通知の処理概要

この通知はファイル形式の変換処理が失敗したことをユーザーに伝え、代替手段（別のドキュメント形式を使用するか、手動でコピーペースト）を案内するためのアプリ内表示である。

**業務上の目的・背景**：Etherpadでは、Word文書（.doc, .docx）、PDF、ODTなど様々なファイル形式のインポートをサポートしている。これらの形式をEtherpadで編集可能なHTMLに変換するために、外部ツール（LibreOfficeまたはAbiWord）が使用される。しかし、ファイルの破損、複雑な書式、非対応の要素などにより、変換処理が失敗することがある。この通知により、ユーザーは変換が失敗した原因を理解し、別の形式で再試行するか、内容を手動でコピーペーストするなどの代替手段を取ることができる。

**通知の送信タイミング**：サーバー側の`doImport`関数内で、`converter.convertFile`の実行中に例外が発生した場合に`ImportError('convertFailed')`がスローされる。このエラーはクライアント側で受信され、`importErrorMessage`関数によってエラーメッセージが表示される。

**通知の受信者**：インポート操作を実行したユーザー本人のみ。

**通知内容の概要**：「Import failed:」というプレフィックスに続いて、「We were not able to import this file. Please use a different document format or copy paste manually」というメッセージが表示される。

**期待されるアクション**：ユーザーは別のファイル形式（例：Word文書をHTMLに変換してから再インポート）を試すか、ファイルの内容を直接コピーしてパッドに貼り付ける。

## 通知種別

アプリ内通知（エラーメッセージ表示）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期（HTTP POSTレスポンス） |
| 優先度 | 高 |
| リトライ | 無 |

### 送信先決定ロジック

インポート操作を実行したクライアントでのみローカルに処理される。サーバーからのエラーレスポンス（code: 1, message: 'convertFailed'）に基づいて表示が決定される。

## 通知テンプレート

### エラーメッセージ表示

| 項目 | 内容 |
|-----|------|
| 表示要素 | #importmessagefail |
| プレフィックス | pad.impexp.importfailed（「Import failed:」） |
| エラーメッセージ | pad.impexp.convertFailed |
| 表示方法 | fadeIn |

### 本文テンプレート

```javascript
// サーバー側エラー発生
try {
  await converter.convertFile(srcFile, destFile, exportExtension);
} catch (err) {
  logger.warn(`Converting Error: ${err.stack || err}`);
  throw new ImportError('convertFailed');
}

// クライアント側表示
// status === 'convertFailed' の場合
const msg = html10n.get('pad.impexp.convertFailed');
// 表示: "We were not able to import this file. Please use a different document format or copy paste manually"
```

### 添付ファイル

なし

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| pad.impexp.importfailed | エラープレフィックス「Import failed」 | src/locales/*.json | Yes |
| pad.impexp.convertFailed | 変換失敗メッセージ | src/locales/*.json | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| サーバーエラー | converter.convertFile例外 | 変換ツールがエラーを返した場合 | ImportError('convertFailed') |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| 変換成功 | ファイル変換が正常に完了した場合 |
| HTMLファイル | HTMLファイルは変換不要のため、このエラーは発生しない |
| テキストファイル | テキストファイルも変換不要 |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[ファイルインポートリクエスト] --> B{ファイル形式判定}
    B -->|HTML/TXT| C[変換スキップ]
    B -->|DOC/DOCX/PDF/ODT| D{converter設定あり?}
    D -->|No| E[importErrorMessage:copypaste]
    D -->|Yes| F[converter.convertFile実行]
    F -->|成功| G[変換結果をパッドに反映]
    F -->|エラー| H[ImportError:convertFailed]
    H --> I[エラーレスポンス送信]
    I --> J[importErrorMessage:convertFailed]
    J --> K[#importmessagefailに表示]
```

## データベース参照・更新仕様

### 参照テーブル一覧

なし（変換エラー時はDBアクセス前にエラー返却）

### 更新テーブル一覧

なし

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| 変換ツールエラー | LibreOffice/AbiWordが変換に失敗 | 別の形式で再試行または手動コピー |
| 変換ツール未設定 | settings.soffice/abiwordが未設定 | 管理者に変換ツールのインストールを依頼 |
| 複雑なフォーマット | 変換不可能な書式要素を含む | シンプルな形式に変換してから再試行 |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | 0（ユーザーが手動で再試行） |
| リトライ間隔 | N/A |
| リトライ対象エラー | N/A |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | 制限なし |
| 1日あたり上限 | 制限なし |

### 配信時間帯

制限なし

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

- 変換エラーの詳細（スタックトレース）はサーバーログにのみ記録
- クライアントには汎用的なエラーメッセージのみ表示
- 悪意のあるファイルによる変換ツールの脆弱性攻撃を防ぐため、変換は隔離された環境で実行されることが推奨される

## 備考

- converterはsettings.soffice（LibreOffice）またはsettings.abiword（AbiWord）で設定
- LibreOfficeが優先され、sofficeが設定されている場合はabiwordは無視される
- LibreOfficeの場合はexportExtensionが'html'、AbiWordの場合は'htm'
- 変換前の一時ファイルはtmpDirectoryに保存される

---

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

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

### 推奨読解順序

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

まず、変換ツールの設定構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | ImportHandler.ts | `src/node/handler/ImportHandler.ts` | converter変数の初期化（59-71行目） |

**読解のコツ**: settings.sofficeとsettings.abiwordの優先順位を確認する。

#### Step 2: エラー発生箇所を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | ImportHandler.ts | `src/node/handler/ImportHandler.ts` | converter.convertFile呼び出しとエラーハンドリング（171-177行目） |

**主要処理フロー**:
1. **167行目**: `if (fileIsHTML || !useConverter)`で変換スキップ判定
2. **171行目**: `await converter.convertFile(srcFile, destFile, exportExtension)`
3. **173-176行目**: catch節でエラーログ出力とImportError('convertFailed')スロー

#### Step 3: 変換ツールの実装を確認

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | LibreOffice.ts | `src/node/utils/LibreOffice.ts` | convertFile関数の実装（存在する場合） |
| 3-2 | Abiword.ts | `src/node/utils/Abiword.ts` | convertFile関数の実装（存在する場合） |

#### Step 4: クライアント側表示を確認

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | pad_impexp.ts | `src/static/js/pad_impexp.ts` | importErrorMessage関数（85行目でconvertFailedがknown配列に含まれる） |
| 4-2 | en.json | `src/locales/en.json` | pad.impexp.convertFailedメッセージ（203行目） |

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

```
ImportHandler.doImport(req, res, padId, authorId)
    │
    ├─ [ファイル受信] Formidable.parse()
    │
    ├─ [ファイル形式判定]
    │      │
    │      └─ fileIsHTML || fileIsTXT → 変換スキップ
    │
    ├─ [変換が必要な場合]
    │      │
    │      └─ converter.convertFile(srcFile, destFile, exportExtension)
    │             │
    │             ├─ [成功] → 変換後ファイルでインポート続行
    │             │
    │             └─ [エラー]
    │                    │
    │                    ├─ logger.warn()
    │                    └─ throw new ImportError('convertFailed')
    │
    └─ [エラーレスポンス]
           │
           └─ res.json({code: 1, message: 'convertFailed', ...})
                  │
                  ▼
           [クライアント側]
                  │
                  └─ importErrorMessage('convertFailed')
                         │
                         └─ html10n.get('pad.impexp.convertFailed')
                                │
                                └─ "We were not able to import..."
```

### データフロー図

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

DOC/PDF/ODT ───▶ ファイル受信 ───▶ 変換ツール実行
                                        │
                              ┌─────────┴─────────┐
                              │                   │
                           [成功]              [エラー]
                              │                   │
                              ▼                   ▼
                       インポート続行    ImportError('convertFailed')
                                                  │
                                                  ▼
                                        エラーレスポンス
                                                  │
                                                  ▼
                                       importErrorMessage表示
                                                  │
                                                  ▼
                                       "We were not able to..."
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| ImportHandler.ts | `src/node/handler/ImportHandler.ts` | ソース | インポート処理、変換エラー生成 |
| LibreOffice.ts | `src/node/utils/LibreOffice.ts` | ソース | LibreOfficeによる変換処理 |
| Abiword.ts | `src/node/utils/Abiword.ts` | ソース | AbiWordによる変換処理 |
| pad_impexp.ts | `src/static/js/pad_impexp.ts` | ソース | クライアント側エラー表示 |
| en.json | `src/locales/en.json` | 設定 | エラーメッセージローカライズ |
| Settings.ts | `src/node/utils/Settings.ts` | 設定 | soffice/abiword設定 |
