# 画面設計書 227-ユーザー編集

## 概要

本ドキュメントは、GitLab管理者エリアにおけるユーザー編集画面の設計について記述する。

### 本画面の処理概要

本画面は管理者が既存ユーザーアカウントの情報を編集するための画面である。ユーザーの基本情報、アクセス権限、プロフィール情報、パスワードの変更などを行う機能を提供する。

**業務上の目的・背景**：ユーザー情報の更新、権限の変更、パスワードの強制リセット、アカウント設定の修正など、管理者がユーザーアカウントを管理するために必要な編集機能を提供する。セキュリティインシデント対応時のパスワード変更や、組織変更に伴う権限修正などで使用される。

**画面へのアクセス方法**：管理者としてログイン後、Admin Area > Overview > Users > ユーザー詳細 > Editボタンをクリック、またはURLで`/admin/users/:username/edit`に直接アクセスする。

**主要な操作・処理内容**：
1. ユーザー基本情報の編集（名前、ユーザー名、メールアドレス）
2. パスワードの変更
3. アクセスレベルの変更
4. 外部ユーザーフラグの変更
5. グループ作成権限の変更
6. プロジェクト数制限の変更
7. プロフィール情報の編集
8. 管理者メモの編集

**画面遷移**：
- ユーザー詳細画面（No.225）からの遷移
- 更新成功時：ユーザー詳細画面へリダイレクト
- キャンセル時：ユーザー詳細画面へ戻る

**権限による表示制御**：管理者権限が必要。管理者が自分以外のユーザーのパスワードを変更した場合、password_expires_atが現在時刻に設定される。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 111 | ユーザー管理 | 主機能 | 管理者によるユーザー編集 |

## 画面種別

編集

## URL/ルーティング

| HTTP メソッド | URL | アクション | 説明 |
|--------------|-----|-----------|------|
| GET | /admin/users/:id/edit | edit | ユーザー編集画面表示 |
| PATCH/PUT | /admin/users/:id | update | ユーザー更新処理 |

## 入出力項目

| 項目名 | 入力/出力 | 型 | 必須 | 説明 |
|--------|----------|----|----|------|
| name | 入力 | 文字列 | はい | ユーザー名（表示名） |
| username | 入力 | 文字列 | はい | ユーザー名（ログインID） |
| email | 入力 | 文字列 | はい | メールアドレス |
| password | 入力 | 文字列 | いいえ | 新しいパスワード（変更時のみ） |
| password_confirmation | 入力 | 文字列 | いいえ | パスワード確認 |
| access_level | 入力 | 選択 | はい | アクセスレベル |
| external | 入力 | チェックボックス | いいえ | 外部ユーザーフラグ |
| can_create_group | 入力 | チェックボックス | いいえ | グループ作成権限 |
| projects_limit | 入力 | 数値 | いいえ | プロジェクト数制限 |
| avatar | 入力 | ファイル | いいえ | アバター画像 |
| linkedin | 入力 | 文字列 | いいえ | LinkedInプロフィール |
| twitter | 入力 | 文字列 | いいえ | X（Twitter）プロフィール |
| website_url | 入力 | URL | いいえ | Webサイト |
| note | 入力 | テキスト | いいえ | 管理者メモ |

## 表示項目

| セクション | 項目 | 説明 |
|-----------|------|------|
| ヘッダー | タイトル | "Edit user: {ユーザー名}" |
| Accountセクション | Name | ユーザー名入力フィールド（既存値表示） |
| Accountセクション | Username | ユーザー名入力フィールド（既存値表示） |
| Accountセクション | Email | メールアドレス入力フィールド（既存値表示） |
| Passwordセクション | Password | 新しいパスワード入力フィールド |
| Passwordセクション | Password confirmation | パスワード確認入力フィールド |
| Accessセクション | Access level | アクセスレベル選択（既存値選択） |
| Accessセクション | External user | 外部ユーザーチェックボックス（既存値） |
| Accessセクション | Can create group | グループ作成権限チェックボックス（既存値） |
| Accessセクション | Projects limit | プロジェクト数制限入力（既存値） |
| Organizationセクション | Organization | 組織選択（条件付き表示） |
| Profileセクション | Avatar | アバターファイル選択 |
| Profileセクション | LinkedIn | LinkedInプロフィール入力（既存値） |
| Profileセクション | X (formerly Twitter) | X（Twitter）プロフィール入力（既存値） |
| Profileセクション | Website URL | Webサイト入力（既存値） |
| Admin notesセクション | Note | 管理者メモ入力（既存値） |
| フッター | Save changesボタン | 更新実行 |
| フッター | Cancelリンク | キャンセル |

## イベント仕様

### 1-Save changesボタン押下

1. フォームをバリデーション
2. PATCH/PUTリクエストを`/admin/users/:id`に送信
3. パスワードが入力されている場合、password_paramsを追加
4. 管理者が自分以外を編集中でパスワードを変更した場合、password_expires_atを現在時刻に設定
5. Users::UpdateServiceを実行
6. 成功時：ユーザー詳細画面にリダイレクト、「User was successfully updated.」メッセージを表示
7. 失敗時：エラーを表示してフォームを再表示

