Add an account block showing who is signed in
[us_account], or the Account block: the signed-in visitor's name, their email, a Sign out link, and — only when the account books for someone besides itself — the students it books for. A parent's first question on seeing "signed in as Grace" is whether this is the account their children's lessons are on. Two decisions worth naming. Signed out with no login page chosen, the block renders nothing. Its whole subject is the person signed in, which a stranger is not, and a bare "you are not signed in" in a site header is noise with no way to act on it. With a login page chosen it offers a Sign in link instead. The editor preview is populated regardless, so the block is never an invisible box to the person placing it. Signing out returns to the chosen login page, or to the current page when there is none. A block meant for a header should not also navigate someone somewhere when they use it; the login page wins when configured, because the page they were on may well be members-only. The name comes from UserName::format(), so the block never exposes a username the way display_name can. Also brings docs/features/editor-blocks.md back in step: it still described "four shortcodes" and had never listed the family block. Closes #142 Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Unsupervised\Schedular\Auth;
|
||||
|
||||
use Unsupervised\Schedular\Guardian\GuardianService;
|
||||
use Unsupervised\Schedular\Val;
|
||||
|
||||
/**
|
||||
* Who is signed in, and the way out.
|
||||
*
|
||||
* Meant for a header, sidebar or account page — somewhere it sits alongside
|
||||
* other content rather than being the whole of it. That shapes the two
|
||||
* decisions below.
|
||||
*/
|
||||
class AccountPage {
|
||||
|
||||
public function __construct( private GuardianService $guardians ) {}
|
||||
|
||||
/**
|
||||
* Renders the account shortcode/block output.
|
||||
*
|
||||
* Signed out, this renders a sign-in link when a login page is configured and
|
||||
* **nothing at all** when one is not. A block whose whole job is "you are
|
||||
* signed in as X" has nothing to say to a stranger, and a bare "you are not
|
||||
* signed in" in a site header is noise with no way to act on it. The editor
|
||||
* preview shows the populated state regardless, so the block is never
|
||||
* invisible to the person placing it.
|
||||
*
|
||||
* @param array<int|string, mixed> $atts Block attributes (`loginPageId`) or
|
||||
* shortcode attributes (`login_page_id`).
|
||||
*/
|
||||
public function render( array $atts ): string {
|
||||
$loginPageId = Val::int( $atts['loginPageId'] ?? $atts['login_page_id'] ?? 0 );
|
||||
$loginUrl = $this->pageUrl( $loginPageId );
|
||||
|
||||
wp_enqueue_style( 'us-scheduler' );
|
||||
|
||||
if ( ! is_user_logged_in() ) {
|
||||
if ( null === $loginUrl ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return sprintf(
|
||||
'<div class="us-account us-account-out"><a class="us-account-signin" href="%s">%s</a></div>',
|
||||
esc_url( $loginUrl ),
|
||||
esc_html__( 'Sign in', 'unsupervised-schedular' )
|
||||
);
|
||||
}
|
||||
|
||||
// Always a WP_User here — is_user_logged_in() above rules out the
|
||||
// id-0 placeholder wp_get_current_user() returns for a visitor.
|
||||
$user = wp_get_current_user();
|
||||
|
||||
$name = UserName::format( $user, get_current_user_id() );
|
||||
$email = $user->user_email;
|
||||
|
||||
// Whose lessons this account books, when that is more than just their own.
|
||||
// A parent's first question on seeing "signed in as Grace" is whether this
|
||||
// is the account their children's lessons are on.
|
||||
$students = [];
|
||||
foreach ( $this->guardians->bookableStudents( get_current_user_id() ) as $student ) {
|
||||
if ( ! $student['is_self'] ) {
|
||||
$students[] = $student['name'];
|
||||
}
|
||||
}
|
||||
|
||||
// Back to where they were, so signing out of a header link does not also
|
||||
// navigate them somewhere. The login page is the better landing spot when
|
||||
// one is configured, since the current page may be members-only.
|
||||
$logoutUrl = wp_logout_url( $loginUrl ?? (string) get_permalink() );
|
||||
|
||||
ob_start();
|
||||
include USC_PLUGIN_DIR . 'templates/frontend/account-page.php';
|
||||
return (string) ob_get_clean();
|
||||
}
|
||||
|
||||
/**
|
||||
* Permalink of a configured page, or null when none is chosen or the chosen
|
||||
* page has since been deleted.
|
||||
*/
|
||||
private function pageUrl( int $pageId ): ?string {
|
||||
if ( $pageId <= 0 ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$url = get_permalink( $pageId );
|
||||
|
||||
return is_string( $url ) ? $url : null;
|
||||
}
|
||||
}
|
||||
@@ -210,6 +210,32 @@ class BlockPreview {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sample account panel. Shown populated whatever the editor's own login
|
||||
* state, since on the published page a signed-out visitor may see nothing at
|
||||
* all and an empty box tells the person placing the block nothing.
|
||||
*/
|
||||
public static function account(): string {
|
||||
return sprintf(
|
||||
'<div class="us-account">%s'
|
||||
. '<p class="us-account-who"><span class="us-account-name">%s</span>'
|
||||
. '<span class="us-account-email">%s</span></p>'
|
||||
. '<p class="us-account-students">%s</p>'
|
||||
. '<p class="us-account-actions"><a class="us-account-signout" href="#">%s</a></p></div>',
|
||||
self::note( __( 'Editor preview — each visitor sees their own account here.', 'unsupervised-schedular' ) ),
|
||||
esc_html__( 'Grace Hopper', 'unsupervised-schedular' ),
|
||||
esc_html__( '[email protected]', 'unsupervised-schedular' ),
|
||||
esc_html(
|
||||
sprintf(
|
||||
/* translators: %s: comma-separated list of the students this account books for. */
|
||||
__( 'Booking for %s', 'unsupervised-schedular' ),
|
||||
__( 'Ada, Alan', 'unsupervised-schedular' )
|
||||
)
|
||||
),
|
||||
esc_html__( 'Sign out', 'unsupervised-schedular' )
|
||||
);
|
||||
}
|
||||
|
||||
private static function note( string $text ): string {
|
||||
return '<p class="us-editor-note">' . esc_html( $text ) . '</p>';
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace Unsupervised\Schedular;
|
||||
|
||||
use Unsupervised\Schedular\Auth\AccountPage;
|
||||
use Unsupervised\Schedular\Auth\LoginPage;
|
||||
use Unsupervised\Schedular\Auth\RegistrationPage;
|
||||
use Unsupervised\Schedular\Booking\BookingPage;
|
||||
@@ -30,6 +31,7 @@ class BlockRegistrar {
|
||||
private RegistrationPage $registrationPage,
|
||||
private GroupClassPage $groupClassPage,
|
||||
private FamilyPage $familyPage,
|
||||
private AccountPage $accountPage,
|
||||
) {}
|
||||
|
||||
public function register(): void {
|
||||
@@ -148,6 +150,15 @@ class BlockRegistrar {
|
||||
],
|
||||
],
|
||||
],
|
||||
'us-scheduler/account' => [
|
||||
'render' => [ $this, 'renderAccount' ],
|
||||
'attributes' => [
|
||||
'loginPageId' => [
|
||||
'type' => 'number',
|
||||
'default' => 0,
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
@@ -195,6 +206,15 @@ class BlockRegistrar {
|
||||
return BlockPreview::groupClasses( Val::int( $attributes['offeringId'] ?? 0 ) > 0 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the account (who is signed in) block.
|
||||
*
|
||||
* @param array<string, mixed> $attributes Block attributes.
|
||||
*/
|
||||
public function renderAccount( array $attributes = [] ): string {
|
||||
return $this->isEditorPreview() ? BlockPreview::account() : $this->accountPage->render( $attributes );
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the family (manage-children) block.
|
||||
*
|
||||
|
||||
+4
-2
@@ -5,6 +5,7 @@ namespace Unsupervised\Schedular;
|
||||
|
||||
use Unsupervised\Schedular\Auth\EmailConfirmationHandler;
|
||||
use Unsupervised\Schedular\Auth\InviteRepository;
|
||||
use Unsupervised\Schedular\Auth\AccountPage;
|
||||
use Unsupervised\Schedular\Auth\LoginPage;
|
||||
use Unsupervised\Schedular\Auth\RegistrationLoginGate;
|
||||
use Unsupervised\Schedular\Auth\RegistrationMailer;
|
||||
@@ -99,6 +100,7 @@ class Plugin {
|
||||
$registrationPage = new RegistrationPage( $invites, $policies, $policyVersions, $acceptances, $settings, $registrationMailer, $questions, $answers, $groupAccess, $guardians );
|
||||
$groupClassPage = new GroupClassPage( $guardians );
|
||||
$familyPage = new FamilyPage( $guardians, $questions, $answers );
|
||||
$accountPage = new AccountPage( $guardians );
|
||||
|
||||
( new ScheduledBillingRunner( $paymentService, $bookings, $enrollments, $offerings, new PaymentDueMailer(), $guardians ) )->register();
|
||||
|
||||
@@ -110,7 +112,7 @@ class Plugin {
|
||||
( new EmailConfirmationHandler( $settings, $registrationMailer ) )->register();
|
||||
( new AdminMenu( $availability, $bookings, $offerings, $questions, $answers, $policies, $policyVersions, $policyService, $acceptances, $invites, $enrollments, $groupAccess, $settings, $paymentRepo, $paymentService, $resolver, $registrationMailer, $creditRepo, $guardians ) )->register();
|
||||
( new RestRegistrar( $availability, $bookings, $offerings, $questions, $policies, $policyVersions, $policyService, $registrationGate, $enrollments, $groupAccess, $paymentService, $guardians ) )->register();
|
||||
( new ShortcodeRegistrar( $bookingPage, $loginPage, $registrationPage, $groupClassPage, $familyPage ) )->register();
|
||||
( new BlockRegistrar( $bookingPage, $loginPage, $registrationPage, $groupClassPage, $familyPage ) )->register();
|
||||
( new ShortcodeRegistrar( $bookingPage, $loginPage, $registrationPage, $groupClassPage, $familyPage, $accountPage ) )->register();
|
||||
( new BlockRegistrar( $bookingPage, $loginPage, $registrationPage, $groupClassPage, $familyPage, $accountPage ) )->register();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace Unsupervised\Schedular;
|
||||
|
||||
use Unsupervised\Schedular\Auth\AccountPage;
|
||||
use Unsupervised\Schedular\Auth\LoginPage;
|
||||
use Unsupervised\Schedular\Auth\RegistrationPage;
|
||||
use Unsupervised\Schedular\Booking\BookingPage;
|
||||
@@ -18,6 +19,7 @@ class ShortcodeRegistrar {
|
||||
private RegistrationPage $registrationPage,
|
||||
private GroupClassPage $groupClassPage,
|
||||
private FamilyPage $familyPage,
|
||||
private AccountPage $accountPage,
|
||||
) {}
|
||||
|
||||
public function register(): void {
|
||||
@@ -26,6 +28,7 @@ class ShortcodeRegistrar {
|
||||
add_shortcode( 'us_student_register', self::shortcode( [ $this->registrationPage, 'render' ] ) );
|
||||
add_shortcode( 'us_group_classes', self::shortcode( [ $this->groupClassPage, 'render' ] ) );
|
||||
add_shortcode( 'us_family', self::shortcode( [ $this->familyPage, 'render' ] ) );
|
||||
add_shortcode( 'us_account', self::shortcode( [ $this->accountPage, 'render' ] ) );
|
||||
// Process registration submissions before output so the invite branch's
|
||||
// auth cookie is actually sent (render() runs too late, during the_content).
|
||||
add_action( 'template_redirect', [ $this->registrationPage, 'maybeHandleSubmit' ] );
|
||||
|
||||
Reference in New Issue
Block a user