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

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