PluginProbe
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses / 2.4.01
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses v2.4.01
2.11.0 2.10.0 2.10.01 2.9.1 2.9.0 2.8.1 2.8.0 2.7.7 2.7.5 2.7.0 2.6.01 2.6.0 2.5.0 2.4.01 trunk 1.0.90 1.0.91 1.0.92 1.0.93 1.0.94 1.0.95 1.0.96 1.0.97 1.0.98 1.0.99 All 78 releases
fluent-community / Modules / Auth / AuthModdule.php

AuthModdule.php in FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses 2.4.01, at Modules/Auth/AuthModdule.php

848 lines 36.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3
4 namespace FluentCommunity\Modules\Auth;
5
6 use FluentAuth\App\Hooks\Handlers\CustomAuthHandler;
7 use FluentCommunity\App\App;
8 use FluentCommunity\App\Functions\Utility;
9 use FluentCommunity\App\Services\AuthenticationService;
10 use FluentCommunity\App\Models\BaseSpace;
11 use FluentCommunity\App\Models\User;
12 use FluentCommunity\App\Services\FeedsHelper;
13 use FluentCommunity\App\Services\Helper;
14 use FluentCommunity\App\Services\ProfileHelper;
15 use FluentCommunity\App\Vite;
16 use FluentCommunity\Framework\Support\Arr;
17 use FluentCommunity\Modules\Auth\Classes\Invitation;
18 use FluentCommunity\Modules\Auth\Classes\InvitationHandler;
19 use FluentCommunity\Modules\Auth\Classes\InvitationService;
20
21 class AuthModdule
22 {
23 public function register($app)
24 {
25 add_action('fluent_community/portal_action_signed_url', [$this, 'maybeAutoLogin'], 10, 1);
26 add_action('fluent_community/portal_action_auth', [$this, 'viewAuthPage']);
27 add_action('wp_ajax_nopriv_fcom_user_registration', [$this, 'handleUserSignup']);
28 add_action('wp_ajax_fcom_user_registration', [$this, 'handleUserSignup']);
29 add_action('wp_ajax_nopriv_fcom_user_login_form', [$this, 'handleUserLogin']);
30 add_action('wp_ajax_fcom_user_login_form', [$this, 'handleUserLogin']);
31
32 add_filter('fluent_auth/login_redirect_url', function ($redirectUrl, $user) {
33 if (empty($_REQUEST['is_fcom_auth']) || empty($_REQUEST['fcom_redirect'])) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
34 return $redirectUrl;
35 }
36
37 // validate the url
38 $redirectUrl = wp_validate_redirect(sanitize_url(wp_unslash($_REQUEST['fcom_redirect'])), Helper::baseUrl()); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
39
40 $redirectUrl = apply_filters('fluent_community/auth/after_login_redirect_url', $redirectUrl, $user);
41 return $redirectUrl;
42 }, 10, 2);
43 }
44
45 public function maybeAutoLogin($requestData)
46 {
47 $urlHash = Arr::get($requestData, 'fcom_url_hash');
48 if ($urlHash && !get_current_user_id()) {
49 $tagetUser = ProfileHelper::getUserByUrlHash($urlHash);
50 if ($tagetUser) {
51 $willAtoLogin = apply_filters('fluent_community/allow_auto_login_by_url', !user_can($tagetUser, 'delete_pages'), $tagetUser);
52 if ($willAtoLogin) {
53 try {
54 InvitationService::makeLogin($tagetUser);
55 } catch (\Throwable $e) {
56 error_log('FluentCommunity: Auto-login failed for user #' . $tagetUser->ID . ': ' . $e->getMessage());
57 }
58 }
59 }
60 }
61
62 // Remove fcom_action and fcom_url_hash from the current url
63 $currentUrl = home_url(add_query_arg($_GET, $GLOBALS['wp']->request)); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
64 $url = remove_query_arg(['fcom_action', 'fcom_url_hash'], $currentUrl);
65 wp_redirect($url, 302); // phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect
66 exit();
67 }
68
69 public function viewAuthPage()
70 {
71
72 add_filter('login_form_defaults', function ($defaults) {
73 $defaults['label_username'] = __('Email Address', 'fluent-community');
74 return $defaults;
75 });
76
77 add_filter('fluent_community/has_color_scheme', '__return_false');
78
79 $currentUserId = get_current_user_id();
80 // check if there has any invitation token
81 $inivtationToken = Arr::get($_GET, 'invitation_token'); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
82
83 $inviation = null;
84 if ($inivtationToken) {
85 $inviation = apply_filters('fluent_community/auth/invitation', null, $inivtationToken);
86 if ($inviation && !$inviation->isValid()) {
87 $inviation = null;
88 }
89 }
90
91 if ($currentUserId && !$inviation) {
92 $redirectUrl = null;
93 if (!empty($_REQUEST['redirect_to'])) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
94 $redirectUrl = sanitize_url(wp_unslash($_REQUEST['redirect_to'])); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
95 }
96 if (!$redirectUrl) {
97 $redirectUrl = Helper::baseUrl();
98 }
99
100 wp_safe_redirect($redirectUrl);
101 exit();
102 }
103
104 if ($currentUserId && $inviation) {
105 $space = BaseSpace::withoutGlobalScopes()->find($inviation->post_id);
106 if ($space) {
107 if (Helper::isUserInSpace($currentUserId, $inviation->post_id)) {
108 // let's redirect the user to the space
109 $redirectUrl = $space->getPermalink();
110 wp_safe_redirect($redirectUrl);
111 exit();
112 }
113
114 if (!empty($_REQUEST['auto_accept'])) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
115 $redirectUrl = (new InvitationHandler())->handleInvitationLogin(Helper::baseUrl(), get_user_by('ID', $currentUserId), $inviation->message_rendered);
116 if (is_wp_error($redirectUrl) || !$redirectUrl) {
117 $redirectUrl = Helper::baseUrl();
118 }
119 wp_safe_redirect($redirectUrl);
120 exit();
121 }
122 }
123 }
124
125 do_action('fluent_community/auth/before_auth_page_process', $currentUserId, $inviation);
126
127 $acceptedForms = ['login', 'register', 'reset_password'];
128 $targetForm = Arr::get($_GET, 'form'); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
129 if (!in_array($targetForm, $acceptedForms)) {
130 $targetForm = 'login';
131 }
132
133 if ($inviation && $targetForm != 'reset_password') {
134 if ($inviation->message) {
135 $isUserAvailable = get_user_by('email', $inviation->message);
136 $targetForm = $isUserAvailable ? 'login' : 'register';
137 }
138 }
139
140 if ($inviation && $currentUserId && $inviation->isValid()) {
141 if ($inviation->message) {
142 $invitedUser = get_user_by('email', $inviation->message);
143 if ($invitedUser && $invitedUser->ID == $currentUserId) {
144 $targetForm = 'accept_invitation';
145 }
146 } else {
147 $targetForm = 'accept_invitation';
148 }
149 }
150
151 $isFluentAuth = AuthHelper::isFluentAuthAvailable();
152 if (!$isFluentAuth && $targetForm == 'reset_password') {
153 wp_safe_redirect(wp_lostpassword_url(Helper::baseUrl()));
154 exit();
155 }
156
157 $portalSettings = Helper::generalSettings();
158 $titleVar = Arr::get($portalSettings, 'site_title');
159
160 $frameData = [
161 'logo' => Arr::get($portalSettings, 'logo', ''),
162 /* translators: %s is replaced by the title of the site */
163 'title' => sprintf(__('Join %s', 'fluent-community'), $titleVar),
164 'description' => __('Login or Signup to join the community', 'fluent-community'),
165 'button_label' => __('Login', 'fluent-community'),
166 ];
167
168 if ($targetForm == 'register') {
169 $frameData['button_label'] = __('Signup', 'fluent-community');
170 if (!$inviation) {
171 $customSignupUrl = Arr::get($portalSettings, 'custom_signup_url');
172 if ($customSignupUrl) {
173 wp_safe_redirect($customSignupUrl);
174 exit();
175 }
176 }
177 }
178
179 $currentUrl = home_url(add_query_arg($_GET, $GLOBALS['wp']->request)); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
180
181 do_action('fluent_community/enqueue_global_assets', true);
182 add_action('wp_enqueue_scripts', function () use ($isFluentAuth, $targetForm, $inviation) {
183 wp_enqueue_style('fluent_auth_styles', Vite::getStaticSrcUrl('user_registration.css'), [], FLUENT_COMMUNITY_PLUGIN_VERSION);
184 if(!$isFluentAuth || $targetForm == 'register' || $inviation) {
185 wp_enqueue_script('fluent_auth_scripts', Vite::getStaticSrcUrl('user_registration.js'), [], FLUENT_COMMUNITY_PLUGIN_VERSION, true);
186 wp_localize_script('fluent_auth_scripts', 'fluentComRegistration', array(
187 'ajax_url' => admin_url('admin-ajax.php'),
188 'is_logged_in' => is_user_logged_in(),
189 'redirecting_text' => __('Redirecting...', 'fluent-community')
190 ));
191 }
192 }, 10);
193
194 $pageVars = [
195 'title' => $frameData['title'],
196 'og_title' => $frameData['title'],
197 'description' => $frameData['description'],
198 'url' => $currentUrl,
199 'featured_image' => '',
200 'css_files' => [],
201 'js_files' => [],
202 'js_vars' => [],
203 'scope' => 'user_registration',
204 'layout' => 'signup',
205 'portal' => [
206 'logo' => Arr::get($portalSettings, 'logo', ''),
207 /* translators: %s is replaced by the title of the site */
208 'title' => \sprintf(__('Welcome to %s', 'fluent-community'), Arr::get($portalSettings, 'site_title')),
209 'description' => get_bloginfo('description')
210 ]
211 ];
212
213 if (Utility::isDev()) {
214 $pageVars['js_files'] = [
215 Vite::getStaticSrcUrl('public/js/user_registration.js')
216 ];
217 }
218
219 $formType = ($targetForm == 'register') ? 'signup' : 'login';
220
221 $formSettings = AuthenticationService::getFormattedAuthSettings($formType);
222
223 if ($formSettings) {
224 $pageVars['portal'] = Arr::get($formSettings, 'banner');
225 $pageVars['portal']['form'] = Arr::get($formSettings, 'form');
226 }
227
228 add_action('fluent_community/headless/content', function ($context) use ($targetForm, $currentUrl, $frameData, $inviation, $formSettings) {
229 $preContent = apply_filters('fluent_community/auth/pre_content', '', $context, $targetForm, $frameData);
230 if ($preContent) {
231 return;
232 }
233
234 if ($targetForm == 'login') {
235 $frameData['button_label'] = Arr::get($formSettings, 'form.button_label', __('Login', 'fluent-community'));
236 $this->showLoginForm($frameData, $inviation);
237 } else if ($targetForm == 'reset_password') {
238 $frameData['title'] = __('Reset your password', 'fluent-community');
239 ?>
240 <div id="fcom_user_onboard_wrap" class="fcom_user_onboard">
241 <div class="fcom_onboard_header">
242 <div class="fcom_onboard_header_title">
243 <h2><?php echo esc_html($frameData['title']); ?></h2>
244 </div>
245 <div class="fcom_onboard_sub">
246 <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>
247 </div>
248 </div>
249 <div class="fcom_onboard_body">
250 <div class="fcom_onboard_form">
251 <?php echo do_shortcode('[fluent_auth_reset_password redirect_to="' . esc_url($currentUrl) . '"]'); ?>
252 <div class="fcom_spaced_divider">
253 <div class="fcom_alt_auth_text">
254 <a href="<?php echo esc_url(add_query_arg('form', 'login', $currentUrl)); ?>">
255 <?php esc_html_e('Back to Login', 'fluent-community'); ?>
256 </a>
257 </div>
258 </div>
259 </div>
260 </div>
261 </div>
262 <?php
263 } else if ($targetForm == 'accept_invitation') {
264 do_action('fluent_community/auth/show_invitation_for_user', $inviation, $frameData);
265 } else {
266 //check if the registration is disabled
267 if (!AuthHelper::isRegistrationEnabled()) {
268 echo '<div class="fcom_completed"><div class="fcom_complted_header"><h4>' . esc_html__('Registration is disabled for this community', 'fluent-community') . '</h4>';
269 return;
270 }
271
272 $frameData['hiddenFields'] = [
273 'register' => 'yes',
274 'action' => 'fcom_user_signup',
275 '_fcom_signup_nonce' => wp_create_nonce('fluent_auth_signup_nonce')
276 ];
277
278 $frameData['loginUrl'] = add_query_arg('form', 'login', $currentUrl);
279 $frameData = wp_parse_args(Arr::get($formSettings, 'form'), $frameData);
280
281 $this->renderRegistrationForm($frameData, $inviation);
282 }
283 }, 10, 1);
284
285 add_action('fluent_community/headless/head_early', function ($scope) use ($formSettings) {
286 $bannerColors = array_filter(Arr::only($formSettings['banner'], ['title_color', 'text_color', 'background_color']));
287 $css = Utility::getColorCssVariables(); ?>
288 <link rel="canonical" href="<?php echo esc_url(Helper::getAuthUrl()); ?>" />
289 <style>
290 .fcom_layout_side {
291 <?php foreach ($bannerColors as $colorKey => $colorValue): ?> --fcom_ <?php echo esc_html($colorKey); ?>: <?php echo esc_html($colorValue); ?>;
292 <?php endforeach; ?>
293 }
294 <?php echo esc_html($css); ?>
295 </style>
296 <?php
297 });
298
299 $pageVars['load_wp'] = 'yes';
300
301 // document title hook
302 add_filter('pre_get_document_title', function ($title) use ($frameData) {
303 return $frameData['title'];
304 }, 9999, 1);
305
306 status_header(200);
307 App::make('view')->render('headless_page', $pageVars);
308 exit(200);
309 }
310
311 public function handleUserSignup()
312 {
313 if (is_user_logged_in()) {
314 return $this->handleSignupCompleted(get_current_user_id());
315 }
316
317 if (!AuthHelper::isRegistrationEnabled()) {
318 wp_send_json([
319 'message' => esc_html__('Registration is disabled for this community', 'fluent-community')
320 ], 422);
321 }
322
323 $signupNonce = isset($_POST['_fcom_signup_nonce']) ? sanitize_text_field(wp_unslash($_POST['_fcom_signup_nonce'])) : '';
324 if (!$signupNonce || !wp_verify_nonce($signupNonce, 'fluent_auth_signup_nonce')) {
325 wp_send_json([
326 'message' => esc_html__('Invalid request. Please refresh the page and try again.', 'fluent-community')
327 ], 403);
328 }
329
330 $app = App::make('app');
331 $request = $app->make('request');
332 $fields = AuthHelper::getFormFields();
333
334 $authSettings = AuthenticationService::getAuthSettings();
335 $termsField = Arr::get($authSettings, 'signup.form.fields.terms');
336
337 $fields['terms'] = $termsField ?: $fields['terms'];
338
339 $requiredFields = array_filter($fields, function ($field) {
340 return ($field['required'] && empty($field['disabled'])) ?? false;
341 });
342
343 $keys = array_keys($fields);
344 $data = Arr::only($request->all(), $keys);
345
346 // remove space and special characters from username
347 $data['username'] = sanitize_user(strtolower(preg_replace('/[^A-Za-z0-9_]/', '', $data['username'])));
348
349 if (empty($data['username'])) {
350 wp_send_json([
351 'message' => esc_html__('Username is not valid', 'fluent-community'),
352 'errors' => [
353 'username' => __('Please provide a valid username', 'fluent-community')
354 ]
355 ], 422);
356 }
357
358 if (!ProfileHelper::isUsernameAvailable($data['username'])) {
359 wp_send_json([
360 'message' => esc_html__('Username is already taken', 'fluent-community'),
361 'errors' => [
362 'username' => __('Username is already taken. Please use a different username', 'fluent-community')
363 ]
364 ], 422);
365 }
366
367 $invitationToken = $request->get('invitation_token');
368 $invitation = null;
369 if ($invitationToken) {
370 $invitation = Invitation::where('message_rendered', $invitationToken)->first();
371 if (!$invitation) {
372 wp_send_json([
373 'message' => __('Invalid invitation token', 'fluent-community')
374 ], 422);
375 }
376
377 if ($invitation->message && $invitation->message != $data['email']) {
378 wp_send_json([
379 'message' => esc_html__('Email does not match with the invitation', 'fluent-community')
380 ], 422);
381 }
382
383 if ($invitation->message) {
384 add_filter('fluent_community/auth/two_factor_enabled', '__return_false');
385 add_filter('fluent_auth/verify_signup_email', '__return_false');
386 }
387 }
388
389 if (!$invitation && AuthenticationService::getCustomSignupPageUrl()) {
390 // we have custom signup page enabled
391 wp_send_json([
392 'message' => esc_html__('Direct Registration is disabled for this community', 'fluent-community')
393 ], 422);
394 }
395
396 $data['email'] = sanitize_email($data['email']);
397
398 $validations = [
399 'full_name' => 'required|max:100|string',
400 'username' => 'required|unique:users,user_login|unique:fcom_xprofile,username|min:4|max:30',
401 'email' => 'required|email|unique:users,user_email',
402 'password' => 'required|same:conf_password|max:50|string',
403 'conf_password' => 'required|same:password'
404 ];
405
406 if (!AuthHelper::isPasswordConfRequired()) {
407 unset($validations['conf_password']);
408 $validations['password'] = 'required|max:50|string';
409 }
410
411 foreach ($requiredFields as $key => $field) {
412 if (!isset($data[$key])) {
413 $validations[$key] = 'required';
414 }
415 }
416
417 $validator = $app->make('validator')->make($data, $validations, [
418 'username.required' => __('Username is required', 'fluent-community'),
419 'username.unique' => __('Username is already taken', 'fluent-community'),
420 'email.required' => __('Email is required', 'fluent-community'),
421 'email.email' => __('Email is not valid', 'fluent-community'),
422 'email.unique' => __('Email is already taken', 'fluent-community'),
423 'password.required' => __('Password is required', 'fluent-community'),
424 'password.same' => __('Password and confirmation password do not match', 'fluent-community'),
425 'conf_password.required' => __('Password confirmation is required', 'fluent-community'),
426 'conf_password.same' => __('Password and confirmation password do not match', 'fluent-community'),
427 'terms.required' => __('You must agree to the terms and conditions', 'fluent-community'),
428 'full_name.required' => __('Full name is required', 'fluent-community'),
429 ]);
430
431 if ($validator->fails()) {
432 wp_send_json([
433 'message' => __('Please fill in all required fields correctly.', 'fluent-community'),
434 'errors' => $validator->errors()
435 ], 422);
436 }
437
438 foreach ($data as $key => $value) {
439 // let's sanitize the data
440 $callBack = $fields[$key]['sanitize_callback'] ?? null;
441 if ($callBack) {
442 $data[$key] = call_user_func($callBack, $value);
443 }
444 }
445
446 // let's extract the full_name and set the first_name and last_name
447 if (!empty($data['full_name'])) {
448 $nameParts = explode(' ', $data['full_name']);
449 $data['first_name'] = $nameParts[0];
450 $data['last_name'] = implode(' ', array_slice($nameParts, 1));
451 unset($data['full_name']);
452 $data = array_filter($data);
453 }
454
455 $rateLimit = AuthHelper::isAuthRateLimit();
456
457 if (is_wp_error($rateLimit)) {
458 wp_send_json([
459 'message' => $rateLimit->get_error_message()
460 ], 422);
461 }
462
463 // We need two-factor authentication here
464 if (AuthHelper::isTwoFactorEnabled()) {
465 // Check if Two Factor code is given
466 $verificationToken = $request->get('__two_fa_signed_token');
467 if ($verificationToken) {
468 $code = $request->get('_email_verification_code');
469 if (!$code) {
470 wp_send_json([
471 'message' => __('Verification code is required', 'fluent-community')
472 ], 422);
473 }
474
475 $validated = AuthHelper::validateVerificationCode($code, $verificationToken, $data);
476 if (is_wp_error($validated)) {
477 wp_send_json([
478 'message' => $validated->get_error_message()
479 ], 422);
480 }
481 } else {
482 // Let's send the verification code
483 $htmlForm = AuthHelper::get2FaRegistrationCodeForm($data);
484 wp_send_json([
485 'verifcation_html' => $htmlForm
486 ]);
487 }
488 }
489
490 // let's create the user now
491 $userId = AuthHelper::registerNewUser($data['username'], $data['email'], $data['password'], [
492 'first_name' => Arr::get($data, 'first_name'),
493 'last_name' => Arr::get($data, 'last_name'),
494 'role' => get_option('default_role', 'subscriber')
495 ]);
496
497 if (is_wp_error($userId)) {
498 wp_send_json([
499 'message' => $userId->get_error_message()
500 ], 422);
501 }
502
503 $this->handleSignupCompleted($userId);
504 }
505
506 private function handleSignupViaFlentAuth($data)
507 {
508 add_action('fluent_auth/after_creating_user', function ($userId) {
509 $this->handleSignupCompleted($userId);
510 }, 1, 1);
511
512 add_filter('fluent_auth/signup_enabled', '__return_true');
513
514 (new CustomAuthHandler())->handleSignupAjax();
515 }
516
517 private function handleSignupCompleted($userId)
518 {
519 // We have the user now let's set the community membership
520 $user = User::find($userId);
521 $user->syncXProfile(true, true);
522
523 $redirectUrl = Helper::baseUrl();
524
525 if (!empty($_REQUEST['redirect_to'])) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
526 $redirectUrl = sanitize_url(wp_unslash($_REQUEST['redirect_to'])); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
527 }
528
529 $redirectUrl = apply_filters('fluent_community/auth/after_signup_redirect_url', $redirectUrl, $user, $_REQUEST); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
530 $btnText = __('Continue to the community', 'fluent-community');
531
532 $html = '<div class="fcom_completed"><div class="fcom_complted_header"><h2>' . __('Congratulations!', 'fluent-community') . '</h2>';
533 $html .= '<p>' . __('You have successfully registered to the community', 'fluent-community') . '</p></div>';
534 $html .= '<a href="' . esc_url($redirectUrl) . '" class="fcom_btn fcom_btn_success">' . $btnText . '</a>';
535 $html .= '</div>';
536
537 if (!get_current_user_id()) {
538 $wpUser = get_user_by('ID', $userId);
539 AuthHelper::makeLogin($wpUser);
540 }
541
542 wp_send_json([
543 'success_html' => $html,
544 'redirect_url' => $redirectUrl
545 ]);
546 }
547
548 public function handleUserLogin()
549 {
550 if (is_user_logged_in()) {
551 $user = get_user_by('ID', get_current_user_id());
552 return $this->handleUserLoginSuccess($user);
553 }
554
555 if (AuthHelper::isFluentAuthAvailable()) {
556 wp_send_json([
557 'message' => __('This form cannot be used to log in. Please reload the page and try again.', 'fluent-community')
558 ], 422);
559 }
560
561 $loginNonce = isset($_POST['_fcom_login_nonce']) ? sanitize_text_field(wp_unslash($_POST['_fcom_login_nonce'])) : '';
562 if (!$loginNonce || !wp_verify_nonce($loginNonce, 'fcom_user_login_nonce')) {
563 wp_send_json([
564 'message' => esc_html__('Invalid request. Please refresh the page and try again.', 'fluent-community')
565 ], 403);
566 }
567
568 $app = App::make('app');
569 $request = $app->make('request');
570
571 $data = $request->all();
572
573 $validator = $app->make('validator')->make($data, [
574 'log' => 'required',
575 'pwd' => 'required'
576 ], [
577 'log.required' => __('Email is required', 'fluent-community'),
578 'pwd.required' => __('Password is required', 'fluent-community')
579 ]);
580
581 if ($validator->fails()) {
582 wp_send_json([
583 'message' => __('Please fill all the required fields correctly', 'fluent-community'),
584 'errors' => $validator->errors()
585 ], 422);
586 }
587
588 $rateLimit = AuthHelper::isAuthRateLimit();
589 if (is_wp_error($rateLimit)) {
590 wp_send_json([
591 'message' => $rateLimit->get_error_message()
592 ], 422);
593 }
594
595 $user = wp_authenticate($data['log'], $data['pwd']);
596
597 if (is_wp_error($user)) {
598 wp_send_json([
599 'message' => $user->get_error_message()
600 ], 422);
601 }
602
603 InvitationService::makeLogin($user);
604
605 $redirectUrl = null;
606 if (!empty($_REQUEST['redirect_to'])) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
607 $redirectUrl = wp_validate_redirect(sanitize_url(wp_unslash($_REQUEST['redirect_to'])), Helper::baseUrl()); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
608 }
609
610 if (!$redirectUrl) {
611 $redirectUrl = Helper::baseUrl();
612 }
613
614 if ($invitationToken = $request->get('invitation_token')) {
615 $maybeRedirectUrl = apply_filters('fluent_community/auth/after_login_with_invitation', null, $user, $invitationToken);
616 if ($maybeRedirectUrl && !is_wp_error($maybeRedirectUrl)) {
617 $redirectUrl = $maybeRedirectUrl;
618 }
619 }
620
621 $this->handleUserLoginSuccess($user, $redirectUrl);
622 }
623
624 private function handleUserLoginSuccess($user, $redirectUrl = null)
625 {
626 if (!$redirectUrl) {
627 $redirectUrl = Helper::baseUrl();
628 }
629
630 $redirectUrl = apply_filters('fluent_community/auth/after_login_redirect_url', $redirectUrl, $user);
631 $btnText = __('Continue to the community', 'fluent-community');
632
633 $html = '<div class="fcom_completed"><div class="fcom_complted_header"><h2>' . __('Welcome back!', 'fluent-community') . '</h2>';
634 $html .= '<p>' . __('You have successfully logged in to the community', 'fluent-community') . '</p></div>';
635 $html .= '<a href="' . esc_url($redirectUrl) . '" class="fcom_btn fcom_btn_success">' . $btnText . '</a>';
636 $html .= '</div>';
637
638 wp_send_json([
639 'success_html' => $html,
640 'redirect_url' => $redirectUrl
641 ]);
642 }
643
644 public function showLoginForm($frameData, $invitation = null)
645 {
646 $portalSettings = Helper::generalSettings();
647 $isFluentAuth = AuthHelper::isFluentAuthAvailable();
648 $loginSettings = AuthenticationService::getFormattedAuthSettings('login');
649 $formSettings = Arr::get($loginSettings, 'form');
650 $currentUrl = home_url(add_query_arg($_GET, $GLOBALS['wp']->request)); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
651 /* translators: %s is replaced by the title of the site */
652 $title = sprintf(__('Login to %s', 'fluent-community'), Arr::get($portalSettings, 'site_title'));
653
654 $description = '';
655 if ($invitation) {
656 $invitationBy = $invitation->xprofile ? $invitation->xprofile->display_name : __('Someone', 'fluent-community');
657 if ($invitation->post_id) {
658 $space = BaseSpace::find($invitation->post_id);
659 if ($space) {
660 $title = $space->title . ' - ' . Arr::get($portalSettings, 'site_title');
661 }
662 }
663 /* translators: %s is replaced by the name of the inviter */
664 $inviteDescription = \sprintf(__('%s has invited you to join this community. Please login to accept your invitation.', 'fluent-community'), $invitationBy);
665 add_action('fluent_community/before_auth_form_header', function ($formType) use ($inviteDescription) {
666 ?>
667 <div class="fcom_highlight_message">
668 <?php echo wp_kses_post($inviteDescription); ?>
669 </div>
670 <?php
671 });
672 }
673
674 $signupUrl = add_query_arg('form', 'register', $currentUrl);
675
676 if (!$invitation) {
677 if ($customSignupUrl = AuthenticationService::getCustomSignupPageUrl()) {
678 $signupUrl = $customSignupUrl;
679 }
680 }
681
682 add_filter('login_form_defaults', function ($defaults) use ($invitation, $frameData) {
683 $defaults['label_log_in'] = Arr::get($frameData, 'button_label');
684 return $defaults;
685 });
686
687 if ($isFluentAuth) {
688 add_filter('login_form_top', function () use ($invitation) {
689 $reditectUrl = Arr::get($_GET, 'redirect_to'); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
690 if (!$reditectUrl) {
691 $reditectUrl = apply_filters('fluent_community/default_redirect_url', Helper::baseUrl());
692 }
693 ob_start();
694 ?>
695 <?php if ($invitation) { ?>
696 <input type="hidden" name="invitation_token" value="<?php echo esc_attr($invitation->message_rendered); ?>"/>
697 <?php } ?>
698 <input name="is_fcom_auth" type="hidden" value="yes"/>
699 <input type="hidden" name="fcom_redirect" value="<?php echo esc_url($reditectUrl); ?>"/>
700 <?php
701 return ob_get_clean();
702 });
703 ?>
704 <div id="fcom_user_onboard_wrap" class="fcom_user_onboard">
705 <div class="fcom_onboard_header">
706 <?php do_action('fluent_community/before_auth_form_header', 'login'); ?>
707 <div class="fcom_onboard_header_title">
708 <?php if (!empty($formSettings['title'])): ?>
709 <h2>
710 <?php echo esc_html($formSettings['title']); ?>
711 </h2>
712 <?php endif; ?>
713 </div>
714 <?php if (!empty($formSettings['description'])): ?>
715 <div class="fcom_onboard_sub">
716 <?php echo wp_kses_post(trim($formSettings['description'])); ?>
717 </div>
718 <?php endif; ?>
719 </div>
720 <div class="fcom_onboard_body">
721 <div class="fcom_onboard_form">
722 <?php echo do_shortcode('[fluent_auth_login redirect_to="' . esc_url($currentUrl) . '"]'); ?>
723 <div class="fcom_spaced_divider">
724 <?php if (AuthHelper::isRegistrationEnabled()): ?>
725 <div class="fcom_alt_auth_text">
726 <?php esc_html_e('Don\'t have an account?', 'fluent-community'); ?>
727 <a href="<?php echo esc_url($signupUrl); ?>">
728 <?php esc_html_e('Signup', 'fluent-community'); ?>
729 </a>
730 </div>
731 <?php endif; ?>
732 <p class="fcom_reset_pass_text">
733 <a href="<?php echo esc_url(AuthHelper::getLostPasswordUrl($currentUrl)); ?>">
734 <?php esc_html_e('Lost your password?', 'fluent-community'); ?>
735 </a>
736 </p>
737 </div>
738 </div>
739 </div>
740 </div>
741 <?php
742 return;
743 }
744
745 $frameData['redirect'] = $currentUrl;
746
747 $frameData['hiddenFields'] = [
748 'action' => 'fcom_user_login_form',
749 '_fcom_login_nonce' => wp_create_nonce('fcom_user_login_nonce'),
750 ];
751 if ($invitation) {
752 $frameData['button_label'] = __('Log In & Accept Invitation', 'fluent-community');
753 $frameData['hiddenFields']['invitation_token'] = $invitation->message_rendered;
754 }
755
756 $frameData['title'] = $title;
757 $frameData['description'] = $description;
758
759 $frameData['defaults'] = [
760 'email' => $invitation ? $invitation->message : ''
761 ];
762
763 if (AuthHelper::isRegistrationEnabled()) {
764 $frameData['signupUrl'] = $signupUrl;
765 }
766
767 $frameData['settings'] = $formSettings;
768
769 if (isset($_GET['redirect_to'])) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
770 $frameData['redirect_to'] = sanitize_url(wp_unslash($_GET['redirect_to'])); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
771 }
772
773 App::make('view')->render('auth.login_form', $frameData);
774 }
775
776 public function renderRegistrationForm($frameData, $invitation = null)
777 {
778 $formFields = AuthHelper::getFormFields($invitation);
779
780 $authSettings = AuthenticationService::getAuthSettings();
781
782 $termsField = Arr::get($authSettings, 'signup.form.fields.terms');
783
784
785 if ($termsField) {
786 unset($termsField['label']);
787
788 // add new tab on the link for $termsField['inline_label']
789 $termsField['inline_label'] = FeedsHelper::addNewTabToLinks($termsField['inline_label']);
790
791 $formFields['terms'] = $termsField;
792 }
793
794 $frameData['formFields'] = $formFields;
795
796 if ($invitation) {
797 $frameData['hiddenFields'] = [
798 'invitation_token' => $invitation->message_rendered,
799 'action' => 'fcom_user_registration',
800 '_fcom_signup_nonce' => wp_create_nonce('fluent_auth_signup_nonce')
801 ];
802
803 $invitationBy = $invitation->xprofile ? $invitation->xprofile->display_name : __('Someone', 'fluent-community');
804 /* translators: %s is replaced by the name of the inviter */
805 $inviteDescription = sprintf(__('%s has invited you to join this community. Please create an account to accept your invitation.', 'fluent-community'), $invitationBy);
806
807 add_action('fluent_community/before_auth_form_header', function ($formType) use ($inviteDescription) {
808 ?>
809 <div class="fcom_highlight_message">
810 <?php echo wp_kses_post($inviteDescription); ?>
811 </div>
812 <?php
813 });
814
815 $frameData['button_label'] = __('Register & Accept invitation', 'fluent-community');
816 } else {
817 $frameData['hiddenFields'] = [
818 'register' => 'yes',
819 'action' => 'fcom_user_registration',
820 '_fcom_signup_nonce' => wp_create_nonce('fluent_auth_signup_nonce'),
821 ];
822
823 if (!empty($_GET['redirect_to'])) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
824 $frameData['hiddenFields']['redirect_to'] = sanitize_url(wp_unslash($_GET['redirect_to'])); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
825 }
826 }
827
828 add_action('fluent_community/before_registration_form', function ($frameData) {
829 if (AuthHelper::isFluentAuthAvailable()) {
830 $currentUrl = home_url(add_query_arg($_GET, $GLOBALS['wp']->request)); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
831
832 ob_start();
833 $titlePrefix = __('Signup with', 'fluent-community');
834 do_shortcode('[fs_auth_buttons redirect="' . $currentUrl . '" title_prefix="' . $titlePrefix . ' " title=""]');
835 $html = ob_get_clean();
836
837 if ($html) {
838 echo '<div class="fcom_social_auth_wrap">';
839 echo $html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
840 echo '</div>';
841 }
842 }
843 });
844
845 App::make('view')->render('auth.user_invitation', $frameData);
846 }
847 }
848