From 474109a5df0a06f5343ab184838fe2d80e3872a8 Mon Sep 17 00:00:00 2001
From: Jake Vanderwerf <get@jakevanderwerf.ca>
Date: Sun, 11 Jan 2026 19:23:20 +0000
Subject: [PATCH] =Fixed timeline CRUD.js issue where this.activeItem was set null when we still needed it
---
inc/managers/MagicLinkManager.php | 534 +++++++++++++++++++++++++----------------------------------
1 files changed, 226 insertions(+), 308 deletions(-)
diff --git a/inc/managers/MagicLinkManager.php b/inc/managers/MagicLinkManager.php
index 5b551d2..f366c96 100644
--- a/inc/managers/MagicLinkManager.php
+++ b/inc/managers/MagicLinkManager.php
@@ -11,20 +11,20 @@
/**
* Magic Link Authentication Manager
*
- * Handles passwordless authentication via email magic links.
- * Can be used for referral signups, password resets, or general login.
+ * NOTE: Login form integration is now handled by LoginManager.php
+ * This class focuses solely on magic link generation and verification
*/
class MagicLinkManager
{
protected CacheManager $cache;
- protected EmailManager $email;
+ protected CacheManager $referral_cache;
// Token settings
protected int $token_expiry = 900; // 15 minutes in seconds
protected int $rate_limit_window = 3600; // 1 hour
protected int $max_attempts_per_hour = 5;
- // Link types - allows different flows for different purposes
+ // Link types
const TYPE_LOGIN = 'login';
const TYPE_SIGNUP = 'signup';
const TYPE_REFERRAL = 'referral';
@@ -32,16 +32,13 @@
public function __construct()
{
- $this->cache = new CacheManager('magic_links', $this->token_expiry);
- $this->email = new EmailManager();
+ $this->cache = CacheManager::for('magic_links', $this->token_expiry);
+ $this->referral_cache = CacheManager::for('referral_magic_links', 14 * DAY_IN_SECONDS);
// Hook into WordPress auth flow
add_action('template_redirect', [$this, 'handleMagicLinkClick']);
add_action('wp_login_failed', [$this, 'handleFailedLogin']);
-
- // Add magic link option to login page
- add_action('login_form', [$this, 'addMagicLinkOption']);
- add_filter('authenticate', [$this, 'blockStandardAuth'], 30, 3);
+ add_action('jvb_process_login_tokens', [$this, 'processRegistrationToken'], 10, 3);
}
/**
@@ -86,38 +83,120 @@
}
/**
+ * Generate a secure token
+ */
+ protected function generateToken(string $email, string $type, array $data = []): string
+ {
+ $token = wp_generate_password(32, false);
+
+ $token_data = array_merge([
+ 'email' => $email,
+ 'type' => $type,
+ 'created' => time()
+ ], $data);
+
+ // Use longer expiry for referral tokens
+ if ($type === self::TYPE_REFERRAL) {
+ $this->referral_cache->set($token, $token_data);
+ } else {
+ $this->cache->set($token, $token_data);
+ }
+
+ return $token;
+ }
+
+ /**
+ * Verify a token
+ */
+ public function verifyToken(string $token, string $email): array|WP_Error
+ {
+ // Try regular cache first, then referral cache
+ $token_data = $this->cache->get($token);
+
+ 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');
+ }
+
+ if ($token_data['email'] !== $email) {
+ return new WP_Error('email_mismatch', 'Token does not match email');
+ }
+
+ // Delete token after verification (single use)
+ // Check which cache it's in and delete from the correct one
+ if ($token_data['type'] === 'referral') {
+ $this->referral_cache->delete($token);
+ } else {
+ $this->cache->delete($token);
+ }
+
+ return $token_data;
+ }
+
+ /**
+ * Check rate limiting for sending magic links
+ */
+ protected function checkRateLimit(string $email): bool|WP_Error
+ {
+ $cache_key = 'rate_limit_' . md5($email);
+ $attempts = $this->cache->get($cache_key);
+
+ if (!$attempts) {
+ $attempts = ['count' => 0, 'timestamp' => time()];
+ }
+
+ // Reset counter if window has passed
+ if (time() - $attempts['timestamp'] > $this->rate_limit_window) {
+ $attempts = ['count' => 0, 'timestamp' => time()];
+ }
+
+ // Check if limit exceeded
+ if ($attempts['count'] >= $this->max_attempts_per_hour) {
+ return new WP_Error(
+ 'rate_limit_exceeded',
+ 'Too many magic link requests. Please try again in an hour.'
+ );
+ }
+
+ // Increment counter
+ $attempts['count']++;
+ $this->cache->set($cache_key, $attempts, $this->rate_limit_window);
+
+ return true;
+ }
+
+ /**
* Send login magic link to existing user
*/
protected function sendLoginLink(string $email, array $context): bool|WP_Error
{
- // Check if user exists
$user = get_user_by('email', $email);
if (!$user) {
return new WP_Error('user_not_found', 'No account found with this email');
}
- // Generate token
$token = $this->generateToken($email, self::TYPE_LOGIN, [
'user_id' => $user->ID
]);
- // Build magic link URL
$magic_url = add_query_arg([
'magic_token' => $token,
- 'email' => urlencode($email),
+ 'email' => rawurlencode($email),
'action' => 'magic_login'
], home_url('/'));
- // Add redirect if specified
if (!empty($context['redirect_to'])) {
$magic_url = add_query_arg('redirect_to', urlencode($context['redirect_to']), $magic_url);
}
- // Send email
$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');
}
@@ -125,14 +204,13 @@
/**
* Send signup magic link for new user registration
*/
- protected function sendSignupLink(string $email, array $context):bool|WP_Error
+ protected function sendSignupLink(string $email, array $context): bool|WP_Error
{
// Check if user already exists
if (email_exists($email)) {
return $this->sendLoginLink($email, $context);
}
- // Generate token with signup data
$token_data = [
'name' => $context['name'] ?? '',
'role' => $context['role'] ?? 'subscriber',
@@ -141,18 +219,16 @@
$token = $this->generateToken($email, self::TYPE_SIGNUP, $token_data);
- // Build signup completion URL
$magic_url = add_query_arg([
'magic_token' => $token,
- 'email' => urlencode($email),
+ 'email' => rawurlencode($email),
'action' => 'magic_signup'
], home_url('/'));
- // Send welcome email
$subject = 'Complete your ' . get_bloginfo('name') . ' registration';
$message = $this->getSignupEmailTemplate($context['name'] ?? '', $magic_url);
- $sent = $this->email->sendEmail($email, $subject, $message, 'Confirm Your Account');
+ $sent = JVB()->email()->sendEmail($email, $subject, $message, 'Complete Registration');
return $sent ? true : new WP_Error('email_failed', 'Failed to send signup link');
}
@@ -160,60 +236,46 @@
/**
* Send referral signup link
*/
- protected function sendReferralLink(string $email, array $context):bool|WP_Error
+ protected function sendReferralLink(string $email, array $context): bool|WP_Error
{
- // Check if user already exists
- if (email_exists($email)) {
- return new WP_Error('user_exists', 'This person already has an account');
- }
-
- // Validate referral code
if (empty($context['referral_code'])) {
- return new WP_Error('missing_referral_code', 'Referral code is required');
+ return new WP_Error('missing_referral', 'Referral code is required');
}
- // Get referrer info for personalized email
- $referrer_name = $context['referrer_name'] ?? 'A friend';
-
- // Generate token with referral context
$token_data = [
- 'name' => $context['name'] ?? '',
'referral_code' => $context['referral_code'],
- 'referrer_id' => $context['referrer_id'] ?? 0
+ 'name' => $context['name'] ?? '',
+ 'role' => $context['role'] ?? 'subscriber',
+ 'email' => $email
];
$token = $this->generateToken($email, self::TYPE_REFERRAL, $token_data);
- // Build referral signup URL
$magic_url = add_query_arg([
'magic_token' => $token,
- 'email' => urlencode($email),
+ 'email' => rawurlencode($email),
'action' => 'magic_referral'
], home_url('/'));
- // Send personalized referral email
- $subject = $referrer_name . ' invited you to ' . get_bloginfo('name');
- $message = $this->getReferralEmailTemplate(
- $context['name'] ?? '',
- $referrer_name,
- $magic_url,
- $context['reward_text'] ?? ''
- );
+ $referrer_name = $context['referrer_name'] ?? 'A friend';
+ $reward_text = $context['reward_text'] ?? '';
- $sent = $this->email->sendEmail($email, $subject, $message, $referrer_name.' invites you to see the difference at Legacy');
+ $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);
- return $sent ? true : new WP_Error('email_failed', 'Failed to send referral invitation');
+ $sent = JVB()->email()->sendEmail($email, $subject, $message, 'Accept Invitation');
+
+ return $sent ? true : new WP_Error('email_failed', 'Failed to send referral link');
}
/**
* Send password reset magic link
*/
- protected function sendResetLink(string $email, array $context):bool|WP_Error
+ protected function sendResetLink(string $email, array $context): bool|WP_Error
{
$user = get_user_by('email', $email);
if (!$user) {
- // Return success even if user doesn't exist (security best practice)
- return true;
+ return new WP_Error('user_not_found', 'No account found with this email');
}
$token = $this->generateToken($email, self::TYPE_RESET, [
@@ -222,14 +284,14 @@
$magic_url = add_query_arg([
'magic_token' => $token,
- 'email' => urlencode($email),
+ 'email' => rawurlencode($email),
'action' => 'magic_reset'
], home_url('/'));
$subject = 'Reset your password';
$message = $this->getResetEmailTemplate($user->display_name, $magic_url);
- $sent = $this->email->sendEmail($email, $subject, $message);
+ $sent = JVB()->email()->sendEmail($email, $subject, $message, 'Reset Password');
return $sent ? true : new WP_Error('email_failed', 'Failed to send reset link');
}
@@ -239,21 +301,18 @@
*/
public function handleMagicLinkClick(): void
{
- // Check if this is a magic link request
if (!isset($_GET['action']) || !isset($_GET['magic_token']) || !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']));
- // Only handle magic link actions
if (!in_array($action, ['magic_login', 'magic_signup', 'magic_referral', 'magic_reset'])) {
return;
}
- // Verify token
$token_data = $this->verifyToken($token, $email);
if (is_wp_error($token_data)) {
@@ -261,7 +320,6 @@
return;
}
- // Handle different action types
switch ($action) {
case 'magic_login':
$this->processLogin($token_data);
@@ -292,18 +350,14 @@
wp_die('Invalid user');
}
- // Log the user in
wp_clear_auth_cookie();
wp_set_current_user($user->ID);
wp_set_auth_cookie($user->ID, true);
- // Trigger login action
do_action('wp_login', $user->user_login, $user);
- // Determine redirect
$redirect = isset($_GET['redirect_to']) ? esc_url_raw($_GET['redirect_to']) : home_url('/dash');
- // Redirect
wp_safe_redirect($redirect);
exit;
}
@@ -313,56 +367,10 @@
*/
protected function processSignup(array $token_data): void
{
- // Create the user account
- $user_id = wp_create_user(
- $token_data['email'],
- wp_generate_password(20, true, true), // Random password
- $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 Signup');
+ return;
}
-
- // Set role
- $user = get_user_by('ID', $user_id);
- $user->set_role($token_data['role']);
-
- // Update display name if provided
- if (!empty($token_data['name'])) {
- wp_update_user([
- 'ID' => $user_id,
- 'display_name' => $token_data['name'],
- 'first_name' => $token_data['name']
- ]);
- }
-
- // Save any additional meta
- if (!empty($token_data['meta'])) {
- foreach ($token_data['meta'] as $key => $value) {
- update_user_meta($user_id, BASE . $key, $value);
- }
- }
-
- // Log the user in
- wp_set_current_user($user_id);
- wp_set_auth_cookie($user_id, true);
-
- // Trigger registration actions
- do_action('user_register', $user_id);
- do_action('wp_login', $user->user_login, $user);
-
- // Redirect to welcome page or dashboard
- wp_safe_redirect(home_url('/dash?welcome=1'));
- exit;
- }
-
- /**
- * Process referral signup via magic link
- */
- protected function processReferralSignup(array $token_data): void
- {
- // Create user account
$user_id = wp_create_user(
$token_data['email'],
wp_generate_password(20, true, true),
@@ -373,7 +381,9 @@
wp_die('Failed to create account: ' . $user_id->get_error_message());
}
- // Update user info
+ $user = get_user_by('ID', $user_id);
+ $user->set_role($token_data['role']);
+
if (!empty($token_data['name'])) {
wp_update_user([
'ID' => $user_id,
@@ -382,192 +392,105 @@
]);
}
- // Store referral code in session for ReferralManager to pick up
- if (session_status() === PHP_SESSION_NONE) {
- session_start();
+ if (!empty($token_data['meta'])) {
+ foreach ($token_data['meta'] as $key => $value) {
+ update_user_meta($user_id, BASE . $key, $value);
+ }
}
- $_SESSION[BASE . 'referral_code'] = $token_data['referral_code'];
- setcookie(BASE . 'referral_code', $token_data['referral_code'], time() + (30 * DAY_IN_SECONDS), '/');
- // Process referral (this will be picked up by ReferralManager::processReferral)
- do_action('user_register', $user_id);
-
- // Log the user in
wp_set_current_user($user_id);
wp_set_auth_cookie($user_id, true);
- do_action('wp_login', get_user_by('ID', $user_id)->user_login, get_user_by('ID', $user_id));
- // Redirect with referral welcome message
- wp_safe_redirect(home_url('/dash?referral_welcome=1'));
+ do_action('user_register', $user_id);
+ do_action('wp_login', $user->user_login, $user);
+
+ wp_safe_redirect(home_url('/dash?welcome=1'));
exit;
}
/**
- * Process password reset
+ * Process referral signup via magic link
+ */
+ /**
+ * Process referral signup via magic link
+ */
+ protected function processReferralSignup(array $token_data): void
+ {
+ if (!array_key_exists('email', $token_data) || !array_key_exists('name', $token_data)) {
+ JVB()->error()->log('[MagicLinkManager]Could not process Referral Signup');
+ return;
+ }
+
+ $email = sanitize_email($token_data['email']);
+ if (email_exists($email)) {
+ wp_die('Looks like you already have an account!');
+ }
+ $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
+ );
+ }
+ }
+
+ /**
+ * Process password reset via magic link
*/
protected function processPasswordReset(array $token_data): void
{
- // Redirect to password reset form with token
- wp_safe_redirect(add_query_arg([
- 'action' => 'rp',
- 'key' => $token_data['token'], // Could use magic token or generate WP reset key
- 'login' => $token_data['email']
- ], wp_login_url()));
+ $user = get_user_by('ID', $token_data['user_id']);
+
+ if (!$user) {
+ wp_die('Invalid user');
+ }
+
+ // Log user in and redirect to password change page
+ wp_set_current_user($user->ID);
+ wp_set_auth_cookie($user->ID, true);
+
+ wp_safe_redirect(admin_url('profile.php?password_reset=1'));
exit;
}
/**
- * Generate a secure token
- */
- protected function generateToken(string $email, string $type, array $data): string
- {
- // Create unique token
- $token = wp_generate_password(64, false, false);
-
- // Store token data in transient
- $token_data = [
- 'email' => $email,
- 'type' => $type,
- 'created_at' => time(),
- 'expires_at' => time() + $this->token_expiry,
- 'data' => $data
- ];
-
- $cache_key = 'magic_token_' . $token;
- set_transient($cache_key, $token_data, $this->token_expiry);
-
- // Also index by email for rate limiting
- $this->recordTokenGeneration($email);
-
- return $token;
- }
-
- /**
- * Verify a magic link token
- */
- protected function verifyToken(string $token, string $email)
- {
- // Retrieve token data
- $cache_key = 'magic_token_' . $token;
- $token_data = get_transient($cache_key);
-
- if (!$token_data) {
- return new WP_Error('expired_token', 'This link has expired. Please request a new one.');
- }
-
- // Verify email matches
- if ($token_data['email'] !== $email) {
- return new WP_Error('invalid_token', 'Invalid magic link');
- }
-
- // Check expiration
- if (time() > $token_data['expires_at']) {
- delete_transient($cache_key);
- return new WP_Error('expired_token', 'This link has expired. Please request a new one.');
- }
-
- // Token is valid - delete it (single use)
- delete_transient($cache_key);
-
- // Return merged data
- return array_merge($token_data['data'], [
- 'email' => $token_data['email'],
- 'type' => $token_data['type']
- ]);
- }
-
- /**
- * Rate limiting for magic link generation
- */
- protected function checkRateLimit(string $email):bool|WP_Error
- {
- $limit_key = 'magic_link_limit_' . md5($email);
- $attempts = (int) get_transient($limit_key);
-
- if ($attempts >= $this->max_attempts_per_hour) {
- return new WP_Error(
- 'rate_limit_exceeded',
- 'Too many login attempts. Please try again in an hour.'
- );
- }
-
- return true;
- }
-
- /**
- * Record token generation for rate limiting
- */
- protected function recordTokenGeneration(string $email): void
- {
- $limit_key = 'magic_link_limit_' . md5($email);
- $attempts = (int) get_transient($limit_key);
- set_transient($limit_key, $attempts + 1, $this->rate_limit_window);
- }
-
- /**
- * Handle invalid/expired tokens
+ * Handle invalid token
*/
protected function handleInvalidToken(WP_Error $error): void
{
- wp_die(
- $error->get_error_message(),
- 'Invalid Link',
- [
- 'response' => 400,
- 'back_link' => true
- ]
- );
+ wp_die($error->get_error_message());
}
/**
- * Add "Send me a magic link" option to login form
+ * Handle failed login - offer magic link option
*/
- public function addMagicLinkOption(): void
+ public function handleFailedLogin(string $username): void
{
- ?>
- <p class="magic-link-option">
- <a href="#" id="use-magic-link">Send me a login link instead</a>
- </p>
- <script>
- document.getElementById('use-magic-link')?.addEventListener('click', function(e) {
- e.preventDefault();
- const email = document.getElementById('user_login')?.value;
-
- if (!email) {
- alert('Please enter your email address first');
- return;
- }
-
- // Send magic link request
- fetch('<?php echo rest_url(BASE . '/v1/magic-link/send'); ?>', {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- },
- body: JSON.stringify({
- email: email,
- type: 'login'
- })
- })
- .then(r => r.json())
- .then(data => {
- if (data.success) {
- alert('Check your email! We sent you a login link.');
- } else {
- alert(data.message || 'Failed to send link');
- }
- });
- });
- </script>
- <?php
+ // Could add logic here to automatically offer magic link
+ // after multiple failed attempts
}
/**
- * Optionally block standard password auth for certain users
+ * Optionally block standard password auth for magic-link-only users
*/
public function blockStandardAuth($user, $username, $password)
{
- // Only block if user has magic-link-only flag
if ($user instanceof WP_User) {
$magic_only = get_user_meta($user->ID, BASE . 'magic_link_only', true);
if ($magic_only) {
@@ -584,59 +507,54 @@
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') . ' and invited you to join!</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 .= '<div style="background: #e7f5ff; padding: 20px; border-radius: 8px; margin: 20px 0;">';
- $content .= '<h3 style="margin-top: 0;">🎉 Special Offer</h3>';
- $content .= '<p>' . esc_html($reward_text) . '</p>';
- $content .= '</div>';
+ 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;">Accept Invitation</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'
+ );
+ }
- return $content;
- }
-
- protected function getResetEmailTemplate(string $name, string $magic_url): string
- {
- $content = '<h2>Reset Your Password</h2>';
- $content .= '<p>Hey ' . esc_html($name) . ', we received a request to reset 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.</p>';
+ $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;
}
}
-
-new MagicLinkManager();
--
Gitblit v1.10.0