# 機能設計書 9-記事公開/非公開

## 概要

本ドキュメントは、LEGACY CMSにおける記事公開/非公開機能の設計を記述したものである。記事の公開ステータスを切り替える仕様を定義する。

### 本機能の処理概要

**業務上の目的・背景**：作成した記事を公開状態にすることで、フロントエンドのサイト訪問者に記事を表示する。また、公開済みの記事を非公開（下書き）状態に戻すことで、一時的に記事を非表示にすることができる。コンテンツのライフサイクル管理において重要な機能である。

**機能の利用シーン**：管理者またはコンテンツ編集者が記事編集画面で「公開」ボタンをクリックすると、確認ダイアログが表示される。確認後、記事のステータスが「published」に変更され、公開日時が設定される。同時に検索インデックスに記事が追加される。

**主要な処理内容**：
1. 公開確認ダイアログの表示
2. 記事内容のバリデーション
3. 記事ステータスの更新（draft → published）
4. 公開日時の設定
5. 記事内容の更新
6. 検索インデックスへの追加

**関連システム・外部連携**：Zend_Search_Luceneを使用した検索インデックスへの追加。

**権限による制御**：`aarticles`（記事モジュールアクセス）および`aarticlepublish`（記事公開）権限が必要。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 36 | 記事編集画面 | 主画面 | 公開ステータスの切り替え |

## 機能種別

ステータス管理 / ワークフロー

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| id | int | Yes | 記事ID | isset |
| confirm | string | Yes | 確認フラグ（'1'で実行） | 値が'1'の場合のみ公開実行 |
| title | string | Yes | 記事タイトル | NotEmpty |
| category | int | Yes | カテゴリID | NotEmpty |
| introduction | string | Yes | ティーザー（導入文） | NotEmpty |
| content | string | No | 本文 | allowEmpty |
| comments | string | No | コメント許可フラグ | allowEmpty |
| moderate | string | No | コメント承認制フラグ | allowEmpty |
| sticky | string | No | スティッキーフラグ | allowEmpty |

### 入力データソース

- URLパラメータ（id, confirm）
- フォーム入力（POSTリクエスト）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| HTML | string | Ajaxダイアログ用HTML（確認画面または完了画面） |

### 出力先

- データベース更新（articlesテーブル）
- 検索インデックス更新
- Ajaxダイアログ（確認画面/完了画面）

## 処理フロー

### 処理シーケンス

```
1. 権限チェック
   └─ aarticles + aarticlepublish 権限を確認
2. レイアウト・ビュー無効化
3. パラメータ取得
4. 確認フラグの判定
   ├─ confirm != '1': 確認ダイアログを表示
   └─ confirm == '1': 公開処理を実行
5. フォームデータのバリデーション
6. ステータス更新
   └─ updateArticleStatus()でpublishedに変更
7. 記事内容更新
   └─ updateArticle()で内容を保存
8. 検索インデックス更新
   └─ 公開済みのため自動的に更新
9. 完了画面の表示
```

### フローチャート

