PluginProbe
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses / 2.6.0
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses v2.6.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 1.1.0 All 77 releases
fluent-community / Modules / Auth / AuthModdule.php

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

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