PluginProbe
Fluent Support – Helpdesk & Customer Support Ticket System / 2.4.0
Fluent Support – Helpdesk & Customer Support Ticket System v2.4.0
2.4.0 2.3.2 2.3.1 2.3.0 2.2.1 2.2.0 trunk 1.10.0 1.10.1 1.10.2 1.10.3 1.10.4 1.10.5 1.4.0 1.4.1 1.4.2 1.4.5 1.4.6 1.4.7 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 All 68 releases
← All changes | app/Http/Controllers/AuthController.php +578 -34 1.5.42.4.0 View file →
@@ -1,20 +1,37 @@
1 1 <?php
2 2
3 3 namespace FluentSupport\App\Http\Controllers;
4 4
5 +use FluentSupport\App\Models\Meta;
5 6 use FluentSupport\App\Services\Helper;
6 7 use FluentSupport\Framework\Support\Arr;
7 -use FluentSupport\Framework\Request\Request;
8 +use FluentSupport\Framework\Http\Request\Request;
8 9 use FluentSupport\App\Hooks\Handlers\AuthHandler;
10 +use FluentSupport\App\Hooks\Handlers\ReCaptchaHandler;
11 +use FluentSupport\App\Hooks\Handlers\TwoFaHandler;
12 +use FluentSupport\App\Hooks\Handlers\EmailVerificationHandler;
9 13
14 +
10 15 class AuthController extends Controller
11 16 {
17 + /**
18 + * signUp method will create new user submitted data from sign up form
19 + * @param Request $request
20 + * @return \WP_REST_Response
21 + * @throws \FluentSupport\Framework\Validator\ValidationException
22 + */
12 23 public function signup(Request $request)
13 24 {
14 25
15 - if (!wp_verify_nonce($request->get('_fsupport_signup_nonce'), 'fluent_support_signup_nonce')) {
16 - $this->sendError([
26 + if(Helper::getAuthProvider() !== 'fluent_support') {
27 + return $this->sendError([
28 + 'message' => __('You are not allowed to signup using this form', 'fluent-support')
29 + ]);
30 + }
31 +
32 + if (!wp_verify_nonce($request->getSafe('_fsupport_signup_nonce', 'sanitize_text_field'), 'fluent_support_signup_nonce')) {
33 + return $this->sendError([
17 34 'message' => __('Security verification failed. Please try again', 'fluent-support')
18 35 ]);
19 36 }
20 37
@@ -23,14 +40,140 @@
23 40 $rules = $this->getRules($fields);
24 41
25 42 $messages = $this->getMessages($rules);
26 43
44 + /*
45 + * Filter user signup form data
46 + *
47 + * @since v1.0.0
48 + * @param array $formData
49 + */
27 50 $formData = apply_filters('fluent_support/signup_form_data', $request->all());
28 51
29 - do_action('fluent_support/before_signup_validation', []);
52 + /*
53 + * Action before validate user signup
54 + *
55 + * @since v1.0.0
56 + * @param array $formData
57 + */
58 + do_action('fluent_support/before_signup_validation', $formData);
30 59
60 + // the verification code is only submitted on the second step, after the customer has
61 + // already passed the captcha and received the email; that step validates the hash
62 + // against an issued record below, so the captcha can be skipped there. The first
63 + // submit (which sends the verification email) always has to pass the captcha.
64 + $isVerificationStep = !empty($formData['_email_verification_token']);
65 +
66 + $checkRecaptchaAvailability = ReCaptchaHandler::isRecaptchaApplicable('signup_form');
67 + if ($checkRecaptchaAvailability && !$isVerificationStep) {
68 + $validateCaptcha = ReCaptchaHandler::validateRecaptcha($formData['g-recaptcha-response'] ?? '', null, null, 'signup');
69 + if (!$validateCaptcha) {
70 + return $this->response([
71 + 'message' => __('Your recaptcha is not verified', 'fluent-support')
72 + ], 422);
73 + }
74 + }
75 +
31 76 $this->validate($formData, $rules, $messages);
32 77
78 + if (!$isVerificationStep) {
79 + $tokenHtml = EmailVerificationHandler::sendSignupEmailVerificationHtml($formData);
80 +
81 + return $this->response([
82 + 'verification_html' => $tokenHtml
83 + ]);
84 + } else {
85 + $token = $formData['_email_verification_token'];
86 + $verificationHash = $formData['_email_verification_hash'] ?? '';
87 +
88 + $logHashMeta = Meta::where('object_type', 'fs_login_hashes',)
89 + ->where('key', $verificationHash)
90 + ->first();
91 +
92 + if (!$logHashMeta) {
93 + wp_send_json([
94 + 'message' => __('Please provide a valid verification code that was sent to your email address', 'fluent-support')
95 + ], 422);
96 + }
97 +
98 + $logHash = Helper::safeUnserialize($logHashMeta->value);
99 +
100 + if (!$logHash) {
101 + wp_send_json([
102 + 'message' => __('Please provide a valid verification code that was sent to your email address', 'fluent-support')
103 + ], 422);
104 + }
105 +
106 + // the code must still be unused and must not have been consumed by a prior request
107 + if (($logHash['status'] ?? '') !== 'issued') {
108 + wp_send_json([
109 + 'message' => __('Your verification code has already been used. Please try again', 'fluent-support')
110 + ], 422);
111 + }
112 +
113 + // records created before the email-binding fix (or any other legacy/malformed record)
114 + // have no bound email; treat them as invalid rather than proceeding with a null email
115 + if (empty($logHash['email'])) {
116 + wp_send_json([
117 + 'message' => __('Your verification code has expired. Please request a new one', 'fluent-support')
118 + ], 422);
119 + }
120 +
121 + // check if it got expired or not
122 + // valid_till is written via gmdate() on a current_time('timestamp') basis (always
123 + // UTC-equivalent, since WP resets the runtime timezone to UTC on every request), so
124 + // it must be parsed back as UTC here - otherwise strtotime() would silently reinterpret
125 + // it under whatever timezone a plugin/theme may have switched to via
126 + // date_default_timezone_set() without restoring it, causing false expiry (or non-expiry)
127 + $validTill = $logHash['valid_till'] ?? '';
128 + if (($logHash['used_count'] ?? 0) > 5 || ($validTill && strtotime($validTill . ' UTC') < current_time('timestamp'))) {
129 + wp_send_json([
130 + 'message' => __('Your verification code has been expired. Please try again', 'fluent-support')
131 + ], 422);
132 + }
133 +
134 + if (!wp_check_password($token, $logHash['two_fa_code_hash'])) {
135 +
136 + $logHash['used_count'] += 1;
137 + Meta::where('key', $logHash['login_hash'])->update([
138 + 'value' => maybe_serialize($logHash)
139 + ]);
140 +
141 + wp_send_json([
142 + 'message' => __('Please provide a valid verification code that was sent to your email address', 'fluent-support')
143 + ], 422);
144 + }
145 +
146 + // atomically consume the code: only succeeds if the record is still in the exact
147 + // state we just read, closing the race where two requests both pass the checks above
148 + $consumed = Meta::where('key', $logHash['login_hash'])
149 + ->where('object_type', 'fs_login_hashes')
150 + ->where('value', $logHashMeta->value)
151 + ->update([
152 + 'value' => maybe_serialize(array_merge($logHash, [
153 + 'used_count' => ($logHash['used_count'] ?? 0) + 1,
154 + 'status' => 'used',
155 + ]))
156 + ]);
157 +
158 + if (!$consumed) {
159 + wp_send_json([
160 + 'message' => __('Your verification code has already been used. Please try again', 'fluent-support')
161 + ], 422);
162 + }
163 +
164 + // the email is now server-verified for this code; ignore whatever the client
165 + // submitted and use the address the code was actually issued to, so the signup
166 + // can never be completed against a different (e.g. victim's) email address
167 + $formData['email'] = $logHash['email'];
168 + }
169 +
170 + /*
171 + * Action After validate user signup validation success
172 + *
173 + * @since v1.0.0
174 + * @param array $formData
175 + */
33 176 do_action('fluent_support/after_signup_validation', $formData);
34 177
35 178 $userId = $this->createUser($formData);
36 179
@@ -41,25 +184,49 @@
41 184 ['error' => $userId->get_error_message()]
42 185 ), 423);
43 186 }
44 187
188 + /*
189 + * Action After creating WP user from ticket sign up form
190 + *
191 + * @since v1.0.0
192 + * @param array $formData
193 + */
45 194 do_action('fluent_support/after_creating_user');
46 195
47 196 $this->maybeUpdateUser($userId, $formData);
197 + $this->addUserMetaData($userId, $formData);
48 198 $this->assignRole($userId);
49 199 $this->login($userId);
50 200
51 - return $this->response(
52 - apply_filters('fluent_support/signup_complete_response', [
53 - 'message' => __('Successfully registered to the site.', 'fluent-support'),
54 - 'redirect' => Arr::get($formData, '__redirect_to', Helper::getPortalBaseUrl())
55 - ])
56 - );
201 + /*
202 + * Filter for user signup complete message and redirect
203 + *
204 + * @since v1.0.0
205 + * @param array $response
206 + */
207 + $response = apply_filters('fluent_support/signup_complete_response', [
208 + 'message' => __('Successfully registered to the site.', 'fluent-support'),
209 + 'redirect' => Arr::get($formData, '__redirect_to', Helper::getPortalBaseUrl())
210 + ]);
211 +
212 + return $this->response($response);
57 213 }
58 214
215 + /**
216 + * handleLogin method will perform login functionality and redirect
217 + * @param Request $request
218 + * @return \WP_REST_Response
219 + */
59 220 public function handleLogin(Request $request)
60 221 {
61 - if (!wp_verify_nonce($request->get('_support_login_nonce'), 'fsupport_login_nonce')) {
222 + if(Helper::getAuthProvider() !== 'fluent_support') {
223 + return $this->sendError([
224 + 'message' => __('You are not allowed to login using this form', 'fluent-support')
225 + ]);
226 + }
227 +
228 + if (!wp_verify_nonce($request->getSafe('_support_login_nonce', 'sanitize_text_field'), 'fsupport_login_nonce')) {
62 229 return $this->response([
63 230 'message' => __('Security verification failed', 'fluent-support')
64 231 ], 403);
65 232 }
@@ -65,15 +232,28 @@
65 232 }
66 233
67 234 $data = $request->all();
68 235
236 + $checkRecaptchaAvailability = ReCaptchaHandler::isRecaptchaApplicable('login_form');
237 + if ($checkRecaptchaAvailability) {
238 + $validateCaptcha = ReCaptchaHandler::validateRecaptcha($data['g-recaptcha-response'] ?? '', null, null, 'login');
239 +
240 + if (!$validateCaptcha) {
241 + return $this->response([
242 + 'message' => __('Your recaptcha is not verified', 'fluent-support')
243 + ], 422);
244 + }
245 + }
246 +
69 247 if (empty($data['pwd']) || empty($data['log'])) {
70 248 return $this->response([
71 249 'message' => __('Email and Password is required', 'fluent-support')
72 250 ], 403);
73 251 }
74 -
75 252 $redirectUrl = Helper::getPortalBaseUrl();
253 + if ($redirect = $request->getSafe('redirect_to', 'sanitize_text_field')) {
254 + $redirectUrl = wp_validate_redirect($redirect, $redirectUrl);
255 + }
76 256
77 257 if (get_current_user_id()) { // user already registered
78 258 return $this->sendSuccess([
79 259 'redirect' => $redirectUrl
@@ -79,10 +259,10 @@
79 259 'redirect' => $redirectUrl
80 260 ]);
81 261 }
82 262
83 - $email = $data['log'];
84 - $password = $data['pwd'];
263 + $email = sanitize_user($data['log']);
264 + $password = trim($data['pwd'] ?? '');
85 265
86 266 if (is_email($email)) {
87 267 $user = get_user_by('email', $email);
88 268 } else {
@@ -88,25 +268,76 @@
88 268 } else {
89 269 $user = get_user_by('login', $email);
90 270 }
91 271
272 + // Rate limiting: per-IP bucket (5 attempts) + per-account bucket (20 attempts)
273 + $ip = Helper::getIp();
274 + $ipKey = $user
275 + ? 'fs_login_ip_' . wp_hash($user->ID . '|' . $ip)
276 + : 'fs_login_ip_' . wp_hash(strtolower($email) . '|' . $ip);
277 + $accountKey = $user
278 + ? 'fs_login_act_' . wp_hash($user->ID)
279 + : 'fs_login_act_' . wp_hash(strtolower($email));
280 +
281 + $ipAttempts = get_transient($ipKey);
282 + $accountAttempts = get_transient($accountKey);
283 +
284 + if (($ipAttempts !== false && $ipAttempts >= 5) || ($accountAttempts !== false && $accountAttempts >= 20)) {
285 + return $this->sendError([
286 + 'message' => __('Too many login attempts. Please try again after 15 minutes.', 'fluent-support')
287 + ], 429);
288 + }
289 +
92 290 if (!$user) {
291 + $user = new \WP_Error('authentication_failed', __('Invalid username, email address or incorrect password.', 'fluent-support'));
292 +
293 + do_action('wp_login_failed', $email, $user);
294 + $this->incrementLoginAttempts($ipKey);
295 + $this->incrementLoginAttempts($accountKey);
296 +
93 297 return $this->response([
94 298 'message' => __('Email or Password is not valid. Please try again', 'fluent-support')
95 299 ], 403);
300 +
96 301 }
97 302
98 - $info = array(
99 - 'user_login' => sanitize_text_field(trim($_POST['log'])),
100 - 'user_password' => trim($_POST['pwd']),
101 - 'remember' => isset($_POST['rememberme']),
102 - );
303 + $twoFactorEnabled = Helper::getBusinessSettings('enable_two_fa');
304 + if ('yes' === $twoFactorEnabled) {
305 + if (!wp_check_password($password, $user->user_pass, $user->ID)) {
306 + $this->incrementLoginAttempts($ipKey);
307 + $this->incrementLoginAttempts($accountKey);
103 308
309 + return $this->response([
310 + 'message' => __('Invalid username, email address or incorrect password.', 'fluent-support')
311 + ], 403);
312 + }
313 +
314 + (new TwoFaHandler)->maybe2FaRedirect($user);
315 + }
316 +
104 317 if (apply_filters('fluent_support_use_native_login', true)) {
105 - return $this->nativeLoginHandler($user, $info, $redirectUrl);
318 + $user = wp_signon();
319 + if (is_wp_error($user)) {
320 + $this->incrementLoginAttempts($ipKey);
321 + $this->incrementLoginAttempts($accountKey);
322 + return $this->response([
323 + 'message' => $user->get_error_message()
324 + ], 403);
325 + }
326 +
327 + // Clear rate limits for the authenticated user
328 + $authIpKey = 'fs_login_ip_' . wp_hash($user->ID . '|' . $ip);
329 + $authAccountKey = 'fs_login_act_' . wp_hash($user->ID);
330 + delete_transient($authIpKey);
331 + delete_transient($authAccountKey);
332 + return $this->sendSuccess([
333 + 'redirect' => $redirectUrl
334 + ]);
106 335 }
107 336
108 337 if (wp_check_password($password, $user->user_pass, $user->ID)) {
338 + delete_transient($ipKey);
339 + delete_transient($accountKey);
109 340 $this->login($user->ID);
110 341 return $this->sendSuccess([
111 342 'redirect' => $redirectUrl
112 343 ]);
@@ -111,13 +342,26 @@
111 342 'redirect' => $redirectUrl
112 343 ]);
113 344 }
114 345
346 + $this->incrementLoginAttempts($ipKey);
347 + $this->incrementLoginAttempts($accountKey);
348 +
115 349 return $this->response([
116 - 'message' => __('Email or Password is not valid. Please try again', 'fluent-support')
350 + 'message' => __('Invalid username, email address or incorrect password.', 'fluent-support')
117 351 ], 403);
118 352 }
119 353
354 + private function incrementLoginAttempts($rateLimitKey)
355 + {
356 + $attempts = get_transient($rateLimitKey);
357 + if ($attempts === false) {
358 + set_transient($rateLimitKey, 1, 15 * MINUTE_IN_SECONDS);
359 + } else {
360 + set_transient($rateLimitKey, $attempts + 1, 15 * MINUTE_IN_SECONDS);
361 + }
362 + }
363 +
120 364 private function nativeLoginHandler($user, $info, $redirectUrl = '')
121 365 {
122 366 if (!$redirectUrl) {
123 367 $redirectUrl = Helper::getPortalBaseUrl();
@@ -142,13 +386,15 @@
142 386 }
143 387
144 388 $user_signon = wp_signon($info, $secure_cookie);
145 389
390 + // Note: No sanitization needed here as we're only checking emptiness, not using the cookie value
146 391 if (!is_wp_error($user_signon) && empty($_COOKIE[LOGGED_IN_COOKIE])) {
147 392 if (headers_sent()) {
148 393 return $this->response([
149 - 'message' => sprintf(__('<strong>ERROR</strong>: Cookies are blocked due to unexpected output. For help, please see <a href="%1$s">this documentation</a> or try the <a href="%2$s">support forums</a>.'),
150 - __('https://codex.wordpress.org/Cookies'), __('https://wordpress.org/support/'))
394 + // translators: %1$s is the URL to WordPress cookies documentation, %2$s is the URL to WordPress support forums
395 + 'message' => sprintf(__('<strong>ERROR</strong>: Cookies are blocked due to unexpected output. For help, please see <a href="%1$s">this documentation</a> or try the <a href="%2$s">support forums</a>.', 'fluent-support'),
396 + 'https://codex.wordpress.org/Cookies', 'https://wordpress.org/support/')
151 397 ], 403);
152 398 }
153 399 }
154 400
@@ -180,8 +426,13 @@
180 426 'redirect' => $redirectUrl
181 427 ]);
182 428 }
183 429
430 + /**
431 + * getRules method will prepare the rules for the input field
432 + * @param array $fields
433 + * @return mixed
434 + */
184 435 protected function getRules($fields = [])
185 436 {
186 437 $rules = [];
187 438
@@ -197,69 +448,355 @@
197 448 } elseif ($field['type'] === 'password') {
198 449 $rules[$fieldName] = $rules[$fieldName] . $pipe . 'min:8';
199 450 }
200 451 }
452 + /*
453 + * Filter user signup validation rules
454 + *
455 + * @since v1.0.0
456 + * @param array $rules
457 + */
458 + return apply_filters('fluent_support/signup_validation_rules', $rules);
459 + }
201 460
202 - return apply_filters('fluent_support/signup_validation_rules', $rules);
461 +
462 + public function resetPassword(Request $request)
463 + {
464 +
465 + if(Helper::getAuthProvider() !== 'fluent_support') {
466 + return $this->sendError([
467 + 'message' => __('You are not allowed to reset password using this form', 'fluent-support')
468 + ]);
469 + }
470 +
471 + $errors = new \WP_Error();
472 +
473 + if (!wp_verify_nonce($request->getSafe('_fsupport_reset_pass_nonce', 'sanitize_text_field'), 'fluent_support_reset_pass_nonce')) {
474 + return $this->sendError([
475 + 'message' => __('Security verification failed. Please try again', 'fluent-support')
476 + ]);
477 + }
478 +
479 + $usernameOrEmail = trim(wp_unslash($request->getSafe('user_login', 'sanitize_text_field')));
480 +
481 + if (!$usernameOrEmail) {
482 + return $this->sendError([
483 + 'message' => 'Username or email is required'
484 + ]);
485 + }
486 +
487 + // IP bucket is a generous volumetric backstop (shared office/NAT IPs can have many
488 + // unrelated users). It runs before the account lookup so that probes for accounts
489 + // that don't exist are throttled too. Keyed on the IP only, so a 429 here reveals
490 + // nothing about whether any given account exists.
491 + if (Helper::hitRateLimit('fs_reset_pass_ip_' . wp_hash(Helper::getIp()), 20)) {
492 + return $this->sendError([
493 + 'message' => __('Too many password reset requests. Please try again after 15 minutes.', 'fluent-support')
494 + ], 429);
495 + }
496 +
497 + $user_data = get_user_by('email', $usernameOrEmail);
498 +
499 + if (!$user_data) {
500 + $user_data = get_user_by('login', $usernameOrEmail);
501 + }
502 +
503 + if (!$user_data) {
504 + return $this->sendResetPassResponse();
505 + }
506 +
507 + $user_data = apply_filters('lostpassword_user_data', $user_data, $errors);
508 +
509 + do_action('lostpassword_post', $errors, $user_data);
510 +
511 + $errors = apply_filters('lostpassword_errors', $errors, $user_data);
512 +
513 + if ($errors->has_errors()) {
514 + return $this->sendResetPassResponse();
515 + }
516 +
517 + if (!$user_data) {
518 + return $this->sendResetPassResponse();
519 + }
520 +
521 + if (is_multisite() && !is_user_member_of_blog($user_data->ID, get_current_blog_id())) {
522 + return $this->sendResetPassResponse();
523 + }
524 +
525 + // Redefining user_login ensures we return the right case in the email.
526 + $user_login = $user_data->user_login;
527 +
528 + do_action('retrieve_password', $user_login);
529 +
530 + $allow = apply_filters('allow_password_reset', true, $user_data->ID);
531 +
532 + if (!$allow || is_wp_error($allow)) {
533 + return $this->sendResetPassResponse();
534 + }
535 +
536 +
537 + /*
538 + * Filter reset password link text
539 + *
540 + * @since v1.5.7
541 + * @param string $linkText
542 + */
543 + // translators: %s is the site name
544 + $linkText = apply_filters("fluent_support/reset_password_link", sprintf(__('Reset your password for %s', 'fluent-support'), get_bloginfo('name')));
545 +
546 + // Issuance cooldown. get_password_reset_key() rotates the stored key, invalidating
547 + // any link already sitting in the account owner's inbox, so an unthrottled caller
548 + // could deny password recovery indefinitely. Suppressing the duplicate issuance is
549 + // safe: reset mail only ever goes to the account owner, so whoever triggered the
550 + // first send has already put a working link in that inbox.
551 + $cooldownKey = 'fs_reset_pass_sent_' . wp_hash($user_data->ID);
552 +
553 + if (get_transient($cooldownKey)) {
554 + return $this->sendResetPassResponse();
555 + }
556 +
557 + set_transient($cooldownKey, 1, 5 * MINUTE_IN_SECONDS);
558 +
559 + $resetUrl = add_query_arg([
560 + 'action' => 'rp',
561 + 'key' => get_password_reset_key($user_data),
562 + 'login' => rawurlencode($user_data->user_login)
563 + ], wp_login_url());
564 +
565 + $resetLink = '<a href="' . $resetUrl . '">' . $linkText . '</a>';
566 +
567 + /*
568 + * Filter reset password email subject
569 + *
570 + * @since v1.5.7
571 + * @param string $mailSubject
572 + */
573 + // translators: %s is the site name
574 + $mailSubject = apply_filters("fluent_support/reset_password_mail_subject", sprintf(__('Reset your password for %s support portal', 'fluent-support'), get_bloginfo('name')));
575 +
576 + // translators: %s is the user's first name
577 + $message = '<p>' . sprintf(__('Hi %s,', 'fluent-support'), $user_data->first_name) . '</p>' .
578 + '<p>' . __('Someone has requested a new password for the following account on WordPress:', 'fluent-support') . '</p>' .
579 + // translators: %s is the username
580 + '<p>' . sprintf(__('Username: %s', 'fluent-support'), $user_login) . '</p>' .
581 + '<p>' . $resetLink . '</p>' .
582 + '<p>' . __('If you did not request to reset your password, please ignore this email.', 'fluent-support') . '</p>';
583 +
584 + /*
585 + * Filter reset password email body text
586 + *
587 + * @since v1.5.7
588 + * @param string $message
589 + * @param object $user
590 + * @param string $resetLink
591 + */
592 + $message = apply_filters('fluent_support/reset_password_message', $message, $user_data, $resetLink);
593 +
594 + $headers = array('Content-Type: text/html; charset=UTF-8');
595 +
596 + wp_mail($user_data->user_email, $mailSubject, $message, $headers);
597 +
598 + return $this->sendResetPassResponse();
203 599 }
204 600
601 + /**
602 + * Single response for every password reset outcome.
603 + *
604 + * Whether the account exists, is disallowed, is not a member of this site or is
605 + * inside the resend cooldown, the caller sees the same thing — otherwise the form
606 + * confirms which usernames and email addresses are real.
607 + *
608 + * @return mixed
609 + */
610 + protected function sendResetPassResponse()
611 + {
612 + return $this->sendSuccess([
613 + 'message' => __('If an account matches that username or email, a password reset link has been sent. Please check your inbox, including the spam folder.', 'fluent-support')
614 + ]);
615 + }
616 +
617 + /**
618 + * getMessages message will return the validation message regarding sign up or sign in
619 + * @param array $rules
620 + * @return mixed
621 + */
205 622 protected function getMessages($rules = [])
206 623 {
624 + /*
625 + * Filter user signup validation message
626 + *
627 + * @since v1.0.0
628 + * @param array $arg
629 + * @param array $rules
630 + */
207 631 return apply_filters('fluent_support/signup_validation_messages', [], $rules);
208 632 }
209 633
634 + /**
635 + * createUser method will create new user
636 + * @param array $formData
637 + * @return mixed
638 + */
210 639 public function createUser($formData = [])
211 640 {
641 + /*
642 + * Filter user signup email
643 + *
644 + * @since v1.0.0
645 + * @param string $email
646 + */
212 647 $email = apply_filters('fluent_support/signup_email', Arr::get($formData, 'email'));
648 +
649 + /*
650 + * Filter user signup username
651 + *
652 + * @since v1.0.0
653 + * @param string $username
654 + */
213 655 $userName = apply_filters('fluent_support/signup_username', Arr::get($formData, 'username'));
214 656
215 657 if (empty($formData['password'])) {
216 - $password = wp_generate_password(8);
658 + $password = wp_generate_password(16, true, true);
217 659 } else {
218 660 $password = $formData['password'];
219 661 }
220 662
663 + /*
664 + * Filter user signup password
665 + *
666 + * @since v1.0.0
667 + * @param string $password
668 + */
221 669 $password = apply_filters('fluent_support/signup_password', $password);
222 670
671 + /*
672 + * Action before creating WP user using Fluent Support signup form
673 + *
674 + * @since v1.0.0
675 + * @param string $userName
676 + * @param string $password
677 + * @param string $email
678 + */
223 679 do_action('fluent_support/before_creating_user', $userName, $password, $email);
224 680
225 - return wp_create_user($userName, $password, $email);
681 + $userId = wp_create_user($userName, $password, $email);
682 +
683 + if (is_wp_error($userId)) {
684 + return false;
685 + }
686 +
687 + return $userId;
688 +
226 689 }
227 690
691 + /**
692 + * maybeUpdateUser method will update user information if exists
693 + * @param $userId
694 + * @param $formData
695 + */
228 696 public function maybeUpdateUser($userId, $formData)
229 697 {
230 - $name = trim(Arr::get($formData, 'first_name') . ' ' . Arr::get($formData, 'last_name'));
698 + $firstName = Arr::get($formData, 'first_name', '');
699 + $lastName = Arr::get($formData, 'last_name', '');
700 + $name = trim($firstName . ' ' . $lastName);
231 701
232 702 $data = array_filter([
233 - 'ID' => $userId,
703 + 'ID' => $userId,
234 704 'user_nicename' => $name,
235 - 'display_name' => $name,
236 - 'first_name' => Arr::get($formData, 'first_name'),
237 - 'last_name' => Arr::get($formData, 'last_name'),
705 + 'display_name' => $name,
706 + 'first_name' => $firstName,
707 + 'last_name' => $lastName,
238 708 ]);
239 709
240 710 if ($name) {
711 + /*
712 + * Action before updating a customer/user
713 + *
714 + * @since v1.0.0
715 + * @param array $data
716 + */
241 717 do_action('fluent_support/before_updating_user', $data);
242 718
243 - wp_update_user(apply_filters('fluent_support/update_user_data', $data));
719 + /*
720 + * Filter user updatable data
721 + *
722 + * @since v1.0.0
723 + * @param $data
724 + */
725 + $updateUserData = apply_filters('fluent_support/update_user_data', $data);
726 + wp_update_user($updateUserData);
244 727
728 + /*
729 + * Action after updating a customer/user
730 + *
731 + * @since v1.0.0
732 + * @param array $data
733 + */
245 734 do_action('fluent_support/after_updating_user', $data);
246 735 }
247 736 }
248 737
738 + public function addUserMetaData($userId, $formData) {
739 + $customFieldsKey = apply_filters('fluent_support/custom_registration_form_fields_key', Helper::getBusinessSettings('custom_registration_form_field'));
740 +
741 + if (empty($customFieldsKey)) {
742 + return;
743 + }
744 +
745 + foreach ($customFieldsKey as $key) {
746 + if (isset($formData[$key])) {
747 + $fieldValue = $formData[$key];
748 + update_user_meta($userId, $key, $fieldValue);
749 + }
750 + }
751 + }
752 +
753 + /**
754 + * assignRole method will assign role to a given user id
755 + * @param $userId
756 + */
249 757 protected function assignRole($userId)
250 758 {
251 759 $user = new \WP_User($userId);
252 760
761 + /*
762 + * Action before assigning role to registered user
763 + *
764 + * @since v1.0.0
765 + * @param array $data
766 + */
253 767 do_action('fluent_support/before_assigning_role', $user);
768 + /*
769 + * Filter user assignable role after signup
770 + *
771 + * @since v1.0.0
772 + * @param string $setRole WordPress user role key
773 + */
774 + $setRole = apply_filters('fluent_support/user_role', 'subscriber');
775 + $user->set_role($setRole);
254 776
255 - $user->set_role(apply_filters('fluent_support/user_role', 'subscriber'));
256 -
777 + /*
778 + * Action after assigning role to registered user
779 + *
780 + * @since v1.0.0
781 + * @param array $data
782 + */
257 783 do_action('fluent_support/after_assigning_role', $user);
258 784 }
259 785
786 +
787 + /**
788 + * login method will clear existing cookies and set new cookie for a given user id
789 + * @param $userId
790 + */
260 791 protected function login($userId)
261 792 {
793 + /*
794 + * Action before login
795 + *
796 + * @since v1.0.0
797 + * @param integer $userId
798 + */
262 799 do_action('fluent_support/before_logging_in_user', $userId);
263 800
264 801 wp_clear_auth_cookie();
265 802 wp_set_current_user($userId);
@@ -264,7 +801,14 @@
264 801 wp_clear_auth_cookie();
265 802 wp_set_current_user($userId);
266 803 wp_set_auth_cookie($userId);
267 804
805 + /*
806 + * Action after login
807 + *
808 + * @since v1.0.0
809 + * @param integer $userId
810 + */
268 811 do_action('fluent_support/after_logging_in_user', $userId);
269 812 }
813 +
270 814 }