PluginProbe
Fluent Support – Helpdesk & Customer Support Ticket System / 1.10.0
Fluent Support – Helpdesk & Customer Support Ticket System v1.10.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 1.10.0, at app/Hooks/Handlers/AuthHandler.php

589 lines 21.5 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\Framework\Support\Arr;
10 use FluentSupport\App\Models\Meta;
11
12 class AuthHandler
13 {
14
15 protected $loaded = false;
16
17 public function init()
18 {
19 add_shortcode('fluent_support_login', array($this, 'loginForm'));
20 add_shortcode('fluent_support_signup', array($this, 'registrationForm'));
21 add_shortcode('fluent_support_auth', array($this, 'authForm'));
22 add_shortcode('fluent_support_reset_password', array($this, 'restPasswordForm'));
23 add_action('wp_ajax_fluent_support_renew_rest_nonce', [$this, 'maybeRenewNonce']);
24 }
25
26 /**
27 * loginForm will generate html for login form
28 * @param $attributes
29 * @return string
30 */
31 public function loginForm($attributes)
32 {
33 if (get_current_user_id()) {
34 return '<p>' . __('You are already logged in.', 'fluent-support') . '</p>';
35 }
36
37 $attributes = $this->getShortcodes($attributes);
38 if (!empty($attributes['redirect-to'])) {
39 $redirect = $attributes['redirect-to'];
40 } else {
41 $redirect = Helper::getPortalBaseUrl();
42 }
43
44
45 $this->handleAlreadyLoggedIn($attributes);
46
47 if ($this->authProvider() == 'fluent_auth') {
48 // Will be handled by FluentAuth plugin
49 $attributes['redirect_to'] = $redirect;
50 return (new \FluentAuth\App\Hooks\Handlers\CustomAuthHandler())->loginForm($attributes);
51 }
52
53 $this->loadAssets();
54
55 $return = '<div class="fst_login_form_auth_wrapper">';
56 $return .= '<div id="fst_login_form" class="fst_login_wrapper">';
57
58
59 /*
60 * Filter login form
61 *
62 * @since v1.0.0
63 *
64 * @param array $loginArgs
65 */
66 $loginArgs = apply_filters('fluent_support/login_form_args', [
67 'echo' => false,
68 'redirect' => $redirect,
69 'remember' => true,
70 'value_remember' => true,
71 ]);
72
73 $return .= wp_login_form($loginArgs);
74
75 if ($attributes['show-signup'] == 'true') {
76 $return .= '<p style="text-align: center">'
77 . __('Not registered?', 'fluent-support')
78 . ' <a href="#" id="fs_show_signup">'
79 . __('Create an account', 'fluent-support')
80 . '</a></p>';
81 }
82
83 if ($attributes['show-reset-password'] == 'true') {
84 $return .= '<p style="text-align: center">'
85 . __('Forgot your password?', 'fluent-support')
86 . ' <a href="#" id="fs_show_reset_password">'
87 . __('Reset password', 'fluent-support')
88 . '</a></p>';
89 }
90
91 $return .= '</div>';
92
93 if ($attributes['show-signup'] == 'true') {
94 $return .= do_shortcode('[fluent_support_signup hide=true]');
95 }
96
97 if ($attributes['show-reset-password'] == 'true') {
98 $return .= do_shortcode('[fluent_support_reset_password hide=true]');
99 }
100
101 $return .= '</div>';
102 return $return;
103 }
104
105 /**
106 * registrationForm method will generate html for sign up form
107 * @param $attributes
108 * @return string
109 */
110 public function registrationForm($attributes)
111 {
112 if (get_current_user_id()) {
113 return '<p>' . __('You are already logged in.', 'fluent-support') . '</p>';
114 }
115
116 $attributes = $this->getShortcodes($attributes);
117 $this->handleAlreadyLoggedIn($attributes);
118
119 if ($this->authProvider() == 'fluent_auth') {
120 return (new \FluentAuth\App\Hooks\Handlers\CustomAuthHandler())->registrationForm($attributes);
121 }
122
123 $customFieldsKey = apply_filters('fluent_support/custom_registration_form_fields_key', Helper::getBusinessSettings('custom_registration_form_field'));
124
125 if (!empty($customFieldsKey)) {
126 add_filter('fluent_support/registration_form_fields', function($fields) use ($customFieldsKey) {
127 return $this->addCustomFieldsToRegistrationForm($fields,$customFieldsKey);
128 });
129 }
130
131 $registrationFields = static::getSignupFields();
132 $hide = $attributes['hide'] == 'true' ? 'hide' : '';
133
134 $this->loadAssets($hide);
135
136 return $this->buildRegistrationForm($registrationFields, $hide, $attributes);
137 }
138
139 // This method `buildRegistrationForm` will generate html for sign up form
140 private function buildRegistrationForm($registrationFields, $hide, $attributes)
141 {
142 $registrationForm = '<div class="fst_registration_wrapper ' . $hide . '"><form id="fstRegistrationForm" class="fs_registration_form" method="post" name="fs_registration_form">';
143
144 foreach ($registrationFields as $fieldName => $registrationField) {
145 $registrationForm .= $this->renderField($fieldName, $registrationField);
146 }
147
148 $registrationForm .= '<input type="hidden" name="__redirect_to" value="' . $attributes['redirect-to'] . '">';
149 $registrationForm .= '<input type="hidden" name="_fsupport_signup_nonce" value="' . wp_create_nonce('fluent_support_signup_nonce') . '">';
150 $registrationForm .= '<button type="submit" id="fst_submit">' . $this->submitBtnLoadingSvg() . '<span>' . __('Signup', 'fluent-support') . '</span></button>';
151
152 $registrationForm .= '</form>';
153
154 $registrationForm .= apply_filters('fluent_support/before_registration_form_close', '', $registrationFields, $attributes);
155
156 if ($hide) {
157 $registrationForm .= '<p style="text-align: center">'
158 . __('Already have an account?', 'fluent-support')
159 . ' <a href="#" id="fs_show_login">'
160 . __('Login', 'fluent-support')
161 . '</a></p>';
162 }
163
164 $registrationForm .= '</div>';
165
166 return $registrationForm;
167 }
168
169 public function restPasswordForm($attributes)
170 {
171 if (get_current_user_id()) {
172 return '<p>' . __('You are already logged in.', 'fluent-support') . '</p>';
173 }
174
175 $attributes = $this->getShortcodes($attributes);
176
177 if ($this->authProvider() == 'fluent_auth') {
178 return (new \FluentAuth\App\Hooks\Handlers\CustomAuthHandler())->restPasswordForm($attributes);
179 }
180
181 $this->handleAlreadyLoggedIn($attributes);
182
183 $resetPasswordFields = static::resetPasswordFields();
184 $hide = $attributes['hide'] == 'true' ? 'hide' : '';
185
186 $this->loadAssets($hide);
187
188 return $this->buildResetPassForm($resetPasswordFields, $hide, $attributes);
189 }
190
191 // This method `buildResetPassForm` will generate html for password reset form
192 private function buildResetPassForm($resetPasswordFields, $hide, $attributes)
193 {
194 $restePasswordForm = '<div class="fst_reset_pass_wrapper ' . $hide . '"><form id="fstResetPasswordForm" class="fs_reset_pass_form" method="post" name="fs_reset_pass_form">';
195
196 foreach ($resetPasswordFields as $fieldName => $resetPasswordField) {
197 $restePasswordForm .= $this->renderField($fieldName, $resetPasswordField);
198 }
199
200 $restePasswordForm .= '<input type="hidden" name="__redirect_to" value="' . $attributes['redirect-to'] . '">';
201 $restePasswordForm .= '<input type="hidden" name="_fsupport_reset_pass_nonce" value="' . wp_create_nonce('fluent_support_reset_pass_nonce') . '">';
202 $restePasswordForm .= '<button type="submit" id="fst_reset_pass">' . $this->submitBtnLoadingSvg() . '<span>' . __('Reset Password', 'fluent-support') . '</span></button>';
203
204 $restePasswordForm .= '</form>';
205
206 $restePasswordForm .= '</div>';
207
208 return $restePasswordForm;
209 }
210
211 /**
212 * authForm will render the login form html
213 * @param $attributes
214 * @return string
215 */
216 public function authForm($attributes)
217 {
218 if (get_current_user_id()) {
219 return '<p>' . sprintf(__('You are already logged in. <a href="%s">Go to support portal</a>', 'fluent-support'), Helper::getPortalBaseUrl()) . '</p>';
220 }
221
222 $attributes = $this->getShortcodes($attributes);
223
224 if ($this->authProvider() == 'fluent_auth') {
225 return (new \FluentAuth\App\Hooks\Handlers\CustomAuthHandler())->authForm($attributes);
226 }
227
228 $authForm = do_shortcode('[fluent_support_login show-signup=true show-reset-password=true]');
229
230 return $authForm;
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)
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' => false,
497 'show-reset-password' => 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 wp_enqueue_style('fluent_support_login_style', $assets . 'admin/css/all_public.css', [], FLUENT_SUPPORT_VERSION);
537 wp_enqueue_script('fluent_support_login_helper', $assets . 'portal/js/login_helper.js', [], FLUENT_SUPPORT_VERSION);
538
539 //Get Recaptcha settings and enqueue recaptcha script
540 $reCaptchaSettingsData = Meta::where('object_type', '_fs_recaptcha_settings')->first();
541 $reCaptchaData = ($reCaptchaSettingsData) ? Helper::safeUnserialize($reCaptchaSettingsData->value, []) : '';
542
543 if (!empty($reCaptchaData) && isset($reCaptchaData['is_enabled']) && $reCaptchaData['is_enabled'] == "true") {
544 unset($reCaptchaData['secretKey']);
545 $recaptchaVersion = $reCaptchaData["reCaptcha_version"];
546 $reCaptchaApiUrl = 'https://www.google.com/recaptcha/api.js';
547
548 if ("recaptcha_v3" === $recaptchaVersion) {
549 $reCaptchaApiUrl .= '?render=' . $reCaptchaData["siteKey"];
550 }
551
552 wp_enqueue_script('recaptcha', $reCaptchaApiUrl);
553 }
554
555 wp_localize_script('fluent_support_login_helper', 'fluentSupportPublic', [
556 'signup' => rest_url($app->config->get('app.rest_namespace') . '/' . $app->config->get('app.rest_version')) . '/signup',
557 'login' => rest_url($app->config->get('app.rest_namespace') . '/' . $app->config->get('app.rest_version')) . '/login',
558 'nonce' => wp_create_nonce('wp_rest'),
559 'hide' => $hide,
560 'redirect_fallback' => Helper::getPortalBaseUrl(),
561 'fsupport_login_nonce' => wp_create_nonce('fsupport_login_nonce'),
562 'resetPass' => rest_url($app->config->get('app.rest_namespace') . '/' . $app->config->get('app.rest_version')) . '/reset_pass',
563 'reCaptchaSettingsData' => $reCaptchaData,
564 'fs_two_fa' => rest_url($app->config->get('app.rest_namespace') . '/' . $app->config->get('app.rest_version')) . '/two_fa'
565 ]);
566
567
568 $this->loaded = true;
569 }
570
571 public function maybeRenewNonce()
572 {
573 if (!PermissionManager::currentUserPermissions()) {
574 wp_send_json([
575 'error' => 'You do not have permission to do this'
576 ], 403);
577 }
578
579 wp_send_json([
580 'nonce' => wp_create_nonce('wp_rest')
581 ], 200);
582 }
583
584 private function authProvider()
585 {
586 return \FluentSupport\App\Services\Helper::getAuthProvider();
587 }
588 }
589