| @@ -4,14 +4,19 @@ | ||
| 4 | 4 | namespace FluentCommunity\Modules\Auth; |
| 5 | 5 | |
| 6 | 6 | use FluentAuth\App\Hooks\Handlers\CustomAuthHandler; |
| 7 | 7 | use FluentCommunity\App\App; |
| 8 | +use FluentCommunity\App\Functions\Utility; | |
| 9 | +use FluentCommunity\App\Services\AuthenticationService; | |
| 8 | 10 | use FluentCommunity\App\Models\BaseSpace; |
| 9 | 11 | use FluentCommunity\App\Models\User; |
| 12 | +use FluentCommunity\App\Services\FeedsHelper; | |
| 10 | 13 | use FluentCommunity\App\Services\Helper; |
| 11 | 14 | use FluentCommunity\App\Services\ProfileHelper; |
| 12 | 15 | use FluentCommunity\App\Vite; |
| 13 | 16 | use FluentCommunity\Framework\Support\Arr; |
| 17 | +use FluentCommunity\Modules\Auth\Classes\Invitation; | |
| 18 | +use FluentCommunity\Modules\Auth\Classes\InvitationHandler; | |
| 14 | 19 | use FluentCommunity\Modules\Auth\Classes\InvitationService; |
| 15 | 20 | |
| 16 | 21 | class AuthModdule |
| 17 | 22 | { |
| @@ -22,72 +27,195 @@ | ||
| 22 | 27 | add_action('wp_ajax_nopriv_fcom_user_registration', [$this, 'handleUserSignup']); |
| 23 | 28 | add_action('wp_ajax_fcom_user_registration', [$this, 'handleUserSignup']); |
| 24 | 29 | add_action('wp_ajax_nopriv_fcom_user_login_form', [$this, 'handleUserLogin']); |
| 25 | 30 | add_action('wp_ajax_fcom_user_login_form', [$this, 'handleUserLogin']); |
| 31 | + | |
| 32 | + /* | |
| 33 | + * Declared here rather than where the auth screen renders, because the form that | |
| 34 | + * screen draws posts back to admin-ajax and that is a different request: nothing | |
| 35 | + * survives into it but what the browser sent. FluentAuth answers those posts only | |
| 36 | + * for a host it already knows about. | |
| 37 | + * | |
| 38 | + * `is_fcom_auth` is the field the login form has always carried; FluentAuth's own | |
| 39 | + * signed marker travels on the rest. | |
| 40 | + */ | |
| 41 | + AuthHelper::registerWithFluentAuth(); | |
| 42 | + | |
| 43 | + add_filter('fluent_auth/login_redirect_url', function ($redirectUrl, $user) { | |
| 44 | + if (empty($_REQUEST['is_fcom_auth']) || empty($_REQUEST['fcom_redirect'])) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended | |
| 45 | + return $redirectUrl; | |
| 46 | + } | |
| 47 | + | |
| 48 | + // validate the url | |
| 49 | + $redirectUrl = wp_validate_redirect(sanitize_url(wp_unslash($_REQUEST['fcom_redirect'])), Helper::baseUrl()); // phpcs:ignore WordPress.Security.NonceVerification.Recommended | |
| 50 | + | |
| 51 | + $redirectUrl = apply_filters('fluent_community/auth/after_login_redirect_url', $redirectUrl, $user); | |
| 52 | + return $redirectUrl; | |
| 53 | + }, 10, 2); | |
| 26 | 54 | } |
| 27 | 55 | |
| 28 | 56 | public function maybeAutoLogin($requestData) |
| 29 | 57 | { |
| 30 | 58 | $urlHash = Arr::get($requestData, 'fcom_url_hash'); |
| 31 | - if ($urlHash) { | |
| 59 | + if ($urlHash && !get_current_user_id()) { | |
| 32 | 60 | $tagetUser = ProfileHelper::getUserByUrlHash($urlHash); |
| 33 | - | |
| 34 | 61 | if ($tagetUser) { |
| 35 | 62 | $willAtoLogin = apply_filters('fluent_community/allow_auto_login_by_url', !user_can($tagetUser, 'delete_pages'), $tagetUser); |
| 36 | - // $willAtoLogin = true; | |
| 37 | 63 | if ($willAtoLogin) { |
| 38 | - InvitationService::makeLogin($tagetUser); | |
| 64 | + try { | |
| 65 | + InvitationService::makeLogin($tagetUser); | |
| 66 | + } catch (\Throwable $e) { | |
| 67 | + if (defined('WP_DEBUG') && WP_DEBUG) { | |
| 68 | + error_log('FluentCommunity: Auto-login failed for user #' . $tagetUser->ID . ': ' . $e->getMessage()); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log | |
| 69 | + } | |
| 70 | + } | |
| 39 | 71 | } |
| 40 | 72 | } |
| 41 | 73 | } |
| 42 | 74 | |
| 43 | 75 | // Remove fcom_action and fcom_url_hash from the current url |
| 44 | - $currentUrl = home_url(add_query_arg($_GET, $GLOBALS['wp']->request)); | |
| 76 | + $currentUrl = home_url(add_query_arg($_GET, $GLOBALS['wp']->request)); // phpcs:ignore WordPress.Security.NonceVerification.Recommended | |
| 45 | 77 | $url = remove_query_arg(['fcom_action', 'fcom_url_hash'], $currentUrl); |
| 46 | - wp_redirect($url); | |
| 78 | + $this->redirectAndExit($url); | |
| 79 | + } | |
| 80 | + | |
| 81 | + /** | |
| 82 | + * Send a redirect and stop. | |
| 83 | + * | |
| 84 | + * Extracted only so it can be observed: a bare `exit()` terminates the PHP | |
| 85 | + * process, which in a test run kills the whole suite with no result (see | |
| 86 | + * FIX-PLAN item 22 for the same problem on PortalHandler). A test subclass | |
| 87 | + * overrides this and the two methods below to record what was about to | |
| 88 | + * happen and throw instead. Behaviour in production is unchanged — this is | |
| 89 | + * the original call, moved. | |
| 90 | + * | |
| 91 | + * This one keeps the UNSAFE variant its single caller already used. That | |
| 92 | + * caller builds its target with home_url(), so it is same-host by | |
| 93 | + * construction rather than by validation. Kept as a separate method from | |
| 94 | + * safeRedirectAndExit(), rather than a $safe flag, so the distinction stays | |
| 95 | + * visible to anyone grepping for wp_redirect. | |
| 96 | + */ | |
| 97 | + protected function redirectAndExit($url, $status = 302) | |
| 98 | + { | |
| 99 | + wp_redirect($url, $status); // phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect | |
| 47 | 100 | exit(); |
| 48 | 101 | } |
| 49 | 102 | |
| 103 | + /** | |
| 104 | + * Send a host-confined redirect and stop. See redirectAndExit(). | |
| 105 | + */ | |
| 106 | + protected function safeRedirectAndExit($url) | |
| 107 | + { | |
| 108 | + wp_safe_redirect($url); | |
| 109 | + exit(); | |
| 110 | + } | |
| 111 | + | |
| 112 | + /** | |
| 113 | + * Render the headless page and stop. See redirectAndExit(). | |
| 114 | + */ | |
| 115 | + protected function renderPageAndExit($template, $pageVars) | |
| 116 | + { | |
| 117 | + status_header(200); | |
| 118 | + App::make('view')->render($template, $pageVars); | |
| 119 | + exit(200); | |
| 120 | + } | |
| 121 | + | |
| 50 | 122 | public function viewAuthPage() |
| 51 | 123 | { |
| 124 | + | |
| 125 | + add_filter('login_form_defaults', function ($defaults) { | |
| 126 | + $defaults['label_username'] = __('Email Address', 'fluent-community'); | |
| 127 | + return $defaults; | |
| 128 | + }); | |
| 129 | + | |
| 130 | + add_filter('fluent_community/has_color_scheme', '__return_false'); | |
| 131 | + | |
| 52 | 132 | $currentUserId = get_current_user_id(); |
| 53 | 133 | // check if there has any invitation token |
| 54 | - $inivtationToken = Arr::get($_GET, 'invitation_token'); | |
| 134 | + $inivtationToken = Arr::get($_GET, 'invitation_token'); // phpcs:ignore WordPress.Security.NonceVerification.Recommended | |
| 55 | 135 | |
| 56 | 136 | $inviation = null; |
| 57 | 137 | if ($inivtationToken) { |
| 58 | 138 | $inviation = apply_filters('fluent_community/auth/invitation', null, $inivtationToken); |
| 139 | + if ($inviation && !$inviation->isValid()) { | |
| 140 | + $inviation = null; | |
| 141 | + } | |
| 59 | 142 | } |
| 60 | 143 | |
| 61 | 144 | if ($currentUserId && !$inviation) { |
| 62 | - wp_redirect(Helper::baseUrl()); | |
| 63 | - exit(); | |
| 145 | + $redirectUrl = null; | |
| 146 | + if (!empty($_REQUEST['redirect_to'])) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended | |
| 147 | + $redirectUrl = sanitize_url(wp_unslash($_REQUEST['redirect_to'])); // phpcs:ignore WordPress.Security.NonceVerification.Recommended | |
| 148 | + } | |
| 149 | + if (!$redirectUrl) { | |
| 150 | + $redirectUrl = Helper::baseUrl(); | |
| 151 | + } | |
| 152 | + | |
| 153 | + $this->safeRedirectAndExit($redirectUrl); | |
| 64 | 154 | } |
| 65 | 155 | |
| 156 | + if ($currentUserId && $inviation) { | |
| 157 | + /** @var BaseSpace|null $space */ | |
| 158 | + $space = BaseSpace::withoutGlobalScopes()->find($inviation->post_id); | |
| 159 | + if ($space) { | |
| 160 | + if (Helper::isUserInSpace($currentUserId, $inviation->post_id)) { | |
| 161 | + // let's redirect the user to the space | |
| 162 | + $redirectUrl = $space->getPermalink(); | |
| 163 | + $this->safeRedirectAndExit($redirectUrl); | |
| 164 | + } | |
| 165 | + | |
| 166 | + if (!empty($_REQUEST['auto_accept'])) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended | |
| 167 | + $redirectUrl = (new InvitationHandler())->handleInvitationLogin(Helper::baseUrl(), get_user_by('ID', $currentUserId), $inviation->message_rendered); | |
| 168 | + if (is_wp_error($redirectUrl) || !$redirectUrl) { | |
| 169 | + $redirectUrl = Helper::baseUrl(); | |
| 170 | + } | |
| 171 | + $this->safeRedirectAndExit($redirectUrl); | |
| 172 | + } | |
| 173 | + } | |
| 174 | + } | |
| 175 | + | |
| 66 | 176 | do_action('fluent_community/auth/before_auth_page_process', $currentUserId, $inviation); |
| 67 | 177 | |
| 68 | 178 | $acceptedForms = ['login', 'register', 'reset_password']; |
| 69 | - $targetForm = Arr::get($_GET, 'form'); | |
| 70 | - if (!in_array($targetForm, $acceptedForms)) { | |
| 179 | + $targetForm = Arr::get($_GET, 'form'); // phpcs:ignore WordPress.Security.NonceVerification.Recommended | |
| 180 | + $explicitForm = in_array($targetForm, $acceptedForms, true); | |
| 181 | + if (!$explicitForm) { | |
| 71 | 182 | $targetForm = 'login'; |
| 72 | 183 | } |
| 73 | 184 | |
| 74 | - if ($inviation && $targetForm != 'reset_password') { | |
| 75 | - $isUserAvailable = get_user_by('email', $inviation->message); | |
| 76 | - $targetForm = $isUserAvailable ? 'login' : 'register'; | |
| 185 | + if ($inviation && !$explicitForm) { | |
| 186 | + if ($inviation->message) { | |
| 187 | + $isUserAvailable = get_user_by('email', $inviation->message); | |
| 188 | + $targetForm = $isUserAvailable ? 'login' : 'register'; | |
| 189 | + } else { | |
| 190 | + $targetForm = 'register'; | |
| 191 | + } | |
| 77 | 192 | } |
| 78 | 193 | |
| 79 | - if ($inviation && $currentUserId) { | |
| 80 | - $invitedUser = get_user_by('email', $inviation->message); | |
| 81 | - if ($invitedUser && $invitedUser->ID == $currentUserId) { | |
| 194 | + if ($inviation && $currentUserId && $inviation->isValid()) { | |
| 195 | + if ($inviation->message) { | |
| 196 | + $invitedUser = get_user_by('email', $inviation->message); | |
| 197 | + if ($invitedUser && $invitedUser->ID == $currentUserId) { | |
| 198 | + $targetForm = 'accept_invitation'; | |
| 199 | + } | |
| 200 | + } else { | |
| 82 | 201 | $targetForm = 'accept_invitation'; |
| 83 | 202 | } |
| 84 | 203 | } |
| 85 | 204 | |
| 205 | + /* | |
| 206 | + * Hand the screen to FluentAuth before asking whether it is available: adopting | |
| 207 | + * is what makes it so. Its front end forms sit behind a site setting meant for | |
| 208 | + * whether an editor may drop the shortcode into a page, and reading that as | |
| 209 | + * "may this portal use FluentAuth" is what used to drop us onto a login form of | |
| 210 | + * our own while FluentAuth went on injecting magic login and enforcing a second | |
| 211 | + * factor against a DOM it no longer recognised. | |
| 212 | + */ | |
| 213 | + AuthHelper::adoptFluentAuth(); | |
| 214 | + | |
| 86 | 215 | $isFluentAuth = AuthHelper::isFluentAuthAvailable(); |
| 87 | 216 | if (!$isFluentAuth && $targetForm == 'reset_password') { |
| 88 | - wp_redirect(wp_lostpassword_url(Helper::baseUrl())); | |
| 89 | - exit(); | |
| 217 | + $this->safeRedirectAndExit(wp_lostpassword_url(Helper::baseUrl())); | |
| 90 | 218 | } |
| 91 | 219 | |
| 92 | 220 | $portalSettings = Helper::generalSettings(); |
| 93 | 221 | $titleVar = Arr::get($portalSettings, 'site_title'); |
| @@ -92,60 +220,86 @@ | ||
| 92 | 220 | $portalSettings = Helper::generalSettings(); |
| 93 | 221 | $titleVar = Arr::get($portalSettings, 'site_title'); |
| 94 | 222 | |
| 95 | 223 | $frameData = [ |
| 96 | - 'logo' => Arr::get($portalSettings, 'logo', ''), | |
| 97 | - 'title' => sprintf(__('Join %s', 'fluent-community'), $titleVar), | |
| 98 | - 'description' => __('Login or Signup to join the community', 'fluent-community'), | |
| 99 | - 'loginBtnText' => __('Login', 'fluent-community'), | |
| 100 | - 'signupBtnText' => __('Signup', 'fluent-community'), | |
| 224 | + 'logo' => Arr::get($portalSettings, 'logo', ''), | |
| 225 | + /* translators: %s is replaced by the title of the site */ | |
| 226 | + 'title' => sprintf(__('Join %s', 'fluent-community'), $titleVar), | |
| 227 | + 'description' => __('Login or Signup to join the community', 'fluent-community'), | |
| 228 | + 'button_label' => __('Login', 'fluent-community'), | |
| 101 | 229 | ]; |
| 102 | 230 | |
| 103 | - $currentUrl = home_url(add_query_arg($_GET, $GLOBALS['wp']->request)); | |
| 231 | + if ($targetForm == 'register') { | |
| 232 | + $frameData['button_label'] = __('Signup', 'fluent-community'); | |
| 233 | + if (!$inviation) { | |
| 234 | + $customSignupUrl = Arr::get($portalSettings, 'custom_signup_url'); | |
| 235 | + if ($customSignupUrl) { | |
| 236 | + $this->safeRedirectAndExit($customSignupUrl); | |
| 237 | + } | |
| 238 | + } | |
| 239 | + } | |
| 104 | 240 | |
| 241 | + $currentUrl = home_url(add_query_arg($_GET, $GLOBALS['wp']->request)); // phpcs:ignore WordPress.Security.NonceVerification.Recommended | |
| 242 | + | |
| 243 | + do_action('fluent_community/enqueue_global_assets', true); | |
| 244 | + add_action('wp_enqueue_scripts', function () use ($isFluentAuth, $targetForm, $inviation) { | |
| 245 | + wp_enqueue_style('fluent_auth_styles', Vite::getStaticSrcUrl('user_registration.css'), [], FLUENT_COMMUNITY_PLUGIN_VERSION); | |
| 246 | + if(!$isFluentAuth || $targetForm == 'register' || $inviation) { | |
| 247 | + wp_enqueue_script('fluent_auth_scripts', Vite::getStaticSrcUrl('user_registration.js'), [], FLUENT_COMMUNITY_PLUGIN_VERSION, true); | |
| 248 | + wp_localize_script('fluent_auth_scripts', 'fluentComRegistration', array( | |
| 249 | + 'ajax_url' => admin_url('admin-ajax.php'), | |
| 250 | + 'is_logged_in' => is_user_logged_in(), | |
| 251 | + 'redirecting_text' => __('Redirecting...', 'fluent-community'), | |
| 252 | + 'i18n' => [ | |
| 253 | + 'generic_error' => esc_html__('Something went wrong. Please try again later', 'fluent-community'), | |
| 254 | + 'network_error' => esc_html__('Could not reach the server. Please check your connection and try again.', 'fluent-community'), | |
| 255 | + ] | |
| 256 | + )); | |
| 257 | + } | |
| 258 | + }, 10); | |
| 259 | + | |
| 105 | 260 | $pageVars = [ |
| 106 | 261 | 'title' => $frameData['title'], |
| 262 | + 'og_title' => $frameData['title'], | |
| 107 | 263 | 'description' => $frameData['description'], |
| 108 | 264 | 'url' => $currentUrl, |
| 109 | 265 | 'featured_image' => '', |
| 110 | - 'css_files' => [ | |
| 111 | - Vite::getDynamicSrcUrl('theme-default.scss'), | |
| 112 | - Vite::getDynamicSrcUrl('public/scss/user_registration.scss') | |
| 113 | - ], | |
| 114 | - 'js_files' => [ | |
| 115 | - Vite::getDynamicSrcUrl('public/js/user_registration.js') | |
| 116 | - ], | |
| 117 | - 'js_vars' => [ | |
| 118 | - 'fluentComRegistration' => [ | |
| 119 | - 'ajax_url' => admin_url('admin-ajax.php'), | |
| 120 | - 'is_logged_in' => is_user_logged_in(), | |
| 121 | - ] | |
| 122 | - ], | |
| 266 | + 'css_files' => [], | |
| 267 | + 'js_files' => [], | |
| 268 | + 'js_vars' => [], | |
| 123 | 269 | 'scope' => 'user_registration', |
| 124 | 270 | 'layout' => 'signup', |
| 125 | 271 | 'portal' => [ |
| 126 | 272 | 'logo' => Arr::get($portalSettings, 'logo', ''), |
| 127 | - 'title' => __(sprintf('Welcome to %s', Arr::get($portalSettings, 'site_title')), 'fluent-community'), | |
| 273 | + /* translators: %s is replaced by the title of the site */ | |
| 274 | + 'title' => \sprintf(__('Welcome to %s', 'fluent-community'), Arr::get($portalSettings, 'site_title')), | |
| 128 | 275 | 'description' => get_bloginfo('description') |
| 129 | 276 | ] |
| 130 | 277 | ]; |
| 131 | 278 | |
| 132 | - if ($isFluentAuth) { | |
| 133 | - $pageVars['js_files'][] = FLUENT_AUTH_PLUGIN_URL . 'dist/public/login_helper.js'; | |
| 134 | - $pageVars['js_vars']['fluentAuthPublic'] = [ | |
| 135 | - 'hide' => false, | |
| 136 | - 'redirect_fallback' => site_url(), | |
| 137 | - 'fls_login_nonce' => wp_create_nonce('fsecurity_login_nonce'), | |
| 138 | - 'ajax_url' => admin_url('admin-ajax.php'), | |
| 139 | - 'i18n' => [ | |
| 140 | - 'Username_or_Email' => __('Email Address', 'fluent-community'), | |
| 141 | - 'Password' => __('Password', 'fluent-community') | |
| 142 | - ] | |
| 279 | + if (Utility::isDev()) { | |
| 280 | + $pageVars['js_files'] = [ | |
| 281 | + Vite::getStaticSrcUrl('public/js/user_registration.js') | |
| 143 | 282 | ]; |
| 144 | 283 | } |
| 145 | 284 | |
| 146 | - add_action('fluent_community/headless/content', function ($context) use ($targetForm, $currentUrl, $frameData, $inviation) { | |
| 285 | + $formType = ($targetForm == 'register') ? 'signup' : 'login'; | |
| 286 | + | |
| 287 | + $formSettings = AuthenticationService::getFormattedAuthSettings($formType); | |
| 288 | + | |
| 289 | + if ($formSettings) { | |
| 290 | + $pageVars['portal'] = Arr::get($formSettings, 'banner'); | |
| 291 | + $pageVars['portal']['form'] = Arr::get($formSettings, 'form'); | |
| 292 | + } | |
| 293 | + | |
| 294 | + add_action('fluent_community/headless/content', function ($context) use ($targetForm, $currentUrl, $frameData, $inviation, $formSettings) { | |
| 295 | + $preContent = apply_filters('fluent_community/auth/pre_content', '', $context, $targetForm, $frameData); | |
| 296 | + if ($preContent) { | |
| 297 | + return; | |
| 298 | + } | |
| 299 | + | |
| 147 | 300 | if ($targetForm == 'login') { |
| 301 | + $frameData['button_label'] = Arr::get($formSettings, 'form.button_label', __('Login', 'fluent-community')); | |
| 148 | 302 | $this->showLoginForm($frameData, $inviation); |
| 149 | 303 | } else if ($targetForm == 'reset_password') { |
| 150 | 304 | $frameData['title'] = __('Reset your password', 'fluent-community'); |
| 151 | 305 | ?> |
| @@ -153,8 +307,11 @@ | ||
| 153 | 307 | <div class="fcom_onboard_header"> |
| 154 | 308 | <div class="fcom_onboard_header_title"> |
| 155 | 309 | <h2><?php echo esc_html($frameData['title']); ?></h2> |
| 156 | 310 | </div> |
| 311 | + <div class="fcom_onboard_sub"> | |
| 312 | + <p><?php esc_html_e('Please enter your email address. You will receive an email message with instructions on how to reset your password.', 'fluent-community'); ?></p> | |
| 313 | + </div> | |
| 157 | 314 | </div> |
| 158 | 315 | <div class="fcom_onboard_body"> |
| 159 | 316 | <div class="fcom_onboard_form"> |
| 160 | 317 | <?php echo do_shortcode('[fluent_auth_reset_password redirect_to="' . esc_url($currentUrl) . '"]'); ?> |
| @@ -160,9 +317,9 @@ | ||
| 160 | 317 | <?php echo do_shortcode('[fluent_auth_reset_password redirect_to="' . esc_url($currentUrl) . '"]'); ?> |
| 161 | 318 | <div class="fcom_spaced_divider"> |
| 162 | 319 | <div class="fcom_alt_auth_text"> |
| 163 | 320 | <a href="<?php echo esc_url(add_query_arg('form', 'login', $currentUrl)); ?>"> |
| 164 | - <?php _e('Back to Login', 'fluent-community'); ?> | |
| 321 | + <?php esc_html_e('Back to Login', 'fluent-community'); ?> | |
| 165 | 322 | </a> |
| 166 | 323 | </div> |
| 167 | 324 | </div> |
| 168 | 325 | </div> |
| @@ -169,13 +326,13 @@ | ||
| 169 | 326 | </div> |
| 170 | 327 | </div> |
| 171 | 328 | <?php |
| 172 | 329 | } else if ($targetForm == 'accept_invitation') { |
| 173 | - do_action('fluent_community/auth/show_inviration_for_user', $inviation, $frameData); | |
| 330 | + do_action('fluent_community/auth/show_invitation_for_user', $inviation, $frameData); | |
| 174 | 331 | } else { |
| 175 | - //check if the registration is disabled | |
| 176 | - if (!AuthHelper::isRegistrationEnabled()) { | |
| 177 | - echo '<div class="fcom_completed"><div class="fcom_complted_header"><h4>' . __('Registration is disabled for this community', 'fluent-community') . '</h4>'; | |
| 332 | + //check if the registration is disabled (a valid invitation still allows signup) | |
| 333 | + if (!$inviation && !AuthHelper::isRegistrationEnabled()) { | |
| 334 | + echo '<div class="fcom_completed"><div class="fcom_complted_header"><h4>' . esc_html__('Registration is disabled for this community', 'fluent-community') . '</h4>'; | |
| 178 | 335 | return; |
| 179 | 336 | } |
| 180 | 337 | |
| 181 | 338 | $frameData['hiddenFields'] = [ |
| @@ -180,20 +337,45 @@ | ||
| 180 | 337 | |
| 181 | 338 | $frameData['hiddenFields'] = [ |
| 182 | 339 | 'register' => 'yes', |
| 183 | 340 | 'action' => 'fcom_user_signup', |
| 184 | - '_fls_signup_nonce' => wp_create_nonce('fluent_auth_signup_nonce') | |
| 341 | + '_fcom_signup_nonce' => wp_create_nonce('fluent_auth_signup_nonce') | |
| 185 | 342 | ]; |
| 186 | 343 | |
| 187 | 344 | $frameData['loginUrl'] = add_query_arg('form', 'login', $currentUrl); |
| 188 | - $frameData['description'] = __('Create an account to join the community', 'fluent-community'); | |
| 345 | + $frameData = wp_parse_args(Arr::get($formSettings, 'form'), $frameData); | |
| 346 | + | |
| 189 | 347 | $this->renderRegistrationForm($frameData, $inviation); |
| 190 | 348 | } |
| 191 | 349 | }, 10, 1); |
| 192 | 350 | |
| 193 | - status_header(200); | |
| 194 | - App::make('view')->render('headless_page', $pageVars); | |
| 195 | - exit(200); | |
| 351 | + add_action('fluent_community/headless/head_early', function ($scope) use ($formSettings) { | |
| 352 | + $bannerColors = array_filter(Arr::only($formSettings['banner'], ['title_color', 'text_color', 'background_color'])); | |
| 353 | + $css = Utility::getColorCssVariables(); | |
| 354 | + | |
| 355 | + $sideVars = ''; | |
| 356 | + foreach ($bannerColors as $colorKey => $colorValue) { | |
| 357 | + $sideVars .= '--fcom_' . $colorKey . ': ' . $colorValue . ';'; | |
| 358 | + } | |
| 359 | + ?> | |
| 360 | + <?php // the auth screen renders with load_wp, which skips headless_page's noindex ?> | |
| 361 | + <meta name="robots" content="noindex, noarchive" /> | |
| 362 | + <link rel="canonical" href="<?php echo esc_url(Helper::getAuthUrl()); ?>" /> | |
| 363 | + <style> | |
| 364 | + .fcom_layout_side { <?php echo esc_html($sideVars); ?> } | |
| 365 | + <?php echo esc_html($css); ?> | |
| 366 | + </style> | |
| 367 | + <?php | |
| 368 | + }); | |
| 369 | + | |
| 370 | + $pageVars['load_wp'] = 'yes'; | |
| 371 | + | |
| 372 | + // document title hook | |
| 373 | + add_filter('pre_get_document_title', function ($title) use ($frameData) { | |
| 374 | + return $frameData['title']; | |
| 375 | + }, 9999, 1); | |
| 376 | + | |
| 377 | + $this->renderPageAndExit('headless_page', $pageVars); | |
| 196 | 378 | } |
| 197 | 379 | |
| 198 | 380 | public function handleUserSignup() |
| 199 | 381 | { |
| @@ -200,11 +382,26 @@ | ||
| 200 | 382 | if (is_user_logged_in()) { |
| 201 | 383 | return $this->handleSignupCompleted(get_current_user_id()); |
| 202 | 384 | } |
| 203 | 385 | |
| 204 | - if (!AuthHelper::isRegistrationEnabled()) { | |
| 386 | + $signupNonce = isset($_POST['_fcom_signup_nonce']) ? sanitize_text_field(wp_unslash($_POST['_fcom_signup_nonce'])) : ''; | |
| 387 | + if (!$signupNonce || !wp_verify_nonce($signupNonce, 'fluent_auth_signup_nonce')) { | |
| 205 | 388 | wp_send_json([ |
| 206 | - 'message' => __('Registration is disabled for this community', 'fluent-community') | |
| 389 | + 'message' => esc_html__('Invalid request. Please refresh the page and try again.', 'fluent-community') | |
| 390 | + ], 403); | |
| 391 | + } | |
| 392 | + | |
| 393 | + $invitationToken = isset($_POST['invitation_token']) ? sanitize_text_field(wp_unslash($_POST['invitation_token'])) : ''; | |
| 394 | + $hasValidInvitation = false; | |
| 395 | + if ($invitationToken) { | |
| 396 | + $pendingInvitation = Invitation::where('message_rendered', $invitationToken)->first(); | |
| 397 | + $hasValidInvitation = $pendingInvitation && $pendingInvitation->isValid(); | |
| 398 | + } | |
| 399 | + | |
| 400 | + // A valid invitation must still allow signup even when public registration is disabled. | |
| 401 | + if (!$hasValidInvitation && !AuthHelper::isRegistrationEnabled()) { | |
| 402 | + wp_send_json([ | |
| 403 | + 'message' => esc_html__('Registration is disabled for this community', 'fluent-community') | |
| 207 | 404 | ], 422); |
| 208 | 405 | } |
| 209 | 406 | |
| 210 | 407 | $app = App::make('app'); |
| @@ -210,10 +407,15 @@ | ||
| 210 | 407 | $app = App::make('app'); |
| 211 | 408 | $request = $app->make('request'); |
| 212 | 409 | $fields = AuthHelper::getFormFields(); |
| 213 | 410 | |
| 411 | + $authSettings = AuthenticationService::getAuthSettings(); | |
| 412 | + $termsField = Arr::get($authSettings, 'signup.form.fields.terms'); | |
| 413 | + | |
| 414 | + $fields['terms'] = $termsField ?: $fields['terms']; | |
| 415 | + | |
| 214 | 416 | $requiredFields = array_filter($fields, function ($field) { |
| 215 | - return $field['required'] ?? false; | |
| 417 | + return ($field['required'] && empty($field['disabled'])) ?? false; | |
| 216 | 418 | }); |
| 217 | 419 | |
| 218 | 420 | $keys = array_keys($fields); |
| 219 | 421 | $data = Arr::only($request->all(), $keys); |
| @@ -222,9 +424,9 @@ | ||
| 222 | 424 | $data['username'] = sanitize_user(strtolower(preg_replace('/[^A-Za-z0-9_]/', '', $data['username']))); |
| 223 | 425 | |
| 224 | 426 | if (empty($data['username'])) { |
| 225 | 427 | wp_send_json([ |
| 226 | - 'message' => __('Username is not valid', 'fluent-community'), | |
| 428 | + 'message' => esc_html__('Username is not valid', 'fluent-community'), | |
| 227 | 429 | 'errors' => [ |
| 228 | 430 | 'username' => __('Please provide a valid username', 'fluent-community') |
| 229 | 431 | ] |
| 230 | 432 | ], 422); |
| @@ -231,9 +433,9 @@ | ||
| 231 | 433 | } |
| 232 | 434 | |
| 233 | 435 | if (!ProfileHelper::isUsernameAvailable($data['username'])) { |
| 234 | 436 | wp_send_json([ |
| 235 | - 'message' => __('Username is already taken', 'fluent-community'), | |
| 437 | + 'message' => esc_html__('Username is already taken', 'fluent-community'), | |
| 236 | 438 | 'errors' => [ |
| 237 | 439 | 'username' => __('Username is already taken. Please use a different username', 'fluent-community') |
| 238 | 440 | ] |
| 239 | 441 | ], 422); |
| @@ -238,9 +440,39 @@ | ||
| 238 | 440 | ] |
| 239 | 441 | ], 422); |
| 240 | 442 | } |
| 241 | 443 | |
| 444 | + $invitationToken = $request->get('invitation_token'); | |
| 445 | + $invitation = null; | |
| 446 | + if ($invitationToken) { | |
| 447 | + $invitation = Invitation::where('message_rendered', $invitationToken)->first(); | |
| 448 | + if (!$invitation) { | |
| 449 | + wp_send_json([ | |
| 450 | + 'message' => __('Invalid invitation token', 'fluent-community') | |
| 451 | + ], 422); | |
| 452 | + } | |
| 453 | + | |
| 454 | + if ($invitation->message && $invitation->message != $data['email']) { | |
| 455 | + wp_send_json([ | |
| 456 | + 'message' => esc_html__('Email does not match with the invitation', 'fluent-community') | |
| 457 | + ], 422); | |
| 458 | + } | |
| 459 | + | |
| 460 | + if ($invitation->message) { | |
| 461 | + add_filter('fluent_community/auth/two_factor_enabled', '__return_false'); | |
| 462 | + add_filter('fluent_auth/verify_signup_email', '__return_false'); | |
| 463 | + } | |
| 464 | + } | |
| 465 | + | |
| 466 | + if (!$invitation && AuthenticationService::getCustomSignupPageUrl()) { | |
| 467 | + // we have custom signup page enabled | |
| 468 | + wp_send_json([ | |
| 469 | + 'message' => esc_html__('Direct Registration is disabled for this community', 'fluent-community') | |
| 470 | + ], 422); | |
| 471 | + } | |
| 472 | + | |
| 242 | 473 | $data['email'] = sanitize_email($data['email']); |
| 474 | + $data['full_name'] = sanitize_text_field(Arr::get($data, 'full_name', '')); | |
| 243 | 475 | |
| 244 | 476 | $validations = [ |
| 245 | 477 | 'full_name' => 'required|max:100|string', |
| 246 | 478 | 'username' => 'required|unique:users,user_login|unique:fcom_xprofile,username|min:4|max:30', |
| @@ -275,9 +507,9 @@ | ||
| 275 | 507 | ]); |
| 276 | 508 | |
| 277 | 509 | if ($validator->fails()) { |
| 278 | 510 | wp_send_json([ |
| 279 | - 'message' => __('Please fill all the required fields correctly', 'fluent-community'), | |
| 511 | + 'message' => __('Please fill in all required fields correctly.', 'fluent-community'), | |
| 280 | 512 | 'errors' => $validator->errors() |
| 281 | 513 | ], 422); |
| 282 | 514 | } |
| 283 | 515 | |
| @@ -297,16 +529,8 @@ | ||
| 297 | 529 | unset($data['full_name']); |
| 298 | 530 | $data = array_filter($data); |
| 299 | 531 | } |
| 300 | 532 | |
| 301 | - if (AuthHelper::isFluentAuthAvailable()) { | |
| 302 | - $data = wp_parse_args($data, $request->all()); | |
| 303 | - $this->handleSignupViaFlentAuth($data); | |
| 304 | - wp_send_json([ | |
| 305 | - 'message' => __('Something is not working! Please try again', 'fluent-community') | |
| 306 | - ], 422); | |
| 307 | - } | |
| 308 | - | |
| 309 | 533 | $rateLimit = AuthHelper::isAuthRateLimit(); |
| 310 | 534 | |
| 311 | 535 | if (is_wp_error($rateLimit)) { |
| 312 | 536 | wp_send_json([ |
| @@ -358,16 +582,14 @@ | ||
| 358 | 582 | } |
| 359 | 583 | |
| 360 | 584 | private function handleSignupViaFlentAuth($data) |
| 361 | 585 | { |
| 362 | - add_filter('fluent_auth/signup_form_data', function ($requestData) use ($data) { | |
| 363 | - return $data; | |
| 364 | - }); | |
| 365 | - | |
| 366 | 586 | add_action('fluent_auth/after_creating_user', function ($userId) { |
| 367 | 587 | $this->handleSignupCompleted($userId); |
| 368 | 588 | }, 1, 1); |
| 369 | 589 | |
| 590 | + add_filter('fluent_auth/signup_enabled', '__return_true'); | |
| 591 | + | |
| 370 | 592 | (new CustomAuthHandler())->handleSignupAjax(); |
| 371 | 593 | } |
| 372 | 594 | |
| 373 | 595 | private function handleSignupCompleted($userId) |
| @@ -373,18 +595,22 @@ | ||
| 373 | 595 | private function handleSignupCompleted($userId) |
| 374 | 596 | { |
| 375 | 597 | // We have the user now let's set the community membership |
| 376 | 598 | $user = User::find($userId); |
| 377 | - $user->syncXProfile(true); | |
| 599 | + $user->syncXProfile(true, true); | |
| 378 | 600 | |
| 379 | 601 | $redirectUrl = Helper::baseUrl(); |
| 380 | 602 | |
| 381 | - $redirectUrl = apply_filters('fluent_community/auth/after_signup_redirect_url', $redirectUrl, $user, $_REQUEST); | |
| 603 | + if (!empty($_REQUEST['redirect_to'])) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended | |
| 604 | + $redirectUrl = wp_validate_redirect(sanitize_url(wp_unslash($_REQUEST['redirect_to'])), Helper::baseUrl()); // phpcs:ignore WordPress.Security.NonceVerification.Recommended | |
| 605 | + } | |
| 606 | + | |
| 607 | + $redirectUrl = apply_filters('fluent_community/auth/after_signup_redirect_url', $redirectUrl, $user, $_REQUEST); // phpcs:ignore WordPress.Security.NonceVerification.Recommended | |
| 382 | 608 | $btnText = __('Continue to the community', 'fluent-community'); |
| 383 | 609 | |
| 384 | 610 | $html = '<div class="fcom_completed"><div class="fcom_complted_header"><h2>' . __('Congratulations!', 'fluent-community') . '</h2>'; |
| 385 | 611 | $html .= '<p>' . __('You have successfully registered to the community', 'fluent-community') . '</p></div>'; |
| 386 | - $html .= '<a href="' . $redirectUrl . '" class="fcom_btn fcom_btn_success">' . $btnText . '</a>'; | |
| 612 | + $html .= '<a href="' . esc_url($redirectUrl) . '" class="fcom_btn fcom_btn_success">' . $btnText . '</a>'; | |
| 387 | 613 | $html .= '</div>'; |
| 388 | 614 | |
| 389 | 615 | if (!get_current_user_id()) { |
| 390 | 616 | $wpUser = get_user_by('ID', $userId); |
| @@ -405,12 +631,19 @@ | ||
| 405 | 631 | } |
| 406 | 632 | |
| 407 | 633 | if (AuthHelper::isFluentAuthAvailable()) { |
| 408 | 634 | wp_send_json([ |
| 409 | - 'message' => __('This form can not be used to login. Please reload the page and try again', 'fluent-community') | |
| 635 | + 'message' => __('This form cannot be used to log in. Please reload the page and try again.', 'fluent-community') | |
| 410 | 636 | ], 422); |
| 411 | 637 | } |
| 412 | 638 | |
| 639 | + $loginNonce = isset($_POST['_fcom_login_nonce']) ? sanitize_text_field(wp_unslash($_POST['_fcom_login_nonce'])) : ''; | |
| 640 | + if (!$loginNonce || !wp_verify_nonce($loginNonce, 'fcom_user_login_nonce')) { | |
| 641 | + wp_send_json([ | |
| 642 | + 'message' => esc_html__('Invalid request. Please refresh the page and try again.', 'fluent-community') | |
| 643 | + ], 403); | |
| 644 | + } | |
| 645 | + | |
| 413 | 646 | $app = App::make('app'); |
| 414 | 647 | $request = $app->make('request'); |
| 415 | 648 | |
| 416 | 649 | $data = $request->all(); |
| @@ -439,10 +672,16 @@ | ||
| 439 | 672 | |
| 440 | 673 | $user = wp_authenticate($data['log'], $data['pwd']); |
| 441 | 674 | |
| 442 | 675 | if (is_wp_error($user)) { |
| 676 | + $enumerationCodes = ['invalid_username', 'invalid_email', 'incorrect_password']; | |
| 677 | + if (in_array($user->get_error_code(), $enumerationCodes, true)) { | |
| 678 | + $message = __('Email or password is incorrect.', 'fluent-community'); | |
| 679 | + } else { | |
| 680 | + $message = $user->get_error_message(); | |
| 681 | + } | |
| 443 | 682 | wp_send_json([ |
| 444 | - 'message' => $user->get_error_message() | |
| 683 | + 'message' => $message | |
| 445 | 684 | ], 422); |
| 446 | 685 | } |
| 447 | 686 | |
| 448 | 687 | InvitationService::makeLogin($user); |
| @@ -447,13 +686,13 @@ | ||
| 447 | 686 | |
| 448 | 687 | InvitationService::makeLogin($user); |
| 449 | 688 | |
| 450 | 689 | $redirectUrl = null; |
| 451 | - if(!empty($_REQUEST['redirect_to'])) { | |
| 452 | - $redirectUrl = sanitize_url($_REQUEST['redirect_to']); | |
| 690 | + if (!empty($_REQUEST['redirect_to'])) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended | |
| 691 | + $redirectUrl = wp_validate_redirect(sanitize_url(wp_unslash($_REQUEST['redirect_to'])), Helper::baseUrl()); // phpcs:ignore WordPress.Security.NonceVerification.Recommended | |
| 453 | 692 | } |
| 454 | 693 | |
| 455 | - if(!$redirectUrl) { | |
| 694 | + if (!$redirectUrl) { | |
| 456 | 695 | $redirectUrl = Helper::baseUrl(); |
| 457 | 696 | } |
| 458 | 697 | |
| 459 | 698 | if ($invitationToken = $request->get('invitation_token')) { |
| @@ -476,9 +715,9 @@ | ||
| 476 | 715 | $btnText = __('Continue to the community', 'fluent-community'); |
| 477 | 716 | |
| 478 | 717 | $html = '<div class="fcom_completed"><div class="fcom_complted_header"><h2>' . __('Welcome back!', 'fluent-community') . '</h2>'; |
| 479 | 718 | $html .= '<p>' . __('You have successfully logged in to the community', 'fluent-community') . '</p></div>'; |
| 480 | - $html .= '<a href="' . $redirectUrl . '" class="fcom_btn fcom_btn_success">' . $btnText . '</a>'; | |
| 719 | + $html .= '<a href="' . esc_url($redirectUrl) . '" class="fcom_btn fcom_btn_success">' . $btnText . '</a>'; | |
| 481 | 720 | $html .= '</div>'; |
| 482 | 721 | |
| 483 | 722 | wp_send_json([ |
| 484 | 723 | 'success_html' => $html, |
| @@ -489,9 +728,12 @@ | ||
| 489 | 728 | public function showLoginForm($frameData, $invitation = null) |
| 490 | 729 | { |
| 491 | 730 | $portalSettings = Helper::generalSettings(); |
| 492 | 731 | $isFluentAuth = AuthHelper::isFluentAuthAvailable(); |
| 493 | - $currentUrl = home_url(add_query_arg($_GET, $GLOBALS['wp']->request)); | |
| 732 | + $loginSettings = AuthenticationService::getFormattedAuthSettings('login'); | |
| 733 | + $formSettings = Arr::get($loginSettings, 'form'); | |
| 734 | + $currentUrl = home_url(add_query_arg($_GET, $GLOBALS['wp']->request)); // phpcs:ignore WordPress.Security.NonceVerification.Recommended | |
| 735 | + /* translators: %s is replaced by the title of the site */ | |
| 494 | 736 | $title = sprintf(__('Login to %s', 'fluent-community'), Arr::get($portalSettings, 'site_title')); |
| 495 | 737 | |
| 496 | 738 | $description = ''; |
| 497 | 739 | if ($invitation) { |
| @@ -501,21 +743,62 @@ | ||
| 501 | 743 | if ($space) { |
| 502 | 744 | $title = $space->title . ' - ' . Arr::get($portalSettings, 'site_title'); |
| 503 | 745 | } |
| 504 | 746 | } |
| 505 | - $description = sprintf(__('%s has invited you to join this community. Please login to accept your invitation.', 'fluent-community'), $invitationBy); | |
| 747 | + /* translators: %s is replaced by the name of the inviter */ | |
| 748 | + $inviteDescription = \sprintf(__('%s has invited you to join this community. Please login to accept your invitation.', 'fluent-community'), $invitationBy); | |
| 749 | + add_action('fluent_community/before_auth_form_header', function ($formType) use ($inviteDescription) { | |
| 750 | + ?> | |
| 751 | + <div class="fcom_highlight_message"> | |
| 752 | + <?php echo wp_kses_post($inviteDescription); ?> | |
| 753 | + </div> | |
| 754 | + <?php | |
| 755 | + }); | |
| 506 | 756 | } |
| 507 | 757 | |
| 758 | + $signupUrl = add_query_arg('form', 'register', $currentUrl); | |
| 759 | + | |
| 760 | + if (!$invitation) { | |
| 761 | + if ($customSignupUrl = AuthenticationService::getCustomSignupPageUrl()) { | |
| 762 | + $signupUrl = $customSignupUrl; | |
| 763 | + } | |
| 764 | + } | |
| 765 | + | |
| 766 | + add_filter('login_form_defaults', function ($defaults) use ($invitation, $frameData) { | |
| 767 | + $defaults['label_log_in'] = Arr::get($frameData, 'button_label'); | |
| 768 | + return $defaults; | |
| 769 | + }); | |
| 770 | + | |
| 508 | 771 | if ($isFluentAuth) { |
| 772 | + add_filter('login_form_top', function () use ($invitation) { | |
| 773 | + $reditectUrl = Arr::get($_GET, 'redirect_to'); // phpcs:ignore WordPress.Security.NonceVerification.Recommended | |
| 774 | + if (!$reditectUrl) { | |
| 775 | + $reditectUrl = apply_filters('fluent_community/default_redirect_url', Helper::baseUrl()); | |
| 776 | + } | |
| 777 | + ob_start(); | |
| 778 | + ?> | |
| 779 | + <?php if ($invitation) { ?> | |
| 780 | + <input type="hidden" name="invitation_token" value="<?php echo esc_attr($invitation->message_rendered); ?>"/> | |
| 781 | + <?php } ?> | |
| 782 | + <input name="is_fcom_auth" type="hidden" value="yes"/> | |
| 783 | + <input type="hidden" name="fcom_redirect" value="<?php echo esc_url($reditectUrl); ?>"/> | |
| 784 | + <?php | |
| 785 | + return ob_get_clean(); | |
| 786 | + }); | |
| 509 | 787 | ?> |
| 510 | 788 | <div id="fcom_user_onboard_wrap" class="fcom_user_onboard"> |
| 511 | 789 | <div class="fcom_onboard_header"> |
| 790 | + <?php do_action('fluent_community/before_auth_form_header', 'login'); ?> | |
| 512 | 791 | <div class="fcom_onboard_header_title"> |
| 513 | - <h2><?php echo esc_html($title); ?></h2> | |
| 792 | + <?php if (!empty($formSettings['title'])): ?> | |
| 793 | + <h2> | |
| 794 | + <?php echo esc_html($formSettings['title']); ?> | |
| 795 | + </h2> | |
| 796 | + <?php endif; ?> | |
| 514 | 797 | </div> |
| 515 | - <?php if ($description): ?> | |
| 798 | + <?php if (!empty($formSettings['description'])): ?> | |
| 516 | 799 | <div class="fcom_onboard_sub"> |
| 517 | - <p><?php echo wp_kses_post($description); ?></p> | |
| 800 | + <?php echo wp_kses_post(trim($formSettings['description'])); ?> | |
| 518 | 801 | </div> |
| 519 | 802 | <?php endif; ?> |
| 520 | 803 | </div> |
| 521 | 804 | <div class="fcom_onboard_body"> |
| @@ -521,19 +804,19 @@ | ||
| 521 | 804 | <div class="fcom_onboard_body"> |
| 522 | 805 | <div class="fcom_onboard_form"> |
| 523 | 806 | <?php echo do_shortcode('[fluent_auth_login redirect_to="' . esc_url($currentUrl) . '"]'); ?> |
| 524 | 807 | <div class="fcom_spaced_divider"> |
| 525 | - <?php if (AuthHelper::isRegistrationEnabled()): ?> | |
| 808 | + <?php if ($invitation || AuthHelper::isRegistrationEnabled()): ?> | |
| 526 | 809 | <div class="fcom_alt_auth_text"> |
| 527 | - <?php _e('Don\'t have an account?', 'fluent-community'); ?> | |
| 528 | - <a href="<?php echo esc_url(add_query_arg('form', 'register', $currentUrl)); ?>"> | |
| 529 | - <?php _e('Signup', 'fluent-community'); ?> | |
| 810 | + <?php esc_html_e('Don\'t have an account?', 'fluent-community'); ?> | |
| 811 | + <a href="<?php echo esc_url($signupUrl); ?>"> | |
| 812 | + <?php esc_html_e('Signup', 'fluent-community'); ?> | |
| 530 | 813 | </a> |
| 531 | 814 | </div> |
| 532 | 815 | <?php endif; ?> |
| 533 | 816 | <p class="fcom_reset_pass_text"> |
| 534 | - <a href="<?php echo esc_url(add_query_arg('form', 'reset_password', $currentUrl)); ?>"> | |
| 535 | - <?php _e('Lost your password?', 'fluent-community'); ?> | |
| 817 | + <a href="<?php echo esc_url(AuthHelper::getLostPasswordUrl($currentUrl)); ?>"> | |
| 818 | + <?php esc_html_e('Lost your password?', 'fluent-community'); ?> | |
| 536 | 819 | </a> |
| 537 | 820 | </p> |
| 538 | 821 | </div> |
| 539 | 822 | </div> |
| @@ -544,11 +827,14 @@ | ||
| 544 | 827 | } |
| 545 | 828 | |
| 546 | 829 | $frameData['redirect'] = $currentUrl; |
| 547 | 830 | |
| 548 | - $frameData['hiddenFields'] = []; | |
| 831 | + $frameData['hiddenFields'] = [ | |
| 832 | + 'action' => 'fcom_user_login_form', | |
| 833 | + '_fcom_login_nonce' => wp_create_nonce('fcom_user_login_nonce'), | |
| 834 | + ]; | |
| 549 | 835 | if ($invitation) { |
| 550 | - $frameData['loginBtnText'] = __('Log In & Accept Invitation', 'fluent-community'); | |
| 836 | + $frameData['button_label'] = __('Log In & Accept Invitation', 'fluent-community'); | |
| 551 | 837 | $frameData['hiddenFields']['invitation_token'] = $invitation->message_rendered; |
| 552 | 838 | } |
| 553 | 839 | |
| 554 | 840 | $frameData['title'] = $title; |
| @@ -557,14 +843,16 @@ | ||
| 557 | 843 | $frameData['defaults'] = [ |
| 558 | 844 | 'email' => $invitation ? $invitation->message : '' |
| 559 | 845 | ]; |
| 560 | 846 | |
| 561 | - if (AuthHelper::isRegistrationEnabled()) { | |
| 562 | - $frameData['signupUrl'] = add_query_arg('form', 'register', $currentUrl); | |
| 847 | + if ($invitation || AuthHelper::isRegistrationEnabled()) { | |
| 848 | + $frameData['signupUrl'] = $signupUrl; | |
| 563 | 849 | } |
| 564 | 850 | |
| 565 | - if(isset($_GET['redirect_to'])) { | |
| 566 | - $frameData['redirect_to'] = sanitize_url($_GET['redirect_to']); | |
| 851 | + $frameData['settings'] = $formSettings; | |
| 852 | + | |
| 853 | + if (isset($_GET['redirect_to'])) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended | |
| 854 | + $frameData['redirect_to'] = sanitize_url(wp_unslash($_GET['redirect_to'])); // phpcs:ignore WordPress.Security.NonceVerification.Recommended | |
| 567 | 855 | } |
| 568 | 856 | |
| 569 | 857 | App::make('view')->render('auth.login_form', $frameData); |
| 570 | 858 | } |
| @@ -572,8 +860,28 @@ | ||
| 572 | 860 | public function renderRegistrationForm($frameData, $invitation = null) |
| 573 | 861 | { |
| 574 | 862 | $formFields = AuthHelper::getFormFields($invitation); |
| 575 | 863 | |
| 864 | + // Prefill the name from the invitation link's query param when present. | |
| 865 | + $inviteName = isset($_GET['invite_name']) ? sanitize_text_field(wp_unslash($_GET['invite_name'])) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended | |
| 866 | + if ($inviteName && isset($formFields['full_name']) && empty($formFields['full_name']['value'])) { | |
| 867 | + $formFields['full_name']['value'] = $inviteName; | |
| 868 | + } | |
| 869 | + | |
| 870 | + $authSettings = AuthenticationService::getAuthSettings(); | |
| 871 | + | |
| 872 | + $termsField = Arr::get($authSettings, 'signup.form.fields.terms'); | |
| 873 | + | |
| 874 | + | |
| 875 | + if ($termsField) { | |
| 876 | + unset($termsField['label']); | |
| 877 | + | |
| 878 | + // add new tab on the link for $termsField['inline_label'] | |
| 879 | + $termsField['inline_label'] = FeedsHelper::addNewTabToLinks($termsField['inline_label']); | |
| 880 | + | |
| 881 | + $formFields['terms'] = $termsField; | |
| 882 | + } | |
| 883 | + | |
| 576 | 884 | $frameData['formFields'] = $formFields; |
| 577 | 885 | |
| 578 | 886 | if ($invitation) { |
| 579 | 887 | $frameData['hiddenFields'] = [ |
| @@ -578,21 +886,50 @@ | ||
| 578 | 886 | if ($invitation) { |
| 579 | 887 | $frameData['hiddenFields'] = [ |
| 580 | 888 | 'invitation_token' => $invitation->message_rendered, |
| 581 | 889 | 'action' => 'fcom_user_registration', |
| 582 | - '_fls_signup_nonce' => wp_create_nonce('fluent_auth_signup_nonce') | |
| 890 | + '_fcom_signup_nonce' => wp_create_nonce('fluent_auth_signup_nonce') | |
| 583 | 891 | ]; |
| 584 | 892 | |
| 585 | 893 | $invitationBy = $invitation->xprofile ? $invitation->xprofile->display_name : __('Someone', 'fluent-community'); |
| 586 | - $frameData['description'] = sprintf(__('%s has invited you to join this community. Please create an account to accept your invitation.', 'fluent-community'), $invitationBy); | |
| 587 | - $frameData['signupBtnText'] = __('Register & Accept invitation', 'fluent-community'); | |
| 894 | + /* translators: %s is replaced by the name of the inviter */ | |
| 895 | + $inviteDescription = sprintf(__('%s has invited you to join this community. Please create an account to accept your invitation.', 'fluent-community'), $invitationBy); | |
| 896 | + | |
| 897 | + add_action('fluent_community/before_auth_form_header', function ($formType) use ($inviteDescription) { | |
| 898 | + ?> | |
| 899 | + <div class="fcom_highlight_message"> | |
| 900 | + <?php echo wp_kses_post($inviteDescription); ?> | |
| 901 | + </div> | |
| 902 | + <?php | |
| 903 | + }); | |
| 904 | + | |
| 905 | + $frameData['button_label'] = __('Register & Accept invitation', 'fluent-community'); | |
| 588 | 906 | } else { |
| 589 | 907 | $frameData['hiddenFields'] = [ |
| 590 | 908 | 'register' => 'yes', |
| 591 | 909 | 'action' => 'fcom_user_registration', |
| 592 | - '_fls_signup_nonce' => wp_create_nonce('fluent_auth_signup_nonce') | |
| 910 | + '_fcom_signup_nonce' => wp_create_nonce('fluent_auth_signup_nonce'), | |
| 593 | 911 | ]; |
| 912 | + | |
| 913 | + if (!empty($_GET['redirect_to'])) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended | |
| 914 | + $frameData['hiddenFields']['redirect_to'] = sanitize_url(wp_unslash($_GET['redirect_to'])); // phpcs:ignore WordPress.Security.NonceVerification.Recommended | |
| 915 | + } | |
| 594 | 916 | } |
| 917 | + | |
| 918 | + add_action('fluent_community/before_registration_form', function ($frameData) { | |
| 919 | + if (AuthHelper::isFluentAuthAvailable()) { | |
| 920 | + $currentUrl = esc_url(home_url(add_query_arg($_GET, $GLOBALS['wp']->request))); // phpcs:ignore WordPress.Security.NonceVerification.Recommended | |
| 921 | + | |
| 922 | + $titlePrefix = __('Signup with', 'fluent-community'); | |
| 923 | + $html = do_shortcode('[fs_auth_buttons redirect="' . $currentUrl . '" title_prefix="' . $titlePrefix . ' " title=""]'); | |
| 924 | + | |
| 925 | + if ($html) { | |
| 926 | + echo '<div class="fcom_social_auth_wrap">'; | |
| 927 | + echo $html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped | |
| 928 | + echo '</div>'; | |
| 929 | + } | |
| 930 | + } | |
| 931 | + }); | |
| 595 | 932 | |
| 596 | 933 | App::make('view')->render('auth.user_invitation', $frameData); |
| 597 | 934 | } |
| 598 | 935 | } |