# 画面設計書 47-ユーザー表示設定

## 概要

本ドキュメントは、Jenkinsのユーザー表示設定画面（Preferences）の画面設計書です。ユーザーの表示に関連するプロパティ（タイムゾーン、言語設定等）を設定するカテゴリ別設定画面について記述します。

### 本画面の処理概要

この画面では、ユーザーの表示に関連するプロパティを一括で設定します。UserPropertyのうち、Preferencesカテゴリに分類されるプロパティがこの画面で設定可能です。タイムゾーンの設定やユーザーインターフェースのカスタマイズなどが典型的な設定項目です。

**業務上の目的・背景**：Jenkins 2.468以降、ユーザー設定はカテゴリ別に分離されました。表示に関する設定を専用画面に集約することで、設定の発見性と管理性を向上させています。異なるタイムゾーンで作業するチームメンバーが、各自の環境に適した表示設定を行うことができます。

**画面へのアクセス方法**：ユーザー詳細画面のサイドパネルから「Preferences」リンクをクリック、または `/user/{userId}/preferences` にURL直接アクセスします。ADMINISTER権限がない場合、サイドパネルにリンクが表示されません。

**主要な操作・処理内容**：
1. Preferencesカテゴリのプロパティ表示
2. 各プロパティの設定値の編集
3. 設定の保存

**画面遷移**：
- 遷移元：ユーザー詳細画面（/user/{userId}/）、サイドパネル
- 遷移先：ユーザー詳細画面（/user/{userId}/）（保存後）

**権限による表示制御**：ADMINISTER権限を持つユーザーのみがこの画面にアクセスできます。権限がない場合、サイドパネルにリンクが表示されません（getIconFileName()がnullを返す）。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 30 | ユーザー管理 | 主機能 | ユーザー表示プロパティの設定 |

## 画面種別

設定

## URL/ルーティング

- **URL**: `/user/{userId}/preferences`
- **Jellyテンプレート**: `core/src/main/resources/hudson/model/userproperty/UserPropertyCategoryPreferencesAction/index.jelly`
- **権限**: `Jenkins.ADMINISTER`

## 入出力項目

### 動的プロパティセクション

入力項目は`UserProperty.allByCategoryClass(UserPropertyCategory.Preferences.class)`で取得されるディスクリプターに依存します。代表的なプロパティ：

| 項目名 | 項目ID | 種別 | 必須 | データ型 | 説明 |
|--------|--------|------|------|----------|------|
| タイムゾーン | userDefinedTimezone | 入力 | - | String | ユーザーのタイムゾーン設定 |

## 表示項目

### ヘッダー部分

| 項目名 | 説明 |
|--------|------|
| タイトル | 「Preferences」（Messages.UserPropertyCategoryPreferencesAction_DisplayName()から取得） |

### プロパティ設定フォーム

| 項目名 | 説明 |
|--------|------|
| 各UserPropertyのconfig.jelly | Preferencesカテゴリの各UserPropertyが提供する設定フォーム |

## イベント仕様

### 1-画面表示

1. 画面アクセス時にADMINISTER権限をチェック
2. getMyCategoryDescriptors()でPreferencesカテゴリのプロパティディスクリプターを取得
3. 各プロパティのconfig.jellyを描画

### 2-設定保存（doConfigSubmit）

1. 「Save」ボタンをクリック
2. POSTリクエストがdoConfigSubmitアクションに送信される
3. 各プロパティの入力値を取得し、reconfigure()またはnewInstance()を呼び出す
4. User.addProperties()で一括更新
5. User.save()で永続化
6. ユーザー詳細画面にリダイレクト（FormApply.success("..")）

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

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

| 操作（イベント） | 対象ファイル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 設定保存 | users/{userId}/config.xml | UPDATE | UserPropertyの更新 |

### ファイル別更新項目詳細

#### users/{userId}/config.xml

