# 機能設計書 11-ページ移動・並び替え

## 概要

本ドキュメントは、QuickerSite CMSにおけるページ移動・並び替え機能の設計について記述する。この機能により、管理者はサイト内のページ階層構造を変更し、メニュー上の表示順序を調整することができる。

### 本機能の処理概要

ページ移動・並び替え機能は、サイト内のページを別の親ページ配下に移動したり、同一階層内での表示順序（ランク）を変更したりする機能である。

**業務上の目的・背景**：Webサイトの情報構造は、ビジネス要件やユーザビリティの観点から頻繁に変更される必要がある。サイトリニューアルやコンテンツ再編成の際に、既存のページを削除・再作成することなく、階層構造を柔軟に変更できることが求められる。本機能は、運用効率を大幅に向上させ、サイト管理者の作業負荷を軽減する。

**機能の利用シーン**：
- サイトリニューアル時にメニュー構造を再編成する場合
- 新規カテゴリを追加し、既存ページをそのカテゴリ配下に移動する場合
- ページの重要度に応じてメニュー内の表示順序を変更する場合
- 季節やキャンペーンに応じてトップメニューの並び順を調整する場合

**主要な処理内容**：
1. ページ選択画面でツリー構造を表示し、移動先の親ページを選択
2. MoveUpアクション：同一階層内でページを上位に移動（iRangを減少）
3. MoveDownアクション：同一階層内でページを下位に移動（iRangを増加）
4. Insertアクション：選択したページを別の親ページ配下に移動
5. 並び順（iRang）の再計算と他ページのランク調整
6. メニューキャッシュのクリア

**関連システム・外部連携**：外部システムとの連携はなし。サイト内のページ管理機能と密接に連携している。

**権限による制御**：セカンドアドミン権限の`bPagesMove`と`bPageOrder`により、移動機能と並び順変更機能へのアクセスが個別に制御される。両方の権限がある場合のみ、並び順変更が可能。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 16 | ページ選択画面 | 主画面 | ページ移動先の選択（ツリー表示） |
| 13 | コンテナ編集 | 参照画面 | 子ページの並び順変更 |
| 194 | ソート順設定 | 補助機能 | ページの並び順設定 |

## 機能種別

CRUD操作（Update） / データ並び替え

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| iId | Integer（暗号化） | Yes | 移動対象ページのID | 数値であること、存在するページIDであること |
| btnaction | String | Yes | アクション種別（Move/MoveUp/MoveDown/Insert） | 許可されたアクション値のみ |
| insertInto | Integer（暗号化） | No（Insertの場合必須） | 移動先親ページID | 数値であること、存在するページIDであること |
| iRang | Integer | No | 新しい並び順 | 1以上の整数 |
| QSSEC | String | Yes | CSRFトークン | 有効なセキュリティコードであること |

### 入力データソース

- 画面入力：ページ選択画面（bs_selectPage.asp）からのクリック操作
- セッションデータ：ログイン管理者情報、セカンドアドミン権限情報
- データベース：tblPageテーブルからの既存ページ情報

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| 更新結果 | Boolean | 移動・並び替えの成功/失敗 |
| リダイレクト先 | URL | 処理完了後の遷移先（bs_default.asp または bs_intranet.asp） |

### 出力先

- 画面表示：処理完了後、ページ一覧画面（bs_default.asp）またはイントラネット画面（bs_intranet.asp）にリダイレクト
- データベース：tblPageテーブルのiParentID、iRang、bLossePagina、sPw等のカラムが更新

## 処理フロー

### 処理シーケンス

