7e2bba79fe50846f3d3754f00135308606fb4d19
16
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
7e2bba79fe
|
Collect a birth year instead of a full date of birth
CI / Tests (PHP 8.1) (pull_request) Successful in 43s
CI / No Debug Code (pull_request) Successful in 2s
CI / Tests (PHP 8.2) (pull_request) Successful in 50s
CI / PHPStan (pull_request) Successful in 2m55s
CI / Coding Standards (pull_request) Successful in 3m0s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m40s
CI / Build Plugin Zip (pull_request) Skipped
Signup and the profile page now ask for a four-digit year between 1900 and the current year. Anything else — a short year, a full date, a year in the future — is discarded rather than stored, so a typo cannot leave a nonsense age on the record. The year lives in a new us_birth_year user meta rather than reusing us_date_of_birth, which would have left one key holding two formats. The old key is not migrated in bulk. Instead GuardianService handles it in two halves: birthYear() falls back to the year of the old date when the new key is absent, so a student added before this change still shows one, and setBirthYear() deletes the old date on every save. That deletion is what makes the fallback safe rather than merely tidy. Without it, clearing the birth year on a student who predates the change would leave the old date behind for the fallback to read straight back, and the year could never be cleared at all. Stored in user meta, so no Schema.php change and no USC_VERSION bump. Closes #147 Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
b772e1811e
|
Let parents register once and book for their children
A parent registers once and manages lessons for one or more children, who need no login of their own. A child is a real wp_users row with the student role but no usable login — so student_id keeps meaning "a WordPress user" on every table, and booking, credits, policies and enrolments work unchanged. A us_guardians link table maps guardian to child. The signup form gains a parent/guardian tick that reveals a block per child, with the account-signup questions asked per child rather than per guardian — they describe the student, not the account holder. Signup policies are recorded once per child with the guardian as the acceptor, which is the record that actually means something. A family that half-creates is rolled back entirely rather than leaving a guardian who cannot re-register. The booking and enrolment forms gain a "Who is this for?" picker listing children first, so the default selection is never the parent — booking for the wrong child is correctable, quietly billing a parent for their kid's lesson is not. POST /bookings and POST /enrollments take an optional student_id honoured only for that child's guardian; anything else is a 403. That check is the authorisation boundary of the feature. Payments and credits gain a payer: the charge names the child it was for and the guardian who owes it, so per-child reporting is unchanged while notices, receipts and the payment step reach the parent. Credit is held by the payer, so one child's cancellation can settle a sibling's charge, and the daily billing scan sends a guardian one notice covering every child. Closes #132 Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
7eb2afc6a3
|
Name the destination page in the continue link
"Continue to your account" says nothing about where the link goes. Use the chosen page's own title instead — "Continue to Book a Lesson" — so the visitor knows before clicking. An untitled page keeps the generic wording rather than rendering "Continue to ". |
||
|
|
8a985f04d6
|
Link a signed-in visitor to the configured continue page
The registration page's already-logged-in branch returned a bare sentence with nowhere to go, leaving the visitor to find their own way to their account. The invited-student branch a few lines above already built exactly the link that was missing. Extract that into continueLink() and use it for both logged-in outcomes. There is deliberately still no wp_login_url() fallback: sending someone already signed in to the login screen is the same dead end with extra steps, so with no page configured there is no link. Both messages now carry the us-register-form wrapper and enqueue the plugin stylesheet, which the invite branch emitted markup for but never loaded. Closes #131 |
||
|
|
13d6b3e14e
|
Send students to a chosen page when registration succeeds
CI / Tests (PHP 8.2) (pull_request) Successful in 49s
CI / Tests (PHP 8.1) (pull_request) Successful in 1m0s
CI / Coding Standards (pull_request) Successful in 2m51s
CI / PHPStan (pull_request) Successful in 2m57s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m40s
CI / Build Plugin Zip (pull_request) Skipped
CI / No Debug Code (pull_request) Successful in 2s
The Student Registration block's "After email confirmation" panel becomes "After registration": the page it selects is now where a newly registered student continues to, and a new autoRedirect toggle sends them there instead of showing the link. Only the two finished states qualify (RegistrationPage::isRegistrationComplete): an invited student who is now logged in, and a self-signup back from the emailed confirmation link. A validation error, an expired confirmation link, and the intermediate "check your email" step all stay on the page so their message is read. The invited-student success previously had no link at all; it gains a "Continue to your account" one. That path deliberately has no WordPress-login-screen fallback — pointing someone already signed in at the login screen helps nobody — so continueUrl() distinguishes "no page chosen" from "page chosen", and the redirect does nothing until one is picked. Closes #115 Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
242150569b
|
Fix invite sign-in persistence, add invite-only text option, repair account questions
CI / Tests (PHP 8.1) (pull_request) Successful in 47s
CI / Tests (PHP 8.2) (pull_request) Successful in 46s
CI / No Debug Code (pull_request) Successful in 3s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m37s
CI / PHPStan (pull_request) Successful in 2m52s
CI / Coding Standards (pull_request) Successful in 3m6s
CI / Build Plugin Zip (pull_request) Skipped
Three registration fixes reported from live use:
- Accepting an invite now keeps the student signed in. The form was
processed inside render() during the_content, so wp_set_auth_cookie()
ran after headers were sent and the cookie never persisted — the new
student was bounced back to the logged-out registration page. The
submission is now handled on template_redirect (before output) with a
post/redirect/get, so the cookie sticks and the student lands logged in.
- The "registration is by invitation only" message is now customisable via
a new block attribute (inviteOnlyMessage / shortcode invite_only_message),
falling back to the default wording when blank.
- Account-registration questions save again. dbDelta does not reliably
relax a column from NOT NULL to NULL, so sites created before account-
scope questions kept us_questions.offering_id NOT NULL and rejected
account inserts ("Column 'offering_id' cannot be null"). A one-time,
self-healing migration (guarded by its own option, not the version gate)
re-applies the nullable definition on next load.
composer test, composer lint, composer cs all pass.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
|
||
|
|
a281935811
|
Add invite-only group classes
CI / Tests (PHP 8.2) (pull_request) Successful in 44s
CI / Tests (PHP 8.1) (pull_request) Successful in 49s
CI / No Debug Code (pull_request) Successful in 2s
CI / PHPStan (pull_request) Successful in 2m49s
CI / Coding Standards (pull_request) Successful in 2m55s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m40s
CI / Build Plugin Zip (pull_request) Skipped
Group classes can now be marked invite-only (us_offerings.access_mode). Invite-only classes are hidden from the public catalog and reachable only when the instructor lets someone in via one of three paths, managed from My Lessons -> My Group Classes: - Add students directly: enrols them now with a pending payment. - Make available: grants registered students access to self-enrol through the normal paid flow (multi-select, emailed a notice). - Invite by email: tokenised registration invite tied to the class for a non-account address; after they register the class becomes enrollable. Reuses an existing pending invite instead of sending a second link. New us_group_access table records grants; GET /offerings merges granted invite-only classes for the caller; enrolment requires a grant (403 invite_required) and flips it to enrolled on success. composer test (487), composer lint, composer cs all pass. Co-Authored-By: Claude Opus 4.8 <[email protected]> |
||
|
|
49c59a950c
|
Add studio-defined account-registration questions
CI / Tests (PHP 8.2) (pull_request) Successful in 44s
CI / Tests (PHP 8.1) (pull_request) Successful in 45s
CI / No Debug Code (pull_request) Successful in 2s
CI / Coding Standards (pull_request) Successful in 2m46s
CI / PHPStan (pull_request) Successful in 2m58s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m41s
CI / Build Plugin Zip (pull_request) Skipped
Studio admins can now define registration questions that every new student answers as a required second step during signup, with each student's answers shown under a "Registration Information" section in the admin. Extends the existing Registration domain: us_questions gains a scope column (offering | account) and a nullable offering_id, and account answers reuse us_question_answers with registration_type = 'account'. Authoring reuses the Offerings -> Questions page via an "Account signup" scope (studio-admin only). The registration form becomes two steps (progressive enhancement via assets/js/register.js; works without JS); required answers are validated before the account is created and apply to all signup paths (invite, group link, self-approval). StudentHistory::registrationInfo() powers the admin section. Bumps the plugin version to 1.1.0 so dbDelta runs the schema migration. Closes #90 Co-Authored-By: Claude Opus 4.8 <[email protected]> |
||
|
|
356d9f984d
|
Add multi-use group invite links with expiry and auto-approval on email confirmation
CI / Tests (PHP 8.2) (pull_request) Successful in 44s
CI / Tests (PHP 8.1) (pull_request) Successful in 46s
CI / No Debug Code (pull_request) Successful in 2s
CI / Coding Standards (pull_request) Successful in 2m46s
CI / PHPStan (pull_request) Successful in 2m51s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m38s
CI / Build Plugin Zip (pull_request) Skipped
A studio admin can generate a shareable group invite link (e.g. for a newsletter) from the Invites page, choosing a required expiry date. Anyone with the link may register while it is valid, in any registration mode: the form collects their own email, they must confirm it via the usual hashed token, and confirming approves the account immediately — group signups never enter the Pending Students queue. - us_invites grows kind (personal/group) and expires_at; an explicit expiry wins over the personal 14-day window. Group links stay pending (multi-use) until revoked or expired. - RegistrationPage: group signups create the account pending with the us_auto_approve marker and send the confirmation email; no auto-login. - EmailConfirmationHandler: auto-approve accounts are approved on confirmation, emailed the approved notice, and redirected to a new us_confirmed=ready notice with a sign-in link. Closes #77 Co-Authored-By: Claude Fable 5 <[email protected]> |
||
|
|
681fc5ae07
|
Lock the registration email to the invite only when the invite is redeemable
CI / Tests (PHP 8.1) (pull_request) Successful in 43s
CI / Tests (PHP 8.2) (pull_request) Successful in 37s
CI / PHPStan (pull_request) Successful in 2m45s
CI / Build Plugin Zip (pull_request) Skipped
CI / No Debug Code (pull_request) Successful in 2s
CI / Coding Standards (pull_request) Successful in 2m49s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m42s
The register form keyed the read-only, prefilled email off any invite row matching the token. A stale token (expired / accepted / revoked) with open registration on therefore showed the stale invite's address read-only while the submit handler took the open branch and required a posted email the locked field never submits, dead-ending the form. The lock now applies exactly when the invite is acceptable; otherwise the editable field renders. Closes #78 Co-Authored-By: Claude Fable 5 <[email protected]> |
||
|
|
7b00811133
|
Show a sign-in link instead of the form after email confirmation
CI / Tests (PHP 8.2) (pull_request) Successful in 41s
CI / Tests (PHP 8.1) (pull_request) Successful in 46s
CI / No Debug Code (pull_request) Successful in 2s
CI / PHPStan (pull_request) Successful in 2m45s
CI / Coding Standards (pull_request) Successful in 2m55s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m35s
CI / Build Plugin Zip (pull_request) Skipped
Closes #67 When a student lands on the registration page from the confirmation email (?us_confirmed=1), replace the registration form with the confirmation message and a "Sign in to your account" link — the form is useless at that point and re-submitting would only produce an "account already exists" error. A confirmed-but-unapproved student can already log in (the pending gate only withholds booking), so signing in is the natural next step. The link target follows the booking block's pattern: a loginPageId block attribute (page picker in the editor sidebar) or login_page_id shortcode attribute, falling back to wp_login_url(). The expired-link notice keeps the form as before. Co-Authored-By: Claude Fable 5 <[email protected]> |
||
|
|
7370755951
|
Add open student registration with email confirmation and approval
CI / Tests (PHP 8.1) (pull_request) Successful in 1m18s
CI / Tests (PHP 8.2) (pull_request) Successful in 1m18s
CI / No Debug Code (pull_request) Successful in 2s
CI / PHPStan (pull_request) Successful in 3m20s
CI / Coding Standards (pull_request) Successful in 3m25s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m33s
CI / Build Plugin Zip (pull_request) Skipped
Students could previously join by invite only. Add an optional self-approval mode, toggled from Studio Settings → Registration: anyone may sign up on the existing [us_student_register] page, confirm their email via a tokenised link, and then be approved by a studio admin before the account is usable. - Enabling the toggle mirrors WordPress's own membership settings (users_can_register + default_role = us_student) and snapshots their previous values so disabling restores them. - WordPress's native registration form is blocked while open registration is on (login_init redirect + registration_errors fail-safe + register_url) so it cannot bypass signup policy acceptance. - Pending accounts: unconfirmed email cannot log in; confirmed but unapproved can log in but the booking capability is withheld and the booking page shows an "awaiting approval" screen. - Approve/reject from Students → Pending Students; reject hard-deletes the account so the email is freed to re-apply. - Invite registration is unchanged; both modes coexist. Account lifecycle lives in user meta (RegistrationStatus); no new tables. Closes #63 Co-Authored-By: Claude Opus 4.8 <[email protected]> |
||
|
|
f3f5c7801f
|
Security fixes: CSV injection, policy body output, invite hashing, slot datetimes
CI / No Debug Code (pull_request) Successful in 3s
CI / Tests (PHP 8.1) (pull_request) Successful in 43s
CI / Tests (PHP 8.3) (pull_request) Successful in 49s
CI / Tests (PHP 8.2) (pull_request) Successful in 59s
CI / Coding Standards (pull_request) Successful in 1m11s
CI / PHPStan (pull_request) Successful in 1m20s
CI / Build Plugin Zip (pull_request) Has been skipped
Four fixes from a security review pass: - Neutralise CSV formula injection in the payments export: fields with a leading =, +, -, @, tab, or CR (e.g. a hostile student display name) are apostrophe-prefixed in PaymentReport::csvLine() so they open as text in Excel/Google Sheets. Fixes #39. - Sanitise policy bodies with wp_kses_post at output in PolicyEndpoint::index() (the booking JS renders that HTML raw), so a future write path that forgets kses can never become stored XSS. Fixes #40. - Store invite tokens hashed (SHA-256) at rest: a database leak can no longer redeem pending invites. The registration link is shown once, at creation; the pending list shows email/invited date; lookups hash the submitted token. Existing plaintext pending invites must be re-issued. Fixes #41. - Validate availability slot datetimes on both creation paths (REST and admin form) via AvailabilitySlot::normalizeDateTime(): canonical and datetime-local forms normalise to Y-m-d H:i:s, garbage and end <= start are rejected (REST 400) instead of reaching the DATETIME column or throwing inside the weekly-series date arithmetic. Fixes #42. composer test (204 tests, 594 assertions), PHPStan L6, and PHPCS all green. Co-Authored-By: Claude Fable 5 <[email protected]> |
||
|
|
330900a246
|
Auto-redirect invite tokens to the registration page; rename invite button
CI / Coding Standards (pull_request) Successful in 55s
CI / PHPStan (pull_request) Successful in 59s
CI / Tests (PHP 8.1) (pull_request) Successful in 51s
CI / Tests (PHP 8.2) (pull_request) Successful in 50s
CI / Tests (PHP 8.3) (pull_request) Successful in 47s
CI / No Debug Code (pull_request) Successful in 3s
CI / Build Plugin Zip (pull_request) Has been skipped
- RegistrationPage::maybeRedirectToRegistrationPage() (hooked on template_redirect): any front-end request carrying a us_invite token is redirected to the configured registration page (token preserved), unless already there. Covers links shared before a page was selected; no-op when no page is set. - Invites button text: "Send Invite" -> "Generate Invitation Link". - Doc updated. Co-Authored-By: Claude Opus 4.8 <[email protected]> |
||
|
|
372f981a08
|
Add registration-page selector to the Invites page
CI / Coding Standards (pull_request) Successful in 1m4s
CI / PHPStan (pull_request) Successful in 1m3s
CI / Tests (PHP 8.1) (pull_request) Successful in 50s
CI / Tests (PHP 8.2) (pull_request) Successful in 52s
CI / Tests (PHP 8.3) (pull_request) Successful in 46s
CI / No Debug Code (pull_request) Successful in 3s
CI / Build Plugin Zip (pull_request) Has been skipped
Invitation links previously pointed at the site home page, which usually does not host the [us_student_register] shortcode. Let the studio admin choose the registration page (stored in the us_registration_page_id option); invitation links now point there, falling back to the home page when unset (with a warning notice). - RegistrationController: OPTION_PAGE constant; set_page action; pass the page id/url to the template. - templates/admin/invites.php: wp_dropdown_pages selector + save; build the invite link from the selected page. - Doc updated. Co-Authored-By: Claude Opus 4.8 <[email protected]> |
||
|
|
9c900d6553
|
Add account registration with signup policy acceptance
CI / Tests (PHP 8.1) (pull_request) Successful in 47s
CI / No Debug Code (pull_request) Successful in 2s
CI / Build Plugin Zip (pull_request) Has been skipped
CI / Coding Standards (pull_request) Successful in 52s
CI / PHPStan (pull_request) Successful in 1m1s
CI / Tests (PHP 8.2) (pull_request) Successful in 48s
CI / Tests (PHP 8.3) (pull_request) Successful in 45s
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]> |