From aeb5a13bfa203281aaa5573e19fe5aa6ac012152 Mon Sep 17 00:00:00 2001
From: Jake Vanderwerf <get@jakevanderwerf.ca>
Date: Fri, 02 Jan 2026 06:03:55 +0000
Subject: [PATCH] Merge branch 'main' of https://github.com/jakevdwerf/jvb
---
inc/managers/LoginManager.php | 173 ++++++++++++++++++++++++++-------------------------------
1 files changed, 78 insertions(+), 95 deletions(-)
diff --git a/inc/managers/LoginManager.php b/inc/managers/LoginManager.php
index 7ded3ba..2e5c569 100644
--- a/inc/managers/LoginManager.php
+++ b/inc/managers/LoginManager.php
@@ -17,10 +17,7 @@
class LoginManager
{
protected Features $siteFeatures;
- protected ?MagicLinkManager $magicLink = null;
protected ?MetaForm $metaForm = null;
- protected EmailManager $emailManager;
- protected AjaxRateLimiter $rateLimiter;
protected CacheManager $cache;
@@ -44,8 +41,7 @@
public function __construct()
{
$this->siteFeatures = Features::forSite();
- $this->emailManager = new EmailManager();
- $this->rateLimiter = new AjaxRateLimiter();
+
$this->cache = CacheManager::for('login');
@@ -67,10 +63,19 @@
// Login success handling
add_action('wp_login', [$this, 'handleSuccessfulLogin'], 10, 2);
+ add_filter( 'login_url', [$this, 'loginUrl'], 10, 3 );
+ add_filter( 'logout_url', [$this, 'logoutUrl'], 10, 2 );
// Allow other features to register handlers
do_action('jvbLoginManagerInit', $this);
+ add_action('user_register', array($this, 'saveRegistrationFields'), 999, 2);
+ add_filter('the_seo_framework_sitemap_exclude_ids', [$this, 'excludeLoginSitemap'], 10, 1);
}
+ public function excludeLoginSitemap(array $ids): array
+ {
+ $ids[] = $this->getLoginPage();
+ return $ids;
+ }
/**************************************************************************
* SETUP & CONFIGURATION
**************************************************************************/
@@ -90,7 +95,7 @@
return;
}
// Build custom login URL with all query args
- $custom_login_page = home_url('/login');
+ $custom_login_page = home_url('/login/');
$query_args = $_GET;
// Remove WordPress internal args
@@ -287,6 +292,34 @@
}
}
}
+ public function loginUrl(string $login_url, string $redirect, bool $force_reauth):string
+ {
+ // This will append /custom-login/ to you main site URL as configured in general settings (ie https://domain.com/custom-login/)
+ $login_url = site_url( '/login/', 'login' );
+ if ( ! empty( $redirect ) ) {
+ $login_url = add_query_arg( 'redirect_to', urlencode( $redirect ), $login_url );
+ }
+ if ( $force_reauth ) {
+ $login_url = add_query_arg( 'reauth', '1', $login_url );
+ }
+ return $login_url;
+ }
+
+ public function logoutUrl(string $logout_url, string $redirect): string
+{
+ // Build custom logout URL
+ $logout_url = site_url('/login/', 'login');
+ $logout_url = add_query_arg('action', 'logout', $logout_url);
+
+ if (!empty($redirect)) {
+ $logout_url = add_query_arg('redirect_to', urlencode($redirect), $logout_url);
+ }
+
+ // Add nonce for security
+ $logout_url = wp_nonce_url($logout_url, 'log-out');
+
+ return $logout_url;
+}
public function getLoginPage():int|false
{
return (int)get_option(BASE.'login_page');
@@ -308,7 +341,6 @@
if (!Features::forSite()->has('magicLink')) {
return;
}
- $this->magicLink = new MagicLinkManager();
}
/*********************************************************************
@@ -597,7 +629,8 @@
$checked = (is_user_logged_in() && current_user_can('prefers_dark_theme', true)) ? ' checked' : '';
$title = ($checked == '') ? 'Toggle Dark Mode' : 'Toggle Light Mode';
echo '<label title="'.$title.'" id="theme-switch" class="toggle-switch" for="theme-switcher">
- <input class="theme-switch row" id="theme-switcher" type="checkbox"'.$checked.' data-setting="theme" data-theme role="switch" name="dark-mode"><span class="slider">'.
+ <span class="screen-reader-text">Toggle dark mode</span>
+ <input class="theme-switch row" id="theme-switcher" name="theme-switcher" type="checkbox"'.$checked.' data-setting="theme" data-theme name="dark-mode" aria-label="Toggle dark mode"><span class="slider">'.
jvbIcon('sun-dim', ['title'=> 'Light Mode']).
jvbIcon('moon', ['title'=>'Dark Mode']).
'</span></label>';
@@ -664,10 +697,7 @@
/*************************************************************************
* SECURITY & VALIDATION
*************************************************************************/
- protected function checkAjaxRateLimit(string $action): bool
- {
- return $this->rateLimiter->checkLimit($action);
- }
+
protected function checkRequestId(): bool
{
$request_id = $_POST['request_id'] ?? '';
@@ -815,7 +845,7 @@
protected function maybeMagicLink(): void
{
- if (!$this->magicLink || !in_array($this->action, ['login', 'lostpassword'])) {
+ if (!JVB()->magicLink() || !in_array($this->action, ['login', 'lostpassword'])) {
return;
}
?>
@@ -841,8 +871,7 @@
$action = $this->getAction();
ob_start();
?>
- <script type="text/javascript">
- window.checkedEmails = new Set();
+
document.addEventListener('DOMContentLoaded', () => {
const form = document.querySelector('.login form');
if (!form) return;
@@ -863,33 +892,34 @@
window.LoginController.subscribe((event, data) => {
if (event === 'form-submit') {
handleFormSubmission(data);
- } else if (event === 'field-validated' && data.name === 'user_email') {
- if (!window.checkedEmails.has(data.value)){
- checkEmail(data.value, data);
- }
}
});
async function handleFormSubmission(data) {
- const { formId, config, data: formData } = data;
- const form = config.element;
- const submit = form.querySelector('[type=submit]');
- let oldText = submit.textContent;
- // Show uploading status
- window.LoginController.showFormStatus(formId, 'uploading');
+ let realFormData = data.fullData;
+ const { formId, config, data: formData } = data;
- try {
- submit.disabled = true;
- submit.textContent = 'Loading...';
- const response = await fetch(`${jvbSettings.api}<?=($action === 'magic') ? $action : 'auth/'.$action?>`, {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- 'X-WP-Nonce': jvbSettings.nonce
- },
- body: JSON.stringify(formData)
- });
+
+ const form = config.element;
+
+ const submit = form.querySelector('[type=submit]');
+ let oldText = submit.textContent;
+
+
+ window.LoginController.showFormStatus(formId, 'uploading');
+
+ try {
+ submit.disabled = true;
+ submit.textContent = 'Loading...';
+ const response = await fetch(`${jvbSettings.api}<?=($action === 'magic') ? $action : 'auth/'.$action?>`, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'X-WP-Nonce': window.auth.getNonce()
+ },
+ body: JSON.stringify(realFormData)
+ });
const result = await response.json();
@@ -908,11 +938,16 @@
window.LoginController.handleFormSuccess(form, result);
}
+ if (window.auth && typeof window.auth.handleLogin === 'function' && Object.hasOwn(result, 'auth')) {
+ console.log('Awaiting Auth...');
+ await window.auth.handleLogin(result.auth); // Pass the full result
+ }
+
// Handle redirect
if (result.redirect) {
setTimeout(() => {
window.location.href = result.redirect;
- }, 500); // Brief delay to show success message
+ }, 200); // Brief delay to show success message
}
} catch (error) {
@@ -927,68 +962,11 @@
submit.disabled = false;
}
}
- async function checkEmail(email, input) {
- window.checkedEmails.add(email);
- let wrapper = input.closest('.field');
- let submit = input.closest('form').querySelector('[type=submit]');
- try {
- submit.disabled = true;
- window.LoginController.showSuccess(wrapper, 'Checking our records...');
- const response = await fetch(`${jvbSettings.api}auth/email`, {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- 'X-WP-Nonce': jvbSettings.nonce
- },
- body: JSON.stringify({ email: email })
- });
- const result = await response.json();
-
- if (!response.ok) {
- return; // On error, allow to proceed (fail open)
- }
-
- if (result.exists) {
- <?php
- switch ($action) {
- case 'register':
- echo 'window.LoginController.showError(wrapper,\'This email is already registered. Log in instead?\');';
- break;
- case 'login':
- case 'lostpassword':
- case 'magic':
- echo 'window.LoginController.showSuccess(wrapper,\'Email exists in our system.\');';
- break;
- }
- ?>
- } else {
- <?php
- switch ($action) {
- case 'register':
- echo 'window.LoginController.showSuccess(wrapper,\'Email is available!\');';
- break;
- case 'login':
- case 'lostpassword':
- case 'magic':
- echo 'window.LoginController.showError(wrapper,\'This email doesn\\\'t seem to exist in our system. Create account instead?\');';
- break;
- }
- ?>
- }
-
- return true; // Email is available
- } catch (error) {
- console.error('Email check failed:', error);
- return true; // On network error, allow to proceed
- }finally {
- submit.disabled = false;
- }
- }
});
- </script>
+
<?php
$script = ob_get_clean();
@@ -1022,6 +1000,11 @@
wp_safe_redirect($login_url);
exit;
}
+
+ public function saveRegistrationFields(int $user_id, array $userdata):void
+ {
+
+ }
}
// Initialize the login manager
--
Gitblit v1.10.0