PluginProbe
Fluent Support – Helpdesk & Customer Support Ticket System / 2.2.0
Fluent Support – Helpdesk & Customer Support Ticket System v2.2.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.2.0, at app/Hooks/Handlers/AuthHandler.php

595 lines 21.8 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="' . $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="' . $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 $fields[$key] = $customFields[$key];
383 }
384
385 return $fields;
386 }
387
388 public static function allCustomFields(): array {
389 $countryList = CountryNames::get();
390
391 return apply_filters('fluent_support/custom_registration_form_fields', [
392 'address_line_1' => [
393 'required' => false,
394 'type' => 'text',
395 'label' => __('Address Line 1', 'fluent-support'),
396 'id' => 'fst_address_line_1',
397 'placeholder' => __('Address Line 1', 'fluent-support'),
398 ],
399 'address_line_2' => [
400 'required' => false,
401 'type' => 'text',
402 'label' => __('Address Line 2', 'fluent-support'),
403 'id' => 'fst_address_line_2',
404 'placeholder' => __('Address Line 2', 'fluent-support'),
405 ],
406 'city' => [
407 'required' => false,
408 'type' => 'text',
409 'label' => __('City', 'fluent-support'),
410 'id' => 'fst_city',
411 'placeholder' => __('City', 'fluent-support'),
412 ],
413 'zip' => [
414 'required' => false,
415 'type' => 'text',
416 'label' => __('Zip', 'fluent-support'),
417 'id' => 'fst_zip',
418 'placeholder' => __('Zip', 'fluent-support'),
419 ],
420 'state' => [
421 'required' => false,
422 'type' => 'text',
423 'label' => __('State', 'fluent-support'),
424 'id' => 'fst_state',
425 'placeholder' => __('State', 'fluent-support'),
426 ],
427 'country' => [
428 'required' => false,
429 'type' => 'select',
430 'label' => __('Country', 'fluent-support'),
431 'id' => 'fst_country',
432 'placeholder' => __('Select a Country', 'fluent-support'),
433 'options' => $countryList,
434 ],
435 ]);
436 }
437
438 public static function resetPasswordFields()
439 {
440 /*
441 * Filter reset password form field
442 *
443 * @since v1.5.7
444 *
445 * @param array $fields Form fields
446 */
447 return apply_filters('fluent_support/reset_password_form', [
448 'user_login' => [
449 'required' => true,
450 'type' => 'text',
451 'label' => __('Email Address', 'fluent-support'),
452 'id' => 'fst_reset_email',
453 'placeholder' => __('Your Email Address', 'fluent-support')
454 ]
455 ]);
456 }
457
458 protected function submitBtnLoadingSvg()
459 {
460 $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"
461 width="40px" height="20px" viewBox="0 0 50 50" style="enable-background:new 0 0 50 50;" xml:space="preserve">
462 <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">
463 <animateTransform attributeType="xml"
464 attributeName="transform"
465 type="rotate"
466 from="0 25 25"
467 to="360 25 25"
468 dur="0.6s"
469 repeatCount="indefinite"/>
470 </path>
471 </svg>';
472
473 /*
474 * Filter signup form loading icon
475 *
476 * @since v1.0.0
477 *
478 * @param string $loadingIcon this accepts html element
479 */
480 return apply_filters('fluent_support/signup_loading_icon', $loadingIcon);
481 }
482
483 protected function getShortcodes($attributes, $isAuthForm = false)
484 {
485 /*
486 * Filter shortcode behavior for agent
487 *
488 * @since v1.0.0
489 *
490 * @param array $shortCodeDefaults
491 */
492 $shortCodeDefaults = apply_filters('fluent_support/auth_shortcode_defaults', [
493 'auto-redirect' => false,
494 'redirect-to' => Helper::getPortalBaseUrl(),
495 'hide' => false,
496 'show-signup' => $isAuthForm ? 'true' : 'false',
497 'show-reset-password' => $isAuthForm ? 'true' : 'false',
498 ]);
499
500 $attributes = shortcode_atts($shortCodeDefaults, $attributes);
501
502 if (isset($attributes['redirect-to'])) {
503 $attributes['redirect_to'] = $attributes['redirect-to'];
504 }
505
506 return $attributes;
507
508 }
509
510 protected function handleAlreadyLoggedIn($attributes)
511 {
512 if (get_current_user_id() && !wp_is_json_request() && is_singular()) {
513 if ($attributes['auto-redirect'] === 'true') {
514 $redirect = $attributes['redirect-to'];
515 ?>
516 <script type="text/javascript">
517 document.addEventListener("DOMContentLoaded", function () {
518 var redirect = "<?php echo esc_url($redirect); ?>";
519 window.location.replace(redirect);
520 });
521 </script>
522 <?php
523 }
524 die();
525 }
526 }
527
528 public function loadAssets($hide = '')
529 {
530 if ($this->loaded) {
531 return false;
532 }
533
534 $app = App::getInstance();
535 $assets = $app['url.assets'];
536
537 if (is_rtl()) {
538 wp_enqueue_style('fluent_support_login_style_rtl', $assets . 'admin/css/all_public-rtl.css', [], FLUENT_SUPPORT_VERSION);
539 } else {
540 wp_enqueue_style('fluent_support_login_style', Vite::getEnqueuePath('admin/css/all_public.css'), [], FLUENT_SUPPORT_VERSION);
541 }
542
543 wp_enqueue_script('fluent_support_login_helper', Vite::getEnqueuePath('portal/js/login_helper.js'), [], FLUENT_SUPPORT_VERSION);
544
545 //Get Recaptcha settings and enqueue recaptcha script
546 $reCaptchaSettingsData = Meta::where('object_type', '_fs_recaptcha_settings')->first();
547 $reCaptchaData = ($reCaptchaSettingsData) ? Helper::safeUnserialize($reCaptchaSettingsData->value, []) : '';
548
549 if (!empty($reCaptchaData) && isset($reCaptchaData['is_enabled']) && $reCaptchaData['is_enabled'] == "true") {
550 unset($reCaptchaData['secretKey']);
551 $recaptchaVersion = $reCaptchaData["reCaptcha_version"];
552 $reCaptchaApiUrl = 'https://www.google.com/recaptcha/api.js';
553
554 if ("recaptcha_v3" === $recaptchaVersion) {
555 $reCaptchaApiUrl .= '?render=' . $reCaptchaData["siteKey"];
556 }
557
558 wp_enqueue_script('recaptcha', $reCaptchaApiUrl);
559 }
560
561 wp_localize_script('fluent_support_login_helper', 'fluentSupportPublic', [
562 'signup' => rest_url($app->config->get('app.rest_namespace') . '/' . $app->config->get('app.rest_version')) . '/signup',
563 'login' => rest_url($app->config->get('app.rest_namespace') . '/' . $app->config->get('app.rest_version')) . '/login',
564 'nonce' => wp_create_nonce('wp_rest'),
565 'hide' => $hide,
566 'redirect_fallback' => Helper::getPortalBaseUrl(),
567 'fsupport_login_nonce' => wp_create_nonce('fsupport_login_nonce'),
568 'resetPass' => rest_url($app->config->get('app.rest_namespace') . '/' . $app->config->get('app.rest_version')) . '/reset_pass',
569 'reCaptchaSettingsData' => $reCaptchaData,
570 'fs_two_fa' => rest_url($app->config->get('app.rest_namespace') . '/' . $app->config->get('app.rest_version')) . '/two_fa'
571 ]);
572
573
574 $this->loaded = true;
575 }
576
577 public function maybeRenewNonce()
578 {
579 if (!PermissionManager::currentUserPermissions()) {
580 wp_send_json([
581 'error' => 'You do not have permission to do this'
582 ], 403);
583 }
584
585 wp_send_json([
586 'nonce' => wp_create_nonce('wp_rest')
587 ], 200);
588 }
589
590 private function authProvider()
591 {
592 return \FluentSupport\App\Services\Helper::getAuthProvider();
593 }
594 }
595