request)); $url = remove_query_arg(['fcom_action', 'fcom_url_hash'], $currentUrl); wp_redirect($url); exit(); } public function viewAuthPage() { $currentUserId = get_current_user_id(); // check if there has any invitation token $inivtationToken = Arr::get($_GET, 'invitation_token'); $inviation = null; if ($inivtationToken) { $inviation = apply_filters('fluent_community/auth/invitation', null, $inivtationToken); } if ($currentUserId && !$inviation) { wp_redirect(Helper::baseUrl()); exit(); } do_action('fluent_community/auth/before_auth_page_process', $currentUserId, $inviation); $acceptedForms = ['login', 'register', 'reset_password']; $targetForm = Arr::get($_GET, 'form'); if (!in_array($targetForm, $acceptedForms)) { $targetForm = 'login'; } if ($inviation && $targetForm != 'reset_password') { $isUserAvailable = get_user_by('email', $inviation->message); $targetForm = $isUserAvailable ? 'login' : 'register'; } if ($inviation && $currentUserId) { $invitedUser = get_user_by('email', $inviation->message); if ($invitedUser && $invitedUser->ID == $currentUserId) { $targetForm = 'accept_invitation'; } } $isFluentAuth = AuthHelper::isFluentAuthAvailable(); if (!$isFluentAuth && $targetForm == 'reset_password') { wp_redirect(wp_lostpassword_url(Helper::baseUrl())); exit(); } $portalSettings = Helper::generalSettings(); $titleVar = Arr::get($portalSettings, 'site_title'); $frameData = [ 'logo' => Arr::get($portalSettings, 'logo', ''), 'title' => sprintf(__('Join %s', 'fluent-community'), $titleVar), 'description' => __('Login or Signup to join the community', 'fluent-community'), 'loginBtnText' => __('Login', 'fluent-community'), 'signupBtnText' => __('Signup', 'fluent-community'), ]; $currentUrl = home_url(add_query_arg($_GET, $GLOBALS['wp']->request)); $pageVars = [ 'title' => $frameData['title'], 'description' => $frameData['description'], 'url' => $currentUrl, 'featured_image' => '', 'css_files' => [ Vite::getDynamicSrcUrl('theme-default.scss'), Vite::getDynamicSrcUrl('public/scss/user_registration.scss') ], 'js_files' => [ Vite::getDynamicSrcUrl('public/js/user_registration.js') ], 'js_vars' => [ 'fluentComRegistration' => [ 'ajax_url' => admin_url('admin-ajax.php'), 'is_logged_in' => is_user_logged_in(), ] ], 'scope' => 'user_registration', 'layout' => 'signup', 'portal' => [ 'logo' => Arr::get($portalSettings, 'logo', ''), 'title' => __(sprintf('Welcome to %s', Arr::get($portalSettings, 'site_title')), 'fluent-community'), 'description' => get_bloginfo('description') ] ]; if ($isFluentAuth) { $pageVars['js_files'][] = FLUENT_AUTH_PLUGIN_URL . 'dist/public/login_helper.js'; $pageVars['js_vars']['fluentAuthPublic'] = [ 'hide' => false, 'redirect_fallback' => site_url(), 'fls_login_nonce' => wp_create_nonce('fsecurity_login_nonce'), 'ajax_url' => admin_url('admin-ajax.php'), 'i18n' => [ 'Username_or_Email' => __('Email Address', 'fluent-community'), 'Password' => __('Password', 'fluent-community') ] ]; } add_action('fluent_community/headless/content', function ($context) use ($targetForm, $currentUrl, $frameData, $inviation) { if ($targetForm == 'login') { $this->showLoginForm($frameData, $inviation); } else if ($targetForm == 'reset_password') { $frameData['title'] = __('Reset your password', 'fluent-community'); ?>

' . __('Registration is disabled for this community', 'fluent-community') . '

'; return; } $frameData['hiddenFields'] = [ 'register' => 'yes', 'action' => 'fcom_user_signup', '_fls_signup_nonce' => wp_create_nonce('fluent_auth_signup_nonce') ]; $frameData['loginUrl'] = add_query_arg('form', 'login', $currentUrl); $frameData['description'] = __('Create an account to join the community', 'fluent-community'); $this->renderRegistrationForm($frameData, $inviation); } }, 10, 1); status_header(200); App::make('view')->render('headless_page', $pageVars); exit(200); } public function handleUserSignup() { if (is_user_logged_in()) { return $this->handleSignupCompleted(get_current_user_id()); } if (!AuthHelper::isRegistrationEnabled()) { wp_send_json([ 'message' => __('Registration is disabled for this community', 'fluent-community') ], 422); } $app = App::make('app'); $request = $app->make('request'); $fields = AuthHelper::getFormFields(); $requiredFields = array_filter($fields, function ($field) { return $field['required'] ?? false; }); $keys = array_keys($fields); $data = Arr::only($request->all(), $keys); // remove space and special characters from username $data['username'] = sanitize_user(strtolower(preg_replace('/[^A-Za-z0-9_]/', '', $data['username']))); if (empty($data['username'])) { wp_send_json([ 'message' => __('Username is not valid', 'fluent-community'), 'errors' => [ 'username' => __('Please provide a valid username', 'fluent-community') ] ], 422); } if (!ProfileHelper::isUsernameAvailable($data['username'])) { wp_send_json([ 'message' => __('Username is already taken', 'fluent-community'), 'errors' => [ 'username' => __('Username is already taken. Please use a different username', 'fluent-community') ] ], 422); } $data['email'] = sanitize_email($data['email']); $validations = [ 'full_name' => 'required|max:100|string', 'username' => 'required|unique:users,user_login|unique:fcom_xprofile,username|min:4|max:30', 'email' => 'required|email|unique:users,user_email', 'password' => 'required|same:conf_password|max:50|string', 'conf_password' => 'required|same:password' ]; if (!AuthHelper::isPasswordConfRequired()) { unset($validations['conf_password']); $validations['password'] = 'required|max:50|string'; } foreach ($requiredFields as $key => $field) { if (!isset($data[$key])) { $validations[$key] = 'required'; } } $validator = $app->make('validator')->make($data, $validations, [ 'username.required' => __('Username is required', 'fluent-community'), 'username.unique' => __('Username is already taken', 'fluent-community'), 'email.required' => __('Email is required', 'fluent-community'), 'email.email' => __('Email is not valid', 'fluent-community'), 'email.unique' => __('Email is already taken', 'fluent-community'), 'password.required' => __('Password is required', 'fluent-community'), 'password.same' => __('Password and confirmation password do not match', 'fluent-community'), 'conf_password.required' => __('Password confirmation is required', 'fluent-community'), 'conf_password.same' => __('Password and confirmation password do not match', 'fluent-community'), 'terms.required' => __('You must agree to the terms and conditions', 'fluent-community'), 'full_name.required' => __('Full name is required', 'fluent-community'), ]); if ($validator->fails()) { wp_send_json([ 'message' => __('Please fill all the required fields correctly', 'fluent-community'), 'errors' => $validator->errors() ], 422); } foreach ($data as $key => $value) { // let's sanitize the data $callBack = $fields[$key]['sanitize_callback'] ?? null; if ($callBack) { $data[$key] = call_user_func($callBack, $value); } } // let's extract the full_name and set the first_name and last_name if (!empty($data['full_name'])) { $nameParts = explode(' ', $data['full_name']); $data['first_name'] = $nameParts[0]; $data['last_name'] = implode(' ', array_slice($nameParts, 1)); unset($data['full_name']); $data = array_filter($data); } if (AuthHelper::isFluentAuthAvailable()) { $data = wp_parse_args($data, $request->all()); $this->handleSignupViaFlentAuth($data); wp_send_json([ 'message' => __('Something is not working! Please try again', 'fluent-community') ], 422); } $rateLimit = AuthHelper::isAuthRateLimit(); if (is_wp_error($rateLimit)) { wp_send_json([ 'message' => $rateLimit->get_error_message() ], 422); } // We need two-factor authentication here if (AuthHelper::isTwoFactorEnabled()) { // Check if Two Factor code is given $verificationToken = $request->get('__two_fa_signed_token'); if ($verificationToken) { $code = $request->get('_email_verification_code'); if (!$code) { wp_send_json([ 'message' => __('Verification code is required', 'fluent-community') ], 422); } $validated = AuthHelper::validateVerificationCode($code, $verificationToken, $data); if (is_wp_error($validated)) { wp_send_json([ 'message' => $validated->get_error_message() ], 422); } } else { // Let's send the verification code $htmlForm = AuthHelper::get2FaRegistrationCodeForm($data); wp_send_json([ 'verifcation_html' => $htmlForm ]); } } // let's create the user now $userId = AuthHelper::registerNewUser($data['username'], $data['email'], $data['password'], [ 'first_name' => Arr::get($data, 'first_name'), 'last_name' => Arr::get($data, 'last_name'), 'role' => get_option('default_role', 'subscriber') ]); if (is_wp_error($userId)) { wp_send_json([ 'message' => $userId->get_error_message() ], 422); } $this->handleSignupCompleted($userId); } private function handleSignupViaFlentAuth($data) { add_filter('fluent_auth/signup_form_data', function ($requestData) use ($data) { return $data; }); add_action('fluent_auth/after_creating_user', function ($userId) { $this->handleSignupCompleted($userId); }, 1, 1); (new CustomAuthHandler())->handleSignupAjax(); } private function handleSignupCompleted($userId) { // We have the user now let's set the community membership $user = User::find($userId); $user->syncXProfile(true); $redirectUrl = Helper::baseUrl(); $redirectUrl = apply_filters('fluent_community/auth/after_signup_redirect_url', $redirectUrl, $user, $_REQUEST); $btnText = __('Continue to the community', 'fluent-community'); $html = '

' . __('Congratulations!', 'fluent-community') . '

'; $html .= '

' . __('You have successfully registered to the community', 'fluent-community') . '

'; $html .= '' . $btnText . ''; $html .= '
'; if (!get_current_user_id()) { $wpUser = get_user_by('ID', $userId); AuthHelper::makeLogin($wpUser); } wp_send_json([ 'success_html' => $html, 'redirect_url' => $redirectUrl ]); } public function handleUserLogin() { if (is_user_logged_in()) { $user = get_user_by('ID', get_current_user_id()); return $this->handleUserLoginSuccess($user); } if (AuthHelper::isFluentAuthAvailable()) { wp_send_json([ 'message' => __('This form can not be used to login. Please reload the page and try again', 'fluent-community') ], 422); } $app = App::make('app'); $request = $app->make('request'); $data = $request->all(); $validator = $app->make('validator')->make($data, [ 'log' => 'required', 'pwd' => 'required' ], [ 'log.required' => __('Email is required', 'fluent-community'), 'pwd.required' => __('Password is required', 'fluent-community') ]); if ($validator->fails()) { wp_send_json([ 'message' => __('Please fill all the required fields correctly', 'fluent-community'), 'errors' => $validator->errors() ], 422); } $rateLimit = AuthHelper::isAuthRateLimit(); if (is_wp_error($rateLimit)) { wp_send_json([ 'message' => $rateLimit->get_error_message() ], 422); } $user = wp_authenticate($data['log'], $data['pwd']); if (is_wp_error($user)) { wp_send_json([ 'message' => $user->get_error_message() ], 422); } InvitationService::makeLogin($user); $redirectUrl = null; if(!empty($_REQUEST['redirect_to'])) { $redirectUrl = sanitize_url($_REQUEST['redirect_to']); } if(!$redirectUrl) { $redirectUrl = Helper::baseUrl(); } if ($invitationToken = $request->get('invitation_token')) { $maybeRedirectUrl = apply_filters('fluent_community/auth/after_login_with_invitation', null, $user, $invitationToken); if ($maybeRedirectUrl && !is_wp_error($maybeRedirectUrl)) { $redirectUrl = $maybeRedirectUrl; } } $this->handleUserLoginSuccess($user, $redirectUrl); } private function handleUserLoginSuccess($user, $redirectUrl = null) { if (!$redirectUrl) { $redirectUrl = Helper::baseUrl(); } $redirectUrl = apply_filters('fluent_community/auth/after_login_redirect_url', $redirectUrl, $user); $btnText = __('Continue to the community', 'fluent-community'); $html = '

' . __('Welcome back!', 'fluent-community') . '

'; $html .= '

' . __('You have successfully logged in to the community', 'fluent-community') . '

'; $html .= '' . $btnText . ''; $html .= '
'; wp_send_json([ 'success_html' => $html, 'redirect_url' => $redirectUrl ]); } public function showLoginForm($frameData, $invitation = null) { $portalSettings = Helper::generalSettings(); $isFluentAuth = AuthHelper::isFluentAuthAvailable(); $currentUrl = home_url(add_query_arg($_GET, $GLOBALS['wp']->request)); $title = sprintf(__('Login to %s', 'fluent-community'), Arr::get($portalSettings, 'site_title')); $description = ''; if ($invitation) { $invitationBy = $invitation->xprofile ? $invitation->xprofile->display_name : __('Someone', 'fluent-community'); if ($invitation->post_id) { $space = BaseSpace::find($invitation->post_id); if ($space) { $title = $space->title . ' - ' . Arr::get($portalSettings, 'site_title'); } } $description = sprintf(__('%s has invited you to join this community. Please login to accept your invitation.', 'fluent-community'), $invitationBy); } if ($isFluentAuth) { ?>

message_rendered; } $frameData['title'] = $title; $frameData['description'] = $description; $frameData['defaults'] = [ 'email' => $invitation ? $invitation->message : '' ]; if (AuthHelper::isRegistrationEnabled()) { $frameData['signupUrl'] = add_query_arg('form', 'register', $currentUrl); } if(isset($_GET['redirect_to'])) { $frameData['redirect_to'] = sanitize_url($_GET['redirect_to']); } App::make('view')->render('auth.login_form', $frameData); } public function renderRegistrationForm($frameData, $invitation = null) { $formFields = AuthHelper::getFormFields($invitation); $frameData['formFields'] = $formFields; if ($invitation) { $frameData['hiddenFields'] = [ 'invitation_token' => $invitation->message_rendered, 'action' => 'fcom_user_registration', '_fls_signup_nonce' => wp_create_nonce('fluent_auth_signup_nonce') ]; $invitationBy = $invitation->xprofile ? $invitation->xprofile->display_name : __('Someone', 'fluent-community'); $frameData['description'] = sprintf(__('%s has invited you to join this community. Please create an account to accept your invitation.', 'fluent-community'), $invitationBy); $frameData['signupBtnText'] = __('Register & Accept invitation', 'fluent-community'); } else { $frameData['hiddenFields'] = [ 'register' => 'yes', 'action' => 'fcom_user_registration', '_fls_signup_nonce' => wp_create_nonce('fluent_auth_signup_nonce') ]; } App::make('view')->render('auth.user_invitation', $frameData); } }