CI / Tests (PHP 8.1) (pull_request) Failing after 47s
CI / Coding Standards (pull_request) Successful in 3m10s
CI / No Debug Code (pull_request) Successful in 2s
CI / Tests (PHP 8.2) (pull_request) Failing after 52s
CI / PHPStan (pull_request) Successful in 2m59s
CI / Tests (PHP 8.3) (pull_request) Failing after 2m40s
CI / Build Plugin Zip (pull_request) Skipped
The block reports who is signed in and nothing more. Dropping the "Booking for …" line takes GuardianService with it — it was the only reason the page had a dependency at all, so AccountPage now constructs with no arguments. Co-Authored-By: Claude Opus 5 <[email protected]>
148 lines
10 KiB
Markdown
148 lines
10 KiB
Markdown
# Editor Blocks
|
|
|
|
Gutenberg dynamic-block wrappers for the plugin's front-end shortcodes, so the
|
|
pages can be previewed and styled inside the block editor instead of appearing
|
|
as grey shortcode text.
|
|
|
|
## Blocks
|
|
|
|
| Block | Wraps shortcode | Front-end renderer |
|
|
|---|---|---|
|
|
| `us-scheduler/booking` | `[us_booking]` | `Booking\BookingPage::render()` |
|
|
| `us-scheduler/student-login` | `[us_student_login]` | `Auth\LoginPage::render()` |
|
|
| `us-scheduler/student-register` | `[us_student_register]` | `Auth\RegistrationPage::render()` |
|
|
| `us-scheduler/group-classes` | `[us_group_classes]` | `GroupClass\GroupClassPage::render()` |
|
|
| `us-scheduler/family` | `[us_family]` | `Guardian\FamilyPage::render()` |
|
|
| `us-scheduler/account` | `[us_account]` | `Auth\AccountPage::render()` |
|
|
|
|
The shortcodes remain registered for back-compat; blocks and shortcodes share
|
|
the same page objects (constructed once in `Plugin::boot()`), so front-end
|
|
output is identical either way. Pasting a shortcode into the block editor
|
|
auto-converts it to the matching block via a `transforms.from` shortcode
|
|
transform.
|
|
|
|
## Block options
|
|
|
|
Most blocks have sidebar (inspector) options:
|
|
|
|
| Block | Attribute | Default | Effect |
|
|
|---|---|---|---|
|
|
| `us-scheduler/booking` | `loginPageId` (number) | `0` | Page the "log in to book a lesson" link points to for logged-out visitors. `0` = the WordPress login screen (with a redirect back to the current page). |
|
|
| `us-scheduler/booking` | `autoRedirect` (boolean) | `false` | Send logged-out visitors straight to the login page instead of showing the link. |
|
|
| `us-scheduler/booking` | `lessonTypeId` (number) | `0` | Pin the calendar to a single private-lesson type: only the times bookable as that type are listed, and it is the only type students can book here (auto-selected on the registration form). `0` = every type. Shortcode equivalent: `[us_booking lesson_type="…"]`. |
|
|
| `us-scheduler/booking` | `showTypeFilter` (boolean) | `true` | Whether students get the **Show Only** button that narrows the calendar to chosen lesson types. Unused when a single type is pinned (there is nothing to choose). Shortcode equivalent: `[us_booking show_filter="no"]`. |
|
|
| `us-scheduler/booking` | `displayMode` (string) | `both` | Which halves of the page to embed: `both`, `booking` (calendar only, no upcoming-lessons panel) or `upcoming` (the student's lessons only, nothing bookable) — so the two halves can live on different pages. Anything unrecognised falls back to `both`. Shortcode equivalent: `[us_booking show="booking"]`. |
|
|
| `us-scheduler/student-login` | `bookingPageId` (number) | `0` | Page the "View available lessons" link points to for logged-in visitors, and the post-login redirect target. `0` = the current page. |
|
|
| `us-scheduler/student-login` | `autoRedirect` (boolean) | `false` | Send logged-in visitors straight to the booking page instead of showing the link. Does nothing until a booking page is chosen. |
|
|
| `us-scheduler/student-register` | `loginPageId` (number) | `0` | Page students continue to once registration finishes — the "Sign in to your account" link after they confirm their email, and the "Continue to your account" link an invited student gets on the spot. `0` = the WordPress login screen for the confirmation link, and no link at all for the (already signed-in) invited student. Shortcode equivalent: `[us_student_register login_page_id="…"]`. |
|
|
| `us-scheduler/student-register` | `autoRedirect` (boolean) | `false` | Send students straight to that page instead of showing the link. Does nothing until a page is chosen — there is no login-screen fallback here. |
|
|
| `us-scheduler/family` | `loginPageId` (number) | `0` | Where visitors who are not signed in are sent to log in. Shortcode equivalent: `[us_family login_page_id="…"]`. |
|
|
| `us-scheduler/account` | `loginPageId` (number) | `0` | Where signing out returns to, and where a signed-out visitor is offered a **Sign in** link. `0` = signing out returns to the current page, and a signed-out visitor sees **nothing at all** — see below. Shortcode equivalent: `[us_account login_page_id="…"]`. |
|
|
| `us-scheduler/group-classes` | `offeringId` (number) | `0` | Restrict the page to a single group class, for embedding on a page dedicated to that class. The class description is then omitted — only the schedule, instructor, price and enrolment controls are shown, so the surrounding page's own copy is not repeated. `0` = browse all classes, descriptions included. Shortcode equivalent: `[us_group_classes offering="…"]`. |
|
|
|
|
The page selects list all published pages; if a chosen page is later deleted,
|
|
the blocks fall back to their defaults. The group-classes block's class
|
|
select is a dropdown of active group classes fetched from
|
|
`GET /us-scheduler/v1/offerings?kind=group_class`; a stored class that is no
|
|
longer offered shows as "Unavailable class #N" rather than silently falling
|
|
back to all classes. The booking block's lesson-type select works the same way
|
|
against `?kind=private_lesson` ("Unavailable lesson type #N"), and the live
|
|
page says so plainly when the pinned type has been withdrawn.
|
|
|
|
The booking block's options reach the front end as data attributes on
|
|
`#us-booking-app` (`data-lesson-type`, `data-type-filter`) or as omitted
|
|
containers (`displayMode`), which `assets/js/booking.js` reads on load — see
|
|
`lesson-booking.md`. Its editor preview follows `displayMode`, showing the
|
|
calendar, the upcoming-lessons panel, or both. The link targets are also available
|
|
to the shortcodes as `[us_booking login_page_id="…"]` and
|
|
`[us_student_login booking_page_id="…"]`; auto-redirect is block-only.
|
|
|
|
Auto-redirect cannot happen during block rendering (output has already
|
|
started, so a `Location` header cannot be sent). Instead
|
|
`BlockRegistrar::maybeAutoRedirect()` runs on `template_redirect`, parses the
|
|
queried singular post's content for the block (including inside nested
|
|
blocks), and redirects when the block opts in. A block whose target is its
|
|
own page is ignored to avoid a redirect loop.
|
|
|
|
The registration block's auto-redirect additionally only fires on a
|
|
**finished** registration — `RegistrationPage::isRegistrationComplete()`: an
|
|
invited student who is now logged in (`?us_registered=invite`), or a
|
|
self-signup back from the emailed confirmation link (`?us_confirmed=ready|1`).
|
|
The intermediate "check your email" step and every failure (a validation
|
|
error, `?us_confirmed=expired`) stay on the page so the student reads the
|
|
message. That check runs before the content is parsed, so an ordinary page
|
|
view does not pay for the extra block scan.
|
|
|
|
## How it works
|
|
|
|
- **`BlockRegistrar`** (`src/BlockRegistrar.php`) hooks `init` and registers
|
|
each block with `register_block_type()`: a `render_callback` per block, the
|
|
shared editor script (`assets/js/blocks.js`, handle
|
|
`us-scheduler-blocks`), and the front-end stylesheet
|
|
(`assets/css/frontend.css`, handle `us-scheduler`) as the block `style` so
|
|
it also loads inside the editor and previews pick up theme styling.
|
|
- **`assets/js/blocks.js`** (vanilla JS, no build step) registers the client
|
|
side of each block — title, icon, keywords, shortcode transform — and
|
|
renders the editor preview with `wp.serverSideRender`, which fetches the
|
|
server-rendered markup via the `/wp/v2/block-renderer` REST route.
|
|
- **`BlockPreview`** (`src/BlockPreview.php`) supplies static, script-free
|
|
markup for editor previews. `BlockRegistrar::isEditorPreview()` detects the
|
|
block-renderer context via the `REST_REQUEST` constant (front-end template
|
|
rendering never happens inside a REST request) and renders the preview
|
|
instead of the live page.
|
|
|
|
## Editor preview behaviour
|
|
|
|
Live pages cannot run in the editor: booking and group classes are populated
|
|
by JavaScript making authenticated REST calls (and may load Stripe.js),
|
|
registration requires a valid invite token, and login short-circuits for
|
|
logged-in users (the editing admin always is). Each preview therefore
|
|
reproduces the live wrapper elements and CSS classes with representative
|
|
placeholder content:
|
|
|
|
- **Booking** — `#us-booking-app` with sample `.us-day` / `.us-slot` rows and
|
|
disabled Book buttons.
|
|
- **Group classes** — `#us-group-app` with a sample `.us-class` card and a
|
|
disabled Enrol button. When `offeringId` pins a single class the preview
|
|
drops the sample description, matching what the live page renders in that
|
|
mode.
|
|
- **Login** — the real `templates/frontend/login-page.php` template (it has
|
|
no request-state dependencies).
|
|
- **Registration** — a disabled sample of the `.us-register-form` fields.
|
|
- **Account** — a populated sample panel. Deliberately populated whatever the
|
|
editor user's own state: on the published page a signed-out visitor may see
|
|
nothing at all, and an empty box tells the person placing the block nothing
|
|
about where it will sit.
|
|
|
|
Each preview starts with a `.us-editor-note` paragraph explaining what the
|
|
published page shows instead. The note class only appears in editor previews.
|
|
|
|
## Tests
|
|
|
|
- `tests/Unit/BlockRegistrarTest.php` — hook registration, block/asset
|
|
registration, attribute schemas, front-end delegation to the page objects,
|
|
preview-mode routing, auto-redirect behaviour.
|
|
- `tests/Unit/Booking/BookingPageTest.php` — logged-out login-link targets
|
|
and fallbacks.
|
|
- `tests/Unit/Auth/LoginPageTest.php` — logged-in booking-link targets and
|
|
fallbacks.
|
|
- `tests/Unit/Auth/AccountPageTest.php` — what each visitor sees, the
|
|
sign-out redirect target, and the signed-out empty render.
|
|
- `tests/Unit/BlockPreviewTest.php` — preview markup mirrors the live CSS
|
|
classes/ids and includes the editor note.
|
|
|
|
## The account block's signed-out behaviour
|
|
|
|
`us-scheduler/account` is the one block that can render **nothing**. It is meant
|
|
for a header, sidebar or account page, and its whole subject is the person
|
|
signed in — which a stranger is not. A bare "you are not signed in" in a site
|
|
header is noise that cannot be acted on, so:
|
|
|
|
- **No login page chosen** → empty string for signed-out visitors.
|
|
- **Login page chosen** → a single **Sign in** link.
|
|
|
|
Signed in, it shows the display name (`Auth\UserName::format()`, so a username
|
|
is never exposed), the account email, and a **Sign out** link — deliberately
|
|
nothing else. Signing out returns to the chosen login page, or to the current page when there
|
|
is none, so a header sign-out does not also navigate the visitor somewhere.
|