PluginProbe
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses / 1.0.98
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses v1.0.98
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 1.0.98, at Modules/Auth/AuthModdule.php

600 lines 24.2 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\Models\BaseSpace;
10 use FluentCommunity\App\Models\User;
11 use FluentCommunity\App\Services\Helper;
12 use FluentCommunity\App\Services\ProfileHelper;
13 use FluentCommunity\App\Vite;
14 use FluentCommunity\Framework\Support\Arr;
15 use FluentCommunity\Modules\Auth\Classes\InvitationService;
16
17 class AuthModdule
18 {
19 public function register($app)
20 {
21 add_action('fluent_community/portal_action_signed_url', [$this, 'maybeAutoLogin'], 10, 1);
22 add_action('fluent_community/portal_action_auth', [$this, 'viewAuthPage']);
23 add_action('wp_ajax_nopriv_fcom_user_registration', [$this, 'handleUserSignup']);
24 add_action('wp_ajax_fcom_user_registration', [$this, 'handleUserSignup']);
25 add_action('wp_ajax_nopriv_fcom_user_login_form', [$this, 'handleUserLogin']);
26 add_action('wp_ajax_fcom_user_login_form', [$this, 'handleUserLogin']);
27 }
28
29 public function maybeAutoLogin($requestData)
30 {
31 $urlHash = Arr::get($requestData, 'fcom_url_hash');
32 if ($urlHash) {
33 $tagetUser = ProfileHelper::getUserByUrlHash($urlHash);
34
35 if ($tagetUser) {
36 $willAtoLogin = apply_filters('fluent_community/allow_auto_login_by_url', !user_can($tagetUser, 'delete_pages'), $tagetUser);
37 // $willAtoLogin = true;
38 if ($willAtoLogin) {
39 InvitationService::makeLogin($tagetUser);
40 }
41 }
42 }
43
44 // Remove fcom_action and fcom_url_hash from the current url
45 $currentUrl = home_url(add_query_arg($_GET, $GLOBALS['wp']->request));
46 $url = remove_query_arg(['fcom_action', 'fcom_url_hash'], $currentUrl);
47 wp_redirect($url);
48 exit();
49 }
50
51 public function viewAuthPage()
52 {
53 $currentUserId = get_current_user_id();
54 // check if there has any invitation token
55 $inivtationToken = Arr::get($_GET, 'invitation_token');
56
57 $inviation = null;
58 if ($inivtationToken) {
59 $inviation = apply_filters('fluent_community/auth/invitation', null, $inivtationToken);
60 }
61
62 if ($currentUserId && !$inviation) {
63 wp_redirect(Helper::baseUrl());
64 exit();
65 }
66
67 do_action('fluent_community/auth/before_auth_page_process', $currentUserId, $inviation);
68
69 $acceptedForms = ['login', 'register', 'reset_password'];
70 $targetForm = Arr::get($_GET, 'form');
71 if (!in_array($targetForm, $acceptedForms)) {
72 $targetForm = 'login';
73 }
74
75 if ($inviation && $targetForm != 'reset_password') {
76 $isUserAvailable = get_user_by('email', $inviation->message);
77 $targetForm = $isUserAvailable ? 'login' : 'register';
78 }
79
80 if ($inviation && $currentUserId) {
81 $invitedUser = get_user_by('email', $inviation->message);
82 if ($invitedUser && $invitedUser->ID == $currentUserId) {
83 $targetForm = 'accept_invitation';
84 }
85 }
86
87 $isFluentAuth = AuthHelper::isFluentAuthAvailable();
88 if (!$isFluentAuth && $targetForm == 'reset_password') {
89 wp_redirect(wp_lostpassword_url(Helper::baseUrl()));
90 exit();
91 }
92
93 $portalSettings = Helper::generalSettings();
94 $titleVar = Arr::get($portalSettings, 'site_title');
95
96 $frameData = [
97 'logo' => Arr::get($portalSettings, 'logo', ''),
98 'title' => sprintf(__('Join %s', 'fluent-community'), $titleVar),
99 'description' => __('Login or Signup to join the community', 'fluent-community'),
100 'loginBtnText' => __('Login', 'fluent-community'),
101 'signupBtnText' => __('Signup', 'fluent-community'),
102 ];
103
104 $currentUrl = home_url(add_query_arg($_GET, $GLOBALS['wp']->request));
105
106 $pageVars = [
107 'title' => $frameData['title'],
108 'description' => $frameData['description'],
109 'url' => $currentUrl,
110 'featured_image' => '',
111 'css_files' => [
112 Vite::getDynamicSrcUrl('theme-default.scss'),
113 Vite::getStaticSrcUrl('user_registration.css')
114 ],
115 'js_files' => [
116 Vite::getStaticSrcUrl('public/js/user_registration.js')
117 ],
118 'js_vars' => [
119 'fluentComRegistration' => [
120 'ajax_url' => admin_url('admin-ajax.php'),
121 'is_logged_in' => is_user_logged_in(),
122 ]
123 ],
124 'scope' => 'user_registration',
125 'layout' => 'signup',
126 'portal' => [
127 'logo' => Arr::get($portalSettings, 'logo', ''),
128 'title' => \sprintf( __('Welcome to %s', 'fluent-community'), Arr::get($portalSettings, 'site_title') ),
129 'description' => get_bloginfo('description')
130 ]
131 ];
132
133 if ($isFluentAuth) {
134 $pageVars['js_files'][] = FLUENT_AUTH_PLUGIN_URL . 'dist/public/login_helper.js';
135 $pageVars['js_vars']['fluentAuthPublic'] = [
136 'hide' => false,
137 'redirect_fallback' => site_url(),
138 'fls_login_nonce' => wp_create_nonce('fsecurity_login_nonce'),
139 'ajax_url' => admin_url('admin-ajax.php'),
140 'i18n' => [
141 'Username_or_Email' => __('Email Address', 'fluent-community'),
142 'Password' => __('Password', 'fluent-community')
143 ]
144 ];
145 }
146
147 add_action('fluent_community/headless/content', function ($context) use ($targetForm, $currentUrl, $frameData, $inviation) {
148 if ($targetForm == 'login') {
149 $this->showLoginForm($frameData, $inviation);
150 } else if ($targetForm == 'reset_password') {
151 $frameData['title'] = __('Reset your password', 'fluent-community');
152 ?>
153 <div id="fcom_user_onboard_wrap" class="fcom_user_onboard">
154 <div class="fcom_onboard_header">
155 <div class="fcom_onboard_header_title">
156 <h2><?php echo esc_html($frameData['title']); ?></h2>
157 </div>
158 </div>
159 <div class="fcom_onboard_body">
160 <div class="fcom_onboard_form">
161 <?php echo do_shortcode('[fluent_auth_reset_password redirect_to="' . esc_url($currentUrl) . '"]'); ?>
162 <div class="fcom_spaced_divider">
163 <div class="fcom_alt_auth_text">
164 <a href="<?php echo esc_url(add_query_arg('form', 'login', $currentUrl)); ?>">
165 <?php _e('Back to Login', 'fluent-community'); ?>
166 </a>
167 </div>
168 </div>
169 </div>
170 </div>
171 </div>
172 <?php
173 } else if ($targetForm == 'accept_invitation') {
174 do_action('fluent_community/auth/show_inviration_for_user', $inviation, $frameData);
175 } else {
176 //check if the registration is disabled
177 if (!AuthHelper::isRegistrationEnabled()) {
178 echo '<div class="fcom_completed"><div class="fcom_complted_header"><h4>' . __('Registration is disabled for this community', 'fluent-community') . '</h4>';
179 return;
180 }
181
182 $frameData['hiddenFields'] = [
183 'register' => 'yes',
184 'action' => 'fcom_user_signup',
185 '_fls_signup_nonce' => wp_create_nonce('fluent_auth_signup_nonce')
186 ];
187
188 $frameData['loginUrl'] = add_query_arg('form', 'login', $currentUrl);
189 $frameData['description'] = __('Create an account to join the community', 'fluent-community');
190 $this->renderRegistrationForm($frameData, $inviation);
191 }
192 }, 10, 1);
193
194 status_header(200);
195 App::make('view')->render('headless_page', $pageVars);
196 exit(200);
197 }
198
199 public function handleUserSignup()
200 {
201 if (is_user_logged_in()) {
202 return $this->handleSignupCompleted(get_current_user_id());
203 }
204
205 if (!AuthHelper::isRegistrationEnabled()) {
206 wp_send_json([
207 'message' => __('Registration is disabled for this community', 'fluent-community')
208 ], 422);
209 }
210
211 $app = App::make('app');
212 $request = $app->make('request');
213 $fields = AuthHelper::getFormFields();
214
215 $requiredFields = array_filter($fields, function ($field) {
216 return $field['required'] ?? false;
217 });
218
219 $keys = array_keys($fields);
220 $data = Arr::only($request->all(), $keys);
221
222 // remove space and special characters from username
223 $data['username'] = sanitize_user(strtolower(preg_replace('/[^A-Za-z0-9_]/', '', $data['username'])));
224
225 if (empty($data['username'])) {
226 wp_send_json([
227 'message' => __('Username is not valid', 'fluent-community'),
228 'errors' => [
229 'username' => __('Please provide a valid username', 'fluent-community')
230 ]
231 ], 422);
232 }
233
234 if (!ProfileHelper::isUsernameAvailable($data['username'])) {
235 wp_send_json([
236 'message' => __('Username is already taken', 'fluent-community'),
237 'errors' => [
238 'username' => __('Username is already taken. Please use a different username', 'fluent-community')
239 ]
240 ], 422);
241 }
242
243 $data['email'] = sanitize_email($data['email']);
244
245 $validations = [
246 'full_name' => 'required|max:100|string',
247 'username' => 'required|unique:users,user_login|unique:fcom_xprofile,username|min:4|max:30',
248 'email' => 'required|email|unique:users,user_email',
249 'password' => 'required|same:conf_password|max:50|string',
250 'conf_password' => 'required|same:password'
251 ];
252
253 if (!AuthHelper::isPasswordConfRequired()) {
254 unset($validations['conf_password']);
255 $validations['password'] = 'required|max:50|string';
256 }
257
258 foreach ($requiredFields as $key => $field) {
259 if (!isset($data[$key])) {
260 $validations[$key] = 'required';
261 }
262 }
263
264 $validator = $app->make('validator')->make($data, $validations, [
265 'username.required' => __('Username is required', 'fluent-community'),
266 'username.unique' => __('Username is already taken', 'fluent-community'),
267 'email.required' => __('Email is required', 'fluent-community'),
268 'email.email' => __('Email is not valid', 'fluent-community'),
269 'email.unique' => __('Email is already taken', 'fluent-community'),
270 'password.required' => __('Password is required', 'fluent-community'),
271 'password.same' => __('Password and confirmation password do not match', 'fluent-community'),
272 'conf_password.required' => __('Password confirmation is required', 'fluent-community'),
273 'conf_password.same' => __('Password and confirmation password do not match', 'fluent-community'),
274 'terms.required' => __('You must agree to the terms and conditions', 'fluent-community'),
275 'full_name.required' => __('Full name is required', 'fluent-community'),
276 ]);
277
278 if ($validator->fails()) {
279 wp_send_json([
280 'message' => __('Please fill in all required fields correctly.', 'fluent-community'),
281 'errors' => $validator->errors()
282 ], 422);
283 }
284
285 foreach ($data as $key => $value) {
286 // let's sanitize the data
287 $callBack = $fields[$key]['sanitize_callback'] ?? null;
288 if ($callBack) {
289 $data[$key] = call_user_func($callBack, $value);
290 }
291 }
292
293 // let's extract the full_name and set the first_name and last_name
294 if (!empty($data['full_name'])) {
295 $nameParts = explode(' ', $data['full_name']);
296 $data['first_name'] = $nameParts[0];
297 $data['last_name'] = implode(' ', array_slice($nameParts, 1));
298 unset($data['full_name']);
299 $data = array_filter($data);
300 }
301
302 if (AuthHelper::isFluentAuthAvailable()) {
303 $data = wp_parse_args($data, $request->all());
304 $this->handleSignupViaFlentAuth($data);
305 wp_send_json([
306 'message' => __('Something is not working! Please try again', 'fluent-community')
307 ], 422);
308 }
309
310 $rateLimit = AuthHelper::isAuthRateLimit();
311
312 if (is_wp_error($rateLimit)) {
313 wp_send_json([
314 'message' => $rateLimit->get_error_message()
315 ], 422);
316 }
317
318 // We need two-factor authentication here
319 if (AuthHelper::isTwoFactorEnabled()) {
320 // Check if Two Factor code is given
321 $verificationToken = $request->get('__two_fa_signed_token');
322 if ($verificationToken) {
323 $code = $request->get('_email_verification_code');
324 if (!$code) {
325 wp_send_json([
326 'message' => __('Verification code is required', 'fluent-community')
327 ], 422);
328 }
329
330 $validated = AuthHelper::validateVerificationCode($code, $verificationToken, $data);
331 if (is_wp_error($validated)) {
332 wp_send_json([
333 'message' => $validated->get_error_message()
334 ], 422);
335 }
336 } else {
337 // Let's send the verification code
338 $htmlForm = AuthHelper::get2FaRegistrationCodeForm($data);
339 wp_send_json([
340 'verifcation_html' => $htmlForm
341 ]);
342 }
343 }
344
345 // let's create the user now
346 $userId = AuthHelper::registerNewUser($data['username'], $data['email'], $data['password'], [
347 'first_name' => Arr::get($data, 'first_name'),
348 'last_name' => Arr::get($data, 'last_name'),
349 'role' => get_option('default_role', 'subscriber')
350 ]);
351
352 if (is_wp_error($userId)) {
353 wp_send_json([
354 'message' => $userId->get_error_message()
355 ], 422);
356 }
357
358 $this->handleSignupCompleted($userId);
359 }
360
361 private function handleSignupViaFlentAuth($data)
362 {
363 add_filter('fluent_auth/signup_form_data', function ($requestData) use ($data) {
364 return $data;
365 });
366
367 add_action('fluent_auth/after_creating_user', function ($userId) {
368 $this->handleSignupCompleted($userId);
369 }, 1, 1);
370
371 (new CustomAuthHandler())->handleSignupAjax();
372 }
373
374 private function handleSignupCompleted($userId)
375 {
376 // We have the user now let's set the community membership
377 $user = User::find($userId);
378 $user->syncXProfile(true, true);
379
380 $redirectUrl = Helper::baseUrl();
381
382 $redirectUrl = apply_filters('fluent_community/auth/after_signup_redirect_url', $redirectUrl, $user, $_REQUEST);
383 $btnText = __('Continue to the community', 'fluent-community');
384
385 $html = '<div class="fcom_completed"><div class="fcom_complted_header"><h2>' . __('Congratulations!', 'fluent-community') . '</h2>';
386 $html .= '<p>' . __('You have successfully registered to the community', 'fluent-community') . '</p></div>';
387 $html .= '<a href="' . $redirectUrl . '" class="fcom_btn fcom_btn_success">' . $btnText . '</a>';
388 $html .= '</div>';
389
390 if (!get_current_user_id()) {
391 $wpUser = get_user_by('ID', $userId);
392 AuthHelper::makeLogin($wpUser);
393 }
394
395 wp_send_json([
396 'success_html' => $html,
397 'redirect_url' => $redirectUrl
398 ]);
399 }
400
401 public function handleUserLogin()
402 {
403 if (is_user_logged_in()) {
404 $user = get_user_by('ID', get_current_user_id());
405 return $this->handleUserLoginSuccess($user);
406 }
407
408 if (AuthHelper::isFluentAuthAvailable()) {
409 wp_send_json([
410 'message' => __('This form cannot be used to log in. Please reload the page and try again.', 'fluent-community')
411 ], 422);
412 }
413
414 $app = App::make('app');
415 $request = $app->make('request');
416
417 $data = $request->all();
418
419 $validator = $app->make('validator')->make($data, [
420 'log' => 'required',
421 'pwd' => 'required'
422 ], [
423 'log.required' => __('Email is required', 'fluent-community'),
424 'pwd.required' => __('Password is required', 'fluent-community')
425 ]);
426
427 if ($validator->fails()) {
428 wp_send_json([
429 'message' => __('Please fill all the required fields correctly', 'fluent-community'),
430 'errors' => $validator->errors()
431 ], 422);
432 }
433
434 $rateLimit = AuthHelper::isAuthRateLimit();
435 if (is_wp_error($rateLimit)) {
436 wp_send_json([
437 'message' => $rateLimit->get_error_message()
438 ], 422);
439 }
440
441 $user = wp_authenticate($data['log'], $data['pwd']);
442
443 if (is_wp_error($user)) {
444 wp_send_json([
445 'message' => $user->get_error_message()
446 ], 422);
447 }
448
449 InvitationService::makeLogin($user);
450
451 $redirectUrl = null;
452 if (!empty($_REQUEST['redirect_to'])) {
453 $redirectUrl = sanitize_url($_REQUEST['redirect_to']);
454 }
455
456 if (!$redirectUrl) {
457 $redirectUrl = Helper::baseUrl();
458 }
459
460 if ($invitationToken = $request->get('invitation_token')) {
461 $maybeRedirectUrl = apply_filters('fluent_community/auth/after_login_with_invitation', null, $user, $invitationToken);
462 if ($maybeRedirectUrl && !is_wp_error($maybeRedirectUrl)) {
463 $redirectUrl = $maybeRedirectUrl;
464 }
465 }
466
467 $this->handleUserLoginSuccess($user, $redirectUrl);
468 }
469
470 private function handleUserLoginSuccess($user, $redirectUrl = null)
471 {
472 if (!$redirectUrl) {
473 $redirectUrl = Helper::baseUrl();
474 }
475
476 $redirectUrl = apply_filters('fluent_community/auth/after_login_redirect_url', $redirectUrl, $user);
477 $btnText = __('Continue to the community', 'fluent-community');
478
479 $html = '<div class="fcom_completed"><div class="fcom_complted_header"><h2>' . __('Welcome back!', 'fluent-community') . '</h2>';
480 $html .= '<p>' . __('You have successfully logged in to the community', 'fluent-community') . '</p></div>';
481 $html .= '<a href="' . $redirectUrl . '" class="fcom_btn fcom_btn_success">' . $btnText . '</a>';
482 $html .= '</div>';
483
484 wp_send_json([
485 'success_html' => $html,
486 'redirect_url' => $redirectUrl
487 ]);
488 }
489
490 public function showLoginForm($frameData, $invitation = null)
491 {
492 $portalSettings = Helper::generalSettings();
493 $isFluentAuth = AuthHelper::isFluentAuthAvailable();
494 $currentUrl = home_url(add_query_arg($_GET, $GLOBALS['wp']->request));
495 $title = sprintf(__('Login to %s', 'fluent-community'), Arr::get($portalSettings, 'site_title'));
496
497 $description = '';
498 if ($invitation) {
499 $invitationBy = $invitation->xprofile ? $invitation->xprofile->display_name : __('Someone', 'fluent-community');
500 if ($invitation->post_id) {
501 $space = BaseSpace::find($invitation->post_id);
502 if ($space) {
503 $title = $space->title . ' - ' . Arr::get($portalSettings, 'site_title');
504 }
505 }
506 $description = sprintf(__('%s has invited you to join this community. Please login to accept your invitation.', 'fluent-community'), $invitationBy);
507 }
508
509 if ($isFluentAuth) {
510 ?>
511 <div id="fcom_user_onboard_wrap" class="fcom_user_onboard">
512 <div class="fcom_onboard_header">
513 <div class="fcom_onboard_header_title">
514 <h2><?php echo esc_html($title); ?></h2>
515 </div>
516 <?php if ($description): ?>
517 <div class="fcom_onboard_sub">
518 <p><?php echo wp_kses_post($description); ?></p>
519 </div>
520 <?php endif; ?>
521 </div>
522 <div class="fcom_onboard_body">
523 <div class="fcom_onboard_form">
524 <?php echo do_shortcode('[fluent_auth_login redirect_to="' . esc_url($currentUrl) . '"]'); ?>
525 <div class="fcom_spaced_divider">
526 <?php if (AuthHelper::isRegistrationEnabled()): ?>
527 <div class="fcom_alt_auth_text">
528 <?php _e('Don\'t have an account?', 'fluent-community'); ?>
529 <a href="<?php echo esc_url(add_query_arg('form', 'register', $currentUrl)); ?>">
530 <?php _e('Signup', 'fluent-community'); ?>
531 </a>
532 </div>
533 <?php endif; ?>
534 <p class="fcom_reset_pass_text">
535 <a href="<?php echo esc_url(add_query_arg('form', 'reset_password', $currentUrl)); ?>">
536 <?php _e('Lost your password?', 'fluent-community'); ?>
537 </a>
538 </p>
539 </div>
540 </div>
541 </div>
542 </div>
543 <?php
544 return;
545 }
546
547 $frameData['redirect'] = $currentUrl;
548
549 $frameData['hiddenFields'] = [];
550 if ($invitation) {
551 $frameData['loginBtnText'] = __('Log In & Accept Invitation', 'fluent-community');
552 $frameData['hiddenFields']['invitation_token'] = $invitation->message_rendered;
553 }
554
555 $frameData['title'] = $title;
556 $frameData['description'] = $description;
557
558 $frameData['defaults'] = [
559 'email' => $invitation ? $invitation->message : ''
560 ];
561
562 if (AuthHelper::isRegistrationEnabled()) {
563 $frameData['signupUrl'] = add_query_arg('form', 'register', $currentUrl);
564 }
565
566 if (isset($_GET['redirect_to'])) {
567 $frameData['redirect_to'] = sanitize_url($_GET['redirect_to']);
568 }
569
570 App::make('view')->render('auth.login_form', $frameData);
571 }
572
573 public function renderRegistrationForm($frameData, $invitation = null)
574 {
575 $formFields = AuthHelper::getFormFields($invitation);
576
577 $frameData['formFields'] = $formFields;
578
579 if ($invitation) {
580 $frameData['hiddenFields'] = [
581 'invitation_token' => $invitation->message_rendered,
582 'action' => 'fcom_user_registration',
583 '_fls_signup_nonce' => wp_create_nonce('fluent_auth_signup_nonce')
584 ];
585
586 $invitationBy = $invitation->xprofile ? $invitation->xprofile->display_name : __('Someone', 'fluent-community');
587 $frameData['description'] = sprintf(__('%s has invited you to join this community. Please create an account to accept your invitation.', 'fluent-community'), $invitationBy);
588 $frameData['signupBtnText'] = __('Register & Accept invitation', 'fluent-community');
589 } else {
590 $frameData['hiddenFields'] = [
591 'register' => 'yes',
592 'action' => 'fcom_user_registration',
593 '_fls_signup_nonce' => wp_create_nonce('fluent_auth_signup_nonce')
594 ];
595 }
596
597 App::make('view')->render('auth.user_invitation', $frameData);
598 }
599 }
600