Skip to content

Phase 14c-3: editor profile module on iManager 2.0#26

Merged
bigin merged 1 commit into
imanager-2.0from
phase-14c3-profile
May 3, 2026
Merged

Phase 14c-3: editor profile module on iManager 2.0#26
bigin merged 1 commit into
imanager-2.0from
phase-14c3-profile

Conversation

@bigin
Copy link
Copy Markdown
Owner

@bigin bigin commented May 3, 2026

Plan §6 lists a separate users (CRUD) sub-phase at 14c-3 and profile at 14c-6, but legacy Scriptor only ships profile (the logged-in user edits their own record). Per Plan §9 (functional parity, no new features) we collapse the two slots: this PR is the profile rewrite, and 14c-6 drops out of the schedule. Master plan table needs the matching update on the iManager docs side.

New Editor\Profile\ProfileModule covers editor/modules/profile/profile.php:

  • GET /editor/profile/ → 302 to /edit/?profile=
  • GET /editor/profile/edit/ render the form (name, email, password,
    password_confirm + CSRF token)
  • POST action=save-profile validate + persist + flash + redirect

Validation:

  • username + email required (same legacy "profile_incomplete" message)
  • new password optional; if set, must be ≥6 chars and equal to confirm
  • email goes through Sanitizer::email; rejects invalid syntax
  • username uniqueness preflight via UserRepository::nameTaken(except)
  • CSRF token (profile) required when protectCSRF is on

Password handling preserves the migrated 1.x PasswordFieldValue wrapper shape ({__class, password, salt}): when the existing field value is a wrapper array we update its password key in place; otherwise we write a plain bcrypt hash so the modern PasswordFieldType reads it directly. Auth still recognises both shapes (added in 14c-1), so a password change here keeps the legacy wrapper intact and stays compatible with any unmigrated tooling.

UserRepository gained:

  • nameTaken(name, exceptId) — uniqueness preflight
  • save(Item) — wraps ItemRepository::save with category check

EditorRouter routes /editor/profile* to ProfileModule and removes both the profile and users placeholder entries.

Manual smoke (PHP built-in server):
GET /editor/profile → 302 → /editor/profile/edit/?profile=9
GET /editor/profile/ → 302 → same
GET /editor/profile/edit/ → 200, form pre-filled (admin / migrated email)
POST update email → 302; DB shows new email
POST password change → 302; logout + retry
old password → 200 (login form, rejected)
new password → 302 → /editor/ (accepted)

Plan §6 lists a separate `users` (CRUD) sub-phase at 14c-3 and
`profile` at 14c-6, but legacy Scriptor only ships `profile` (the
logged-in user edits their own record). Per Plan §9 (functional
parity, no new features) we collapse the two slots: this PR is the
profile rewrite, and 14c-6 drops out of the schedule. Master plan
table needs the matching update on the iManager docs side.

New Editor\Profile\ProfileModule covers editor/modules/profile/profile.php:
  - GET  /editor/profile/         → 302 to /edit/?profile=<currentUserId>
  - GET  /editor/profile/edit/    render the form (name, email, password,
                                  password_confirm + CSRF token)
  - POST action=save-profile      validate + persist + flash + redirect

Validation:
  - username + email required (same legacy "profile_incomplete" message)
  - new password optional; if set, must be ≥6 chars and equal to confirm
  - email goes through Sanitizer::email; rejects invalid syntax
  - username uniqueness preflight via UserRepository::nameTaken(except)
  - CSRF token (`profile`) required when protectCSRF is on

Password handling preserves the migrated 1.x PasswordFieldValue
wrapper shape (`{__class, password, salt}`): when the existing field
value is a wrapper array we update its `password` key in place;
otherwise we write a plain bcrypt hash so the modern PasswordFieldType
reads it directly. Auth still recognises both shapes (added in 14c-1),
so a password change here keeps the legacy wrapper intact and stays
compatible with any unmigrated tooling.

UserRepository gained:
  - nameTaken(name, exceptId) — uniqueness preflight
  - save(Item)                — wraps ItemRepository::save with category check

EditorRouter routes /editor/profile* to ProfileModule and removes both
the `profile` and `users` placeholder entries.

Manual smoke (PHP built-in server):
  GET  /editor/profile          → 302 → /editor/profile/edit/?profile=9
  GET  /editor/profile/         → 302 → same
  GET  /editor/profile/edit/    → 200, form pre-filled (admin / migrated email)
  POST update email             → 302; DB shows new email
  POST password change          → 302; logout + retry
       old password             → 200 (login form, rejected)
       new password             → 302 → /editor/ (accepted)
@bigin bigin merged commit 53423b9 into imanager-2.0 May 3, 2026
@bigin bigin deleted the phase-14c3-profile branch May 15, 2026 05:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant