'.$d.'
'; } $default[$location] .= 'siteFeatures = Features::forSite();
$this->cache = Cache::for('login');
// Initialize magic link support if enabled
if ($this->siteFeatures->has('magicLink')) {
$this->initMagicLinkSupport();
}
// Create login page if it doesn't exist
$this->ensureLoginPageExists();
// Redirect wp-login.php to custom page
add_action('login_init', [$this, 'redirectToCustomLogin']);
add_action('template_include', [$this, 'renderLoginPage']);
add_action('wp_enqueue_scripts', [$this, 'enqueueScripts'], 15);
// 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
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
**************************************************************************/
/**
* Redirect wp-login.php to custom login page
*/
public function redirectToCustomLogin(): void
{
// Handle interim login
if (isset($_GET['interim-login'])) {
// Don't redirect - let WP handle it
return;
}
// Don't redirect if AJAX or REST
if ((defined('DOING_AJAX') && DOING_AJAX) || (defined('REST_REQUEST') && REST_REQUEST)) {
return;
}
// Build custom login URL with all query args
$custom_login_page = home_url('/login/');
$query_args = $_GET;
// Remove WordPress internal args
unset($query_args['interim-login'], $query_args['wp-auth-check']);
if (!empty($query_args)) {
$custom_login_page = add_query_arg($query_args, $custom_login_page);
}
wp_safe_redirect($custom_login_page);
exit;
}
protected function getRegistrationFormFields():array
{
$form = get_option(BASE.'registration_form_fields');
if (!$form) {
$form = [];
$select = [];
//Basic fields, for any
$fields = [
'user_name' => [
'type' => 'text',
'required' => true,
'label' => 'Your Name',
'placeholder'=> 'Mister Meseeks'
],
'user_email' => [
'type' => 'email',
'required' => true,
'label' => 'Your Email',
'placeholder'=> 'look@me.com'
]
];
if (Features::forSite()->has('referrals')) {
$fields['referral_code'] = [
'type' => 'text',
'required'=> false,
'label' => 'Referral Code',
'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'] ?? '';
$icon = ($icon !== '') ? jvbIcon($icon) : '';
$select[$slug] = ''.$icon.$config['label'].''.$config['register']['text']??''.'';
if (!empty($config['register']['fields']??[])){
foreach ($config['register']['fields'] as $field) {
$field['condition'] = [
'field' => 'user_select',
'value' => $slug,
'operator' => '=='
];
$fields[] = $field;
}
}
}
if (!empty($select)) {
$select = array_merge(
[
'subscriber' => 'Subscriber',
],
$select
);
$form = array_merge(
[
'user_select' => [
'type' => 'radio',
'label' => 'Register as',
'options' => $select,
'required' => true,
'default' => 'subscriber'
]
],
$fields
);
}
}else {
$form = $fields;
}
update_option(BASE.'registration_form_fields', $form);
}
return $form;
}
protected function setupFields():void
{
$fields = [];
switch($this->action) {
case 'register':
$fields = $this->getRegistrationFormFields();
break;
case 'lostpassword':
case 'magic':
$fields = [
'user_email' => [
'type' => 'email',
'label' => __('Email Address', 'jvb'),
'required' => true,
'placeholder' => 'look@me.com',
],
];
break;
case 'rp':
case 'resetpass':
$fields = [
'pass1' => [
'type' => 'text',
'subtype' => 'password',
'label' => __('New Password', 'jvb'),
'required' => true,
],
'pass2' => [
'type' => 'text',
'subtype' => 'password',
'label' => __('Confirm Password', 'jvb'),
'required' => true,
],
];
break;
case 'login':
$fields = [
'user_email' => [
'type' => 'email',
'label' => __('Email Address', 'jvb'),
'required' => true,
'autocomplete' => 'email',
'placeholder' => 'look@me.com',
],
'user_password' => [
'type' => 'text',
'subtype'=> 'password',
'label' => __('Password', 'jvb'),
'autocomplete' => 'current-password',
'required' => true,
],
'remember_me' => [
'type' => 'true_false',
'label' => __('Remember Me', 'jvb'),
'default' => true
]
];
break;
case 'postpass':
$fields = [
'post_password' => [
'type' => 'text',
'subtype' => 'password',
'label' => __('Password', 'jvb'),
'required' => true,
'hint' => 'This post is password protected. Please enter the password to view it.',
],
];
break;
case 'confirmaction':
break;
}
$this->fields = $fields;
}
/**
* Ensure login page exists
*/
protected function ensureLoginPageExists(): void
{
$login_page = $this->getLoginPage();
if (!$login_page || !is_int($login_page)) {
$page_id = get_page_by_path('login');
if (!$page_id) {
$page_id = wp_insert_post([
'post_title' => 'Login',
'post_name' => 'login',
'post_content' => '[jvb_login_form]',
'post_status' => 'publish',
'post_type' => 'page',
'post_author' => 1
]);
}
if ($page_id && !is_wp_error($page_id)) {
if (is_object($page_id)) {
$page_id = (int)$page_id->ID;
}
update_option(BASE.'login_page', $page_id);
// Hide from menus/search
update_post_meta($page_id, '_wp_page_template', 'default');
update_post_meta($page_id, BASE . 'exclude_from_search', true);
}
}
}
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
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));
return str_replace('wp_login.php', 'login/', $url);
}
public function getLoginPage():int|false
{
return (int)get_option(BASE.'login_page');
}
public function isLoginPage():bool
{
return is_page($this->getLoginPage());
}
public static function isLogin():bool
{
$self = new self;
return $self->isLoginPage();
}
protected function initMagicLinkSupport(): void
{
if (!Features::forSite()->has('magicLink')) {
return;
}
}
/*********************************************************************
RENDERING
*********************************************************************/
public function renderLoginPage(string $template):string
{
if (!$this->isLoginPage()) {
return $template;
}
$this->setup();
$page = $this->cache->remember(
$this->getAction(),
function() {
return $this->renderPage();
},
5
);
echo $page;
return '';
}
protected function renderPage() {
ob_start();
jvbInlineStyles('nav');
jvbInlineStyles('dash');
jvbInlineStyles('forms');
$this->customStyles();
$this->renderHeader();
$this->renderForms();
$this->renderFooter();
return ob_get_clean();
}
protected function getAction():string
{
if (array_key_exists('action', $_GET)) {
switch ($_GET['action']){
case 'lostpassword':
case 'retrievepassword': // Alias
$action = 'lostpassword';
break;
case 'rp':
case 'resetpass':
$action = 'resetpass';
break;
default:
$action = $_GET['action'];
}
} else {
$action = 'login';
}
return $action;
}
protected function setup():void
{
$this->action = $this->getAction();
if ($this->action == 'logout' || array_key_exists('loggedout', $_GET)) {
wp_logout();
wp_redirect(esc_attr($_GET['redirect_to'] ?? get_home_url()));
exit;
}
$this->setupLabels();
$this->setupFields();
$this->setupTitle();
}
protected function setupTitle():void
{
switch ($this->action) {
case 'lostpassword':
$title = 'Lost Your Password?';
break;
case 'resetpass':
$title = 'Reset Your Password';
break;
case 'register':
$title = 'Create Your Account';
break;
default:
$title = 'Log In To Your Account';
}
$this->title = $title;
}
protected function customStyles():void
{
$logo = get_theme_mod('custom_logo');
if ($logo) {
$small = wp_get_attachment_image_src($logo, 'medium')[0]??'';
$large = wp_get_attachment_image_src($logo, 'large')[0]??'';
}
echo '';
}
protected function renderForms():void
{
$form = $this->action.'form';
?>
'.$extra.'=$this->labels['title']?>
= $this->labels['description'] ?>
labels['extra'])) {
echo '
'.$d.'
'; } $default[$location] .= '