From fff721dd185f5b97f7ae7a6e64189e55887ff590 Mon Sep 17 00:00:00 2001
From: Jake Vanderwerf <get@jakevanderwerf.ca>
Date: Sun, 05 Jul 2026 18:36:57 +0000
Subject: [PATCH] =Cleaning up the Square integration (still a bit more to do yet). Also majorly overhauled /rest/ files to ignore a rest request 'user' paramater, and rely on get_current_user_id() instead.
---
inc/managers/MagicLinkManager.php | 318 ++++++++++++++++++++++++++++++++--------------------
1 files changed, 195 insertions(+), 123 deletions(-)
diff --git a/inc/managers/MagicLinkManager.php b/inc/managers/MagicLinkManager.php
index 466d3b9..30c11f4 100644
--- a/inc/managers/MagicLinkManager.php
+++ b/inc/managers/MagicLinkManager.php
@@ -1,6 +1,10 @@
<?php
namespace JVBase\managers;
+use JetBrains\PhpStorm\NoReturn;
+use JVBase\base\Site;
+use JVBase\meta\Form;
+use JVBase\ui\Tabs;
use WP_User;
use WP_Error;
@@ -16,8 +20,8 @@
*/
class MagicLinkManager
{
- protected CacheManager $cache;
- protected EmailManager $email;
+ protected Cache $cache;
+ protected Cache $referral_cache;
// Token settings
protected int $token_expiry = 900; // 15 minutes in seconds
@@ -32,8 +36,8 @@
public function __construct()
{
- $this->cache = CacheManager::for('magic_links', $this->token_expiry);
- $this->email = new EmailManager();
+ $this->cache = Cache::for('magic_links', $this->token_expiry);
+ $this->referral_cache = Cache::for('referral_magic_links', 14 * DAY_IN_SECONDS);
// Hook into WordPress auth flow
add_action('template_redirect', [$this, 'handleMagicLinkClick']);
@@ -54,32 +58,25 @@
// Validate email
$email = sanitize_email($email);
if (!is_email($email)) {
+ error_log('Invalid email for Magic Link: '.$email);
return new WP_Error('invalid_email', 'Invalid email address');
}
// Check rate limiting
$rate_check = $this->checkRateLimit($email);
if (is_wp_error($rate_check)) {
+ error_log('Rate limit reached for Magic Link: '.$email);
return $rate_check;
}
// Handle different link types
- switch ($type) {
- case self::TYPE_LOGIN:
- return $this->sendLoginLink($email, $context);
-
- case self::TYPE_SIGNUP:
- return $this->sendSignupLink($email, $context);
-
- case self::TYPE_REFERRAL:
- return $this->sendReferralLink($email, $context);
-
- case self::TYPE_RESET:
- return $this->sendResetLink($email, $context);
-
- default:
- return new WP_Error('invalid_type', 'Invalid magic link type');
- }
+ return match ($type) {
+ self::TYPE_LOGIN => $this->sendLoginLink($email, $context),
+ self::TYPE_SIGNUP => $this->sendSignupLink($email, $context),
+ self::TYPE_REFERRAL => $this->sendReferralLink($email, $context),
+ self::TYPE_RESET => $this->sendResetLink($email, $context),
+ default => new WP_Error('invalid_type', 'Invalid magic link type'),
+ };
}
/**
@@ -88,6 +85,7 @@
protected function generateToken(string $email, string $type, array $data = []): string
{
$token = wp_generate_password(32, false);
+ error_log('Generated Token: '.$token);
$token_data = array_merge([
'email' => $email,
@@ -95,7 +93,15 @@
'created' => time()
], $data);
- $this->cache->set($token, $token_data);
+ // Use longer expiry for referral tokens
+ if ($type === self::TYPE_REFERRAL) {
+ $this->referral_cache->set($token, $token_data);
+ } else {
+ error_log('Setting to $this->cache');
+ $this->cache->set($token, $token_data);
+ }
+
+ error_log('Generated token: '.print_r($token_data, true));
return $token;
}
@@ -105,9 +111,17 @@
*/
public function verifyToken(string $token, string $email): array|WP_Error
{
+ error_log('Verifying token: '.$token);
+ // Try regular cache first, then referral cache
$token_data = $this->cache->get($token);
+ error_log('Got token data from cache: '.print_r($token_data, true));
if (!$token_data) {
+ $token_data = $this->referral_cache->get($token);
+ }
+
+ if (!$token_data) {
+ error_log('Token not found. Checking cache stats...');
return new WP_Error('invalid_token', 'Invalid or expired token');
}
@@ -116,7 +130,12 @@
}
// Delete token after verification (single use)
- $this->cache->delete($token);
+ // Check which cache it's in and delete from the correct one
+ if ($token_data['type'] === 'referral') {
+ $this->referral_cache->forget($token);
+ } else {
+ $this->cache->forget($token);
+ }
return $token_data;
}
@@ -160,7 +179,8 @@
{
$user = get_user_by('email', $email);
if (!$user) {
- return new WP_Error('user_not_found', 'No account found with this email');
+ error_log('Attempted signin with no account, redirecting to signup link '.$email);
+ return $this->sendSignupLink($email, $context);
}
$token = $this->generateToken($email, self::TYPE_LOGIN, [
@@ -180,7 +200,7 @@
$subject = 'Sign in to ' . get_bloginfo('name');
$message = $this->getLoginEmailTemplate($user->display_name, $magic_url);
- $sent = $this->email->sendEmail($email, $subject, $message, 'Log in to '. get_bloginfo('name'));
+ $sent = JVB()->email()->sendEmail($email, $subject, $message, 'Log in to '. get_bloginfo('name'));
return $sent ? true : new WP_Error('email_failed', 'Failed to send magic link');
}
@@ -197,7 +217,7 @@
$token_data = [
'name' => $context['name'] ?? '',
- 'role' => $context['role'] ?? 'subscriber',
+ 'role' => $context['role'] ?? Site::getDefaultRole(),
'meta' => $context['meta'] ?? []
];
@@ -212,7 +232,7 @@
$subject = 'Complete your ' . get_bloginfo('name') . ' registration';
$message = $this->getSignupEmailTemplate($context['name'] ?? '', $magic_url);
- $sent = $this->email->sendEmail($email, $subject, $message, 'Complete Registration');
+ $sent = JVB()->email()->sendEmail($email, $subject, $message, 'Complete Registration');
return $sent ? true : new WP_Error('email_failed', 'Failed to send signup link');
}
@@ -229,7 +249,8 @@
$token_data = [
'referral_code' => $context['referral_code'],
'name' => $context['name'] ?? '',
- 'role' => $context['role'] ?? 'subscriber'
+ 'role' => $context['role'] ?? 'subscriber',
+ 'email' => $email
];
$token = $this->generateToken($email, self::TYPE_REFERRAL, $token_data);
@@ -243,10 +264,10 @@
$referrer_name = $context['referrer_name'] ?? 'A friend';
$reward_text = $context['reward_text'] ?? '';
- $subject = $referrer_name . ' invited you to join ' . get_bloginfo('name');
- $message = $this->getReferralEmailTemplate($context['name'] ?? '', $referrer_name, $magic_url, $reward_text);
+ $subject = (array_key_exists('subject', $context) && $context['subject'] !== '') ? $context['subject'] : $referrer_name . ' invited you to join ' . get_bloginfo('name');
+ $message = $this->getReferralEmailTemplate($context['name'] ?? '', $referrer_name, $magic_url, $reward_text, $context);
- $sent = $this->email->sendEmail($email, $subject, $message, 'Accept Invitation');
+ $sent = JVB()->email()->sendEmail($email, $subject, $message, 'Accept Invitation');
return $sent ? true : new WP_Error('email_failed', 'Failed to send referral link');
}
@@ -274,7 +295,7 @@
$subject = 'Reset your password';
$message = $this->getResetEmailTemplate($user->display_name, $magic_url);
- $sent = $this->email->sendEmail($email, $subject, $message, 'Reset Password');
+ $sent = JVB()->email()->sendEmail($email, $subject, $message, 'Reset Password');
return $sent ? true : new WP_Error('email_failed', 'Failed to send reset link');
}
@@ -284,13 +305,12 @@
*/
public function handleMagicLinkClick(): void
{
- if (!isset($_GET['action']) || !isset($_GET['magic_token']) || !isset($_GET['email'])) {
+ if (!isset($_GET['magic_token']) || !isset($_GET['action']) || !isset($_GET['email'])) {
return;
}
-
$action = sanitize_text_field($_GET['action']);
$token = sanitize_text_field($_GET['magic_token']);
- $email = sanitize_email($_GET['email']);
+ $email = sanitize_email(rawurldecode($_GET['email']));
if (!in_array($action, ['magic_login', 'magic_signup', 'magic_referral', 'magic_reset'])) {
return;
@@ -299,8 +319,7 @@
$token_data = $this->verifyToken($token, $email);
if (is_wp_error($token_data)) {
- $this->handleInvalidToken($token_data);
- return;
+ $this->cleanURL();
}
switch ($action) {
@@ -322,6 +341,19 @@
}
}
+ #[NoReturn] protected function cleanURL():void
+ {
+ global $wp;
+ $redirect = isset($_GET['redirect_to']) ? esc_url_raw($_GET['redirect_to']) : get_home_url(null, '/'.$wp->request);
+ if (isset($_GET['magic_token'])) {
+ error_log('============= Redirecting and removing magic token stuffs =============');
+ wp_safe_redirect(remove_query_arg(['magic_token', 'action', 'email'], esc_url($redirect)));
+ exit;
+ }
+ wp_safe_redirect(esc_url($redirect));
+ exit;
+ }
+
/**
* Process login via magic link
*/
@@ -330,6 +362,7 @@
$user = get_user_by('ID', $token_data['user_id']);
if (!$user) {
+ error_log('No user found: '.print_r($user, true));
wp_die('Invalid user');
}
@@ -338,18 +371,18 @@
wp_set_auth_cookie($user->ID, true);
do_action('wp_login', $user->user_login, $user);
-
- $redirect = isset($_GET['redirect_to']) ? esc_url_raw($_GET['redirect_to']) : home_url('/dash');
-
- wp_safe_redirect($redirect);
- exit;
+ $this->cleanURL();
}
/**
* Process signup via magic link
*/
- protected function processSignup(array $token_data): void
+ #[NoReturn] protected function processSignup(array $token_data): void
{
+ if (!array_key_exists('email', $token_data) || !array_key_exists('name', $token_data)) {
+ JVB()->error()->log('MagicLinkManager', 'Could not process Signup');
+ $this->cleanURL();
+ }
$user_id = wp_create_user(
$token_data['email'],
wp_generate_password(20, true, true),
@@ -357,7 +390,7 @@
);
if (is_wp_error($user_id)) {
- wp_die('Failed to create account: ' . $user_id->get_error_message());
+ $this->cleanURL();
}
$user = get_user_by('ID', $user_id);
@@ -380,57 +413,53 @@
wp_set_current_user($user_id);
wp_set_auth_cookie($user_id, true);
- do_action('user_register', $user_id);
do_action('wp_login', $user->user_login, $user);
- wp_safe_redirect(home_url('/dash?welcome=1'));
- exit;
+ $this->cleanURL();
}
/**
* Process referral signup via magic link
*/
+ /**
+ * Process referral signup via magic link
+ */
protected function processReferralSignup(array $token_data): void
{
- $user_id = wp_create_user(
- $token_data['email'],
- wp_generate_password(20, true, true),
- $token_data['email']
- );
-
- if (is_wp_error($user_id)) {
- wp_die('Failed to create account: ' . $user_id->get_error_message());
+ if (!array_key_exists('email', $token_data) || !array_key_exists('name', $token_data)) {
+ JVB()->error()->log('[MagicLinkManager]Could not process Referral Signup');
+ return;
}
- if (!empty($token_data['name'])) {
- wp_update_user([
- 'ID' => $user_id,
- 'display_name' => $token_data['name'],
- 'first_name' => $token_data['name']
- ]);
+ $email = sanitize_email($token_data['email']);
+ if (email_exists($email)) {
+ $this->processSignup($token_data);
}
- // Store referral code for ReferralManager
- if (session_status() === PHP_SESSION_NONE) {
- session_start();
+ $role = JVB()->referrals()->getRole();
+ $pass = wp_generate_password(20, true, true);
+ $name = sanitize_text_field($token_data['name']);
+ $user_id = wp_insert_user([
+ 'user_login' => $email,
+ 'user_email' => $email,
+ 'user_pass' => $pass,
+ 'display_name' => $name,
+ 'role' => $role
+ ]);
+ if (!is_wp_error($user_id)) {
+ $response = JVB()->routes('login')->login($email, $pass, true);
+ if ($response) {
+ wp_safe_redirect(home_url('/dash?welcome=1&referral=1'));
+ exit;
+ }
+ } else {
+ JVB()->error()->log(
+ '[MagicLinkManager]',
+ $user_id->get_error_message(),
+ $token_data
+ );
+ $this->cleanURL();
}
- $_SESSION[BASE . 'referral_code'] = $token_data['referral_code'];
- setcookie(
- BASE . 'referral_code',
- $token_data['referral_code'],
- time() + (86400 * 30),
- '/'
- );
-
- $user = get_user_by('ID', $user_id);
- wp_set_current_user($user_id);
- wp_set_auth_cookie($user_id, true);
-
- do_action('user_register', $user_id);
- do_action('wp_login', $user->user_login, $user);
-
- wp_safe_redirect(home_url('/dash?welcome=1&referral=1'));
- exit;
}
/**
@@ -441,26 +470,22 @@
$user = get_user_by('ID', $token_data['user_id']);
if (!$user) {
- wp_die('Invalid user');
+ JVB()->error()->log(
+ 'MagicLinkManager',
+ 'Invalid user, could not reset password',
+ );
+ $this->cleanURL();
}
// Log user in and redirect to password change page
wp_set_current_user($user->ID);
wp_set_auth_cookie($user->ID, true);
-
+ //TODO: hmmm
wp_safe_redirect(admin_url('profile.php?password_reset=1'));
exit;
}
/**
- * Handle invalid token
- */
- protected function handleInvalidToken(WP_Error $error): void
- {
- wp_die($error->get_error_message());
- }
-
- /**
* Handle failed login - offer magic link option
*/
public function handleFailedLogin(string $username): void
@@ -490,56 +515,103 @@
protected function getLoginEmailTemplate(string $name, string $magic_url): string
{
- $content = '<h2>Hey ' . esc_html($name) . '!</h2>';
- $content .= '<p>Click the button below to sign in to your account. This link expires in 15 minutes.</p>';
- $content .= '<p style="text-align: center; margin: 30px 0;">';
- $content .= '<a href="' . $magic_url . '" style="background: #2271b1; color: #fff; padding: 12px 24px; text-decoration: none; border-radius: 4px; display: inline-block;">Sign In</a>';
- $content .= '</p>';
- $content .= '<p style="color: #666; font-size: 14px;">If you didn\'t request this, you can safely ignore this email.</p>';
-
+ $content = JVB()->email()->h2('Hey ' . esc_html($name) . '!');
+ $content .= '<p>Click the button below to sign in to your account instantly - no password needed!</p>';
+ $content .= JVB()->email()->button($magic_url, 'Sign In Now');
+ $content .= '<p>Or copy and paste this link into your browser:</p>';
+ $content .= JVB()->email()->link($magic_url);
+ $content .= JVB()->email()->divider();
+ $content .= '<p>If you didn\'t request this, you can safely ignore this email. This link expires in 15 minutes.</p>';
return $content;
}
protected function getSignupEmailTemplate(string $name, string $magic_url): string
{
- $content = '<h2>Welcome' . ($name ? ', ' . esc_html($name) : '') . '!</h2>';
- $content .= '<p>You\'re almost there! Click the button below to complete your registration and access your account.</p>';
- $content .= '<p style="text-align: center; margin: 30px 0;">';
- $content .= '<a href="' . $magic_url . '" style="background: #2271b1; color: #fff; padding: 12px 24px; text-decoration: none; border-radius: 4px; display: inline-block;">Complete Registration</a>';
- $content .= '</p>';
- $content .= '<p style="color: #666; font-size: 14px;">This link expires in 15 minutes.</p>';
-
+ $content = JVB()->email()->h2('Welcome' . ($name ? ', ' . esc_html($name) : '') . '!');
+ $content .= '<p>Click the button below to complete your registration and access your account!</p>';
+ $content .= JVB()->email()->button($magic_url, 'Complete Registration');
+ $content .= '<p>Or copy and paste this link:</p>';
+ $content .= JVB()->email()->link($magic_url);
+ $content .= JVB()->email()->spacer(10);
+ $content .= '<p><small>This link expires in 24 hours.</small></p>';
return $content;
}
- protected function getReferralEmailTemplate(string $name, string $referrer_name, string $magic_url, string $reward_text): string
+ protected function getReferralEmailTemplate(string $name, string $referrer_name, string $magic_url, string $reward_text, array $context): string
{
- $content = '<h2>Hey' . ($name ? ' ' . esc_html($name) : '') . '!</h2>';
- $content .= '<p><strong>' . esc_html($referrer_name) . '</strong> thinks you\'d love ' . get_bloginfo('name') . '!</p>';
+ $content = JVB()->email()->h2('Hey' . ($name ? ' ' . esc_html($name) : '') . '!');
+ $content .= sprintf(
+ '<p><strong>%s</strong> thinks you\'d love %s!</p>',
+ esc_html($referrer_name),
+ esc_html(get_bloginfo('name'))
+ );
- if ($reward_text) {
- $content .= '<p>' . esc_html($reward_text) . '</p>';
+ if (!empty($context['message'])) {
+ $content .= JVB()->email()->callout(nl2br(esc_html($context['message'])));
}
- $content .= '<p style="text-align: center; margin: 30px 0;">';
- $content .= '<a href="' . $magic_url . '" style="background: #2271b1; color: #fff; padding: 12px 24px; text-decoration: none; border-radius: 4px; display: inline-block;">Join Now</a>';
- $content .= '</p>';
- $content .= '<p style="color: #666; font-size: 14px;">This link expires in 15 minutes.</p>';
+ if ($reward_text) {
+ $content .= JVB()->email()->alert(
+ '<strong>Special Welcome Offer:</strong> ' . esc_html($reward_text),
+ 'success'
+ );
+ }
+
+ $content .= JVB()->email()->button($magic_url, 'Join Now');
+ $content .= '<p>Or copy and paste this link:</p>';
+ $content .= JVB()->email()->link($magic_url);
+ $content .= JVB()->email()->spacer(10);
+ $content .= '<p><small>This invitation expires in 14 days.</small></p>';
return $content;
}
- protected function getResetEmailTemplate(string $name, string $magic_url): string
+ public function renderForm():string
{
- $content = '<h2>Hey ' . esc_html($name) . '!</h2>';
- $content .= '<p>We received a request to reset your password. Click the button below to sign in and update your password.</p>';
- $content .= '<p style="text-align: center; margin: 30px 0;">';
- $content .= '<a href="' . $magic_url . '" style="background: #2271b1; color: #fff; padding: 12px 24px; text-decoration: none; border-radius: 4px; display: inline-block;">Reset Password</a>';
- $content .= '</p>';
- $content .= '<p style="color: #666; font-size: 14px;">If you didn\'t request this, you can safely ignore this email. This link expires in 15 minutes.</p>';
+ ob_start();
+ JVB()->connect('cloudflare')->renderTurnstile();
+ $turnstile = ob_get_clean();
+ global $wp;
- return $content;
+ $magicLink = sprintf(
+ '<form id="magic-link-form" data-save="auth/magic">
+ %s%s
+ <input type="hidden" name="redirect_to" value="%s">
+ %s
+ <button type="submit">%sLogin with Magic Link</button>
+ <p class="hint"><a href="" data-tab="login">Prefer to use a password?</a></p>
+ </form>
+ <div class="success-content" hidden>
+ <h3>Check Your Email!</h3>
+ <p>We\'ve sent you a magic link to log in - no password required! Click the link in your email to log in.</p>
+ <p class="hint">Can\'t find it? Check your spam folder.</p>
+ </div>',
+ jvbFormStatus(),
+ Form::render('user_email', null, [
+ 'required' => true,
+ 'type' => 'email',
+ 'label' => 'Your Email',
+ 'autocomplete'=>'email'
+ ]),
+ get_home_url(null,'/'.$wp->request),
+ $turnstile,
+ jvbIcon('magic-wand'),
+// wp_login_url()
+ );
+
+ $login = LoginManager::getInstance()->renderLoginForm('login', get_home_url(null,'/'.$wp->request));
+
+ $tabs = new Tabs();
+ $tabs->addTab('magic')
+ ->title('With Magic Link')
+ ->icon('magic-wand')
+ ->content($magicLink);
+
+ $tabs->addTab('login')
+ ->title('With Password')
+ ->icon('password')
+ ->content($login);
+
+ return $tabs->render();
}
}
-
-new MagicLinkManager();
--
Gitblit v1.10.0