# 機能設計書 15-ページ公開非公開

## 概要

本ドキュメントは、LEGACY CMSにおける固定ページの公開/非公開（Publish）機能について記述する。この機能は管理画面のAjaxダイアログを通じて、ドラフト状態のページを公開状態に変更し、同時にフォームの内容を保存する。

### 本機能の処理概要

**業務上の目的・背景**：固定ページのコンテンツ作成は通常ドラフト状態で行われ、内容が確定した段階で公開する。公開処理と保存処理を同時に行うことで、最新の編集内容が公開されることを保証する。公開後は検索インデックスにも登録され、サイト内検索で検索可能になる。

**機能の利用シーン**：管理者がページ編集画面で内容を編集後、「Publish」ボタンをクリックして確認ダイアログで公開を実行する。

**主要な処理内容**：
1. ユーザーの権限チェック（ppages + ppagepublish権限の確認）
2. 確認ダイアログの表示（confirm=未指定時）
3. 公開実行（confirm=1時）
   - フォームデータのバリデーション
   - ページステータスを'published'に更新
   - ページ内容の保存
   - 検索インデックスへの登録/更新

**関連システム・外部連携**：
- Zend_Search_Lucene：検索インデックスへの登録/更新

**権限による制御**：`ppages`および`ppagepublish`リソースへのアクセス権限が必要。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 61 | ページ編集画面 | 呼び出し元画面 | Publishボタンからダイアログ呼び出し |
| - | 公開確認ダイアログ | 主画面 | 確認メッセージと公開実行 |

## 機能種別

CRUD操作（Update）/ ステータス変更 / 確認ダイアログ

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| id | integer | Yes | 公開対象ページID | 数値のみ |
| confirm | string | No | 確認フラグ | '1'で公開実行 |
| title | string | Yes（confirm=1時） | ページタイトル | NotEmpty |
| content | string | Yes（confirm=1時） | ページ本文 | NotEmpty |
| slug | string | Yes（confirm=1時） | URLスラッグ | NotEmpty, Alnum, 重複チェック |
| section | string | No | セクション名 | 任意 |

### 入力データソース

- URLパラメータ（ページID、確認フラグ）
- POSTリクエストボディ（フォームデータ）
- セッション（ログインユーザー情報、ACL情報）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| HTML | string | Ajaxダイアログ内のHTML（確認/エラー/完了メッセージ） |

### 出力パターン

1. **確認画面**：「Are you sure you want to publish this page?」メッセージとPublish/Closeボタン
2. **エラー画面**：バリデーションエラーメッセージとCloseボタン
3. **完了画面**：「Page Published」メッセージとCloseボタン

### 出力先

- Ajaxダイアログへのレスポンス
- pagesテーブルのステータス更新
- 検索インデックス（Zend_Search_Lucene）への登録/更新

## 処理フロー

### 処理シーケンス

```
1. publishAction()が呼び出される（Ajaxダイアログ経由）
   └─ レイアウト/ビュー無効化

2. 権限チェック
   ├─ ppages権限チェック
   └─ ppagepublish権限チェック
      ├─ 権限なし → privileges画面へフォワード
      └─ 権限あり → 次のステップへ

3. パラメータ取得
   ├─ idパラメータ（ページID）
   └─ confirmパラメータ（確認フラグ）

4. confirmパラメータによる分岐
   ├─ confirm != '1' → 確認ダイアログ表示
   │     └─ Publish/Closeボタンを含むHTML出力
   └─ confirm == '1' && id存在 → 公開処理実行

5. バリデーション処理（confirm=1の場合）
   └─ Zend_Filter_Inputによる検証
      ├─ title: NotEmpty
      ├─ content: NotEmpty
      ├─ slug: NotEmpty, Alnum, DB重複チェック
      └─ section: 任意

6. バリデーション結果判定
   ├─ 失敗 → エラーメッセージ表示
   └─ 成功 → 次のステップへ

7. 公開処理
   ├─ Pages::updatePageStatus() [ステータス更新]
   │     └─ page_status = 'published', page_published = NOW()
   └─ Pages::updatePage() [内容更新]
         └─ 検索インデックス更新（updatePage内で実行）

8. 完了表示
   └─ 「Page Published」メッセージとCloseボタン出力
```

