# 画面設計書 36-記事編集画面

## 概要

本ドキュメントは、Legacy CMSにおける記事編集画面の設計仕様を定義します。

### 本画面の処理概要

記事編集画面は、既存の記事を編集するためのフル機能エディタ画面です。タイトル、カテゴリ、ティーザー（導入文）、本文の編集に加え、タグ管理、コメント管理も行えます。

**業務上の目的・背景**：コンテンツ管理の中核機能として、記事の内容を編集・更新できるようにすることが目的です。FCKeditorを使用したリッチテキストエディタにより、HTMLの知識がなくても視覚的に記事を編集できます。タブ構成により、記事本文、タグ、コメントを効率的に管理できます。

**画面へのアクセス方法**：
1. 記事管理画面の記事タイトルまたは「Edit」リンクをクリック
2. 直接URL（/admin/articles/edit/id/{id}）にアクセス
3. 記事新規作成後の「Continue」ボタンで遷移

**主要な操作・処理内容**：
1. 記事タイトルの編集
2. カテゴリの変更
3. ティーザー（導入文）の編集（FCKeditor）
4. 本文の編集（FCKeditor）
5. コメント許可、コメントモデレート、スティッキー設定
6. タグの追加・削除（Tagsタブ）
7. コメントの管理（Commentsタブ）
8. 記事の保存、削除、公開

**画面遷移**：
- 遷移元：記事管理画面、記事新規作成ダイアログ
- 遷移先：記事管理画面（保存後/削除後）

**権限による表示制御**：
- aarticles + aarticleedit権限：画面へのアクセス
- aarticledelete権限：「Delete」ボタンの表示
- aarticlepublish権限：「Publish」ボタンの表示（下書き時のみ）
- gcomments権限：Commentsタブの表示

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 7 | 記事編集 | 主機能 | 記事の編集・更新処理 |
| 63 | 入力検証 | 補助機能 | 記事フォームのバリデーション |
| 9 | 記事公開/非公開 | 補助機能 | 公開ステータスの切り替え |
| 60 | 検索インデックス更新 | 補助機能 | 保存時に検索インデックスを更新 |
| 64 | Ajaxダイアログ | 補助機能 | アセット選択などのダイアログ表示 |

## 画面種別

編集 / フォーム入力

## URL/ルーティング

- URL: `/admin/articles/edit/id/{id}`
- モジュール: admin
- コントローラ: articles
- アクション: edit

## 入出力項目

### 入力項目

| 項目名 | 項目ID | データ型 | 必須 | バリデーション | 説明 |
|--------|--------|----------|------|---------------|------|
| タイトル | title | 文字列 | Yes | NotEmpty | 記事タイトル |
| カテゴリ | category | 整数 | Yes | NotEmpty | カテゴリID |
| ティーザー | introduction | HTML | Yes | NotEmpty | 導入文（リッチテキスト） |
| 本文 | content | HTML | No | - | 記事本文（リッチテキスト） |
| コメント許可 | comments | 文字列 | No | - | Y/N |
| コメントモデレート | moderate | 文字列 | No | - | Y/N |
| スティッキー | sticky | 文字列 | No | - | Y/N |

## 表示項目

### 記事詳細（details.phtml）

| 項目名 | 項目ID | データ型 | 説明 |
|--------|--------|----------|------|
| 著者名 | user_alias | 文字列 | 記事作成者 |
| ステータス | article_status | 文字列 | published/draft |
| 作成日 | article_date | 日時 | 記事作成日時 |
| 公開日 | article_published | 日時 | 記事公開日時（公開時のみ） |
| 最終編集日 | article_edit | 日時 | 最終更新日時 |

## イベント仕様

### 1-画面初期表示

1. CMS_Controller_Action_Admin::preDispatch()で認証・ACLチェック
2. Admin_ArticlesController::editAction()が呼び出される
3. aarticles + aarticleedit権限チェック
4. パラメータから記事IDを取得
5. Articles::fetchArticle()で記事データを取得
6. 記事が存在しない場合は記事管理画面へリダイレクト
7. ビューに記事データを渡してレンダリング
8. details.phtmlをContentPaneでAjax読み込み

### 2-Saveボタン押下

1. postDialog()でフォームデータをAjax送信
2. Admin_ArticlesController::saveAction()が呼び出される
3. バリデーション実行（title, category, introduction必須）
4. Articles::updateArticle()で記事を更新
5. 公開済みの場合は検索インデックスも更新
6. 結果をダイアログに表示