```
1. アクションの判定
   └─ btnactionパラメータに基づいて処理を分岐
2. CSRF検証
   └─ checkCSRF()関数でセキュリティトークンを検証
3. ページ情報取得
   └─ cls_pageクラスでiIdに基づいてページ情報をロード
4. アクション別処理
   ├─ Move: 移動先選択画面へリダイレクト
   ├─ MoveUp: moveUp()メソッドで上位移動
   ├─ MoveDown: moveDown()メソッドで下位移動
   └─ Insert: 親ページ変更と新しいランク設定
5. ランク再計算
   └─ removeRang()またはsetRangByNumber()で他ページのランクを調整
6. 付随処理
   ├─ パスワード同期（親ページのパスワードを継承）
   ├─ オンライン状態の同期
   └─ メニューキャッシュのクリア
7. リダイレクト
   └─ イントラネットフラグに応じて適切な画面へ遷移
```

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B{アクション判定}
    B -->|Move| C[CSRF検証]
    B -->|MoveUp| D[CSRF検証]
    B -->|MoveDown| E[CSRF検証]
    B -->|Insert| F[CSRF検証]

    C --> G[移動先選択画面へリダイレクト]

    D --> H{現在位置 = 1?}
    H -->|Yes| I[処理終了]
    H -->|No| J[iRangを-1]
    J --> K[上位ページのiRangを+1]
    K --> L[メニューキャッシュクリア]

    E --> M{現在位置 = 最大?}
    M -->|Yes| I
    M -->|No| N[iRangを+1]
    N --> O[下位ページのiRangを-1]
    O --> L

    F --> P[元の親からランク削除]
    P --> Q[新しい親IDを設定]
    Q --> R[bLossePaginaをfalseに]
    R --> S{新親がオフライン?}
    S -->|Yes| T[ページをオフラインに]
    S -->|No| U[新ランクを計算]
    T --> U
    U --> V{親にパスワード?}
    V -->|Yes| W[パスワードを継承]
    V -->|No| X[保存]
    W --> X
    X --> L

    L --> Y[ページ一覧へリダイレクト]
    Y --> Z[終了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-11-01 | 上位移動限界 | ランクが1のページは上位移動できない | MoveUpアクション実行時 |
| BR-11-02 | 下位移動限界 | ランクが同一階層の最大値のページは下位移動できない | MoveDownアクション実行時 |
| BR-11-03 | パスワード継承 | 移動先親ページにパスワードが設定されている場合、移動ページと全サブページにパスワードを継承 | Insertアクション実行時 |
| BR-11-04 | オフライン継承 | 移動先親ページがオフラインの場合、移動ページもオフラインに設定 | Insertアクション実行時 |
| BR-11-05 | イントラネット継承 | 移動先親ページがイントラネット属性を持つ場合、移動ページにも継承 | Insertアクション実行時 |
| BR-11-06 | フリーページ変換 | フリーページ（bLossePagina=true）を階層構造に移動すると、通常ページに変換される | Insertアクション実行時 |

### 計算ロジック

**新規ランク計算**：
- 移動先親ページ配下の最大iRang + 1 を新しいランクとして設定
- 計算式: `page.iRang = convertGetal(page.getRang) + 1`

**ランク調整（上位移動）**：
- 対象ページ: `iRang = iRang - 1`
- 影響を受けるページ: `iRang + 1` (iRang = 元のiRang - 1 のページ)

**ランク調整（下位移動）**：
- 対象ページ: `iRang = iRang + 1`
- 影響を受けるページ: `iRang - 1` (iRang = 元のiRang + 1 のページ)

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| ページ移動 | tblPage | UPDATE | 対象ページのiParentID、iRang、bLossePagina等を更新 |
| ランク調整 | tblPage | UPDATE | 同一階層内の他ページのiRangを更新 |
| サブページパスワード同期 | tblPage | UPDATE | サブページ全体のsPwを更新 |

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

#### tblPage

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | iParentID | 新しい親ページID | Insertアクション時 |
| UPDATE | iRang | 新しい並び順（1〜n） | 移動・並び替え時 |
| UPDATE | bLossePagina | false | フリーページからの移動時 |
| UPDATE | bOnline | false | 親がオフラインの場合 |
| UPDATE | sPw | 親ページのパスワード | 親にパスワードがある場合 |
| UPDATE | updatedTS | 現在日時 | 更新時に自動設定 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | CSRF検証エラー | セキュリティトークンが無効 | エラーメッセージを表示し処理中断 |
| - | ページ不存在エラー | 指定されたiIdに対応するページが存在しない | 処理をスキップ |
| - | 権限エラー | bPagesMoveまたはbPageOrder権限がない | アクセス拒否 |
| - | 移動限界エラー | 上位/下位移動の限界に達した | 処理をスキップ（エラーメッセージなし） |

### リトライ仕様

リトライは実装されていない。エラー発生時は処理を中断し、ユーザーに再操作を促す。

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

本機能では明示的なトランザクション制御は行われていない。各UPDATE文は個別に実行される。ただし、ADODBの暗黙的トランザクションにより、単一のUPDATE文は原子性が保証される。

メニューキャッシュのクリア（`clearMenuCache`）は、データベース更新後に実行される。

## パフォーマンス要件

- レスポンス時間: 通常操作で1秒以内
- ランク調整のSQL: 同一階層内のページ数に比例（通常数十件以内）
- メニューキャッシュ更新: 次回メニュー表示時にキャッシュを再構築

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

1. **CSRF対策**: 全てのアクションでcheckCSRF()によるトークン検証を実施
2. **権限チェック**: セカンドアドミン権限（bPagesMove、bPageOrder）によるアクセス制御
3. **パラメータ暗号化**: ページIDはencrypt/decrypt関数で暗号化して受け渡し
4. **入力検証**: isNumeriek()関数でIDの数値検証を実施

## 備考

- イントラネットページと通常ページは別々の階層として管理される
- 移動操作後、アプリケーションキャッシュ内のメニュー情報がクリアされ、次回アクセス時に再構築される
- ホームページに設定されているページは、移動しても位置は変わるがホームページ設定は維持される

---

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

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

### 推奨読解順序

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

ページの階層構造とランク管理の仕組みを理解することが重要である。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | page.asp | `asp/includes/page.asp` | cls_pageクラスの定義、iParentID/iRangプロパティの役割 |

**読解のコツ**: Classic ASPではクラス定義が`Class cls_page ... End Class`形式で記述される。プロパティ定義は`Public`キーワードで始まる。

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

処理の起点となるファイルを特定する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | bs_process.asp | `asp/bs_process.asp` | アクション判定とルーティングロジック |

**主要処理フロー**:
1. **20-35行目**: MoveUp/MoveDownアクションの処理。moveUp()/moveDown()メソッドを呼び出し、イントラネットフラグに応じてリダイレクト先を決定
2. **36-40行目**: Moveアクションの処理。bs_selectPage.aspへリダイレクト
3. **41-64行目**: Insertアクションの処理。親ページ変更、ランク計算、パスワード同期を実行

#### Step 3: ページ選択画面を理解する

移動先を選択するためのUI処理を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | bs_selectPage.asp | `asp/bs_selectPage.asp` | ページツリーの表示とInsertリンクの生成 |

**主要処理フロー**:
- **4行目**: セキュリティチェック（secondAdmin.bPagesMove）
- **5-8行目**: cls_menuクラスを使用してページツリーを描画
- **10-19行目**: JavaScriptでの移動確認ダイアログ

#### Step 4: 並び替えロジックを理解する

ランク変更のSQLと処理ロジックを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | page.asp | `asp/includes/page.asp` | moveUp/moveDown/setRangByNumber関数の実装 |

**主要処理フロー**:
- **827-843行目**: moveUp関数 - 現在のランクが1でなければ上位移動を実行
- **845-862行目**: moveDown関数 - 現在のランクが最大でなければ下位移動を実行
- **807-826行目**: setRangByNumber関数 - 指定ランクへの移動と他ページの調整

#### Step 5: メニュー表示への影響を理解する

メニューキャッシュの管理と再構築の仕組みを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | menu.asp | `asp/includes/menu.asp` | cls_menuクラスのgetBOMenu/getReplaceMenu関数 |

**主要処理フロー**:
- **150-212行目**: getBOMenu関数 - 管理画面用メニューツリーの生成
- **237-281行目**: getReplaceMenu関数 - 移動先選択用メニューの生成

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

```
bs_default.asp（またはbs_intranet.asp）
    │
    ├─ bs_process.asp（アクション処理）
    │      ├─ checkCSRF()
    │      ├─ cls_page.pick()
    │      ├─ cls_page.moveUp() / moveDown()
    │      │      └─ db.execute() [UPDATE tblPage]
    │      │      └─ clearMenuCache()
    │      └─ Insertアクション
    │             ├─ parentPage.removeRang()
    │             ├─ cls_page.save()
    │             │      └─ db.execute() [UPDATE tblPage]
    │             └─ resetAllSubPasswords()
    │
    └─ bs_selectPage.asp（移動先選択画面）
           ├─ bs_security.asp [権限チェック]
           └─ cls_menu.getReplaceMenu()
                  └─ db.execute() [SELECT from tblPage]
```

### データフロー図

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

ページID(iId)    ───▶  bs_process.asp           ───▶  tblPage更新
アクション種別   ───▶  (アクション判定)         ───▶  メニューキャッシュクリア
移動先ID         ───▶  cls_page                 ───▶  リダイレクトURL
CSRFトークン     ───▶  (moveUp/moveDown/save)
                        │
                        ▼
                 tblPage
                 (iParentID, iRang更新)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| bs_process.asp | `asp/bs_process.asp` | ソース | アクション処理のルーティング |
| bs_selectPage.asp | `asp/bs_selectPage.asp` | ソース | 移動先選択画面 |
| bs_sortorder.asp | `asp/bs_sortorder.asp` | ソース | 並び順選択UIコンポーネント |
| page.asp | `asp/includes/page.asp` | ソース | ページクラス定義（移動・ランク処理含む） |
| menu.asp | `asp/includes/menu.asp` | ソース | メニュークラス定義（ツリー表示） |
| bs_security.asp | `asp/bs_security.asp` | ソース | セキュリティチェック |
| bs_default.asp | `asp/bs_default.asp` | ソース | ページ一覧画面（移動後のリダイレクト先） |
| bs_intranet.asp | `asp/bs_intranet.asp` | ソース | イントラネット画面（移動後のリダイレクト先） |