```mermaid
flowchart TD
    A[公開リクエスト] --> B{権限チェック}
    B -->|権限なし| C[権限エラー画面]
    B -->|権限あり| D{confirm == 1?}
    D -->|No| E[確認ダイアログ表示]
    E --> F[ユーザー操作]
    F -->|確認| G[confirm=1で再リクエスト]
    G --> D
    F -->|キャンセル| H[ダイアログを閉じる]
    D -->|Yes| I[バリデーション]
    I --> J{バリデーション成功?}
    J -->|No| K[エラーメッセージ表示]
    J -->|Yes| L[ステータス更新]
    L --> M[記事内容更新]
    M --> N[検索インデックス更新]
    N --> O[完了画面表示]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-001 | 確認必須 | 公開前に確認ダイアログを表示 | 常時 |
| BR-002 | バリデーション必須 | タイトル、カテゴリ、ティーザーの入力が必要 | 公開時 |
| BR-003 | 公開日時自動設定 | article_publishedに現在日時を設定 | 公開時 |
| BR-004 | 検索インデックス追加 | 公開時に検索インデックスに追加 | 公開時 |
| BR-005 | 内容同時保存 | ステータス変更と同時に記事内容も保存 | 公開時 |

### 計算ロジック

特になし

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| ステータス更新 | articles | UPDATE | article_status, article_publishedを更新 |
| 記事更新 | articles | UPDATE | 記事内容を更新 |

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

#### articles（UPDATE - ステータス）

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | article_status | 'published' | 公開状態 |
| UPDATE | article_published | NOW() | 公開日時 |

#### articles（UPDATE - 内容）

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | article_title | 入力値 | 必須 |
| UPDATE | article_category | 入力値 | 必須 |
| UPDATE | article_intro | 入力値 | 必須 |
| UPDATE | article_content | 入力値 | 任意 |
| UPDATE | article_comments | Y/N | デフォルトN |
| UPDATE | article_moderate | Y/N | デフォルトN |
| UPDATE | article_sticky | Y/N | デフォルトN |
| UPDATE | article_edit | NOW() | 自動設定 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | 権限エラー | aarticles または aarticlepublish 権限がない | 権限エラー画面へフォワード |
| IS_EMPTY (title) | バリデーションエラー | タイトル未入力 | 'Title is required'を表示 |
| IS_EMPTY (category) | バリデーションエラー | カテゴリ未選択 | 'Category is required'を表示 |
| IS_EMPTY (introduction) | バリデーションエラー | ティーザー未入力 | 'Teaser is required'を表示 |

### リトライ仕様

特になし

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

明示的なトランザクション制御は実装されていない。ステータス更新と記事更新が別々のUPDATE文で実行される。

## パフォーマンス要件

- レスポンス時間: 1秒以内（検索インデックス更新含む）

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

- 管理者権限（aarticles + aarticlepublish）が必要
- 確認ダイアログによる誤操作防止
- 公開前にバリデーションを実施

## 備考

- 公開と同時に記事内容も保存される
- 一度公開した記事を下書きに戻す機能は別途実装が必要

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | database.sql | `database.sql` | articlesテーブル（5-20行目）のarticle_status, article_publishedカラム |

**読解のコツ**: article_statusのデフォルト値は'draft'、article_publishedはNULL許可で公開時に設定される。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | ArticlesController.php | `application/modules/admin/controllers/ArticlesController.php` | publishAction()メソッド（217-310行目） |

**主要処理フロー**:
1. **219行目**: 権限チェック（`aarticles` + `aarticlepublish`）
2. **221-222行目**: レイアウトとビューの無効化
3. **224-225行目**: パラメータ取得（confirm, id）
4. **227行目**: 確認フラグとIDの検証
5. **233-253行目**: バリデータの設定
6. **255行目**: バリデーション実行
7. **257-270行目**: ステータス更新と記事更新

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | Articles.php | `application/models/Articles.php` | updateArticleStatus()メソッド（375-383行目）、updateArticle()メソッド（314-369行目） |

**主要処理フロー**:
- **375-383行目**: updateArticleStatus() - ステータスと公開日時の更新
- **314-369行目**: updateArticle() - 記事内容の更新と検索インデックス更新

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

```
Admin_ArticlesController::publishAction()
    │
    ├─ ACL権限チェック
    │
    ├─ Zend_Filter_Input (バリデーション)
    │
    ├─ Articles::updateArticleStatus($params)
    │      └─ UPDATE articles (status, published)
    │
    └─ Articles::updateArticle($params)
           │
           ├─ UPDATE articles (内容)
           │
           └─ (status='published'の場合)
                  └─ Search::updateEntry($params)
```

### データフロー図

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

id, confirm    ───▶ publishAction()      ───▶ 確認ダイアログ
title, category,    │                        または
introduction, etc.  │                        完了画面
                    ▼
              バリデーション
                    │
                    ▼
              updateArticleStatus()
              (status='published',
               published=NOW())
                    │
                    ▼
              updateArticle()
                    │
              ┌─────┴─────┐
              ▼           ▼
           UPDATE    Search::updateEntry()
           articles  (検索インデックス)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| ArticlesController.php | `application/modules/admin/controllers/ArticlesController.php` | コントローラー | 公開処理（217-310行目: publishAction） |
| Articles.php | `application/models/Articles.php` | モデル | ステータス更新と記事更新（375-383行目: updateArticleStatus, 314-369行目: updateArticle） |
| Search.php | `application/models/Search.php` | モデル | 検索インデックス更新 |
| database.sql | `database.sql` | DDL | articlesテーブル定義 |