### フローチャート

```mermaid
flowchart TD
    A[開始: publishAction] --> B[レイアウト/ビュー無効化]
    B --> C{ppages+ppagepublish権限あり?}
    C -->|No| D[privileges画面へフォワード]
    C -->|Yes| E[パラメータ取得 id, confirm]
    E --> F{confirm='1' かつ id存在?}
    F -->|No| G[確認ダイアログHTML出力]
    F -->|Yes| H[バリデーション実行]
    H --> I{バリデーション成功?}
    I -->|No| J[エラーメッセージHTML出力]
    I -->|Yes| K[Pages::updatePageStatus]
    K --> L[Pages::updatePage]
    L --> M[検索インデックス更新]
    M --> N[完了メッセージHTML出力]
    G --> O[終了]
    J --> O
    N --> O
    D --> O
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | 2段階確認 | 確認ダイアログ表示後に公開実行 | 常時 |
| BR-02 | 保存と公開の同時実行 | 公開時にフォーム内容も保存 | 常時 |
| BR-03 | 公開日時記録 | 公開時にpage_publishedを現在時刻で設定 | 常時 |
| BR-04 | 検索インデックス同期 | 公開時に検索インデックスを更新 | 常時 |
| BR-05 | バリデーション必須 | 公開前にコンテンツのバリデーション | 常時 |

### 計算ロジック

検索インデックスのURL生成：`/page/{slug}/`

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| ステータス更新 | pages | UPDATE | 公開ステータスへの変更 |
| 内容更新 | pages | UPDATE | ページ内容の保存 |

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

#### pagesテーブル（ステータス更新）

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | page_status | 'published' | ステータス変更 |
| UPDATE | page_published | NOW() | 公開日時設定 |

#### pagesテーブル（内容更新）

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | page_title | フォーム入力値 | |
| UPDATE | page_content | html_entity_decode(フォーム入力値) | |
| UPDATE | page_slug | フォーム入力値 | |
| UPDATE | page_section | フォーム入力値 | nullable |
| UPDATE | page_edit | NOW() | 自動設定 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | 権限エラー | ppages/ppagepublish権限なし | privileges画面へフォワード |
| IS_EMPTY | バリデーションエラー | タイトルが空 | "Title is required"メッセージ |
| IS_EMPTY | バリデーションエラー | コンテンツが空 | "Content is required"メッセージ |
| IS_EMPTY | バリデーションエラー | URLが空 | "URL is required"メッセージ |
| NOT_ALNUM | バリデーションエラー | URLに不正文字 | "Invalid URL"メッセージ |
| ERROR_RECORD_FOUND | バリデーションエラー | URLが重複 | "URL in use"メッセージ |

### リトライ仕様

特になし（ユーザーがフォームを修正して再送信）

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

2回のUPDATE操作（ステータス更新と内容更新）が行われるが、明示的なトランザクション管理は行っていない。

## パフォーマンス要件

- 2回のUPDATE操作
- 検索インデックス更新はファイルI/Oを伴う

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

- ACLによる権限チェック（ppages + ppagepublishリソース）
- Zend_Filter_Inputによる入力バリデーション
- 2段階確認による誤操作防止
- Zend_Validate_Db_NoRecordExistsによるスラッグ重複チェック

## 備考

- Publishボタンはフォームデータも一緒にPOST送信（postDialog関数使用）
- 現在の実装では「非公開に戻す」機能は未実装（draftへの変更機能なし）
- Dojo Toolkitのダイアログウィジェット使用

---

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

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

### 推奨読解順序

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

ページステータスの遷移とカラム構成を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | database.sql | `database.sql` | pagesテーブル（206-220行目）、page_status, page_publishedカラム |

**読解のコツ**: `page_status`は'draft'または'published'の値を取る。`page_published`は公開日時を記録。

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

publishActionの実装を確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | PagesController.php | `application/modules/admin/controllers/PagesController.php` | publishAction（216-308行目） |

**主要処理フロー**:
1. **218行目**: ppages + ppagepublish権限の複合チェック
2. **220-221行目**: レイアウト/ビュー無効化
3. **223-224行目**: confirm, idパラメータ取得
4. **226行目**: confirm='1' && id存在の条件判定
5. **232-251行目**: バリデータ定義（saveActionと同様）
6. **258行目**: バリデーション成功判定
7. **260-262行目**: Pages::updatePageStatus()呼び出し
8. **263-268行目**: Pages::updatePage()呼び出し
9. **270-275行目**: 完了メッセージHTML出力
10. **292-298行目**: 確認ダイアログHTML出力（postDialogで呼び出し）

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

ステータス更新メソッドを確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | Pages.php | `application/models/Pages.php` | updatePageStatus()（243-251行目） |

**主要処理フロー**:
- **245-248行目**: 更新データ配列の構築（status, published）
- **250行目**: UPDATE文実行

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

```
edit.phtml [Publishボタンクリック]
    │
    └─ postDialog('/admin/pages/publish/id/{id}/','editForm','Publish Page')
           │
           └─ PagesController::publishAction()
                  │
                  ├─ ACL::isAllowed() [権限チェック x2]
                  │
                  ├─ [confirm未設定] 確認ダイアログHTML出力
                  │     │
                  │     └─ [Publishボタンクリック]
                  │            │
                  │            └─ postDialog(..., confirm=1)
                  │
                  └─ [confirm=1]
                         │
                         ├─ Zend_Filter_Input [バリデーション]
                         │
                         ├─ [バリデーション失敗] エラーメッセージ表示
                         │
                         └─ [バリデーション成功]
                                │
                                ├─ Pages::updatePageStatus()
                                │      └─ UPDATE pages SET page_status='published'
                                │
                                └─ Pages::updatePage()
                                       ├─ UPDATE pages SET page_title, content...
                                       └─ Search::updateEntry() [検索インデックス]
