main
56 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
aea731c2f8
|
Merge pull request 'Upgrade PHPStan to 2.x and raise analysis level from 6 to 10' (#46) from chore/phpstan-2-upgrade into main
CI / No Debug Code (push) Successful in 3s
CI / Tests (PHP 8.1) (push) Successful in 45s
CI / Tests (PHP 8.2) (push) Successful in 52s
CI / Tests (PHP 8.3) (push) Successful in 56s
CI / PHPStan (push) Successful in 1m2s
CI / Coding Standards (push) Successful in 1m5s
CI / Build Plugin Zip (push) Successful in 56s
Reviewed-on: #46 |
||
|
|
1d6ac46ba3
|
Upgrade PHPStan to 2.x and raise analysis level from 6 to 10
CI / No Debug Code (pull_request) Successful in 3s
CI / Tests (PHP 8.2) (pull_request) Successful in 48s
CI / Tests (PHP 8.3) (pull_request) Successful in 52s
CI / Coding Standards (pull_request) Successful in 57s
CI / Tests (PHP 8.1) (pull_request) Successful in 1m1s
CI / PHPStan (pull_request) Successful in 1m11s
CI / Build Plugin Zip (pull_request) Has been skipped
- Bump phpstan/phpstan ^2.0 and szepeviktor/phpstan-wordpress ^2.0 - Move the analysis level into phpstan.neon (single source) and raise it to 10 - Add Val, a runtime coercion helper that narrows untyped WordPress boundary values (wpdb rows, REST params, superglobals, options) with explicit checks instead of blind casts, plus unit tests - Type value-object fromRow() params as stdClass (what wpdb returns) and map columns through Val so unexpected shapes degrade safely - Use %i identifier placeholders for table names in all wpdb::prepare() calls so every query string is a literal and identifiers are escaped by WordPress; raises the minimum WordPress version to 6.2 where %i was introduced - Guard wpdb::prepare() null result before wpdb::query() in updateTax() - Fix nullable get_permalink()/strtotime() handling, list types at REST and capability call sites, dead null-coalescing on checked superglobals, and narrow get_users() results before mapping - Register Val method names with the ValidatedSanitizedInput sniff so it validates the real sanitizer around each superglobal read - Update repository unit tests for the %i placeholder arguments Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
b23508f726
|
Merge pull request 'Gutenberg dynamic-block wrappers for shortcodes with editor previews' (#45) from feature/editor-blocks into main
CI / No Debug Code (push) Successful in 3s
CI / Tests (PHP 8.2) (push) Successful in 54s
CI / Tests (PHP 8.1) (push) Successful in 54s
CI / Tests (PHP 8.3) (push) Successful in 1m6s
CI / Coding Standards (push) Successful in 1m12s
CI / PHPStan (push) Successful in 1m12s
CI / Build Plugin Zip (push) Successful in 1m27s
Reviewed-on: #45 |
||
|
|
fc70cde9d5
|
Add Gutenberg dynamic-block wrappers for the front-end shortcodes
CI / No Debug Code (pull_request) Successful in 4s
CI / Tests (PHP 8.2) (pull_request) Successful in 52s
CI / Tests (PHP 8.1) (pull_request) Successful in 54s
CI / Tests (PHP 8.3) (pull_request) Successful in 1m29s
CI / Coding Standards (pull_request) Successful in 1m57s
CI / PHPStan (pull_request) Successful in 2m14s
CI / Build Plugin Zip (pull_request) Has been skipped
Wrap the four shortcodes (us_booking, us_student_login, us_student_register, us_group_classes) in dynamic blocks so pages can be previewed and styled in the block editor. Front-end rendering delegates to the same page objects the shortcodes use; in the editor's block-renderer REST preview a static, script-free BlockPreview is rendered instead (no live REST calls, redirects, or Stripe.js). The editor script (vanilla JS, no build step) registers each block with wp.serverSideRender previews and shortcode transforms; frontend.css is attached as the block style so previews pick up theme styling. Resolves #44 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
63e2fbcc5b
|
Merge pull request 'Security fixes: CSV injection, policy body output, invite token hashing, slot datetime validation' (#43) from feature/security-review-fixes into main
CI / No Debug Code (push) Successful in 3s
CI / Tests (PHP 8.2) (push) Successful in 45s
CI / Tests (PHP 8.1) (push) Successful in 50s
CI / Tests (PHP 8.3) (push) Successful in 54s
CI / Coding Standards (push) Successful in 1m1s
CI / PHPStan (push) Successful in 1m9s
CI / Build Plugin Zip (push) Successful in 1m6s
Reviewed-on: #43 |
||
|
|
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 <noreply@anthropic.com> |
||
|
|
693246c1c1
|
Merge pull request 'Security hardening: booking auth, offering exposure, payments, invites (#31–#37)' (#38) from feature/security-fixes into main
CI / No Debug Code (push) Successful in 3s
CI / Tests (PHP 8.1) (push) Successful in 47s
CI / Tests (PHP 8.2) (push) Successful in 51s
CI / Coding Standards (push) Successful in 58s
CI / PHPStan (push) Successful in 1m2s
CI / Tests (PHP 8.3) (push) Successful in 1m41s
CI / Build Plugin Zip (push) Successful in 55s
Reviewed-on: #38 |
||
|
|
061d09e034
|
Harden booking, offering exposure, payments, and invites
CI / No Debug Code (pull_request) Successful in 3s
CI / Tests (PHP 8.1) (pull_request) Successful in 49s
CI / Coding Standards (pull_request) Successful in 55s
CI / PHPStan (pull_request) Successful in 1m7s
CI / Tests (PHP 8.3) (pull_request) Successful in 1m41s
CI / Tests (PHP 8.2) (pull_request) Successful in 44s
CI / Build Plugin Zip (pull_request) Has been skipped
Security fixes from a pen-test review (issues #31–#37): - #31 Booking no longer trusts a client-supplied offering_id: a slot-tied offering is authoritative and any offering used must belong to the slot's instructor, closing a free/misrouted-payment bypass. - #34 Availability slot creation rejects an offering the instructor does not own (AvailabilityEndpoint now takes OfferingRepository). - #32 Offering/question/policy listing endpoints now require book_lesson instead of being public (no anonymous consumer exists); Offering::toArray also omits etransfer_email from listings as defense-in-depth. - #33 Slots are claimed atomically (UPDATE ... WHERE is_booked = 0) before a lesson is inserted, preventing a double-booking race. - #35 A single weekly booking is capped (MAX_WEEKLY_OCCURRENCES) and only creates lessons for slots it actually claimed. - #36 Stripe secret/webhook keys are write-only in the settings UI and a blank submit keeps the stored value; secrets are never echoed back into HTML. - #37 Pending invites expire after 14 days (Invite::isAcceptable), enforced at registration and surfaced on the admin invites list. Adds BookingEndpointTest plus Invite/Offering/AvailabilityRepository coverage and minimal WP_REST_Request/WP_REST_Response stubs. composer test (200), lint, and cs all green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
fe43f91fb1
|
Merge pull request 'Add Instructors admin page (create + per-capability access)' (#30) from feature/instructor-management into main
CI / No Debug Code (push) Successful in 3s
CI / Tests (PHP 8.3) (push) Successful in 45s
CI / Tests (PHP 8.1) (push) Successful in 47s
CI / Tests (PHP 8.2) (push) Successful in 50s
CI / Coding Standards (push) Successful in 54s
CI / PHPStan (push) Successful in 1m5s
CI / Build Plugin Zip (push) Successful in 50s
Reviewed-on: #30 |
||
|
|
b5c076c3d6
|
Add Instructors admin page (create + per-capability access)
CI / No Debug Code (pull_request) Successful in 3s
CI / Tests (PHP 8.3) (pull_request) Successful in 48s
CI / Tests (PHP 8.2) (pull_request) Successful in 49s
CI / Tests (PHP 8.1) (pull_request) Successful in 54s
CI / Coding Standards (pull_request) Successful in 1m5s
CI / PHPStan (pull_request) Successful in 1m11s
CI / Build Plugin Zip (pull_request) Has been skipped
Completes the instructor-management half of #9: the studio admin can now create instructor accounts and toggle each instructor's capabilities. - InstructorController (manage_instructors): list instructors, create a us_instructor WP user (emailing a set-password link), and a per-instructor capability detail view. - InstructorCapabilities: pure, unit-tested rules for which managed caps an admin may assign and how a submitted form maps to assignments. Managed caps are manage_offerings, manage_questions, view_own_payments, export_payments; manage_availability and view_own_lessons are core to every instructor. - A studio admin can never grant a capability it does not itself hold: only held caps (checked via current_user_can, so an administrator's dynamic grant counts) are offered, and on creation any managed cap the admin lacks is denied on the new instructor so they never exceed their creator. The role grants the managed caps by default; the page layers per-user overrides. - AdminMenu: register the Instructors page in the people section. - Tests for the capability logic; docs/features/user-roles.md updated. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
fdadd2fb92
|
Merge pull request 'Make WP admins instructors too, with an Access toggle page' (#29) from feature/admin-access-control into main
CI / No Debug Code (push) Successful in 3s
CI / Tests (PHP 8.3) (push) Successful in 47s
CI / Coding Standards (push) Successful in 54s
CI / Tests (PHP 8.1) (push) Successful in 59s
CI / Tests (PHP 8.2) (push) Successful in 1m0s
CI / PHPStan (push) Successful in 1m14s
CI / Build Plugin Zip (push) Successful in 57s
Reviewed-on: #29 |
||
|
|
67f8144a4a
|
Make WP admins instructors too, and add an Access toggle page
CI / No Debug Code (pull_request) Successful in 3s
CI / Tests (PHP 8.2) (pull_request) Successful in 41s
CI / Tests (PHP 8.3) (pull_request) Successful in 51s
CI / Tests (PHP 8.1) (pull_request) Successful in 54s
CI / Coding Standards (pull_request) Successful in 58s
CI / PHPStan (pull_request) Successful in 1m9s
CI / Build Plugin Zip (pull_request) Has been skipped
A WordPress administrator previously inherited the studio-admin capabilities but not `manage_availability`, so the studio owner running as an admin had no way to reach "My Availability" or act as the instructor — breaking single-instructor businesses. Grant the instructor capabilities to administrators as well (via the existing `user_has_cap` filter), and make both grants — studio-admin and instructor — independently toggleable from a new Access admin page. - RoleManager: extract `INSTRUCTOR_CAPS`; apply studio and instructor cap sets to administrators, each gated on a stored toggle (default on). - AccessSettings + templates/admin/access.php: two options (`us_admin_grant_studio` / `us_admin_grant_instructor`), gated on the core `manage_options` capability so disabling a grant can never lock an administrator out of re-enabling it. - AdminMenu: register the Access page after Studio Settings; keep the studio sidebar separator visible for any administrator. - Tests for the toggles and the new settings reader; docs updated. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
0a78f4b1ac
|
Merge pull request 'Add live Stripe card charges (PaymentIntent + Elements + webhook)' (#28) from feature/payments-stripe into main
CI / No Debug Code (push) Successful in 3s
CI / Tests (PHP 8.1) (push) Successful in 52s
CI / Tests (PHP 8.3) (push) Successful in 52s
CI / Build Plugin Zip (push) Successful in 57s
CI / Coding Standards (push) Successful in 55s
CI / PHPStan (push) Successful in 1m10s
CI / Tests (PHP 8.2) (push) Successful in 1m29s
Reviewed-on: #28 |
||
|
|
925a4b79ba
|
Add live Stripe card charges (PaymentIntent + Elements + webhook)
CI / No Debug Code (pull_request) Successful in 40s
CI / Tests (PHP 8.2) (pull_request) Successful in 48s
CI / Coding Standards (pull_request) Successful in 1m0s
CI / PHPStan (pull_request) Successful in 1m13s
CI / Tests (PHP 8.1) (pull_request) Successful in 2m9s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m8s
CI / Build Plugin Zip (pull_request) Has been skipped
Completes the deferred half of payments: real credit-card processing on top of the existing ledger/e-transfer/comp foundation. - StripeGateway wraps stripe/stripe-php: creates idempotent PaymentIntents (amount in cents, registration ids in metadata) and verifies webhook signatures. Stripe calls sit behind protected seams for unit testing. - PaymentService::createIntent resolves the client-side step for a new registration (card → client secret; e-transfer → display data; comp → none) with caller-ownership enforcement. - PaymentService::handleWebhook finalises a payment exactly once on payment_intent.succeeded (mark paid → confirm → receipt) and marks it failed on payment_intent.payment_failed. - PaymentEndpoint: POST /payments/intent (book_lesson) and public, signature-verified POST /payments/webhook. - PaymentRepository: setStripeIntentId / findByStripeIntentId. - StudioSettings: us_stripe_webhook_secret option, with the webhook URL and required events surfaced on the settings page. - Front end: shared payment.js mounts Stripe Payment Elements and confirms the card (or shows e-transfer instructions); Stripe.js enqueued only when configured. Wired into booking and group-class flows. Tests: new StripeGatewayTest; PaymentService card-intent + webhook cases; repository coverage. composer test/lint/cs all green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
4b34d2464d
|
Merge pull request 'Add HST/tax support and payment reporting with HST aggregation' (#27) from feature/hst-and-reporting into main
CI / Tests (PHP 8.2) (push) Successful in 52s
CI / Coding Standards (push) Successful in 59s
CI / Tests (PHP 8.1) (push) Successful in 56s
CI / Build Plugin Zip (push) Successful in 40s
CI / No Debug Code (push) Successful in 3s
CI / PHPStan (push) Successful in 1m13s
CI / Tests (PHP 8.3) (push) Successful in 52s
Reviewed-on: #27 |
||
|
|
2aa0d5ad5d
|
Merge remote-tracking branch 'origin/main' into feature/hst-and-reporting
CI / Build Plugin Zip (pull_request) Has been skipped
CI / Tests (PHP 8.1) (pull_request) Successful in 52s
CI / Tests (PHP 8.2) (pull_request) Successful in 53s
CI / No Debug Code (pull_request) Successful in 4s
CI / Coding Standards (pull_request) Successful in 1m3s
CI / PHPStan (pull_request) Successful in 1m18s
CI / Tests (PHP 8.3) (pull_request) Successful in 51s
# Conflicts: # src/AdminMenu.php |
||
|
|
553cfafa49
|
Add HST/tax support and payment reporting with HST aggregation
CI / Tests (PHP 8.1) (pull_request) Successful in 51s
CI / Coding Standards (pull_request) Successful in 1m1s
CI / Tests (PHP 8.2) (pull_request) Successful in 58s
CI / No Debug Code (pull_request) Successful in 4s
CI / PHPStan (pull_request) Successful in 1m16s
CI / Tests (PHP 8.3) (pull_request) Successful in 45s
CI / Build Plugin Zip (pull_request) Has been skipped
Studio Settings gains a default HST rate; the rate is frozen onto each payment at booking and computed against the pre-tax subtotal, with the total billed as subtotal + tax. The rate is overridable per booking on My Lessons while unpaid (recomputing the tax amount), comped registrations are never taxed, and receipts break out subtotal/HST/total. Builds the payments report (roadmap #8) from us_payments: a monthly per-instructor view with subtotal, HST collected, and grand-total aggregation, plus a nonce-protected CSV export via admin-post. Studio admins see all instructors and can filter; instructors are scoped to their own rows. The Payment Report menu is gated on export_payments so instructors (who lack manage_billing) can reach it. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
d229b83b23
|
Merge pull request 'Group the studio admin sidebar into three sections' (#26) from feature/admin-menu-separator into main
CI / No Debug Code (push) Successful in 3s
CI / Tests (PHP 8.2) (push) Successful in 44s
CI / Coding Standards (push) Successful in 52s
CI / Tests (PHP 8.3) (push) Successful in 49s
CI / Tests (PHP 8.1) (push) Successful in 55s
CI / PHPStan (push) Successful in 1m10s
CI / Build Plugin Zip (push) Successful in 39s
Reviewed-on: #26 |
||
|
|
ca458bb8ae
|
Group the studio admin menu into three sections with separators
CI / No Debug Code (pull_request) Successful in 3s
CI / Tests (PHP 8.1) (pull_request) Successful in 46s
CI / Coding Standards (pull_request) Successful in 54s
CI / Tests (PHP 8.3) (pull_request) Successful in 51s
CI / Tests (PHP 8.2) (pull_request) Successful in 59s
CI / PHPStan (pull_request) Successful in 1m14s
CI / Build Plugin Zip (pull_request) Has been skipped
Add sidebar separators (wp-menu-separator) that set the studio menus apart from the core WordPress items and split them into three sections: [sep] Studio Settings · Policies · Invites · Offerings [sep] Students · Group Classes · Payments [sep] Scheduler (then the instructor menus: My Availability, My Lessons) Separators live at positions 29 / 34 / 38; each is only added when the user can see a menu in the following section, to avoid orphaned dividers. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
b73d81421f
|
Merge pull request 'Add payments foundation (e-transfer/comp, Stripe config, receipts, e-transfer email)' (#25) from feature/payments into main
CI / No Debug Code (push) Successful in 3s
CI / Tests (PHP 8.1) (push) Successful in 45s
CI / Coding Standards (push) Successful in 1m2s
CI / Tests (PHP 8.2) (push) Successful in 1m2s
CI / PHPStan (push) Successful in 1m8s
CI / Tests (PHP 8.3) (push) Successful in 1m18s
CI / Build Plugin Zip (push) Successful in 51s
Reviewed-on: #25 |
||
|
|
9873cb5e30
|
Add e-transfer destination email (studio default + offering/booking overrides)
CI / No Debug Code (pull_request) Successful in 3s
CI / Coding Standards (pull_request) Successful in 46s
CI / Tests (PHP 8.1) (pull_request) Successful in 52s
CI / Tests (PHP 8.3) (pull_request) Successful in 52s
CI / Tests (PHP 8.2) (pull_request) Successful in 57s
CI / PHPStan (pull_request) Successful in 1m12s
CI / Build Plugin Zip (pull_request) Has been skipped
The e-transfer destination is resolved at booking time (offering override -> studio default) and frozen onto the payment, so each record keeps where the student was directed. It can then be corrected per booking. - StudioSettings: us_etransfer_email option + a Default e-transfer email field on the Studio Settings page. - Offering: etransfer_email column/field (instructor override) across VO, repo, REST endpoint, admin controller, and form. - Payment: etransfer_email column on the payment (frozen record) + PaymentRepository::updateEtransferEmail; PaymentService freezes it from the offering override or studio default at creation; booking/enrolment pass the offering override. - My Lessons: instructors edit the e-transfer email per pending lesson payment (ownership-checked). - Payments queue: studio admin can correct the email at confirmation (for when a student sends it to the wrong place). - Docs updated. Tests: Payment/Offering rows + PaymentService freezing. composer test (148), cs, and PHPStan level 6 all pass. Refs #7 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
6c4097b385
|
Add payments foundation (e-transfer/comp, Stripe config, receipts)
CI / Tests (PHP 8.1) (pull_request) Successful in 45s
CI / Tests (PHP 8.3) (pull_request) Successful in 50s
CI / No Debug Code (pull_request) Successful in 3s
CI / Coding Standards (pull_request) Successful in 1m2s
CI / Tests (PHP 8.2) (pull_request) Successful in 1m0s
CI / PHPStan (pull_request) Successful in 1m4s
CI / Build Plugin Zip (pull_request) Has been skipped
Implements the payments foundation for #7. Without Stripe credentials everything works on e-transfer (pending payment confirmed by a studio admin); when Stripe keys are configured the default flips to credit card. Per-student override (card/etransfer/comp) is set on the student detail. - Schema: us_payments (amount DECIMAL dollars, method, status, receipt, stripe intent id). - src/Payment/: Payment VO, PaymentRepository, StudioSettings (Stripe options + isStripeConfigured + settings page), BillingMethodResolver (per-student override; default card if configured else etransfer), ReceiptMailer, PaymentService (create at registration, link payment_id, comp->paid+confirm, markPaid->confirm+receipt), PaymentController (e-transfer confirmation queue), PaymentEndpoint (PATCH /payments/{id}). - Booking + enrolment create the payment from the offering price; comp auto-confirms the lesson; setPaymentId on both repositories. - Admin: Studio Settings + Payments menus (manage_billing); per-student billing method on the student detail page. - Docs: payments.md + README updated. Deferred to a follow-up: the live Stripe card charge (PaymentIntent + Stripe.js Elements + webhook + stripe/stripe-php). Until then a card payment is created pending and confirmed like an e-transfer. Tests: tests/Unit/Payment/ (VO, repository, resolver, service, mailer). composer test (147), cs, and PHPStan level 6 all pass. Refs #7 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
071ef7fc2a
|
Merge pull request 'Add student administration view (studio-admin)' (#24) from feature/student-administration into main
CI / No Debug Code (push) Successful in 34s
CI / Coding Standards (push) Successful in 55s
CI / PHPStan (push) Successful in 2m9s
CI / Tests (PHP 8.3) (push) Successful in 1m4s
CI / Tests (PHP 8.1) (push) Successful in 3m1s
CI / Tests (PHP 8.2) (push) Successful in 3m6s
CI / Build Plugin Zip (push) Successful in 41s
Reviewed-on: #24 |
||
|
|
8fb5ff8270
|
Add student administration view (studio-admin)
CI / Tests (PHP 8.1) (pull_request) Successful in 43s
CI / Coding Standards (pull_request) Successful in 56s
CI / PHPStan (pull_request) Successful in 57s
CI / No Debug Code (pull_request) Successful in 2s
CI / Tests (PHP 8.2) (pull_request) Successful in 44s
CI / Tests (PHP 8.3) (pull_request) Successful in 48s
CI / Build Plugin Zip (pull_request) Has been skipped
Implements #22: a read-only Students area for studio admins. - StudentController (manage_students): a list of us_student users with upcoming-lesson and active-enrolment counts, each linking to a detail page showing account info, upcoming/past lessons (offering, instructor, status), and group-class enrolments. - StudentSchedule::partition() — pure, unit-tested upcoming/past split. - Repo counts: BookingRepository::countUpcomingForStudent and EnrollmentRepository::countActiveForStudent (single-query, tested). - Templates: templates/admin/students.php, student-detail.php. - Students admin menu wired in AdminMenu (no Plugin change — the repos were already available there). - Docs: README status flipped to implemented; feature spec updated. Payment history slots into the detail when Payments (#7) lands. Tests: StudentScheduleTest + the two repo count tests. composer test (127), cs, and PHPStan level 6 all pass. Refs #22 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
d86e852edc
|
Merge pull request 'Add README and student-administration feature spec' (#23) from docs/readme-and-student-admin into main
CI / Coding Standards (push) Successful in 51s
CI / Tests (PHP 8.1) (push) Successful in 47s
CI / PHPStan (push) Successful in 54s
CI / No Debug Code (push) Successful in 2s
CI / Tests (PHP 8.2) (push) Successful in 43s
CI / Tests (PHP 8.3) (push) Successful in 46s
CI / Build Plugin Zip (push) Successful in 52s
Reviewed-on: #23 |
||
|
|
8a3e9b65df
|
Add README and student-administration feature spec
CI / Tests (PHP 8.1) (pull_request) Successful in 43s
CI / Coding Standards (pull_request) Successful in 55s
CI / PHPStan (pull_request) Successful in 1m0s
CI / No Debug Code (pull_request) Successful in 2s
CI / Tests (PHP 8.2) (pull_request) Successful in 43s
CI / Tests (PHP 8.3) (pull_request) Successful in 45s
CI / Build Plugin Zip (pull_request) Has been skipped
- README.md: plugin overview, package-by-domain architecture, feature/status table linking each docs/features spec (implemented vs planned), shortcodes, REST namespace, roles, install via composer build, dev commands, and CI. - docs/features/student-administration.md: spec for the planned studio-admin Students list + per-student detail (upcoming/past lessons, group enrolments); read-only; manage_students; no new tables. Tracked as #22. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
9799f7c1e2
|
Merge pull request 'Add group-class enrolment (year commitment, capacity, registration gate)' (#21) from feature/group-classes into main
CI / Tests (PHP 8.1) (push) Successful in 42s
CI / Coding Standards (push) Successful in 51s
CI / PHPStan (push) Successful in 56s
CI / No Debug Code (push) Successful in 2s
CI / Tests (PHP 8.2) (push) Successful in 41s
CI / Tests (PHP 8.3) (push) Successful in 45s
CI / Build Plugin Zip (push) Successful in 40s
Reviewed-on: #21 |
||
|
|
9cb5207dcd
|
Add group-class enrolment (year commitment, capacity, registration gate)
CI / Tests (PHP 8.1) (pull_request) Successful in 45s
CI / Coding Standards (pull_request) Successful in 50s
CI / PHPStan (pull_request) Successful in 1m4s
CI / No Debug Code (pull_request) Successful in 2s
CI / Tests (PHP 8.2) (pull_request) Successful in 42s
CI / Tests (PHP 8.3) (pull_request) Successful in 42s
CI / Build Plugin Zip (pull_request) Has been skipped
Implements #4: students enrol in a group_class offering via the same registration gate as private lessons (intake questions + booking-scoped policy acceptance). Enrolment is capacity-enforced and prevents duplicates. - Schema: us_group_enrollments table. - Enrollment value object + EnrollmentRepository (countActiveForOffering, hasActiveEnrollment, per-student/instructor/all-active queries, status). - EnrollmentEndpoint: GET /enrollments (scoped) and POST /enrollments (validates group_class, capacity, no-duplicate; reuses RegistrationGate; records answers/acceptances type enrollment). - GroupClassController + admin page (view_all_lessons): all active enrolments. - Front-end: [us_group_classes] shortcode (GroupClassPage) + group-classes.js enrol flow (list classes -> questions + policies -> POST /enrollments). - Wiring in Plugin, RestRegistrar, AdminMenu, ShortcodeRegistrar. Payment is the deferred seam (#7): enrolment lands active, payment_id null. JS left untested for parity with the repo's no-build vanilla-JS posture. Tests: tests/Unit/GroupClass/ (Enrollment, EnrollmentRepository). composer test (121), cs, and PHPStan level 6 all pass. Refs #4 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
0b3832309d
|
Merge pull request 'Add lesson booking registration flow (offering, questions, policies)' (#20) from feature/registration-flow into main
CI / Coding Standards (push) Successful in 45s
CI / Tests (PHP 8.1) (push) Successful in 47s
CI / PHPStan (push) Successful in 58s
CI / No Debug Code (push) Successful in 2s
CI / Tests (PHP 8.2) (push) Successful in 43s
CI / Tests (PHP 8.3) (push) Successful in 49s
CI / Build Plugin Zip (push) Successful in 57s
Reviewed-on: #20 |
||
|
|
6d163e5d0e
|
Add lesson booking registration flow (offering, questions, policies)
CI / Coding Standards (pull_request) Successful in 1m51s
CI / PHPStan (pull_request) Successful in 2m17s
CI / Tests (PHP 8.1) (pull_request) Successful in 2m24s
CI / No Debug Code (pull_request) Successful in 2s
CI / Tests (PHP 8.2) (pull_request) Successful in 42s
CI / Tests (PHP 8.3) (pull_request) Successful in 47s
CI / Build Plugin Zip (pull_request) Has been skipped
Implements #3: students register for a private lesson by picking a slot, answering the offering's intake questions, and accepting booking-scoped policies. Payment is a clean seam for #7 (lessons land pending; payment_id null; instructor confirms via PATCH /bookings/{id}/status). - Schema: us_lessons += offering_id, recurrence, series_id, payment_id. - Lesson: new fields + recurrence constants. - BookingRepository::insertSeries() builds a weekly series sharing a series_id; AvailabilityRepository::findUnbookedInGroup() reserves a group. - RegistrationGate (src/Registration/): validate + record intake answers and booking-scoped policy acceptances. Reused by group enrolment (#4). - BookingEndpoint::book(): offering_id, recurrence, answers, accepted_policy_version_ids; single or weekly; records answers/acceptances (type lesson). - GET /policies?scope=booking filter. - Front-end booking.js: slot -> questions + policies -> submit. - Wiring: RegistrationGate built in Plugin, passed via RestRegistrar. - Test-only WP_Error stub in tests/bootstrap.php for gate testing. Refs #3 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
d0dddd9075
|
Merge pull request 'Invite links: page selector + auto-redirect for stray tokens' (#19) from fix/invite-registration-page-selector into main
CI / Coding Standards (push) Successful in 57s
CI / No Debug Code (push) Successful in 3s
CI / Build Plugin Zip (push) Successful in 48s
CI / PHPStan (push) Successful in 1m7s
CI / Tests (PHP 8.1) (push) Successful in 55s
CI / Tests (PHP 8.2) (push) Successful in 52s
CI / Tests (PHP 8.3) (push) Successful in 54s
Reviewed-on: #19 |
||
|
|
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 <noreply@anthropic.com> |
||
|
|
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 <noreply@anthropic.com> |
||
|
|
9b8fa5d698
|
Merge pull request 'Add account registration with signup policy acceptance' (#18) from feature/account-registration into main
CI / Coding Standards (push) Successful in 47s
CI / PHPStan (push) Successful in 1m3s
CI / Tests (PHP 8.1) (push) Successful in 48s
CI / Tests (PHP 8.2) (push) Successful in 47s
CI / Tests (PHP 8.3) (push) Successful in 46s
CI / No Debug Code (push) Successful in 3s
CI / Build Plugin Zip (push) Successful in 41s
Reviewed-on: #18 |
||
|
|
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 <noreply@anthropic.com> |
||
|
|
5eb096c5cf
|
Merge pull request 'Extend availability (durations, weekly recurrence, calendar); price offerings in dollars' (#17) from feature/availability-extensions into main
CI / Build Plugin Zip (push) Successful in 40s
CI / Coding Standards (push) Successful in 44s
CI / PHPStan (push) Successful in 1m2s
CI / Tests (PHP 8.1) (push) Successful in 49s
CI / Tests (PHP 8.2) (push) Successful in 47s
CI / Tests (PHP 8.3) (push) Successful in 47s
CI / No Debug Code (push) Successful in 3s
Reviewed-on: #17 |
||
|
|
19e663d6fa
|
Extend availability (durations, weekly recurrence, calendar); price offerings in dollars
CI / Coding Standards (pull_request) Successful in 50s
CI / PHPStan (pull_request) Successful in 1m2s
CI / Tests (PHP 8.1) (pull_request) Successful in 47s
CI / Tests (PHP 8.2) (pull_request) Successful in 48s
CI / Tests (PHP 8.3) (pull_request) Successful in 46s
CI / No Debug Code (pull_request) Successful in 2s
CI / Build Plugin Zip (pull_request) Has been skipped
Availability (#2): - us_availability gains offering_id, duration_minutes (default 60), and recurrence_group; AvailabilitySlot carries the new fields. - AvailabilityRepository::createWeeklySeries() generates N weekly rows sharing a recurrence_group; findAvailable() filters by offering and duration. Date math uses DateTimeImmutable::modify() (the no-debug CI regex `dd\(` matches `->add(`). - REST GET filters by offering_id/duration_minutes; POST accepts duration_minutes, offering_id, recurrence (single|weekly) + weeks. - Admin form adds duration, an offering picker, and one-off/weekly options (OfferingRepository wired into AvailabilityController). - booking.js renders an agenda calendar (slots grouped by day, with duration). The richer booking UX lands with the booking-flow work. Offering price in dollars: - Switch us_offerings.price_cents (INT) to price DECIMAL(10,2); Offering uses float $price. Admin form and REST take dollars. - Fix a pre-existing misalignment in the Offering insert/update $wpdb format arrays (billing_mode/capacity/is_active were mapped to the wrong specifiers, which would corrupt values) via a single COLUMN_FORMATS list. Also bump PHPStan to --memory-limit=1G in the lint script; 128M now crashes analysis as the codebase has grown. Refs #2 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
5352fb7d69
|
Merge pull request 'Add Policies domain (drafting, versioning, tracked acceptance)' (#15) from feature/policies into main
CI / Coding Standards (push) Successful in 58s
CI / PHPStan (push) Successful in 1m7s
CI / Tests (PHP 8.2) (push) Successful in 58s
CI / Tests (PHP 8.1) (push) Successful in 52s
CI / Tests (PHP 8.3) (push) Successful in 46s
CI / No Debug Code (push) Successful in 2s
CI / Build Plugin Zip (push) Successful in 40s
Reviewed-on: #15 |
||
|
|
6225e772f8
|
Add Policies domain (drafting, versioning, tracked acceptance)
CI / Coding Standards (pull_request) Successful in 1m0s
CI / PHPStan (pull_request) Successful in 1m4s
CI / Tests (PHP 8.1) (pull_request) Successful in 59s
CI / Tests (PHP 8.2) (pull_request) Successful in 56s
CI / Tests (PHP 8.3) (pull_request) Successful in 57s
CI / No Debug Code (pull_request) Successful in 3s
CI / Build Plugin Zip (pull_request) Has been skipped
Implements #6: studio admins draft, version, and publish policies; the public registration gate reads the current published version of each, and acceptance is recorded against the exact version so a new version must be re-accepted at the next booking. - src/Policy/: Policy, PolicyVersion, PolicyAcceptance value objects; PolicyRepository, PolicyVersionRepository, AcceptanceRepository; PolicyService (orchestrates create/add-draft/publish across the policies and versions tables); PolicyEndpoint (REST); PolicyController + templates/admin/policies.php (Policies admin menu, manage_policies) - us_policies, us_policy_versions, us_policy_acceptances tables in Schema - REST: public GET /policies (current published versions); manage_policies for create, add version, edit draft, and publish - Wiring in Plugin, RestRegistrar, AdminMenu AcceptanceRepository is built now and consumed by the booking/enrolment gate in #3/#4. Also bump PHPStan to --memory-limit=1G in the composer lint script; the default 128M now crashes the analysis as the codebase has grown. Tests: tests/Unit/Policy/ (value objects, repositories, service). composer test (90 total), cs, and PHPStan level 6 all pass. Refs #6 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
74fb27ea05
|
Merge pull request 'Fix CI artifact double-zip so the plugin installs directly' (#14) from fix/ci-artifact-double-zip into main
CI / Coding Standards (push) Successful in 51s
CI / PHPStan (push) Successful in 1m6s
CI / Tests (PHP 8.1) (push) Successful in 51s
CI / Tests (PHP 8.2) (push) Successful in 48s
CI / Tests (PHP 8.3) (push) Successful in 46s
CI / No Debug Code (push) Successful in 3s
CI / Build Plugin Zip (push) Successful in 42s
Reviewed-on: #14 |
||
|
|
2011319750
|
Fix CI artifact so the downloaded plugin installs directly
CI / Coding Standards (pull_request) Successful in 52s
CI / PHPStan (pull_request) Successful in 58s
CI / Tests (PHP 8.1) (pull_request) Successful in 51s
CI / Tests (PHP 8.2) (pull_request) Successful in 49s
CI / Tests (PHP 8.3) (pull_request) Successful in 49s
CI / No Debug Code (pull_request) Successful in 2s
CI / Build Plugin Zip (pull_request) Has been skipped
Gitea/Actions re-zips artifacts on download, so uploading the built plugin zip produced a double-wrapped archive (a zip containing a zip). WordPress then reported "No valid plugins were found" because the upload had no plugin folder/header at its top level. Unpack the built zip and upload the resulting plugin folder instead, so the downloaded artifact's top level is unsupervised-schedular/ and installs directly via Plugins -> Add New -> Upload. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
0988655aba
|
Allow additional composer and tea commands
CI / PHPStan (push) Successful in 57s
CI / Tests (PHP 8.1) (push) Successful in 45s
CI / Tests (PHP 8.3) (push) Successful in 48s
CI / No Debug Code (push) Successful in 3s
CI / Coding Standards (push) Successful in 51s
CI / Tests (PHP 8.2) (push) Successful in 50s
CI / Build Plugin Zip (push) Successful in 42s
|
||
|
|
3afa8d80f2
|
Merge pull request 'Grant studio-admin caps to administrators; re-gate Scheduler; mark 1.0.0-rc.1' (#13) from feature/admin-studio-caps into main
CI / Coding Standards (push) Successful in 48s
CI / PHPStan (push) Successful in 1m4s
CI / Tests (PHP 8.1) (push) Successful in 45s
CI / Tests (PHP 8.2) (push) Successful in 49s
CI / Tests (PHP 8.3) (push) Successful in 48s
CI / No Debug Code (push) Successful in 3s
CI / Build Plugin Zip (push) Successful in 41s
Reviewed-on: #13 |
||
|
|
cbf05fe363
|
Mark version as 1.0.0-rc.1
CI / Coding Standards (pull_request) Successful in 55s
CI / PHPStan (pull_request) Successful in 1m4s
CI / Tests (PHP 8.1) (pull_request) Successful in 50s
CI / Tests (PHP 8.2) (pull_request) Successful in 45s
CI / Tests (PHP 8.3) (pull_request) Successful in 48s
CI / No Debug Code (pull_request) Successful in 2s
CI / Build Plugin Zip (pull_request) Has been skipped
Reflect the in-progress, pre-release state. Bump to 1.0.0 before tagging a release. Updates both the plugin header and USC_VERSION; the build/CI zip artifact name tracks this automatically. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
b4acae34a3
|
Grant studio-admin capabilities to WordPress administrators
WordPress administrators (manage_options) now implicitly hold every studio-admin capability via a user_has_cap filter, so the site owner runs the studio without being assigned the separate us_studio_admin role. The grant persists nothing and is removed on deactivation. The us_studio_admin role still exists for non-administrator staff and does NOT confer any core WordPress admin powers. Also re-gate the studio-wide "Scheduler" dashboard off manage_options onto a new view_all_lessons capability (added to the studio-admin cap set), so a us_studio_admin user can see it too — previously it was administrator-only. - RoleManager: STUDIO_ADMIN_CAPS constant, CAP_VIEW_ALL_LESSONS, grantStudioCapsToAdministrators() user_has_cap filter - AdminMenu + LessonController: Scheduler gated on view_all_lessons - Docs: user-roles.md cap matrix + administrator note; lesson-booking.md - Tests: administrators receive studio caps; non-admins do not Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
0b0dfe8f1c
|
Merge pull request 'Add plugin zip build task and CI release artifact' (#12) from feature/build-and-release into main
CI / Coding Standards (push) Successful in 52s
CI / PHPStan (push) Successful in 1m3s
CI / Tests (PHP 8.1) (push) Successful in 50s
CI / Tests (PHP 8.2) (push) Successful in 49s
CI / Tests (PHP 8.3) (push) Successful in 51s
CI / No Debug Code (push) Successful in 3s
CI / Build Plugin Zip (push) Successful in 58s
Reviewed-on: #12 |
||
|
|
e9d6c189bc
|
Add plugin zip build task and CI release artifact
CI / Coding Standards (pull_request) Successful in 52s
CI / PHPStan (pull_request) Successful in 1m1s
CI / Tests (PHP 8.1) (pull_request) Successful in 52s
CI / Tests (PHP 8.2) (pull_request) Successful in 48s
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
- bin/build-zip.sh + `composer build`: stage runtime files only, generate a production (no-dev) optimized autoloader, and emit dist/<slug>-<version>.zip with a single top-level plugin folder, ready to upload via wp-admin. Tests, tooling configs, docs, and dev dependencies are excluded; version is read from the plugin header. - CI `build` job: on push to main (post-merge), after lint/static-analysis/ test/no-debug pass, runs the build and uploads the zip via actions/upload-artifact. - Ignore build/ and dist/. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
6397f08398
|
Merge pull request 'Add Registration Questions domain (per-offering intake forms)' (#11) from feature/registration-questions into main
CI / Coding Standards (push) Successful in 50s
CI / PHPStan (push) Successful in 56s
CI / Tests (PHP 8.1) (push) Successful in 49s
CI / Tests (PHP 8.2) (push) Successful in 47s
CI / Tests (PHP 8.3) (push) Successful in 48s
CI / No Debug Code (push) Successful in 2s
Reviewed-on: #11 |
||
|
|
e61d99daed
|
Add Registration Questions domain (per-offering intake forms)
CI / Coding Standards (pull_request) Successful in 51s
CI / PHPStan (pull_request) Successful in 1m0s
CI / Tests (PHP 8.1) (pull_request) Successful in 46s
CI / Tests (PHP 8.2) (pull_request) Successful in 48s
CI / Tests (PHP 8.3) (pull_request) Successful in 47s
CI / No Debug Code (pull_request) Successful in 3s
Implements #5: studio admin / instructors author intake questions scoped per offering; answers are stored against a lesson or group enrolment via a polymorphic registration reference. - src/Registration/: Question + Answer value objects, QuestionRepository and AnswerRepository, QuestionEndpoint (REST), QuestionController + templates/admin/questions.php (Offerings -> Questions submenu) - us_questions and us_question_answers tables in Schema.php - REST: public GET /offerings/{id}/questions; POST/PATCH/DELETE /questions gated by manage_questions + offering ownership (owner or studio admin) - Field types text/textarea/select/checkbox; select options stored as JSON - Wiring in Plugin, RestRegistrar, AdminMenu AnswerRepository is built now and consumed by the booking/enrolment flow in #3/#4. Tests: tests/Unit/Registration/ (19 tests). composer test (63 total), cs, and PHPStan level 6 all pass. Refs #5 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
5b6cc4e89b
|
Merge pull request 'Add Offerings domain and studio-admin capabilities' (#10) from feature/offerings into main
CI / Coding Standards (push) Successful in 53s
CI / PHPStan (push) Successful in 59s
CI / Tests (PHP 8.1) (push) Successful in 49s
CI / Tests (PHP 8.2) (push) Successful in 48s
CI / Tests (PHP 8.3) (push) Successful in 46s
CI / No Debug Code (push) Successful in 2s
Reviewed-on: #10 |