true ) );
Ajax::register( 'register', array( $this, 'ajax_register' ), array( 'public' => true ) );
Ajax::register( 'lost', array( $this, 'ajax_lost' ), array( 'public' => true ) );
Ajax::register( 'reset', array( $this, 'ajax_reset' ), array( 'public' => true ) );
Ajax::register( 'resend_verify', array( $this, 'ajax_resend' ), array( 'public' => true ) );
Ajax::register( 'profile_save', array( $this, 'ajax_profile' ) );
Ajax::register( 'password_change', array( $this, 'ajax_password' ) );
Ajax::register( 'privacy_request', array( $this, 'ajax_privacy' ) );
Ajax::register( 'user_approve', array( $this, 'ajax_approve' ), array( 'cap' => 'frontglut_manage_settings' ) );
// Security.
add_filter( 'authenticate', array( $this, 'guard_lockout' ), 5, 3 );
add_filter( 'authenticate', array( $this, 'guard_status' ), 40, 3 );
add_action( 'wp_login_failed', array( $this, 'on_failed' ) );
add_action( 'wp_login', array( $this, 'on_success' ), 10, 2 );
add_action( 'admin_init', array( $this, 'guard_admin' ) );
add_filter( 'show_admin_bar', array( $this, 'admin_bar' ) );
add_filter( 'login_redirect', array( $this, 'login_redirect' ), 20, 3 );
add_filter( 'logout_redirect', function ( $to, $req, $user ) {
return self::logout_redirect_url( $to );
}, 20, 3 );
add_filter( 'retrieve_password_message', array( $this, 'reset_message' ), 20, 4 );
add_filter( 'lostpassword_url', array( $this, 'lost_url' ), 20 );
add_filter( 'register_url', array( $this, 'register_url' ), 20 );
add_filter( 'login_url', array( $this, 'login_url' ), 20, 3 );
// Avatar.
add_filter( 'pre_get_avatar_data', array( $this, 'avatar' ), 10, 2 );
// Flows via URL.
add_action( 'template_redirect', array( $this, 'handle_urls' ) );
add_action( 'init', array( $this, 'rewrites' ) );
add_filter( 'query_vars', function ( $v ) {
$v[] = 'frontglut_member';
return $v;
} );
add_action( 'delete_user', function ( $id ) {
global $wpdb;
$wpdb->delete( $wpdb->prefix . 'frontglut_follows', array( 'user_id' => $id ) );
} );
// Dashboard integration.
add_filter( 'frontglut_menu_items', function ( $i ) {
$i['profile'] = array( 'label' => __( 'My profile', 'frontglut' ), 'icon' => 'admin-users', 'group' => 'account', 'order' => 90, 'render' => array( $this, 'render_profile_section' ) );
if ( current_user_can( 'manage_options' ) ) {
$i['users'] = array( 'label' => __( 'Users', 'frontglut' ), 'icon' => 'groups', 'group' => 'account', 'order' => 91, 'cap' => 'frontglut_manage_settings', 'render' => array( $this, 'render_users' ) );
}
return $i;
} );
add_action( 'show_user_profile', array( $this, 'admin_user_fields' ) );
add_action( 'edit_user_profile', array( $this, 'admin_user_fields' ) );
add_filter( 'manage_users_columns', function ( $c ) {
$c['frontglut_status'] = __( 'FrontGlut status', 'frontglut' );
return $c;
} );
add_filter( 'manage_users_custom_column', function ( $v, $col, $uid ) {
if ( 'frontglut_status' === $col ) {
return get_user_meta( $uid, 'frontglut_pending_approval', true ) ? __( 'Awaiting approval', 'frontglut' ) : ( get_user_meta( $uid, 'frontglut_verify_token', true ) ? __( 'Unverified email', 'frontglut' ) : '—' );
}
return $v;
}, 10, 3 );
}
/* ---------------- settings ---------------- */
public function defaults( $d ) {
$d['auth'] = array(
'default_role' => 'subscriber',
'approval' => 0,
'verify_email' => 0,
'redirect_login' => '',
'redirect_register' => '',
'redirect_logout' => '',
'redirect_roles' => '',
'restrict_roles' => array( 'subscriber', 'customer' ),
'hide_admin_bar' => 1,
'max_attempts' => 5,
'lockout_min' => 15,
'reg_per_hour' => 5,
'allow_delete' => 1,
'google_id' => '',
'google_secret' => '',
'facebook_id' => '',
'facebook_secret' => '',
'registration_open' => 0, // Off until the administrator enables frontend registration.
);
return $d;
}
public function schema( $s ) {
$roles = Caps::registration_roles(); // Only low-privilege roles can be given to self-registered users.
$s['auth'] = array( 'label' => __( 'Accounts & Login', 'frontglut' ), 'order' => 25, 'fields' => array(
'auth.registration_open' => array( 'type' => 'checkbox', 'label' => __( 'Allow frontend registration', 'frontglut' ) ),
'auth.default_role' => array( 'type' => 'select', 'label' => __( 'Default role for new users', 'frontglut' ), 'options' => $roles, 'help' => __( 'Only roles without publishing, editing or management rights are listed.', 'frontglut' ) ),
'auth.approval' => array( 'type' => 'checkbox', 'label' => __( 'New accounts require admin approval', 'frontglut' ) ),
'auth.verify_email' => array( 'type' => 'checkbox', 'label' => __( 'Require email verification', 'frontglut' ) ),
'auth.reg_per_hour' => array( 'type' => 'number', 'label' => __( 'Registrations per IP per hour', 'frontglut' ), 'min' => 1 ),
'auth.max_attempts' => array( 'type' => 'number', 'label' => __( 'Failed logins before lockout', 'frontglut' ), 'min' => 1 ),
'auth.lockout_min' => array( 'type' => 'number', 'label' => __( 'Lockout minutes', 'frontglut' ), 'min' => 1 ),
'auth.redirect_login' => array( 'type' => 'url', 'label' => __( 'After login redirect (URL)', 'frontglut' ), 'help' => __( 'Empty = dashboard for users with access, otherwise home.', 'frontglut' ) ),
'auth.redirect_register' => array( 'type' => 'url', 'label' => __( 'After registration redirect (URL)', 'frontglut' ) ),
'auth.redirect_logout' => array( 'type' => 'url', 'label' => __( 'After logout redirect (URL)', 'frontglut' ) ),
'auth.redirect_roles' => array( 'type' => 'textarea', 'label' => __( 'Per-role login redirects', 'frontglut' ), 'help' => __( 'One per line: role|https://example.com/page', 'frontglut' ) ),
'auth.restrict_roles' => array( 'type' => 'roles', 'label' => __( 'Block wp-admin for roles', 'frontglut' ), 'help' => __( 'These roles are redirected to the frontend dashboard and have the admin bar hidden.', 'frontglut' ) ),
'auth.hide_admin_bar' => array( 'type' => 'checkbox', 'label' => __( 'Hide admin bar for restricted roles', 'frontglut' ) ),
'auth.allow_delete' => array( 'type' => 'checkbox', 'label' => __( 'Let users request export / deletion of their data', 'frontglut' ) ),
/* translators: %s: value. */
'auth.google_id' => array( 'type' => 'text', 'label' => __( 'Google OAuth client ID', 'frontglut' ), 'help' => sprintf( __( 'Redirect URI: %s', 'frontglut' ), esc_url( add_query_arg( 'frontglut_social_cb', 'google', home_url( '/' ) ) ) ) ),
'auth.google_secret' => array( 'type' => 'text', 'label' => __( 'Google OAuth client secret', 'frontglut' ) ),
/* translators: %s: value. */
'auth.facebook_id' => array( 'type' => 'text', 'label' => __( 'Facebook app ID', 'frontglut' ), 'help' => sprintf( __( 'Redirect URI: %s', 'frontglut' ), esc_url( add_query_arg( 'frontglut_social_cb', 'facebook', home_url( '/' ) ) ) ) ),
'auth.facebook_secret' => array( 'type' => 'text', 'label' => __( 'Facebook app secret', 'frontglut' ) ),
) );
return $s;
}
/* ---------------- URL helpers ---------------- */
public static function page_url( $key, $fallback = '' ) {
$id = (int) Settings::get( 'general.' . $key, 0 );
return $id && 'publish' === get_post_status( $id ) ? get_permalink( $id ) : $fallback;
}
public function lost_url( $url ) {
return self::page_url( 'lost_page', $url );
}
public function register_url( $url ) {
return self::page_url( 'register_page', $url );
}
public function login_url( $url, $redirect = '', $force = false ) {
$p = self::page_url( 'login_page' );
if ( ! $p || is_admin() || false !== strpos( (string) $url, 'action=' ) ) {
return $url;
}
return $redirect ? add_query_arg( 'redirect_to', rawurlencode( $redirect ), $p ) : $p;
}
public static function logout_redirect_url( $default = '' ) {
$u = Settings::get( 'auth.redirect_logout' );
return $u ? $u : ( $default ? $default : home_url( '/' ) );
}
private static function role_redirect( $user, $key ) {
$lines = preg_split( '/\r\n|\r|\n/', (string) Settings::get( 'auth.redirect_roles', '' ) );
foreach ( $lines as $l ) {
$p = array_map( 'trim', explode( '|', $l, 2 ) );
if ( 2 === count( $p ) && in_array( $p[0], (array) $user->roles, true ) && 'redirect_login' === $key ) {
return esc_url_raw( $p[1] );
}
}
$u = Settings::get( 'auth.' . $key );
return $u ? $u : '';
}
public static function after_login_url( $user, $requested = '' ) {
$role = self::role_redirect( $user, 'redirect_login' );
if ( $role ) {
return $role;
}
if ( $requested && wp_validate_redirect( $requested, '' ) ) {
return $requested;
}
return \FrontGlut\Core\Caps::can( 'frontglut_access_dashboard', $user->ID ) ? Dashboard::url() : home_url( '/' );
}
public function login_redirect( $to, $requested, $user ) {
if ( ! $user || is_wp_error( $user ) ) {
return $to;
}
$role = self::role_redirect( $user, 'redirect_login' );
if ( $role ) {
return $role;
}
if ( $this->is_restricted( $user ) ) {
return self::after_login_url( $user, $requested );
}
return $to;
}
/* ---------------- guards ---------------- */
private function is_restricted( $user ) {
if ( ! $user || user_can( $user, 'manage_options' ) ) {
return false;
}
return (bool) array_intersect( (array) $user->roles, (array) Settings::get( 'auth.restrict_roles', array() ) );
}
public function guard_admin() {
if ( wp_doing_ajax() || ( defined( 'DOING_CRON' ) && DOING_CRON ) || ( defined( 'REST_REQUEST' ) && REST_REQUEST ) || ! is_user_logged_in() ) {
return;
}
global $pagenow;
if ( in_array( $pagenow, array( 'admin-post.php', 'async-upload.php', 'admin-ajax.php' ), true ) ) {
return;
}
if ( $this->is_restricted( wp_get_current_user() ) && ! apply_filters( 'frontglut_allow_admin_request', false, $pagenow ) ) {
wp_safe_redirect( Dashboard::url() );
exit;
}
}
public function admin_bar( $show ) {
if ( $show && Settings::get( 'auth.hide_admin_bar' ) && is_user_logged_in() && $this->is_restricted( wp_get_current_user() ) ) {
return false;
}
return $show;
}
private function ident( $login ) {
return strtolower( (string) $login ) . '|' . Audit::ip();
}
public function guard_lockout( $user, $login, $pass ) {
if ( $login && RateLimiter::blocked( 'login', (int) Settings::get( 'auth.max_attempts', 5 ), $this->ident( $login ) ) ) {
/* translators: %s: value. */
return new \WP_Error( 'frontglut_locked', sprintf( __( 'Too many failed attempts. Try again in %d minutes.', 'frontglut' ), (int) Settings::get( 'auth.lockout_min', 15 ) ) );
}
return $user;
}
public function on_failed( $login ) {
RateLimiter::hit( 'login', 9999, max( 1, (int) Settings::get( 'auth.lockout_min', 15 ) ) * MINUTE_IN_SECONDS, $this->ident( $login ) );
}
public function on_success( $login, $user ) {
RateLimiter::reset( 'login', $this->ident( $login ) );
}
public function guard_status( $user, $login, $pass ) {
if ( $user instanceof \WP_User ) {
if ( get_user_meta( $user->ID, 'frontglut_pending_approval', true ) ) {
return new \WP_Error( 'frontglut_pending', __( 'Your account is awaiting administrator approval.', 'frontglut' ) );
}
if ( get_user_meta( $user->ID, 'frontglut_verify_token', true ) ) {
return new \WP_Error( 'frontglut_unverified', __( 'Please verify your email address first. Check your inbox.', 'frontglut' ) );
}
}
return $user;
}
/* ---------------- forms (HTML) ---------------- */
private function social_buttons() {
$h = '';
foreach ( apply_filters( 'frontglut_social_buttons', array( 'google' => 'Google', 'facebook' => 'Facebook' ) ) as $k => $l ) {
if ( Settings::get( 'auth.' . $k . '_id' ) && Settings::get( 'auth.' . $k . '_secret' ) ) {
/* translators: %s: value. */
$h .= '' . esc_html( sprintf( __( 'Continue with %s', 'frontglut' ), $l ) ) . ''; // phpcs:ignore WordPress.Security.NonceVerification
}
}
return $h ? '
' . $h . '
' : '';
}
public static function login_form( $a = array() ) {
return ( new self() )->login_html( $a );
}
public function login_html( $a = array() ) {
$redirect = isset( $a['redirect'] ) ? $a['redirect'] : Request::route( 'redirect_to', 'url', '' );
$h = '';
$err = Request::route( 'frontglut_social_err', 'int', 0 );
$msgs = array(
1 => __( 'Social login failed. Please try again.', 'frontglut' ),
2 => __( 'Your social account did not share a verified email address.', 'frontglut' ),
3 => __( 'For security, this account must log in with its username and password.', 'frontglut' ),
);
if ( isset( $msgs[ $err ] ) ) {
$h .= Ui::notice( esc_html( $msgs[ $err ] ), 'warn' );
} elseif ( Request::route( 'frontglut_pending', 'bool', false ) ) {
$h .= Ui::notice( esc_html__( 'Your account is awaiting administrator approval.', 'frontglut' ), 'warn' );
}
$h .= Ui::form_open( 'login', array( 'after' => 'reload' ) ) . Ui::hidden( 'redirect_to', $redirect ) . Forms::honeypot();
$h .= Fields::render( array( 'key' => 'log', 'type' => 'text', 'label' => __( 'Username or email', 'frontglut' ), 'required' => 1 ), '', 'log' );
$h .= Fields::render( array( 'key' => 'pwd', 'type' => 'password', 'label' => __( 'Password', 'frontglut' ), 'required' => 1 ), '', 'pwd' );
$h .= '';
if ( Captcha::active_for( 'login' ) ) {
$h .= Captcha::render();
}
$h .= $this->social_buttons();
$lost = self::page_url( 'lost_page', wp_lostpassword_url() );
$reg = Settings::get( 'auth.registration_open' ) ? self::page_url( 'register_page' ) : '';
$h .= Ui::form_close( __( 'Log in', 'frontglut' ), '' );
$h .= '';
return $h;
}
public function sc_login( $atts ) {
Assets::need();
if ( is_user_logged_in() ) {
/* translators: %s: value. */
return '' . Ui::notice( sprintf( esc_html__( 'You are logged in as %s.', 'frontglut' ), esc_html( wp_get_current_user()->display_name ) ) ) . '
' . Ui::btn( __( 'Go to dashboard', 'frontglut' ), Dashboard::url(), 'frontglut-primary' ) . ' ' . Ui::btn( __( 'Log out', 'frontglut' ), wp_logout_url( self::logout_redirect_url() ) ) . '
';
}
return '' . esc_html__( 'Log in', 'frontglut' ) . '
' . $this->login_html() . '';
}
public function sc_register( $atts ) {
Assets::need();
if ( is_user_logged_in() ) {
return '' . Ui::notice( esc_html__( 'You are already registered and logged in.', 'frontglut' ) ) . '
';
}
if ( ! Settings::get( 'auth.registration_open' ) ) {
return '' . Ui::notice( esc_html__( 'Registration is currently closed.', 'frontglut' ), 'warn' ) . '
';
}
$form = Forms::default_form( 'register' );
$h = '' . esc_html__( 'Create an account', 'frontglut' ) . '
' . Ui::form_open( 'register', array( 'after' => 'reload' ) ) . Forms::honeypot();
$h .= Forms::render_fields( $form['fields'], array(), 'frontglut_f', 0 );
if ( Captcha::active_for( 'register' ) ) {
$h .= Captcha::render();
}
$h .= $this->social_buttons();
$h .= Ui::form_close( __( 'Register', 'frontglut' ) );
return $h . '
';
}
public function sc_lost( $atts ) {
Assets::need();
$key = isset( $_GET['frontglut_key'] ) ? sanitize_text_field( wp_unslash( $_GET['frontglut_key'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification -- password-reset key, verified by check_password_reset_key() below.
$log = isset( $_GET['frontglut_login'] ) ? sanitize_user( wp_unslash( $_GET['frontglut_login'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification -- password-reset link from email: the key is verified by check_password_reset_key() below.
if ( $key && $log ) {
$u = check_password_reset_key( $key, $log );
if ( is_wp_error( $u ) ) {
return '';
}
$h = '' . esc_html__( 'Choose a new password', 'frontglut' ) . '
' . Ui::form_open( 'reset', array( 'after' => 'none' ) ) . Ui::hidden( 'key', $key ) . Ui::hidden( 'login', $log );
$h .= Fields::render( array( 'key' => 'pass1', 'type' => 'password', 'label' => __( 'New password', 'frontglut' ), 'required' => 1, 'minlength' => 8 ), '', 'pass1' );
$h .= Fields::render( array( 'key' => 'pass2', 'type' => 'password', 'label' => __( 'Confirm password', 'frontglut' ), 'required' => 1 ), '', 'pass2' );
return $h . Ui::form_close( __( 'Save password', 'frontglut' ) ) . '';
}
$h = '' . esc_html__( 'Lost your password?', 'frontglut' ) . '
' . esc_html__( 'Enter your username or email and we will send you a reset link.', 'frontglut' ) . '
' . Ui::form_open( 'lost' ) . Forms::honeypot();
$h .= Fields::render( array( 'key' => 'user_login', 'type' => 'text', 'label' => __( 'Username or email', 'frontglut' ), 'required' => 1 ), '', 'user_login' );
if ( Captcha::active_for( 'lost' ) ) {
$h .= Captcha::render();
}
return $h . Ui::form_close( __( 'Email me a link', 'frontglut' ) ) . '
';
}
/* ---------------- AJAX: login ---------------- */
public function ajax_login( $req ) {
if ( ! Forms::check_honeypot( $req ) ) {
Ajax::fail( __( 'Spam detected.', 'frontglut' ) );
}
if ( Captcha::active_for( 'login' ) ) {
$c = Captcha::verify( $req );
if ( is_wp_error( $c ) ) {
Ajax::fail( $c->get_error_message() );
}
}
$user = wp_signon( array( 'user_login' => isset( $req['log'] ) ? sanitize_text_field( $req['log'] ) : '', 'user_password' => isset( $req['pwd'] ) ? (string) $req['pwd'] : '', 'remember' => ! empty( $req['rememberme'] ) ), is_ssl() );
if ( is_wp_error( $user ) ) {
$code = $user->get_error_code();
$msg = in_array( $code, array( 'invalid_username', 'invalid_email', 'incorrect_password', 'empty_username', 'empty_password' ), true ) ? __( 'Incorrect username or password.', 'frontglut' ) : wp_strip_all_tags( $user->get_error_message() );
Ajax::fail( $msg, 401, array( 'code' => $code ) );
}
$to = self::after_login_url( $user, isset( $req['redirect_to'] ) ? esc_url_raw( $req['redirect_to'] ) : '' );
return array( 'message' => __( 'Welcome back!', 'frontglut' ), 'redirect' => $to );
}
/* ---------------- AJAX: register ---------------- */
public function ajax_register( $req ) {
if ( ! Settings::get( 'auth.registration_open' ) ) {
Ajax::fail( __( 'Registration is closed.', 'frontglut' ), 403 );
}
if ( ! Forms::check_honeypot( $req ) ) {
Ajax::fail( __( 'Spam detected.', 'frontglut' ) );
}
if ( Captcha::active_for( 'register' ) ) {
$c = Captcha::verify( $req );
if ( is_wp_error( $c ) ) {
Ajax::fail( $c->get_error_message() );
}
}
if ( ! RateLimiter::hit( 'register', max( 1, (int) Settings::get( 'auth.reg_per_hour', 5 ) ), HOUR_IN_SECONDS ) ) {
Ajax::fail( __( 'Too many registrations from your network. Try later.', 'frontglut' ), 429 );
}
$form = Forms::default_form( 'register' );
$post = isset( $req['frontglut_f'] ) && is_array( $req['frontglut_f'] ) ? $req['frontglut_f'] : array();
list( $v, $errors ) = Fields::process( $form['fields'], $post );
$login = isset( $v['user_login'] ) ? sanitize_user( $v['user_login'], true ) : '';
$email = isset( $v['user_email'] ) ? $v['user_email'] : '';
$pass = isset( $post['user_pass'] ) && is_string( $post['user_pass'] ) ? $post['user_pass'] : '';
if ( $login && ( username_exists( $login ) || ! validate_username( $login ) ) ) {
$errors['user_login'] = __( 'This username is not available.', 'frontglut' );
}
if ( $email && email_exists( $email ) ) {
$errors['user_email'] = __( 'This email is already registered.', 'frontglut' );
}
if ( isset( $post['user_pass2'] ) && $pass !== (string) $post['user_pass2'] ) {
$errors['user_pass2'] = __( 'Passwords do not match.', 'frontglut' );
}
if ( strlen( $pass ) < 8 ) {
$errors['user_pass'] = __( 'Password must be at least 8 characters.', 'frontglut' );
}
if ( $errors ) {
Ajax::fail( __( 'Please fix the highlighted fields.', 'frontglut' ), 400, array( 'errors' => $errors ) );
}
$role = Caps::registration_role( Settings::get( 'auth.default_role', 'subscriber' ) );
$id = wp_insert_user( array( 'user_login' => $login, 'user_email' => $email, 'user_pass' => $pass, 'role' => $role, 'first_name' => isset( $v['first_name'] ) ? $v['first_name'] : '', 'last_name' => isset( $v['last_name'] ) ? $v['last_name'] : '' ) );
if ( is_wp_error( $id ) ) {
Ajax::fail( $id->get_error_message() );
}
$this->save_extra( $id, $form['fields'], $v, array( 'user_login', 'user_email', 'user_pass', 'user_pass2', 'first_name', 'last_name' ) );
do_action( 'frontglut_user_registered', $id, $v );
$user = get_userdata( $id );
list( $message, $auto ) = self::account_gate( $id, $login, $email );
Audit::log( 'registered', $login, '', $id );
$out = array( 'message' => $message );
if ( $auto ) {
wp_set_current_user( $id );
wp_set_auth_cookie( $id, true );
$out['redirect'] = Settings::get( 'auth.redirect_register' ) ? Settings::get( 'auth.redirect_register' ) : self::after_login_url( $user );
} else {
$out['redirect'] = '';
}
return $out;
}
/**
* Applies the site's email-verification and admin-approval settings to a newly self-registered
* account and sends the matching emails. Shared by every self-registration path.
*
* @return array [ message, bool $may_log_in_now ]
*/
public static function account_gate( $id, $login, $email ) {
$user = get_userdata( $id );
$vars = array( 'name' => $user->display_name, 'username' => $login, 'email' => $email, 'login_url' => self::page_url( 'login_page', wp_login_url() ), 'review_url' => admin_url( 'users.php?s=' . rawurlencode( $login ) ) );
$message = __( 'Account created.', 'frontglut' );
$auto = true;
if ( Settings::get( 'auth.verify_email' ) ) {
$token = wp_generate_password( 32, false );
update_user_meta( $id, 'frontglut_verify_token', wp_hash( $token ) );
$vars['verify_url'] = add_query_arg( array( 'frontglut_verify' => $token, 'uid' => $id ), home_url( '/' ) );
Mailer::send_template( 'verify_email', $email, $vars );
$message = __( 'Almost done! Check your inbox to verify your email.', 'frontglut' );
$auto = false;
}
if ( Settings::get( 'auth.approval' ) ) {
update_user_meta( $id, 'frontglut_pending_approval', 1 );
$message = __( 'Thanks! Your account is awaiting administrator approval.', 'frontglut' );
$auto = false;
}
if ( ! Settings::get( 'auth.verify_email' ) ) {
Mailer::send_template( 'welcome', $email, $vars );
}
Mailer::send_template( 'admin_new_user', get_option( 'admin_email' ), $vars );
return array( $message, $auto );
}
/** Store non-core fields as user meta (respecting storage = meta only). */
private function save_extra( $user_id, array $fields, array $values, array $skip ) {
foreach ( $fields as $f ) {
$k = $f['key'];
if ( in_array( $k, $skip, true ) || ! array_key_exists( $k, $values ) || in_array( $f['type'], array( 'password', 'heading', 'label' ), true ) ) {
continue;
}
if ( preg_match( '/capabilit|user_level|session_tokens|^wp_|^frontglut_(vendor|pending|verify|store)|^_/i', $k ) ) {
continue; // never let a form write privilege / system meta.
}
$val = $values[ $k ];
if ( in_array( $f['type'], array( 'file', 'image', 'gallery' ), true ) ) {
$val = Uploads::filter_owned( (array) $val );
Uploads::claim( $val, 0 );
$val = 'gallery' === $f['type'] || ! empty( $f['multiple'] ) ? $val : ( $val ? $val[0] : '' );
}
update_user_meta( $user_id, sanitize_key( $k ), $val );
}
}
/* ---------------- verify / approve ---------------- */
public function handle_urls() {
// Email-verification link: authorised by the single-use token (hash_equals against the stored hash), not a nonce, because it is opened from an email.
if ( ! empty( $_GET['frontglut_verify'] ) && ! empty( $_GET['uid'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification -- single-use token checked below.
$uid = (int) $_GET['uid']; // phpcs:ignore WordPress.Security.NonceVerification -- single-use token checked below.
$stored = (string) get_user_meta( $uid, 'frontglut_verify_token', true );
$tok = sanitize_text_field( wp_unslash( $_GET['frontglut_verify'] ) ); // phpcs:ignore WordPress.Security.NonceVerification -- this is the token being verified.
$ok = $stored && hash_equals( $stored, wp_hash( $tok ) );
if ( $ok ) {
delete_user_meta( $uid, 'frontglut_verify_token' );
$u = get_userdata( $uid );
Mailer::send_template( 'welcome', $u->user_email, array( 'name' => $u->display_name, 'username' => $u->user_login, 'login_url' => self::page_url( 'login_page', wp_login_url() ) ) );
}
wp_safe_redirect( add_query_arg( 'frontglut_verified', $ok ? 1 : 0, self::page_url( 'login_page', home_url( '/' ) ) ) );
exit;
}
// Social login start/callback: protected by the OAuth "state" value bound to this browser's cookie (see social_callback()).
if ( ! empty( $_GET['frontglut_social'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification -- starts the OAuth flow; nothing is changed.
$this->social_start( sanitize_key( $_GET['frontglut_social'] ) ); // phpcs:ignore WordPress.Security.NonceVerification -- see above.
}
if ( ! empty( $_GET['frontglut_social_cb'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification -- OAuth callback, verified by the state check in social_callback().
$this->social_callback( sanitize_key( $_GET['frontglut_social_cb'] ) ); // phpcs:ignore WordPress.Security.NonceVerification -- see above.
}
$m = get_query_var( 'frontglut_member' );
if ( $m ) {
$this->render_member_page( $m );
}
}
public function ajax_resend( $req ) {
$login = isset( $req['user'] ) ? sanitize_text_field( $req['user'] ) : '';
$u = get_user_by( is_email( $login ) ? 'email' : 'login', $login );
if ( $u && get_user_meta( $u->ID, 'frontglut_verify_token', true ) && RateLimiter::hit( 'resend', 3, HOUR_IN_SECONDS, (string) $u->ID ) ) {
$token = wp_generate_password( 32, false );
update_user_meta( $u->ID, 'frontglut_verify_token', wp_hash( $token ) );
Mailer::send_template( 'verify_email', $u->user_email, array( 'name' => $u->display_name, 'verify_url' => add_query_arg( array( 'frontglut_verify' => $token, 'uid' => $u->ID ), home_url( '/' ) ) ) );
}
return array( 'message' => __( 'If the account needs verification, a new email was sent.', 'frontglut' ) );
}
public function ajax_approve( $req ) {
$uid = isset( $req['id'] ) ? (int) $req['id'] : 0;
$u = get_userdata( $uid );
if ( ! $u ) {
Ajax::fail( __( 'User not found.', 'frontglut' ), 404 );
}
delete_user_meta( $uid, 'frontglut_pending_approval' );
Mailer::send_template( 'account_approved', $u->user_email, array( 'name' => $u->display_name, 'login_url' => self::page_url( 'login_page', wp_login_url() ) ) );
Audit::log( 'user_approved', (string) $uid );
return array( 'message' => __( 'User approved.', 'frontglut' ) );
}
/* ---------------- lost / reset ---------------- */
public function ajax_lost( $req ) {
if ( ! Forms::check_honeypot( $req ) ) {
Ajax::fail( __( 'Spam detected.', 'frontglut' ) );
}
if ( Captcha::active_for( 'lost' ) ) {
$c = Captcha::verify( $req );
if ( is_wp_error( $c ) ) {
Ajax::fail( $c->get_error_message() );
}
}
if ( ! RateLimiter::hit( 'lost', 5, HOUR_IN_SECONDS ) ) {
Ajax::fail( __( 'Too many requests. Try again later.', 'frontglut' ), 429 );
}
$_POST['user_login'] = isset( $req['user_login'] ) ? sanitize_text_field( $req['user_login'] ) : '';
retrieve_password(); // Result intentionally not revealed (prevents user enumeration).
return array( 'message' => __( 'If that account exists, a reset link is on its way.', 'frontglut' ) );
}
public function reset_message( $message, $key, $user_login, $user_data ) {
$page = self::page_url( 'lost_page' );
if ( ! $page ) {
return $message;
}
$url = add_query_arg( array( 'frontglut_key' => $key, 'frontglut_login' => rawurlencode( $user_login ) ), $page );
return sprintf( __( "Someone requested a password reset for %1\$s.\n\nIf this was you, open this link:\n%2\$s\n\nIf not, ignore this email.", 'frontglut' ), $user_login, $url );
}
public function ajax_reset( $req ) {
$u = check_password_reset_key( isset( $req['key'] ) ? sanitize_text_field( $req['key'] ) : '', isset( $req['login'] ) ? sanitize_user( $req['login'] ) : '' );
if ( is_wp_error( $u ) ) {
Ajax::fail( __( 'This reset link is invalid or has expired.', 'frontglut' ) );
}
$p1 = isset( $req['pass1'] ) ? (string) $req['pass1'] : '';
if ( strlen( $p1 ) < 8 ) {
Ajax::fail( __( 'Password must be at least 8 characters.', 'frontglut' ), 400, array( 'errors' => array( 'pass1' => __( 'Too short.', 'frontglut' ) ) ) );
}
if ( $p1 !== ( isset( $req['pass2'] ) ? (string) $req['pass2'] : '' ) ) {
Ajax::fail( __( 'Passwords do not match.', 'frontglut' ), 400, array( 'errors' => array( 'pass2' => __( 'Does not match.', 'frontglut' ) ) ) );
}
reset_password( $u, $p1 );
Audit::log( 'password_reset', $u->user_login, '', $u->ID );
return array( 'message' => __( 'Password updated. You can log in now.', 'frontglut' ), 'redirect' => self::page_url( 'login_page', wp_login_url() ) );
}
/* ---------------- profile ---------------- */
public function render_profile_section() {
return $this->profile_html();
}
public function sc_profile( $atts ) {
Assets::need();
if ( ! is_user_logged_in() ) {
return '' . Ui::notice( esc_html__( 'Please log in to edit your profile.', 'frontglut' ), 'warn' ) . $this->login_html() . '
';
}
return '' . $this->profile_html() . '
';
}
private function user_values( $user, array $fields ) {
$vals = array();
foreach ( $fields as $f ) {
$k = $f['key'];
if ( in_array( $f['storage'], array( 'user_email', 'user_url', 'display_name' ), true ) ) {
$vals[ $k ] = $user->{ $f['storage'] };
} elseif ( in_array( $f['storage'], array( 'first_name', 'last_name', 'description' ), true ) ) {
$vals[ $k ] = get_user_meta( $user->ID, $f['storage'], true );
} else {
$vals[ $k ] = get_user_meta( $user->ID, $k, true );
}
}
return $vals;
}
public function profile_html() {
$user = wp_get_current_user();
$form = Forms::default_form( 'profile' );
$tabs = apply_filters( 'frontglut_profile_tabs', array(
'profile' => array( 'label' => __( 'Profile', 'frontglut' ), 'render' => function () use ( $user, $form ) {
$h = Ui::form_open( 'profile_save', array( 'after' => 'reload' ) );
$h .= Forms::render_fields( $form['fields'], $this->user_values( $user, $form['fields'] ) );
return $h . Ui::form_close( __( 'Save profile', 'frontglut' ) );
} ),
'password' => array( 'label' => __( 'Password', 'frontglut' ), 'render' => function () {
$h = Ui::form_open( 'password_change', array( 'after' => 'reset' ) );
$h .= Fields::render( array( 'key' => 'current', 'type' => 'password', 'label' => __( 'Current password', 'frontglut' ), 'required' => 1, 'width' => '100' ), '', 'current' );
$h .= Fields::render( array( 'key' => 'pass1', 'type' => 'password', 'label' => __( 'New password', 'frontglut' ), 'required' => 1, 'width' => '50' ), '', 'pass1' );
$h .= Fields::render( array( 'key' => 'pass2', 'type' => 'password', 'label' => __( 'Confirm new password', 'frontglut' ), 'required' => 1, 'width' => '50' ), '', 'pass2' );
return $h . Ui::form_close( __( 'Change password', 'frontglut' ) );
} ),
'privacy' => array( 'label' => __( 'Privacy', 'frontglut' ), 'render' => function () {
if ( ! Settings::get( 'auth.allow_delete' ) ) {
return '' . esc_html__( 'Data requests are disabled.', 'frontglut' ) . '
';
}
return '' . esc_html__( 'Request a copy of your personal data, or ask us to erase it. You will receive a confirmation email first.', 'frontglut' ) . '
' .
Ui::action_btn( __( 'Request data export', 'frontglut' ), 'privacy_request', array( 'type' => 'export_personal_data' ), '', false, 'none' ) . ' ' .
Ui::action_btn( __( 'Request account deletion', 'frontglut' ), 'privacy_request', array( 'type' => 'remove_personal_data' ), 'frontglut-danger', true, 'none' );
} ),
) );
$h = '';
$first = true;
foreach ( $tabs as $id => $t ) {
$h .= '';
$first = false;
}
$h .= '
';
$first = true;
foreach ( $tabs as $id => $t ) {
$h .= '
' . Ui::kses( call_user_func( $t['render'] ) ) . '
';
$first = false;
}
return Ui::header( __( 'My profile', 'frontglut' ) ) . '
' . $h . '
';
}
public function ajax_profile( $req ) {
$user = wp_get_current_user();
$form = Forms::default_form( 'profile' );
$post = isset( $req['frontglut_f'] ) && is_array( $req['frontglut_f'] ) ? $req['frontglut_f'] : array();
$fields = array_filter( $form['fields'], function ( $f ) use ( $user ) {
return empty( $f['roles'] ) || array_intersect( (array) $f['roles'], (array) $user->roles );
} );
list( $v, $errors ) = Fields::process( array_values( $fields ), $post );
foreach ( $fields as $f ) {
if ( 'user_email' === $f['storage'] && ! empty( $v[ $f['key'] ] ) ) {
$other = email_exists( $v[ $f['key'] ] );
if ( $other && (int) $other !== $user->ID ) {
$errors[ $f['key'] ] = __( 'This email is used by another account.', 'frontglut' );
}
}
}
if ( $errors ) {
Ajax::fail( __( 'Please fix the highlighted fields.', 'frontglut' ), 400, array( 'errors' => $errors ) );
}
$upd = array( 'ID' => $user->ID );
foreach ( $fields as $f ) {
$k = $f['key'];
if ( ! array_key_exists( $k, $v ) ) {
continue;
}
if ( in_array( $f['storage'], array( 'user_email', 'user_url', 'display_name' ), true ) ) {
$upd[ $f['storage'] ] = $v[ $k ];
} elseif ( in_array( $f['storage'], array( 'first_name', 'last_name', 'description' ), true ) ) {
update_user_meta( $user->ID, $f['storage'], $v[ $k ] );
} else {
$this->save_extra( $user->ID, array( $f ), $v, array() );
}
}
$r = wp_update_user( $upd );
if ( is_wp_error( $r ) ) {
Ajax::fail( $r->get_error_message() );
}
Audit::log( 'profile_updated', (string) $user->ID );
return array( 'message' => __( 'Profile updated.', 'frontglut' ) );
}
public function ajax_password( $req ) {
$user = wp_get_current_user();
if ( ! wp_check_password( isset( $req['current'] ) ? (string) $req['current'] : '', $user->user_pass, $user->ID ) ) {
Ajax::fail( __( 'Current password is wrong.', 'frontglut' ), 400, array( 'errors' => array( 'current' => __( 'Wrong password.', 'frontglut' ) ) ) );
}
$p1 = isset( $req['pass1'] ) ? (string) $req['pass1'] : '';
if ( strlen( $p1 ) < 8 || $p1 !== ( isset( $req['pass2'] ) ? (string) $req['pass2'] : '' ) ) {
Ajax::fail( __( 'New passwords must match and be at least 8 characters.', 'frontglut' ) );
}
wp_set_password( $p1, $user->ID );
wp_set_auth_cookie( $user->ID, true );
Audit::log( 'password_changed', (string) $user->ID );
return array( 'message' => __( 'Password changed.', 'frontglut' ) );
}
public function ajax_privacy( $req ) {
if ( ! Settings::get( 'auth.allow_delete' ) ) {
Ajax::fail( __( 'Disabled.', 'frontglut' ), 403 );
}
$type = isset( $req['type'] ) && 'remove_personal_data' === $req['type'] ? 'remove_personal_data' : 'export_personal_data';
if ( 'remove_personal_data' === $type && user_can( get_current_user_id(), 'manage_options' ) ) {
Ajax::fail( __( 'Administrators cannot request deletion here.', 'frontglut' ) );
}
$id = wp_create_user_request( wp_get_current_user()->user_email, $type );
if ( is_wp_error( $id ) ) {
Ajax::fail( $id->get_error_message() );
}
wp_send_user_request( $id );
return array( 'message' => __( 'We emailed you a confirmation link.', 'frontglut' ) );
}
public function avatar( $args, $id_or_email ) {
$uid = 0;
if ( is_numeric( $id_or_email ) ) {
$uid = (int) $id_or_email;
} elseif ( $id_or_email instanceof \WP_User ) {
$uid = $id_or_email->ID;
} elseif ( $id_or_email instanceof \WP_Post ) {
$uid = (int) $id_or_email->post_author;
} elseif ( is_string( $id_or_email ) && is_email( $id_or_email ) ) {
$u = get_user_by( 'email', $id_or_email );
$uid = $u ? $u->ID : 0;
}
if ( $uid ) {
$aid = (int) get_user_meta( $uid, 'frontglut_avatar', true );
if ( $aid ) {
$url = wp_get_attachment_image_url( $aid, array( (int) ( $args['size'] ?? 96 ), (int) ( $args['size'] ?? 96 ) ) );
if ( $url ) {
$args['url'] = $url;
$args['found_avatar'] = true;
}
}
}
return $args;
}
/* ---------------- admin users list (frontend) ---------------- */
public function render_users() {
$q = Request::filter( 's', 'text', '' );
$pg = Request::page();
$args = array( 'number' => 20, 'paged' => $pg, 'orderby' => 'registered', 'order' => 'DESC' );
if ( $q ) {
$args['search'] = '*' . $q . '*';
}
$uq = new \WP_User_Query( $args );
$rows = array();
foreach ( $uq->get_results() as $u ) {
$pending = get_user_meta( $u->ID, 'frontglut_pending_approval', true );
$rows[] = array(
'name' => '' . esc_html( $u->display_name ) . '
' . esc_html( $u->user_email ) . '',
'role' => esc_html( implode( ', ', array_map( 'translate_user_role', array_map( function ( $r ) { return wp_roles()->roles[ $r ]['name'] ?? $r; }, $u->roles ) ) ) ),
'status' => $pending ? Ui::badge( __( 'Awaiting approval', 'frontglut' ), 'warn' ) : ( get_user_meta( $u->ID, 'frontglut_verify_token', true ) ? Ui::badge( __( 'Unverified', 'frontglut' ), 'warn' ) : Ui::badge( __( 'Active', 'frontglut' ), 'ok' ) ),
'since' => Ui::date( $u->user_registered ),
'act' => $pending ? Ui::action_btn( __( 'Approve', 'frontglut' ), 'user_approve', array( 'id' => $u->ID ), 'frontglut-primary' ) : '',
);
}
return Ui::header( __( 'Users', 'frontglut' ) ) . Ui::filters( array(), array( 's' => $q ) ) . Ui::table( array( 'name' => __( 'User', 'frontglut' ), 'role' => __( 'Role', 'frontglut' ), 'status' => __( 'Status', 'frontglut' ), 'since' => __( 'Registered', 'frontglut' ), 'act' => '' ), $rows ) . Ui::pager( $uq->get_total(), 20, $pg, array( 's' => $q ) );
}
public function admin_user_fields( $user ) {
if ( ! current_user_can( 'edit_users' ) ) {
return;
}
$p = get_user_meta( $user->ID, 'frontglut_pending_approval', true );
echo 'FrontGlut
';
}
/* ---------------- public member page ---------------- */
public function rewrites() {
add_rewrite_rule( '^member/([^/]+)/?$', 'index.php?frontglut_member=$matches[1]', 'top' );
}
public function sc_member( $atts ) {
$a = shortcode_atts( array( 'id' => 0 ), $atts );
$u = $a['id'] ? get_userdata( (int) $a['id'] ) : null;
if ( ! $u ) {
return '';
}
Assets::need();
return $this->member_html( $u );
}
private function member_html( $u ) {
$posts = get_posts( array( 'author' => $u->ID, 'posts_per_page' => 10, 'post_status' => 'publish' ) );
$h = '' . get_avatar( $u->ID, 80 ) . '
' . esc_html( $u->display_name ) . '
' . esc_html( get_user_meta( $u->ID, 'description', true ) ) . '
';
if ( $posts ) {
$h .= '
' . esc_html__( 'Recent posts', 'frontglut' ) . '
';
}
// Filtered markup is sanitised before it is returned to the shortcode / page.
return Ui::kses( apply_filters( 'frontglut_member_html', $h . '
', $u ) );
}
private function render_member_page( $login ) {
$u = get_user_by( 'login', sanitize_user( $login ) );
if ( ! $u ) {
global $wp_query;
$wp_query->set_404();
status_header( 404 );
return;
}
Assets::need();
get_header();
echo '' . wp_kses( $this->member_html( $u ), Ui::allowed_html() ) . '';
get_footer();
exit;
}
/* ---------------- social login ---------------- */
private function provider( $p ) {
$cfg = array(
'google' => array( 'auth' => 'https://accounts.google.com/o/oauth2/v2/auth', 'token' => 'https://oauth2.googleapis.com/token', 'info' => 'https://openidconnect.googleapis.com/v1/userinfo', 'scope' => 'openid email profile' ),
'facebook' => array( 'auth' => 'https://www.facebook.com/v19.0/dialog/oauth', 'token' => 'https://graph.facebook.com/v19.0/oauth/access_token', 'info' => 'https://graph.facebook.com/me?fields=id,name,email', 'scope' => 'email,public_profile' ),
);
$cfg = apply_filters( 'frontglut_oauth_providers', $cfg );
if ( ! isset( $cfg[ $p ] ) || ! Settings::get( 'auth.' . $p . '_id' ) || ! Settings::get( 'auth.' . $p . '_secret' ) ) {
return null;
}
return $cfg[ $p ];
}
private function social_start( $p ) {
$c = $this->provider( $p );
if ( ! $c || is_user_logged_in() ) {
return;
}
$state = wp_generate_password( 20, false );
// Bind the OAuth state to this browser so a callback link cannot be replayed in someone else's browser (login CSRF).
setcookie( 'frontglut_oauth', wp_hash( $state ), time() + 600, COOKIEPATH ? COOKIEPATH : '/', COOKIE_DOMAIN, is_ssl(), true );
set_transient( 'frontglut_oauth_' . $state, isset( $_GET['to'] ) ? esc_url_raw( wp_unslash( $_GET['to'] ) ) : '', 600 ); // phpcs:ignore WordPress.Security.NonceVerification -- where to go after login; later passed through wp_validate_redirect().
$url = add_query_arg( array( 'client_id' => Settings::get( 'auth.' . $p . '_id' ), 'redirect_uri' => add_query_arg( 'frontglut_social_cb', $p, home_url( '/' ) ), 'response_type' => 'code', 'scope' => $c['scope'], 'state' => $state ), $c['auth'] );
wp_redirect( $url ); // phpcs:ignore WordPress.Security.SafeRedirect -- external OAuth provider.
exit;
}
private function social_callback( $p ) {
$c = $this->provider( $p );
$state = isset( $_GET['state'] ) ? sanitize_text_field( wp_unslash( $_GET['state'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification -- the OAuth state is the CSRF token; verified just below.
$to = get_transient( 'frontglut_oauth_' . $state );
$bound = isset( $_COOKIE['frontglut_oauth'] ) ? sanitize_text_field( wp_unslash( $_COOKIE['frontglut_oauth'] ) ) : '';
if ( ! $c || false === $to || '' === $state || '' === $bound || ! hash_equals( wp_hash( $state ), $bound ) || empty( $_GET['code'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification -- authorised by the OAuth state bound to this browser.
wp_safe_redirect( add_query_arg( 'frontglut_social_err', 1, self::page_url( 'login_page', home_url( '/' ) ) ) );
exit;
}
delete_transient( 'frontglut_oauth_' . $state );
setcookie( 'frontglut_oauth', '', time() - 3600, COOKIEPATH ? COOKIEPATH : '/', COOKIE_DOMAIN, is_ssl(), true );
$tok = wp_remote_post( $c['token'], array( 'timeout' => 15, 'headers' => array( 'Accept' => 'application/json' ), 'body' => array( 'code' => sanitize_text_field( wp_unslash( $_GET['code'] ) ), 'client_id' => Settings::get( 'auth.' . $p . '_id' ), 'client_secret' => Settings::get( 'auth.' . $p . '_secret' ), 'redirect_uri' => add_query_arg( 'frontglut_social_cb', $p, home_url( '/' ) ), 'grant_type' => 'authorization_code' ) ) ); // phpcs:ignore WordPress.Security.NonceVerification
$tok = is_wp_error( $tok ) ? array() : json_decode( wp_remote_retrieve_body( $tok ), true );
if ( empty( $tok['access_token'] ) ) {
wp_safe_redirect( add_query_arg( 'frontglut_social_err', 1, self::page_url( 'login_page', home_url( '/' ) ) ) );
exit;
}
$info = wp_remote_get( $c['info'], array( 'timeout' => 15, 'headers' => array( 'Authorization' => 'Bearer ' . $tok['access_token'] ) ) );
$info = is_wp_error( $info ) ? array() : json_decode( wp_remote_retrieve_body( $info ), true );
$info = apply_filters( 'frontglut_oauth_userinfo', is_array( $info ) ? $info : array(), $p, $tok['access_token'] );
$mail = isset( $info['email'] ) ? sanitize_email( $info['email'] ) : '';
// Google says whether it verified the address; Facebook only returns confirmed addresses.
if ( 'google' === $p && empty( $info['email_verified'] ) ) {
$mail = '';
}
if ( ! $mail ) {
wp_safe_redirect( add_query_arg( 'frontglut_social_err', 2, self::page_url( 'login_page', home_url( '/' ) ) ) );
exit;
}
$user = get_user_by( 'email', $mail );
if ( ! $user ) {
if ( ! Settings::get( 'auth.registration_open' ) ) {
wp_die( esc_html__( 'Registration is closed.', 'frontglut' ) );
}
$login = sanitize_user( current( explode( '@', $mail ) ), true );
while ( username_exists( $login ) ) {
$login .= wp_rand( 1, 99 );
}
$role = Caps::registration_role( Settings::get( 'auth.default_role', 'subscriber' ) );
$id = wp_insert_user( array( 'user_login' => $login, 'user_email' => $mail, 'user_pass' => wp_generate_password( 24 ), 'display_name' => isset( $info['name'] ) ? sanitize_text_field( $info['name'] ) : $login, 'role' => $role ) );
if ( is_wp_error( $id ) ) {
wp_die( esc_html( $id->get_error_message() ) );
}
$user = get_userdata( $id );
do_action( 'frontglut_user_registered', $id, array() );
Mailer::send_template( 'admin_new_user', get_option( 'admin_email' ), array( 'username' => $login, 'email' => $mail, 'review_url' => admin_url( 'users.php' ) ) );
if ( Settings::get( 'auth.approval' ) ) {
update_user_meta( $id, 'frontglut_pending_approval', 1 );
wp_safe_redirect( add_query_arg( 'frontglut_pending', 1, self::page_url( 'login_page', home_url( '/' ) ) ) );
exit;
}
}
if ( get_user_meta( $user->ID, 'frontglut_pending_approval', true ) ) {
wp_safe_redirect( add_query_arg( 'frontglut_pending', 1, self::page_url( 'login_page', home_url( '/' ) ) ) );
exit;
}
// Privileged accounts must use the normal WordPress login, so login-security and 2FA plugins always apply to them.
foreach ( apply_filters( 'frontglut_social_login_blocked_caps', array( 'manage_options', 'edit_users', 'promote_users', 'edit_others_posts', 'manage_woocommerce' ) ) as $cap ) {
if ( user_can( $user, $cap ) ) {
wp_safe_redirect( add_query_arg( 'frontglut_social_err', 3, self::page_url( 'login_page', home_url( '/' ) ) ) );
exit;
}
}
wp_set_current_user( $user->ID );
wp_set_auth_cookie( $user->ID, true );
do_action( 'wp_login', $user->user_login, $user ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- core hook, fired so security/audit plugins see social logins.
wp_safe_redirect( self::after_login_url( $user, (string) $to ) );
exit;
}
}