```

### データフロー図

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

Publishボタン      ───▶ publishAction (confirm未設定) ───▶ 確認ダイアログ
(editForm POST)                                            ├─ Publishボタン
                                                           └─ Closeボタン

                                    │
                                    ▼

確認Publishボタン  ───▶ publishAction (confirm=1)    ───▶
(editForm POST)          │
                         ├─ バリデーション
                         │     ├─ [失敗] エラーメッセージ
                         │     └─ [成功] ↓
                         │
                         ├─ Pages::updatePageStatus()
                         │      └─ page_status='published'
                         │      └─ page_published=NOW()
                         │
                         └─ Pages::updatePage()           完了メッセージ
                                ├─ pagesテーブルUPDATE     └─ Closeボタン
                                └─ Search::updateEntry()
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| PagesController.php | `application/modules/admin/controllers/PagesController.php` | コントローラー | publishActionの実装 |
| Pages.php | `application/models/Pages.php` | モデル | updatePageStatus(), updatePage() |
| Search.php | `application/models/Search.php` | モデル | updateEntry()検索インデックス更新 |
| edit.phtml | `application/modules/admin/views/scripts/pages/edit.phtml` | ビュー | Publishボタン配置（details.phtmlから読み込み） |
| details.phtml | `application/modules/admin/views/scripts/pages/details.phtml` | ビュー | Publishボタンの定義 |
| common.js | `public/_scripts/admin/common.js` | JavaScript | postDialog()関数 |
| database.sql | `database.sql` | スキーマ | pagesテーブル定義 |
