Implements #16 — invite-only student self-registration through a front-end page, accepting signup-scoped policies at account creation. Unblocked now that Policies (#6) is merged.
Policy domain extension (the "when to accept" model)
PolicyAcceptance::REG_ACCOUNT — account-time acceptance, registration_id = the new user's ID.
Scope threaded through PolicyService::createPolicy(), the REST create, the admin controller, and the Policies form (an "Accept at" selector).
Auth — invites + registration
Invite value object + InviteRepository; new us_invites table.
RegistrationController + Invites admin page (manage_students): invite an email, copy the registration link, revoke.
RegistrationPage ([us_student_register] shortcode): validates the invite token, collects name/password, renders signup-scoped published policies with required acceptance, creates the us_student user, records account-type acceptances (with IP), marks the invite accepted, and logs the user in.
RoleManager: new manage_students capability in STUDIO_ADMIN_CAPS (administrators inherit it via the existing user_has_cap filter).
Modes
Invite-only is implemented. The us_registration_modeself_approval path (public registration → pending approval) is documented as a future seam.
Notes
Front-end POST is nonce-checked in render(); handleSubmit() uses a scoped phpcs:disable NonceVerification with that rationale (phpcs can't trace cross-method).
The invite link points the token at home_url() with a note to aim it at whichever page hosts the shortcode (no hard-coded page slug).
Tests
tests/Unit/Auth/ — Invite, InviteRepository; plus Policy scope test updates.
composer test (104 tests), composer cs, and PHPStan level 6 all pass.
Implements **#16** — invite-only student self-registration through a front-end page, accepting signup-scoped policies at account creation. Unblocked now that Policies (#6) is merged.
## Policy domain extension (the "when to accept" model)
- `us_policies.acceptance_scope` (`signup` / `booking` / `both`); `Policy::appliesTo()`; `PolicyRepository::findForScope()`.
- `PolicyAcceptance::REG_ACCOUNT` — account-time acceptance, `registration_id` = the new user's ID.
- Scope threaded through `PolicyService::createPolicy()`, the REST create, the admin controller, and the **Policies** form (an "Accept at" selector).
## Auth — invites + registration
- `Invite` value object + `InviteRepository`; new `us_invites` table.
- `RegistrationController` + **Invites** admin page (`manage_students`): invite an email, copy the registration link, revoke.
- `RegistrationPage` (`[us_student_register]` shortcode): validates the invite token, collects name/password, renders signup-scoped published policies with required acceptance, creates the `us_student` user, records `account`-type acceptances (with IP), marks the invite accepted, and logs the user in.
- `RoleManager`: new `manage_students` capability in `STUDIO_ADMIN_CAPS` (administrators inherit it via the existing `user_has_cap` filter).
## Modes
Invite-only is implemented. The `us_registration_mode` `self_approval` path (public registration → pending approval) is documented as a future seam.
## Notes
- Front-end POST is nonce-checked in `render()`; `handleSubmit()` uses a scoped `phpcs:disable NonceVerification` with that rationale (phpcs can't trace cross-method).
- The invite link points the token at `home_url()` with a note to aim it at whichever page hosts the shortcode (no hard-coded page slug).
## Tests
- `tests/Unit/Auth/` — `Invite`, `InviteRepository`; plus Policy scope test updates.
- `composer test` (104 tests), `composer cs`, and PHPStan level 6 all pass.
Refs #16
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Implements #16: invite-only student self-registration through a front-end
page, accepting signup-scoped policies at account creation.
Policy domain:
- us_policies.acceptance_scope (signup/booking/both); Policy::appliesTo();
PolicyRepository::findForScope(); scope threaded through PolicyService,
the REST create, the admin controller, and the Policies form.
- PolicyAcceptance::REG_ACCOUNT (registration_id = the new user's ID).
Auth:
- Invite value object + InviteRepository; us_invites table.
- RegistrationController + Invites admin page (manage_students): invite an
email, share the registration link, revoke.
- RegistrationPage ([us_student_register] shortcode): validates the invite
token, collects name/password, renders signup-scoped published policies
with required acceptance, creates the us_student user, records account-type
acceptances, marks the invite accepted, and logs the user in.
- RoleManager: manage_students cap added to STUDIO_ADMIN_CAPS.
Invite-only is implemented; the us_registration_mode self_approval path is a
documented future seam.
Docs: docs/features/account-registration.md; policies.md updated.
Tests: tests/Unit/Auth/ (Invite, InviteRepository) plus Policy scope
updates. composer test (104), cs, and PHPStan level 6 all pass.
Refs #16
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Implements #16 — invite-only student self-registration through a front-end page, accepting signup-scoped policies at account creation. Unblocked now that Policies (#6) is merged.
Policy domain extension (the "when to accept" model)
us_policies.acceptance_scope(signup/booking/both);Policy::appliesTo();PolicyRepository::findForScope().PolicyAcceptance::REG_ACCOUNT— account-time acceptance,registration_id= the new user's ID.PolicyService::createPolicy(), the REST create, the admin controller, and the Policies form (an "Accept at" selector).Auth — invites + registration
Invitevalue object +InviteRepository; newus_invitestable.RegistrationController+ Invites admin page (manage_students): invite an email, copy the registration link, revoke.RegistrationPage([us_student_register]shortcode): validates the invite token, collects name/password, renders signup-scoped published policies with required acceptance, creates theus_studentuser, recordsaccount-type acceptances (with IP), marks the invite accepted, and logs the user in.RoleManager: newmanage_studentscapability inSTUDIO_ADMIN_CAPS(administrators inherit it via the existinguser_has_capfilter).Modes
Invite-only is implemented. The
us_registration_modeself_approvalpath (public registration → pending approval) is documented as a future seam.Notes
render();handleSubmit()uses a scopedphpcs:disable NonceVerificationwith that rationale (phpcs can't trace cross-method).home_url()with a note to aim it at whichever page hosts the shortcode (no hard-coded page slug).Tests
tests/Unit/Auth/—Invite,InviteRepository; plus Policy scope test updates.composer test(104 tests),composer cs, and PHPStan level 6 all pass.Refs #16
🤖 Generated with Claude Code