### 3-Deleteボタン押下

1. getDialog()で削除確認ダイアログを表示
2. 確認後、Articles::deleteArticle()を実行
3. 記事、タグ、コメント、検索インデックスを削除
4. 結果をダイアログに表示

### 4-Publishボタン押下

1. getDialog()で公開確認ダイアログを表示
2. 確認後、saveAction同様にバリデーション
3. Articles::updateArticleStatus()でステータスを'published'に更新
4. Articles::updateArticle()で記事データも更新
5. 検索インデックスに追加

### 5-タグ操作（Tagsタブ）

tagsパーシャルとtags.jsで処理。タグの追加・削除はAjaxで実行。

### 6-コメント操作（Commentsタブ）

commentsパーシャルとcomments.jsで処理。コメントの表示・承認・削除はAjaxで実行。

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 画面表示 | articles, articles_categories, users | SELECT | 記事データ取得 |
| 記事保存 | articles | UPDATE | 記事データ更新 |
| 記事保存（公開済み） | Luceneインデックス | UPDATE | 検索インデックス更新 |
| 記事公開 | articles | UPDATE | ステータスと公開日を更新 |
| 記事削除 | articles | DELETE | 記事削除 |
| 記事削除 | tags_slaves | DELETE | 関連タグ削除 |
| 記事削除 | comments | DELETE | 関連コメント削除 |

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

#### articles

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | article_title | フォーム入力値 | タイトル |
| UPDATE | article_category | フォーム入力値 | カテゴリID |
| UPDATE | article_intro | フォーム入力値（HTMLデコード） | ティーザー |
| UPDATE | article_content | フォーム入力値（HTMLデコード） | 本文 |
| UPDATE | article_comments | Y/N | コメント許可 |
| UPDATE | article_moderate | Y/N | モデレート設定 |
| UPDATE | article_sticky | Y/N | スティッキー設定 |
| UPDATE | article_edit | NOW() | 更新日時 |
| UPDATE | article_status | 'published' | 公開時のみ |
| UPDATE | article_published | NOW() | 公開時のみ |

## メッセージ仕様

| メッセージID | 種別 | メッセージ内容 | 表示条件 |
|-------------|------|---------------|---------|
| MSG-001 | エラー | Title is required | タイトル未入力 |
| MSG-002 | エラー | Category is required | カテゴリ未選択 |
| MSG-003 | エラー | Teaser is required | ティーザー未入力 |
| MSG-004 | 完了 | Article Saved | 保存成功 |
| MSG-005 | 完了 | Article Published | 公開成功 |
| MSG-006 | 確認 | Are you sure you want to delete this article? | 削除確認 |
| MSG-007 | 確認 | Are you sure you want to publish this article? | 公開確認 |
| MSG-008 | 完了 | Article Deleted | 削除成功 |
| MSG-009 | エラー | Article Not Specified! | 記事ID未指定 |

## 例外処理

| 例外種別 | 条件 | 処理内容 |
|---------|------|---------|
| 権限エラー | aarticles/aarticleedit権限なし | 権限エラー画面へフォワード |
| 記事不存在 | 指定IDの記事がない | 記事管理画面へリダイレクト |
| バリデーションエラー | 必須項目未入力 | エラーメッセージ表示 |

## 備考

- FCKeditorを使用したWYSIWYGエディタ
- タブ構成：Article（メイン）、Tags、Comments
- 保存はAjaxで実行され、画面遷移なし
- 公開済み記事は検索インデックスに自動登録
- html_entity_decode()でHTMLエンティティをデコードして保存

---

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

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

### 推奨読解順序

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

記事テーブルの構造と更新項目を理解します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | Articles モデル | `application/models/Articles.php` | fetchArticle(), updateArticle() |

**読解のコツ**: html_entity_decode()でHTMLエンティティがデコードされる点に注意。検索インデックス更新の条件分岐も重要。

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

コントローラの編集・保存処理フローを把握します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | Admin_ArticlesController.php | `application/modules/admin/controllers/ArticlesController.php` | editAction(), saveAction(), publishAction() |

**主要処理フロー（editAction）**:
1. **55-56行目**: editAction()開始
2. **57行目**: aarticles + aarticleedit権限チェック
3. **59行目**: パラメータから記事ID取得
4. **61-62行目**: Articles::fetchArticle()で記事取得
5. **64-66行目**: 記事不存在時はリダイレクト

