From 46d681c6b825d21b3f698d793c4e630c687d90ad Mon Sep 17 00:00:00 2001
From: Jake Vanderwerf <get@jakevanderwerf.ca>
Date: Thu, 21 May 2026 21:41:53 +0000
Subject: [PATCH] =Major CustomBlocks.php overhaul, expanding block support and customization from the editor. theme.json should now be updated on new themes to set brand colours, etc. Also note: major change to .col vs .row alignment: simplifying it to .top .bottom vs the confusion of the differences for .col/.row .start and .a-start

---
 inc/managers/LoginManager.php |  280 +++++++++++++++++++++++++++++--------------------------
 1 files changed, 149 insertions(+), 131 deletions(-)

diff --git a/inc/managers/LoginManager.php b/inc/managers/LoginManager.php
index 5a25159..68db551 100644
--- a/inc/managers/LoginManager.php
+++ b/inc/managers/LoginManager.php
@@ -1,13 +1,11 @@
 <?php
 namespace JVBase\managers;
 
-use JVBase\blocks\CustomBlocks;
+use JVBase\base\Site;
 use JVBase\forms\TaxonomySelector;
-use JVBase\meta\MetaManager;
-use JVBase\meta\MetaForm;
-use JVBase\managers\AjaxRateLimiter;
-use JVBase\utility\Features;
-use WP_Error;
+use JVBase\meta\Form;
+
+use JVBase\registrar\Registrar;use WP_Error;
 use WP_User;
 
 if (!defined('ABSPATH')) {
@@ -16,8 +14,7 @@
 
 class LoginManager
 {
-	protected Features $siteFeatures;
-	protected ?MetaForm $metaForm = null;
+	protected ?Form $form = null;
 	protected Cache $cache;
 
 
@@ -26,6 +23,7 @@
 	protected array $fields = [];
 	protected ?string $action = null;
 	protected string $title = '';
+	protected static LoginManager $instance;
 
 	// Token handlers registry
 	protected array $messageHandlers = [];
@@ -38,15 +36,14 @@
 	];
 	private int $max_file_size = 5242880; // 5MB in bytes
 
+
 	public function __construct()
 	{
-		$this->siteFeatures = Features::forSite();
-
-
+		self::$instance = $this;
 		$this->cache = Cache::for('login');
-
+		$this->cache->flush();
 		// Initialize magic link support if enabled
-		if ($this->siteFeatures->has('magicLink')) {
+		if (Site::has('magicLink')) {
 			$this->initMagicLinkSupport();
 		}
 
@@ -63,6 +60,7 @@
 		// Login success handling
 		add_action('wp_login', [$this, 'handleSuccessfulLogin'], 10, 2);
 
+		add_filter('lostpassword_url', [$this, 'resetPasswordUrl'], 10, 2);
 		add_filter( 'login_url', [$this, 'loginUrl'], 10, 3 );
 		add_filter( 'logout_url', [$this, 'logoutUrl'], 10, 2 );
 		// Allow other features to register handlers
@@ -70,6 +68,10 @@
 		add_action('user_register', array($this, 'saveRegistrationFields'), 999, 2);
 		add_filter('the_seo_framework_sitemap_exclude_ids', [$this, 'excludeLoginSitemap'], 10, 1);
 	}
+	public static function getInstance():self
+	{
+		return self::$instance;
+	}
 
 	public function excludeLoginSitemap(array $ids): array
 	{
@@ -130,7 +132,7 @@
 					'placeholder'=> 'look@me.com'
 				]
 			];
-			if (Features::forSite()->has('referrals')) {
+			if (Site::has('referrals')) {
 				$fields['referral_code'] = [
 					'type'	=> 'text',
 					'required'=> false,
@@ -138,19 +140,19 @@
 					'hint'	=> 'Have a referral code? Paste it here!'
 				];
 			}
-			if (count(JVB_USER) > 1) {
-				foreach (JVB_USER as $slug => $config) {
-					if (!array_key_exists('can_register', $config) || !$config['can_register']) {
-						continue;
-					}
-					$icon = $config['icon'] ?? '';
+			$canRegister = Registrar::getFeatured('can_register', 'user');
+			if (!empty($canRegister)) {
+				foreach ($canRegister as $role) {
+					$registrar = Registrar::getInstance($role);
+					$config = $registrar->getConfig('register');
+					$icon = $registrar->getIcon('user');
 					$icon = ($icon !== '') ? jvbIcon($icon) : '';
-					$select[$slug] = '<span class="label">'.$icon.$config['label'].'</span><span class="text">'.$config['register']['text']??''.'</span>';
-					if (!empty($config['register']['fields']??[])){
-						foreach ($config['register']['fields'] as $field) {
+					$select[$role] = '<span class="label">'.$icon.$registrar->getSingular().'</span><span class="text">'.$config['description']??Site::login()->getDescription('register')??''.'</span>';
+					if (!empty($config['fields'])){
+						foreach ($config['fields'] as $field) {
 							$field['condition'] = [
 								'field'	=> 'user_select',
-								'value'	=> $slug,
+								'value'	=> $role,
 								'operator'	=> '=='
 							];
 							$fields[] = $field;
@@ -188,8 +190,13 @@
 
 	protected function setupFields():void
 	{
+		$this->fields = $this->getFieldsForAction($this->action);
+	}
+
+	protected function getFieldsForAction(string $action):array
+	{
 		$fields = [];
-		switch($this->action) {
+		switch($action) {
 			case 'register':
 				$fields = $this->getRegistrationFormFields();
 				break;
@@ -260,9 +267,10 @@
 				break;
 
 		}
-		$this->fields = $fields;
+		return $fields;
 	}
 
+
 	/**
 	 * Ensure login page exists
 	 */
@@ -294,7 +302,7 @@
 			}
 		}
 	}
-	public function loginUrl(string $login_url, string $redirect, bool $force_reauth):string
+	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' );
@@ -308,18 +316,26 @@
 	}
 
 	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);
+	{
+		// 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);
+		if (!empty($redirect)) {
+			$logout_url = add_query_arg('redirect_to', urlencode($redirect), $logout_url);
+		}
+
+		// Add nonce for security
+		return wp_nonce_url($logout_url, 'log-out');
 	}
+	public function resetPasswordUrl(string $url, string $redirect):string
+	{
+		error_log('reset Password Url:'.print_r($url, true));
+		error_log('reset password redirect: '.print_r($redirect, true));
 
-	// Add nonce for security
-	return wp_nonce_url($logout_url, 'log-out');
-}
+		return str_replace('wp_login.php', 'login/', $url);
+
+	}
 	public function getLoginPage():int|false
 	{
 		return (int)get_option(BASE.'login_page');
@@ -338,7 +354,7 @@
 
 	protected function initMagicLinkSupport(): void
 	{
-		if (!Features::forSite()->has('magicLink')) {
+		if (!Site::has('magicLink')) {
 			return;
 		}
 	}
@@ -351,6 +367,11 @@
 		if (!$this->isLoginPage()) {
 			return $template;
 		}
+		global $_GET;
+		if (is_user_logged_in() && (!array_key_exists('action', $_GET) || $_GET['action']!=='logout')) {
+			wp_redirect(get_home_url(null, '/dash'));
+			exit;
+		}
 		$this->setup();
 		$page = $this->cache->remember(
 				$this->getAction(),
@@ -406,12 +427,6 @@
 			wp_redirect(esc_attr($_GET['redirect_to'] ?? get_home_url()));
 			exit;
 		}
-		if (in_array($this->action, ['rp', 'resetpass']) && !is_user_logged_in()) {
-			wp_redirect(wp_login_url());
-			exit;
-		} elseif (is_user_logged_in()) {
-			wp_redirect(get_home_url(null, '/dash/'));
-		}
 		$this->setupLabels();
 		$this->setupFields();
 		$this->setupTitle();
@@ -438,11 +453,9 @@
 	protected function customStyles():void
 	{
 		$logo = get_theme_mod('custom_logo');
-		$small = $large = '';
 		if ($logo) {
-			$small = wp_get_attachment_image_src($logo, 'medium')[0];
-			$large = wp_get_attachment_image_src($logo, 'large')[0];
-
+			$small = wp_get_attachment_image_src($logo, 'medium')[0]??'';
+			$large = wp_get_attachment_image_src($logo, 'large')[0]??'';
 		}
 		echo '<style>
 			.login header,
@@ -475,8 +488,8 @@
 			.login main .login-box {
 				--gap: .75rem;
 				padding: 1rem;
+				background-color:rgba(var(--base-rgb),var(--op-6));
 				border-radius: var(--outerRadius);
-				background-color: var(--overlay-heavy);
 				box-shadow: var(--shadow-right), var(--shadow-down);
 				margin: 15vh auto 0!important;
 			}
@@ -506,7 +519,8 @@
 				.login main .navigation,
 				.login main .login-box {
 					max-width: 60vw!important;
-					margin: 0 2rem 0 auto!important;
+					padding-right: 4rem!important;
+					margin: 0 0 0 auto!important;
 				}
 				.login main .login-box {
 					padding: 2rem;
@@ -533,35 +547,14 @@
 
 	protected function renderForms():void
 	{
-		$this->metaForm = new MetaForm();
 		$form = $this->action.'form';
 		?>
-		<section class="login-box col btw">
+		<section class="login-box col y-btw">
 			<h1><?=$this->labels['title']?></h1>
 			<?= $this->labels['description'] ?>
 
+			<?= $this->renderLoginForm($this->action); ?>
 
-			<form name="<?=$form?>" method="post" data-action="jvb_<?=$this->action?>">
-				<?= jvbFormStatus() ?>
-				<?php wp_nonce_field('jvb_'.$this->action, '_wpnonce'); ?>
-				<input type="hidden" name="action" value="jvb_<?=$this->action?>">
-				<input type="hidden" name="redirect_to" value="<?= esc_attr($_GET['redirect_to'] ?? '') ?>">
-				<input type="hidden" name="request_id" value="<?= wp_generate_password(16, false) ?>">
-				<?= ($this->action === 'magic') ? '<input type="hidden" name="type" value="login">' : '' ?>
-				<?php
-				do_action('jvb_add_token_inputs', $this->action);
-
-				foreach ($this->fields as $name => $config) {
-					$this->metaForm->render($name, '', $config);
-				}
-
-				$this->maybeTurnstile();
-				 ?>
-				 <div class="row btw nowrap">
-					<button type="submit" class="button button-primary button-large"><?=$this->labels['submit']?></button>
-					<?php $this->maybeMagicLink(); ?>
-				</div>
-			</form>
 
 			<?php
 			if (is_array($this->labels['extra'])) {
@@ -575,7 +568,7 @@
 			}
 			?>
 
-			<div class="options row btw">
+			<div class="options row x-btw">
 				<?php
 				switch ($this->action) {
 					case 'login': ?>
@@ -600,7 +593,7 @@
 
 			</div>
 		</section>
-		<div class="navigation row btw">
+		<div class="navigation row x-btw">
 			<a href="<?= get_home_url() ?>">Home</a>
 			<?php
 			$privacy = get_privacy_policy_url();
@@ -610,6 +603,59 @@
 		</div>
 		<?php
 	}
+	public function renderLoginForm(string $action = 'login', string $redirect = '', string $title = ''):string
+	{
+		ob_start();
+		do_action('jvb_add_token_inputs', $this->action);
+		$additionalInputs = ob_get_clean();
+
+		$fields = '';
+		$theFields = $this->getFieldsForAction($action);
+		foreach ($theFields as $name => $config) {
+			$fields .= Form::render($name, '', $config);
+		}
+
+		ob_start();
+		$this->maybeTurnstile();
+		$turnstile = ob_get_clean();
+
+		ob_start();
+		$this->maybeMagicLink();
+		$magicLink = ob_get_clean();
+
+		$redirect = !empty($redirect) ? $redirect : esc_attr($_GET['redirect_to'] ?? '');
+
+		return sprintf(
+			'<form name="%sform" method="post" data-action="jvb_%s">
+				%s%s%s
+				<input type="hidden" name="action" value="jvb_%s">
+				<input type="hidden" name="redirect_to" value="%s">
+				<input type="hidden" name="request_id" value="%s">
+				%s
+				%s
+				%s
+				%s
+				 <div class="row x-btw nowrap">
+					<button type="submit" class="button button-primary button-large">%s</button>
+					%s
+				</div>
+			</form>',
+			$action,
+			$action,
+			jvbFormStatus(),
+			$title,
+			wp_nonce_field('jvb_'.$action),
+			$action,
+			$redirect,
+			wp_generate_password(16, false),
+			($action === 'magic') ? '<input type="hidden" name="type" value="login">' : '',
+			$additionalInputs,
+			$fields,
+			$turnstile,
+			$this->labels['submit'],
+			$magicLink
+		);
+	}
 	protected function renderHeader():void
     {
     ?>
@@ -628,7 +674,7 @@
             <?php
             $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">
+            echo '<label title="'.$title.'" id="theme-switch" class="switch" for="theme-switcher">
     				<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']).
@@ -717,7 +763,7 @@
 
 	protected function maybeTurnstile(): void
 	{
-		if (!Features::hasIntegration('cloudflare')) {
+		if (!Site::hasIntegration('cloudflare')) {
 			return;
 		}
 		JVB()->connect('cloudflare')->renderTurnstile();
@@ -725,7 +771,7 @@
 
 	protected function maybeTurnstileScripts(): void
 	{
-		if (!Features::hasIntegration('cloudflare')) {
+		if (!Site::hasIntegration('cloudflare')) {
 			return;
 		}
 		JVB()->connect('cloudflare')->enqueueTurnstileScripts();
@@ -733,7 +779,7 @@
 
 	protected function verifyTurnstile(): bool
 	{
-		if (!Features::hasIntegration('cloudflare')) {
+		if (!Site::hasIntegration('cloudflare')) {
 			return true; // Not enabled, pass verification
 		}
 
@@ -786,60 +832,19 @@
 	{
 		switch ($this->action) {
 			case 'register':
-				return [
-					'title' => JVB_LOGIN['register']['title'] ?? 'Create Your Account',
-					'description' => JVB_LOGIN['register']['description'] ?? [],
-					'extra' => JVB_LOGIN['register']['extra'] ?? [],
-					'footer' => JVB_LOGIN['register']['footer'] ?? '',
-					'email' => JVB_LOGIN['register']['email']['subject'] ?? '['.get_bloginfo('name').'] Finish Creating Your Account',
-					'submit' => JVB_LOGIN['register']['submit'] ?? 'Create Account',
-					'successTitle' => JVB_LOGIN['register']['success']['title'] ?? 'Success!',
-					'successDescription' => JVB_LOGIN['register']['success']['description'] ?? ['See your email for next steps','(Check your spam folder if you cannot find it after a couple minutes.)'],
-				];
+				return Site::login()->getLabels('register');
 			case 'lostpassword':
-				return [
-					'title' => JVB_LOGIN['forgot_password']['title'] ?? 'Reset Password',
-					'description' => JVB_LOGIN['forgot_password']['description'] ?? [],
-					'extra' => JVB_LOGIN['forgot_password']['extra'] ?? [],
-					'footer' => JVB_LOGIN['forgot_password']['footer'] ?? '',
-					'submit' => JVB_LOGIN['forgot_password']['submit'] ?? 'Send Reset Link',
-					'successTitle' => JVB_LOGIN['forgot_password']['success']['title'] ?? 'Success!',
-					'successDescription' => JVB_LOGIN['forgot_password']['success']['description'] ?? ['Check your email for reset instructions'],
-				];
+				return Site::login()->getLabels('lostPassword');
 			case 'resetpass':
-				return [
-					'title' => JVB_LOGIN['reset_pass']['title'] ?? 'Reset Your Password',
-					'description' => JVB_LOGIN['reset_pass']['description'] ?? [],
-					'extra' => JVB_LOGIN['reset_pass']['extra'] ?? [],
-					'footer' => JVB_LOGIN['reset_pass']['footer'] ?? '',
-					'submit' => JVB_LOGIN['reset_pass']['submit'] ?? 'Reset Password',
-				];
+			case 'rp':
+				return Site::login()->getLabels('resetPassword');
 			case 'logout':
-				return [
-					'title' => JVB_LOGIN['logout']['title'] ?? 'Logged Out!',
-					'description' => JVB_LOGIN['logout']['description'] ?? [],
-					'extra' => JVB_LOGIN['logout']['extra'] ?? [],
-					'footer' => JVB_LOGIN['logout']['footer'] ?? '',
-					'submit' => JVB_LOGIN['logout']['submit'] ?? '',
-				];
+				return Site::login()->getLabels('logout');
 			case 'magic':
-				return [
-					'title' => JVB_LOGIN['magic']['title'] ?? 'Log in with Magic Link',
-					'description' => JVB_LOGIN['magic']['description'] ?? ['Enter your email.','You\'ll get an email with a magic link.','Click it, and you\'re logged in!'],
-					'extra' => JVB_LOGIN['magic']['extra'] ?? [],
-					'footer' => JVB_LOGIN['magic']['footer'] ?? '',
-					'submit' => JVB_LOGIN['magic']['submit'] ?? jvbIcon('magic-wand').'Send Magic Link',
-
-				];
+				return Site::login()->getLabels('magic');
 			case 'login':
 			default:
-				return [
-					'title' => JVB_LOGIN['login']['title'] ?? 'Sign in',
-					'description' => JVB_LOGIN['login']['description'] ?? [],
-					'extra' => JVB_LOGIN['login']['extra'] ?? [],
-					'footer' => JVB_LOGIN['login']['footer'] ?? '',
-					'submit' => JVB_LOGIN['login']['submit'] ?? 'Sign In',
-				];
+				return Site::login()->getLabels('login');
 		}
 	}
 
@@ -871,9 +876,10 @@
     $action = $this->getAction();
 
     $redirect_to = isset($_GET['redirect_to']) ? esc_url_raw($_GET['redirect_to']) : '';
-    $has_turnstile = Features::hasIntegration('cloudflare');
+    $has_turnstile = Site::hasIntegration('cloudflare');
 
     ob_start();
+
     ?>
 
 	document.addEventListener('DOMContentLoaded', async function () {
@@ -886,9 +892,8 @@
 				if (!form || !window.jvbForm) return;
 
 				window.jvbForm.registerForm(form, {
-					autosave: false,
 					endpoint: '<?= $action ?>',
-					formStatus: false,
+					showStatus: false,
 					cache: false,
 				});
 
@@ -901,6 +906,14 @@
 						const formData = new FormData(formElement);
 						const formObject = Object.fromEntries(formData.entries());
 
+						let params = new URLSearchParams(window.location.search);
+						if (params.has('key')) {
+							formObject['key'] = params.get('key');
+						}
+						if (params.has('login')) {
+							formObject['login'] = params.get('login');
+						}
+
 						// Add redirect_to from URL
 						if (redirectTo) {
 							formObject.redirect_to = redirectTo;
@@ -937,13 +950,13 @@
 									if (result.redirect) {
 										setTimeout(() => {
 											window.location.href = result.redirect;
-										}, 100);
+										}, 20);
 									}
 								});
 							} else if (result.redirect) {
 								setTimeout(() => {
 									window.location.href = result.redirect;
-								}, 100);
+								}, 20);
 							}
 						})
 						.catch(error => {
@@ -1001,6 +1014,11 @@
 	{
 
 	}
+	public function setAction(string $action = 'login'):void
+	{
+		$this->action = $action;
+		$this->setup();
+	}
 }
 
 // Initialize the login manager

--
Gitblit v1.10.0