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
fluent-support / app / Hooks / Handlers / AuthHandler.php

AuthHandler.php in Fluent Support – Helpdesk & Customer Support Ticket System 2.4.0, at app/Hooks/Handlers/AuthHandler.php

598 lines 22.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentSupport\App\Hooks\Handlers;
4
5 use FluentSupport\App\App;
6 use FluentSupport\App\Modules\PermissionManager;
7 use FluentSupport\App\Services\Helper;
8 use FluentSupport\App\Services\Includes\CountryNames;
9 use FluentSupport\App\Vite;
10 use FluentSupport\Framework\Support\Arr;
11 use FluentSupport\App\Models\Meta;
12
13 class AuthHandler
14 {
15
16 protected $loaded = false;
17
18 public function init()
19 {
20 add_shortcode('fluent_support_login', array($this, 'loginForm'));
21 add_shortcode('fluent_support_signup', array($this, 'registrationForm'));
22 add_shortcode('fluent_support_auth', array($this, 'authForm'));
23 add_shortcode('fluent_support_reset_password', array($this, 'restPasswordForm'));
24 add_action('wp_ajax_fluent_support_renew_rest_nonce', [$this, 'maybeRenewNonce']);
25 }
26
27 /**
28 * loginForm will generate html for login form
29 * @param $attributes
30 * @return string
31 */
32 public function loginForm($attributes)
33 {
34 if (get_current_user_id()) {
35 return '<p>' . __('You are already logged in.', 'fluent-support') . '</p>';
36 }
37
38 $attributes = $this->getShortcodes($attributes);
39 if (!empty($attributes['redirect-to'])) {
40 $redirect = $attributes['redirect-to'];
41 } else {
42 $redirect = Helper::getPortalBaseUrl();
43 }
44
45
46 $this->handleAlreadyLoggedIn($attributes);
47
48 if ($this->authProvider() == 'fluent_auth') {
49 // Will be handled by FluentAuth plugin
50 $attributes['redirect_to'] = $redirect;
51 return (new \FluentAuth\App\Hooks\Handlers\CustomAuthHandler())->loginForm($attributes);
52 }
53
54 $this->loadAssets();
55
56 $return = '<div class="fst_login_form_auth_wrapper">';
57 $return .= '<div id="fst_login_form" class="fst_login_wrapper">';
58
59
60 /*
61 * Filter login form
62 *
63 * @since v1.0.0
64 *
65 * @param array $loginArgs
66 */
67 $loginArgs = apply_filters('fluent_support/login_form_args', [
68 'echo' => false,
69 'redirect' => $redirect,
70 'remember' => true,
71 'value_remember' => true,
72 ]);
73
74 $return .= wp_login_form($loginArgs);
75
76 if ($attributes['show-signup'] == 'true') {
77 $return .= '<p style="text-align: center">'
78 . __('Not registered?', 'fluent-support')
79 . ' <a href="#" id="fs_show_signup">'
80 . __('Create an account', 'fluent-support')
81 . '</a></p>';
82 }
83
84 if ($attributes['show-reset-password'] == 'true') {
85 $return .= '<p style="text-align: center">'
86 . __('Forgot your password?', 'fluent-support')
87 . ' <a href="#" id="fs_show_reset_password">'
88 . __('Reset password', 'fluent-support')
89 . '</a></p>';
90 }
91
92 $return .= '</div>';
93
94 if ($attributes['show-signup'] == 'true') {
95 $return .= do_shortcode('[fluent_support_signup hide=true]');
96 }
97
98 if ($attributes['show-reset-password'] == 'true') {
99 $return .= do_shortcode('[fluent_support_reset_password hide=true]');
100 }
101
102 $return .= '</div>';
103 return $return;
104 }
105
106 /**
107 * registrationForm method will generate html for sign up form
108 * @param $attributes
109 * @return string
110 */
111 public function registrationForm($attributes)
112 {
113 if (get_current_user_id()) {
114 return '<p>' . __('You are already logged in.', 'fluent-support') . '</p>';
115 }
116
117 $attributes = $this->getShortcodes($attributes);
118 $this->handleAlreadyLoggedIn($attributes);
119
120 if ($this->authProvider() == 'fluent_auth') {
121 return (new \FluentAuth\App\Hooks\Handlers\CustomAuthHandler())->registrationForm($attributes);
122 }
123
124 $customFieldsKey = apply_filters('fluent_support/custom_registration_form_fields_key', Helper::getBusinessSettings('custom_registration_form_field'));
125
126 if (!empty($customFieldsKey)) {
127 add_filter('fluent_support/registration_form_fields', function($fields) use ($customFieldsKey) {
128 return $this->addCustomFieldsToRegistrationForm($fields,$customFieldsKey);
129 });
130 }
131
132 $registrationFields = static::getSignupFields();
133 $hide = $attributes['hide'] == 'true' ? 'hide' : '';
134
135 $this->loadAssets($hide);
136
137 return $this->buildRegistrationForm($registrationFields, $hide, $attributes);
138 }
139
140 // This method `buildRegistrationForm` will generate html for sign up form
141 private function buildRegistrationForm($registrationFields, $hide, $attributes)
142 {
143 $registrationForm = '<div class="fst_registration_wrapper ' . $hide . '"><form id="fstRegistrationForm" class="fs_registration_form" method="post" name="fs_registration_form">';
144
145 foreach ($registrationFields as $fieldName => $registrationField) {
146 $registrationForm .= $this->renderField($fieldName, $registrationField);
147 }
148
149 $registrationForm .= '<input type="hidden" name="__redirect_to" value="' . esc_url($attributes['redirect-to']) . '">';
150 $registrationForm .= '<input type="hidden" name="_fsupport_signup_nonce" value="' . wp_create_nonce('fluent_support_signup_nonce') . '">';
151 $registrationForm .= '<button type="submit" id="fst_submit">' . $this->submitBtnLoadingSvg() . '<span>' . __('Signup', 'fluent-support') . '</span></button>';
152
153 $registrationForm .= '</form>';
154
155 $registrationForm .= apply_filters('fluent_support/before_registration_form_close', '', $registrationFields, $attributes);
156
157 if ($hide) {
158 $registrationForm .= '<p style="text-align: center">'
159 . __('Already have an account?', 'fluent-support')
160 . ' <a href="#" id="fs_show_login">'
161 . __('Login', 'fluent-support')
162 . '</a></p>';
163 }
164
165 $registrationForm .= '</div>';
166
167 return $registrationForm;
168 }
169
170 public function restPasswordForm($attributes)
171 {
172 if (get_current_user_id()) {
173 return '<p>' . __('You are already logged in.', 'fluent-support') . '</p>';
174 }
175
176 $attributes = $this->getShortcodes($attributes);
177
178 if ($this->authProvider() == 'fluent_auth') {
179 return (new \FluentAuth\App\Hooks\Handlers\CustomAuthHandler())->restPasswordForm($attributes);
180 }
181
182 $this->handleAlreadyLoggedIn($attributes);
183
184 $resetPasswordFields = static::resetPasswordFields();
185 $hide = $attributes['hide'] == 'true' ? 'hide' : '';
186
187 $this->loadAssets($hide);
188
189 return $this->buildResetPassForm($resetPasswordFields, $hide, $attributes);
190 }
191
192 // This method `buildResetPassForm` will generate html for password reset form
193 private function buildResetPassForm($resetPasswordFields, $hide, $attributes)
194 {
195 $restePasswordForm = '<div class="fst_reset_pass_wrapper ' . $hide . '"><form id="fstResetPasswordForm" class="fs_reset_pass_form" method="post" name="fs_reset_pass_form">';
196
197 foreach ($resetPasswordFields as $fieldName => $resetPasswordField) {
198 $restePasswordForm .= $this->renderField($fieldName, $resetPasswordField);
199 }
200
201 $restePasswordForm .= '<input type="hidden" name="__redirect_to" value="' . esc_url($attributes['redirect-to']) . '">';
202 $restePasswordForm .= '<input type="hidden" name="_fsupport_reset_pass_nonce" value="' . wp_create_nonce('fluent_support_reset_pass_nonce') . '">';
203 $restePasswordForm .= '<button type="submit" id="fst_reset_pass">' . $this->submitBtnLoadingSvg() . '<span>' . __('Reset Password', 'fluent-support') . '</span></button>';
204
205 $restePasswordForm .= '</form>';
206
207 $restePasswordForm .= '</div>';
208
209 return $restePasswordForm;
210 }
211
212 /**
213 * authForm will render the login form html
214 * @param $attributes
215 * @return string
216 */
217 public function authForm($attributes)
218 {
219 if (get_current_user_id()) {
220 // translators: %s is the URL to the support portal
221 return '<p>' . sprintf(__('You are already logged in. <a href="%s">Go to support portal</a>', 'fluent-support'), Helper::getPortalBaseUrl()) . '</p>';
222 }
223
224 $attributes = $this->getShortcodes($attributes, true);
225
226 if ($this->authProvider() == 'fluent_auth') {
227 return (new \FluentAuth\App\Hooks\Handlers\CustomAuthHandler())->authForm($attributes);
228 }
229
230 return $this->loginForm($attributes);
231 }
232
233 /**
234 * renderField method will generate html for a field
235 * @param $fieldName
236 * @param $field
237 * @return string
238 */
239 private function renderField($fieldName, $field)
240 {
241 $fieldType = Arr::get($field, 'type');
242 $isRequired = Arr::get($field, 'required');
243 $isRequired = $isRequired ? 'is-required' : '';
244
245 $html = '<div class="fst_field_group fst_field_' . $fieldName . '">';
246
247 if ($label = Arr::get($field, 'label')) {
248 $html .= '<div class="fst_field_label ' . $isRequired . '"><label for="' . Arr::get($field, 'id') . '">' . $label . '</label></div>';
249 }
250
251 $textTypes = ['text', 'email', 'password', 'number', 'date'];
252 $selectTypes = ['select']; // Add 'select' type for dropdowns
253
254 if (in_array($fieldType, $textTypes)) {
255 $html .= $this->renderTextInput($fieldName, $field);
256 } elseif (in_array($fieldType, $selectTypes)) {
257 $html .= $this->renderSelectInput($fieldName, $field);
258 } else {
259 return '';
260 }
261
262 $html = $html . '</div>';
263
264 return '<div class="fst_registration_fields">' . $html . '</div>';
265 }
266
267 private function renderTextInput($fieldName, $field)
268 {
269 $inputAtts = array_filter([
270 'type' => esc_attr(Arr::get($field, 'type', '')),
271 'id' => esc_attr(Arr::get($field, 'id', '')),
272 'placeholder' => esc_attr(Arr::get($field, 'placeholder', '')),
273 'name' => esc_attr($fieldName),
274 'required' => Arr::get($field, 'required') ? 'required' : '',
275 ]);
276
277 $atts = '';
278
279 foreach ($inputAtts as $attKey => $att) {
280 $atts .= $attKey . '="' . $att . '" ';
281 }
282
283 return '<div class="fs_input_wrap"><input ' . $atts . ' /></div>';
284 }
285
286 /**
287 * Render a select input field based on the provided field configuration.
288 *
289 * @param string $fieldName The name of the select input field.
290 * @param array $field The field configuration containing options and attributes.
291 *
292 * @return string The rendered select input field HTML.
293 */
294 private function renderSelectInput($fieldName, $field)
295 {
296 $choices = Arr::get($field, 'options', []);
297 $placeholder = Arr::get($field, 'placeholder', '');
298
299 $options = '<option value="" disabled selected>' . esc_attr($placeholder) . '</option>';
300
301 foreach ($choices as $value => $optionData) {
302 $options .= '<option value="' . esc_attr($value) . '">' . esc_attr($optionData) . '</option>';
303 }
304
305 $selectAtts = [
306 'id' => esc_attr(Arr::get($field, 'id', '')),
307 'name' => esc_attr($fieldName),
308 'required' => Arr::get($field, 'required') ? 'required' : '',
309 ];
310
311 $select = '<div class="fs_input_wrap"><select ' . $this->renderAttributes($selectAtts) . '>' . $options . '</select></div>';
312
313 return $select;
314 }
315
316 private function renderAttributes($attributes)
317 {
318 $atts = '';
319
320 foreach ($attributes as $attKey => $att) {
321 $atts .= $attKey . '="' . $att . '" ';
322 }
323
324 return $atts;
325 }
326
327 /**
328 * getSignupFields method will return the list of fields that will be used for sign up form
329 * @return mixed
330 */
331 public static function getSignupFields()
332 {
333 /*
334 * Filter signup form field
335 *
336 * @since v1.0.0
337 *
338 * @param array $fields Form fields
339 */
340 return apply_filters('fluent_support/registration_form_fields', [
341 'first_name' => [
342 'required' => true,
343 'type' => 'text',
344 'label' => __('First name', 'fluent-support'),
345 'id' => 'fst_first_name',
346 'placeholder' => __('First name', 'fluent-support')
347 ],
348 'last_name' => [
349 'type' => 'text',
350 'label' => __('Last Name', 'fluent-support'),
351 'id' => 'fst_last_name',
352 'placeholder' => __('Last name', 'fluent-support')
353 ],
354 'username' => [
355 'required' => true,
356 'type' => 'text',
357 'label' => __('Username', 'fluent-support'),
358 'id' => 'fst_username',
359 'placeholder' => __('Username', 'fluent-support')
360 ],
361 'email' => [
362 'required' => true,
363 'type' => 'email',
364 'label' => __('Email Address', 'fluent-support'),
365 'id' => 'fst_email',
366 'placeholder' => __('Your Email Address', 'fluent-support')
367 ],
368 'password' => [
369 'required' => true,
370 'type' => 'password',
371 'label' => __('Password', 'fluent-support'),
372 'id' => 'fst_password',
373 'placeholder' => __('Password', 'fluent-support')
374 ]
375 ]);
376 }
377
378 private function addCustomFieldsToRegistrationForm($fields, $customFieldsKey) {
379 $customFields = $this->allCustomFields();
380
381 foreach ($customFieldsKey as $key) {
382 if (isset($customFields[$key])) {
383 $fields[$key] = $customFields[$key];
384 }
385 }
386
387 return $fields;
388 }
389
390 public static function allCustomFields(): array {
391 $countryList = CountryNames::get();
392
393 return apply_filters('fluent_support/custom_registration_form_fields', [
394 'address_line_1' => [
395 'required' => false,
396 'type' => 'text',
397 'label' => __('Address Line 1', 'fluent-support'),
398 'id' => 'fst_address_line_1',
399 'placeholder' => __('Address Line 1', 'fluent-support'),
400 ],
401 'address_line_2' => [
402 'required' => false,
403 'type' => 'text',
404 'label' => __('Address Line 2', 'fluent-support'),
405 'id' => 'fst_address_line_2',
406 'placeholder' => __('Address Line 2', 'fluent-support'),
407 ],
408 'city' => [
409 'required' => false,
410 'type' => 'text',
411 'label' => __('City', 'fluent-support'),
412 'id' => 'fst_city',
413 'placeholder' => __('City', 'fluent-support'),
414 ],
415 'zip' => [
416 'required' => false,
417 'type' => 'text',
418 'label' => __('Zip', 'fluent-support'),
419 'id' => 'fst_zip',
420 'placeholder' => __('Zip', 'fluent-support'),
421 ],
422 'state' => [
423 'required' => false,
424 'type' => 'text',
425 'label' => __('State', 'fluent-support'),
426 'id' => 'fst_state',
427 'placeholder' => __('State', 'fluent-support'),
428 ],
429 'country' => [
430 'required' => false,
431 'type' => 'select',
432 'label' => __('Country', 'fluent-support'),
433 'id' => 'fst_country',
434 'placeholder' => __('Select a Country', 'fluent-support'),
435 'options' => $countryList,
436 ],
437 ]);
438 }
439
440 public static function resetPasswordFields()
441 {
442 /*
443 * Filter reset password form field
444 *
445 * @since v1.5.7
446 *
447 * @param array $fields Form fields
448 */
449 return apply_filters('fluent_support/reset_password_form', [
450 'user_login' => [
451 'required' => true,
452 'type' => 'text',
453 'label' => __('Email Address', 'fluent-support'),
454 'id' => 'fst_reset_email',
455 'placeholder' => __('Your Email Address', 'fluent-support')
456 ]
457 ]);
458 }
459
460 protected function submitBtnLoadingSvg()
461 {
462 $loadingIcon = '<svg version="1.1" id="loader-1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
463 width="40px" height="20px" viewBox="0 0 50 50" style="enable-background:new 0 0 50 50;" xml:space="preserve">
464 <path fill="#000" d="M43.935,25.145c0-10.318-8.364-18.683-18.683-18.683c-10.318,0-18.683,8.365-18.683,18.683h4.068c0-8.071,6.543-14.615,14.615-14.615c8.072,0,14.615,6.543,14.615,14.615H43.935z">
465 <animateTransform attributeType="xml"
466 attributeName="transform"
467 type="rotate"
468 from="0 25 25"
469 to="360 25 25"
470 dur="0.6s"
471 repeatCount="indefinite"/>
472 </path>
473 </svg>';
474
475 /*
476 * Filter signup form loading icon
477 *
478 * @since v1.0.0
479 *
480 * @param string $loadingIcon this accepts html element
481 */
482 return apply_filters('fluent_support/signup_loading_icon', $loadingIcon);
483 }
484
485 protected function getShortcodes($attributes, $isAuthForm = false)
486 {
487 /*
488 * Filter shortcode behavior for agent
489 *
490 * @since v1.0.0
491 *
492 * @param array $shortCodeDefaults
493 */
494 $shortCodeDefaults = apply_filters('fluent_support/auth_shortcode_defaults', [
495 'auto-redirect' => false,
496 'redirect-to' => Helper::getPortalBaseUrl(),
497 'hide' => false,
498 'show-signup' => $isAuthForm ? 'true' : 'false',
499 'show-reset-password' => $isAuthForm ? 'true' : 'false',
500 ]);
501
502 $attributes = shortcode_atts($shortCodeDefaults, $attributes);
503
504 if (isset($attributes['redirect-to'])) {
505 $attributes['redirect-to'] = esc_url_raw($attributes['redirect-to']);
506 $attributes['redirect_to'] = $attributes['redirect-to'];
507 }
508
509 return $attributes;
510
511 }
512
513 protected function handleAlreadyLoggedIn($attributes)
514 {
515 if (get_current_user_id() && !wp_is_json_request() && is_singular()) {
516 if ($attributes['auto-redirect'] === 'true') {
517 $redirect = $attributes['redirect-to'];
518 ?>
519 <script type="text/javascript">
520 document.addEventListener("DOMContentLoaded", function () {
521 var redirect = "<?php echo esc_url($redirect); ?>";
522 window.location.replace(redirect);
523 });
524 </script>
525 <?php
526 }
527 die();
528 }
529 }
530
531 public function loadAssets($hide = '')
532 {
533 if ($this->loaded) {
534 return false;
535 }
536
537 $app = App::getInstance();
538 $assets = $app['url.assets'];
539
540 if (is_rtl()) {
541 wp_enqueue_style('fluent_support_login_style_rtl', $assets . 'admin/css/all_public-rtl.css', [], FLUENT_SUPPORT_VERSION);
542 } else {
543 wp_enqueue_style('fluent_support_login_style', Vite::getEnqueuePath('admin/css/all_public.css'), [], FLUENT_SUPPORT_VERSION);
544 }
545
546 wp_enqueue_script('fluent_support_login_helper', Vite::getEnqueuePath('portal/js/login_helper.js'), [], FLUENT_SUPPORT_VERSION);
547
548 //Get Recaptcha settings and enqueue recaptcha script
549 $reCaptchaSettingsData = Meta::where('object_type', '_fs_recaptcha_settings')->first();
550 $reCaptchaData = ($reCaptchaSettingsData) ? Helper::safeUnserialize($reCaptchaSettingsData->value, []) : '';
551
552 if (!empty($reCaptchaData) && isset($reCaptchaData['is_enabled']) && $reCaptchaData['is_enabled'] == "true") {
553 unset($reCaptchaData['secretKey']);
554 $recaptchaVersion = $reCaptchaData["reCaptcha_version"];
555 $reCaptchaApiUrl = 'https://www.google.com/recaptcha/api.js';
556
557 if ("recaptcha_v3" === $recaptchaVersion) {
558 $reCaptchaApiUrl .= '?render=' . $reCaptchaData["siteKey"];
559 }
560
561 wp_enqueue_script('recaptcha', $reCaptchaApiUrl);
562 }
563
564 wp_localize_script('fluent_support_login_helper', 'fluentSupportPublic', [
565 'signup' => rest_url($app->config->get('app.rest_namespace') . '/' . $app->config->get('app.rest_version')) . '/signup',
566 'login' => rest_url($app->config->get('app.rest_namespace') . '/' . $app->config->get('app.rest_version')) . '/login',
567 'nonce' => wp_create_nonce('wp_rest'),
568 'hide' => $hide,
569 'redirect_fallback' => Helper::getPortalBaseUrl(),
570 'fsupport_login_nonce' => wp_create_nonce('fsupport_login_nonce'),
571 'resetPass' => rest_url($app->config->get('app.rest_namespace') . '/' . $app->config->get('app.rest_version')) . '/reset_pass',
572 'reCaptchaSettingsData' => $reCaptchaData,
573 'fs_two_fa' => rest_url($app->config->get('app.rest_namespace') . '/' . $app->config->get('app.rest_version')) . '/two_fa'
574 ]);
575
576
577 $this->loaded = true;
578 }
579
580 public function maybeRenewNonce()
581 {
582 if (!PermissionManager::currentUserPermissions()) {
583 wp_send_json([
584 'error' => 'You do not have permission to do this'
585 ], 403);
586 }
587
588 wp_send_json([
589 'nonce' => wp_create_nonce('wp_rest')
590 ], 200);
591 }
592
593 private function authProvider()
594 {
595 return \FluentSupport\App\Services\Helper::getAuthProvider();
596 }
597 }
598