**主要処理フロー（saveAction）**:
1. **123-128行目**: saveAction()開始、レイアウト/ビュー無効化
2. **130行目**: 記事ID取得
3. **134-158行目**: バリデータ定義
4. **160行目**: Zend_Filter_Inputでバリデーション
5. **162行目**: バリデーション成功判定
6. **164-173行目**: Articles::updateArticle()呼び出し

#### Step 3: モデル層を理解する

記事更新ロジックと検索インデックス連携を確認します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | Articles.php | `application/models/Articles.php` | updateArticle(), updateArticleStatus() |

**主要処理フロー（updateArticle）**:
- **314-345行目**: 記事データの更新準備
- **316-332行目**: チェックボックス値のY/N変換
- **334-343行目**: 更新データ配列作成
- **345行目**: DBへのUPDATE実行
- **347行目**: 更新後の記事データ再取得
- **349-367行目**: 公開済みなら検索インデックス更新

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

フォーム構造とFCKeditor統合を確認します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | edit.phtml | `application/modules/admin/views/scripts/articles/edit.phtml` | タブ構造とFCKeditor |
| 4-2 | details.phtml | `application/modules/admin/views/scripts/articles/details.phtml` | 記事詳細情報とボタン |

**主要処理フロー（edit.phtml）**:
- **9-27行目**: Dojoモジュールとスクリプト読み込み
- **40-42行目**: FCKeditorインクルード
- **45行目**: details.phtmlのAjax読み込み（ContentPane）
- **47-153行目**: TabContainerによるタブ構成
- **48-145行目**: Articleタブ（メインフォーム）
- **81-89行目**: FCKeditor（ティーザー）
- **96-104行目**: FCKeditor（本文）
- **147行目**: tagsパーシャル
- **149-151行目**: commentsパーシャル（権限付き）

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

```
HTTP Request (/admin/articles/edit/id/{id})
    │
    ├─ CMS_Controller_Action_Admin::preDispatch()
    │      └─ 認証・gadmin権限チェック
    │
    └─ Admin_ArticlesController::editAction()
           │
           ├─ $acl->isAllowed('aarticles', 'aarticleedit')
           │
           ├─ Articles::fetchArticle($id)
           │      └─ SELECT from articles JOIN categories JOIN users
           │
           └─ View rendering [edit.phtml]
                  │
                  ├─ ContentPane href="/admin/articles/details"
                  │      └─ details.phtml (Ajaxロード)
                  │
                  ├─ TabContainer
                  │      ├─ Article tab (FCKeditor)
                  │      ├─ Tags tab (tagspane.phtml)
                  │      └─ Comments tab (commentspane.phtml)
                  │
                  └─ [Save/Publish/Delete]
                         │
                         └─ Ajax POST to saveAction/publishAction/deleteAction
```

### データフロー図

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

記事ID ────────────────▶ fetchArticle() ────────▶ $view->articleArray
                              │
                              ▼
                         View rendering
                              │
                              ▼
[フォーム送信]
title, category,      ──▶ saveAction() ─────────▶ ダイアログメッセージ
introduction, content        │
                              ├─ Zend_Filter_Input [バリデーション]
                              │
                              ├─ updateArticle()
                              │      │
                              │      └─ UPDATE articles
                              │
                              └─ [公開済みなら]
                                     │
                                     └─ Search::updateEntry()
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| ArticlesController.php | `application/modules/admin/controllers/ArticlesController.php` | コントローラ | 記事操作の制御 |
| edit.phtml | `application/modules/admin/views/scripts/articles/edit.phtml` | ビュー | 編集フォームテンプレート |
| details.phtml | `application/modules/admin/views/scripts/articles/details.phtml` | ビュー | 記事詳細・ボタン表示 |
| Articles.php | `application/models/Articles.php` | モデル | 記事データの操作 |
| Search.php | `application/models/Search.php` | モデル | 検索インデックス操作 |
| tagspane.phtml | `application/modules/admin/views/scripts/_partials/tagspane.phtml` | パーシャル | タグ管理タブ |
| commentspane.phtml | `application/modules/admin/views/scripts/_partials/commentspane.phtml` | パーシャル | コメント管理タブ |
| tags.js | `public/_scripts/admin/tags.js` | JavaScript | タグ操作 |
| comments.js | `public/_scripts/admin/comments.js` | JavaScript | コメント操作 |
| FCKeditor | `public/_scripts/fckeditor/` | ライブラリ | リッチテキストエディタ |
| EditCatSelect (View Helper) | `application/views/helpers/EditCatSelect.php` | ヘルパー | カテゴリ選択生成 |