### 2-Cancelリンク押下

1. ユーザー詳細画面（/admin/users/:id）にリダイレクト

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 画面表示 | users | SELECT | ユーザー情報取得 |
| ユーザー更新 | users | UPDATE | ユーザー情報更新 |
| パスワード変更 | users | UPDATE | encrypted_password、password_expires_at更新 |

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

#### users

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | name | フォーム入力値 | |
| UPDATE | username | フォーム入力値 | |
| UPDATE | email | フォーム入力値 | |
| UPDATE | encrypted_password | 新しいパスワードのハッシュ | パスワード変更時 |
| UPDATE | password_expires_at | Time.current | 管理者が他ユーザーのパスワード変更時 |
| UPDATE | admin | access_level == 'admin' | |
| UPDATE | external | フォーム入力値 | |
| UPDATE | can_create_group | フォーム入力値 | |
| UPDATE | projects_limit | フォーム入力値 | |

## メッセージ仕様

| 種別 | メッセージ | 表示条件 |
|------|----------|---------|
| 成功 | User was successfully updated. | 更新成功時 |
| エラー | バリデーションエラーメッセージ | 入力エラー時 |

## 例外処理

| 状態 | 処理 |
|------|------|
| 権限不足 | 403エラー画面にリダイレクト |
| ユーザー不存在 | 404エラー画面にリダイレクト |
| バリデーションエラー | フォームを再表示し、エラーメッセージを表示 |
| ユーザー名重複 | 「Username has already been taken」エラー |
| メールアドレス重複 | 「Email has already been taken」エラー |

## 備考

- パスワード欄を空にすると、パスワードは変更されない
- 管理者が自分自身を編集する場合、password_expires_atは設定されない
- skip_reconfirmation!が呼ばれるため、メールアドレス変更時の再確認は不要
- 管理者が他ユーザーのパスワードを変更した場合、send_only_admin_changed_your_password_notification!が呼ばれる
- EE版では追加のフィールドが表示される

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | user.rb | `app/models/user.rb` | Userモデルのバリデーション、属性 |

**読解のコツ**: バリデーションルール、パスワード関連の処理を確認。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | users_controller.rb | `app/controllers/admin/users_controller.rb` | editアクション、updateアクション |

**主要処理フロー**:
- **行51-53**: editアクションでuserを取得
- **行239-275**: updateアクション
  - **行240**: user_paramsをコピー
  - **行242-250**: パスワード変更処理
  - **行248**: password_expires_atを現在時刻に設定（他ユーザーの場合）
  - **行259**: Users::UpdateServiceを実行
  - **行260-261**: 更新前の準備処理（prepare_user_for_update）

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | edit.html.haml | `app/views/admin/users/edit.html.haml` | 編集フォームのレイアウト |
| 3-2 | _form.html.haml | `app/views/admin/users/_form.html.haml` | フォームフィールド定義（new.htmlと共通） |

**主要処理フロー**:
- **edit.html.haml 行1-4**: パンくずリスト、ページタイトル
- **edit.html.haml 行6**: タイトル「Edit user: {ユーザー名}」
- **_form.html.haml 行34-40**: パスワードフィールド（編集時のみ表示）

#### Step 4: サービスクラスを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | update_service.rb | `app/services/users/update_service.rb` | ユーザー更新ロジック |

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

```
Admin::UsersController#edit
    │
    └─ user（既存ユーザー取得）
           │
           └─ view: edit.html.haml
                  │
                  └─ partial: _form.html.haml

Admin::UsersController#update
    │
    ├─ user_params（パラメータ取得）
    │
    ├─ パスワード処理
    │      └─ admin_making_changes_for_another_user?
    │             └─ password_expires_at = Time.current
    │
    ├─ credit_card_validation処理
    │
    └─ Users::UpdateService#execute
           │
           ├─ prepare_user_for_update
           │      ├─ skip_reconfirmation!
           │      └─ send_only_admin_changed_your_password_notification!
           │
           └─ User.update
```

### データフロー図

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

URL /:id ───────▶ UsersController#edit ───▶ @user（既存データ）
                         │
                         └─ view: edit.html.haml（既存値表示）

フォーム入力 ───────▶ UsersController#update ───▶ Users::UpdateService
                         │                           │
                         │ user_params               ▼
                         │ + password_params      User.update
                         │                           │
                         └───────────────────────────┘
                                  │
                                  ▼
                         リダイレクト（詳細画面）
                              + フラッシュ
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| users_controller.rb | `app/controllers/admin/users_controller.rb` | コントローラ | edit/updateアクション |
| edit.html.haml | `app/views/admin/users/edit.html.haml` | テンプレート | 編集画面 |
| _form.html.haml | `app/views/admin/users/_form.html.haml` | パーシャル | フォームフィールド |
| user.rb | `app/models/user.rb` | モデル | ユーザーモデル |
| update_service.rb | `app/services/users/update_service.rb` | サービス | 更新ロジック |
| admin.rb | `config/routes/admin.rb` | ルーティング | URL定義 |
