Jake Vanderwerf
2026-07-12 c204185ae86a98994f80010abf35a190c9406739
inc/managers/LoginManager.php
@@ -23,6 +23,7 @@
   protected array $fields = [];
   protected ?string $action = null;
   protected string $title = '';
   protected static LoginManager $instance;
   // Token handlers registry
   protected array $messageHandlers = [];
@@ -35,12 +36,14 @@
   ];
   private int $max_file_size = 5242880; // 5MB in bytes
   public function __construct()
   {
      self::$instance = $this;
      $this->cache = Cache::for('login');
      $this->cache->flush();
      // Initialize magic link support if enabled
      if (Site::has('magicLink')) {
      if (Site::has('magic_link')) {
         $this->initMagicLinkSupport();
      }
@@ -62,13 +65,20 @@
      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);
      add_action('user_register', [$this, 'saveRegistrationFields'], 50, 2);
      add_filter('the_seo_framework_sitemap_exclude_ids', [$this, 'excludeLoginSitemap'], 8, 1);
   }
   public static function getInstance():self
   {
      return self::$instance;
   }
   public function excludeLoginSitemap(array $ids): array
   {
      $ids[] = $this->getLoginPage();
      $loginPage = $this->getLoginPage();
      if (!empty($loginPage)) {
         $ids = array_merge($ids, [$loginPage]);
      }
      return $ids;
   }
   /**************************************************************************
@@ -133,7 +143,7 @@
               'hint'   => 'Have a referral code? Paste it here!'
            ];
         }
         $canRegister = Registrar::getFeatured('can_register', 'user');
         $canRegister = Registrar::withFeature('can_register', 'user');
         if (!empty($canRegister)) {
            foreach ($canRegister as $role) {
               $registrar = Registrar::getInstance($role);
@@ -183,8 +193,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;
@@ -207,12 +222,14 @@
                  'subtype' => 'password',
                  'label' => __('New Password', 'jvb'),
                  'required' => true,
                  'autocomplete'=> 'new-password'
               ],
               'pass2' => [
                  'type' => 'text',
                  'subtype' => 'password',
                  'label' => __('Confirm Password', 'jvb'),
                  'required' => true,
                  'autocomplete'=> 'new-password'
               ],
            ];
            break;
@@ -255,9 +272,10 @@
            break;
      }
      $this->fields = $fields;
      return $fields;
   }
   /**
    * Ensure login page exists
    */
@@ -341,7 +359,7 @@
   protected function initMagicLinkSupport(): void
   {
      if (!Site::has('magicLink')) {
      if (!Site::has('magic_link')) {
         return;
      }
   }
@@ -354,6 +372,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(),
@@ -435,6 +458,7 @@
   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]??'';
@@ -470,8 +494,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;
         }
@@ -501,7 +525,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;
@@ -530,32 +555,12 @@
   {
      $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) {
               echo Form::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'])) {
@@ -569,7 +574,7 @@
         }
         ?>
         <div class="options row btw">
         <div class="options row x-btw">
            <?php
            switch ($this->action) {
               case 'login': ?>
@@ -594,7 +599,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();
@@ -604,6 +609,68 @@
      </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" data-save="auth/login">
            %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']??$this->determineSubmit($action),
         $magicLink
      );
   }
      protected function determineSubmit(string $action):string
      {
         return match($action) {
            'rp','resetpass' => 'Reset Password',
            'register' => 'Register',
            'lostpassword' => 'Send me a reset link',
            default => 'Login',
         };
      }
   protected function renderHeader():void
    {
    ?>
@@ -622,7 +689,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']).
@@ -821,6 +888,7 @@
    $this->maybeTurnstileScripts();
    wp_enqueue_script('jvb-form');
   wp_enqueue_script('jvb-login');
    $action = $this->getAction();
    $redirect_to = isset($_GET['redirect_to']) ? esc_url_raw($_GET['redirect_to']) : '';
@@ -898,13 +966,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 => {
@@ -962,6 +1030,11 @@
   {
   }
   public function setAction(string $action = 'login'):void
   {
      $this->action = $action;
      $this->setup();
   }
}
// Initialize the login manager