| 操作 | 要素 | 更新値・取得条件 | 備考 |
|-----|------|-----------------|------|
| UPDATE | properties/*/（Preferencesカテゴリ） | フォーム入力値 | 各プロパティのシリアライズ形式に依存 |

## メッセージ仕様

| 種別 | メッセージ | 発生条件 |
|------|----------|----------|
| タイトル | Preferences | 画面タイトル（Messagesから取得） |

## 例外処理

| 状態 | 処理 |
|------|------|
| 権限不足 | サイドパネルにリンク非表示、直接アクセス時は403エラー |
| バリデーションエラー | エラーメッセージを表示し、フォームを再表示 |

## 備考

- このアクションクラスはExtension(ordinal = 300)で登録されており、サイドパネルでの表示順序は300です
- シンボル名は`@Symbol("preferences")`で「preferences」です
- アイコンは`symbol-parameters`が使用されます
- 共通の基底クラス`UserPropertyCategoryAction`がフォーム処理を提供します

---

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

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

### 推奨読解順序

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

ユーザープロパティとカテゴリの関係を把握します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | UserProperty.java | `core/src/main/java/hudson/model/UserProperty.java` | UserPropertyの基本構造 |
| 1-2 | UserPropertyCategory.java | `core/src/main/java/hudson/model/userproperty/UserPropertyCategory.java` | カテゴリ分類の仕組み |

**読解のコツ**: `UserPropertyCategory.Preferences`クラスがPreferencesカテゴリを定義し、各`UserPropertyDescriptor`がカテゴリに属します。

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

アクションクラスの構造を把握します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | UserPropertyCategoryAction.java | `core/src/main/java/hudson/model/userproperty/UserPropertyCategoryAction.java` | 基底クラス、doConfigSubmit実装 |
| 2-2 | UserPropertyCategoryPreferencesAction.java | `core/src/main/java/hudson/model/userproperty/UserPropertyCategoryPreferencesAction.java` | Preferencesカテゴリ固有実装 |
| 2-3 | index.jelly | `core/src/main/resources/hudson/model/userproperty/UserPropertyCategoryPreferencesAction/index.jelly` | 画面テンプレート |

**主要処理フロー**:
1. **getMyCategoryDescriptors()**: `UserProperty.allByCategoryClass(UserPropertyCategory.Preferences.class)`でカテゴリフィルタ
2. **doConfigSubmit()**: 基底クラスで共通処理
3. **getUrlName()**: "preferences"を返す

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

```
ブラウザ (index.jelly)
    |
    +-- getMyCategoryDescriptors()
    |      |
    |      +-- UserProperty.allByCategoryClass(Preferences.class)
    |
    +-- [POST] doConfigSubmit()
           |
           +-- UserPropertyCategoryAction.doConfigSubmit()
                  |
                  +-- foreach descriptor:
                  |      +-- user.getProperty(descriptor.clazz)
                  |      +-- property.reconfigure() または descriptor.newInstance()
                  |
                  +-- user.addProperties(props)
                  +-- user.save()
                  +-- FormApply.success("..")
```

### データフロー図

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

フォーム入力値 ──────> doConfigSubmit() ───────────────> config.xml更新
                              |
                              +-- プロパティ再構成
                              +-- user.addProperties()
                              +-- user.save()
                              |
                              +────────────────────────> リダイレクト（..）
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| UserPropertyCategoryPreferencesAction.java | `core/src/main/java/hudson/model/userproperty/UserPropertyCategoryPreferencesAction.java` | ソース | 表示設定カテゴリアクション |
| UserPropertyCategoryAction.java | `core/src/main/java/hudson/model/userproperty/UserPropertyCategoryAction.java` | ソース | カテゴリアクション基底クラス |
| index.jelly | `core/src/main/resources/hudson/model/userproperty/UserPropertyCategoryPreferencesAction/index.jelly` | テンプレート | 画面表示 |
| UserProperty.java | `core/src/main/java/hudson/model/UserProperty.java` | ソース | ユーザープロパティ基底クラス |
| UserPropertyCategory.java | `core/src/main/java/hudson/model/userproperty/UserPropertyCategory.java` | ソース | カテゴリ定義 |
