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

590 lines 21.6 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 // translators: %s is the URL to the support portal
220 return '<p>' . sprintf(__('You are already logged in. <a href="%s">Go to support portal</a>', 'fluent-support'), Helper::getPortalBaseUrl()) . '</p>';
221 }
222
223 $attributes = $this->getShortcodes($attributes);
224
225 if ($this->authProvider() == 'fluent_auth') {
226 return (new \FluentAuth\App\Hooks\Handlers\CustomAuthHandler())->authForm($attributes);
227 }
228
229 $authForm = do_shortcode('[fluent_support_login show-signup=true show-reset-password=true]');
230
231 return $authForm;
232 }
233
234 /**
235 * renderField method will generate html for a field
236 * @param $fieldName
237 * @param $field
238 * @return string
239 */
240 private function renderField($fieldName, $field)
241 {
242 $fieldType = Arr::get($field, 'type');
243 $isRequired = Arr::get($field, 'required');
244 $isRequired = $isRequired ? 'is-required' : '';
245
246 $html = '<div class="fst_field_group fst_field_' . $fieldName . '">';
247
248 if ($label = Arr::get($field, 'label')) {
249 $html .= '<div class="fst_field_label ' . $isRequired . '"><label for="' . Arr::get($field, 'id') . '">' . $label . '</label></div>';
250 }
251
252 $textTypes = ['text', 'email', 'password', 'number', 'date'];
253 $selectTypes = ['select']; // Add 'select' type for dropdowns
254
255 if (in_array($fieldType, $textTypes)) {
256 $html .= $this->renderTextInput($fieldName, $field);
257 } elseif (in_array($fieldType, $selectTypes)) {
258 $html .= $this->renderSelectInput($fieldName, $field);
259 } else {
260 return '';
261 }
262
263 $html = $html . '</div>';
264
265 return '<div class="fst_registration_fields">' . $html . '</div>';
266 }
267
268 private function renderTextInput($fieldName, $field)
269 {
270 $inputAtts = array_filter([
271 'type' => esc_attr(Arr::get($field, 'type')),
272 'id' => esc_attr(Arr::get($field, 'id')),
273 'placeholder' => esc_attr(Arr::get($field, 'placeholder')),
274 'name' => esc_attr($fieldName),
275 'required' => Arr::get($field, 'required') ? 'required' : '',
276 ]);
277
278 $atts = '';
279
280 foreach ($inputAtts as $attKey => $att) {
281 $atts .= $attKey . '="' . $att . '" ';
282 }
283
284 return '<div class="fs_input_wrap"><input ' . $atts . ' /></div>';
285 }
286
287 /**
288 * Render a select input field based on the provided field configuration.
289 *
290 * @param string $fieldName The name of the select input field.
291 * @param array $field The field configuration containing options and attributes.
292 *
293 * @return string The rendered select input field HTML.
294 */
295 private function renderSelectInput($fieldName, $field)
296 {
297 $choices = Arr::get($field, 'options', []);
298 $placeholder = Arr::get($field, 'placeholder', '');
299
300 $options = '<option value="" disabled selected>' . esc_attr($placeholder) . '</option>';
301
302 foreach ($choices as $value => $optionData) {
303 $options .= '<option value="' . esc_attr($value) . '">' . esc_attr($optionData) . '</option>';
304 }
305
306 $selectAtts = [
307 'id' => esc_attr(Arr::get($field, 'id')),
308 'name' => esc_attr($fieldName),
309 'required' => Arr::get($field, 'required') ? 'required' : '',
310 ];
311
312 $select = '<div class="fs_input_wrap"><select ' . $this->renderAttributes($selectAtts) . '>' . $options . '</select></div>';
313
314 return $select;
315 }
316
317 private function renderAttributes($attributes)
318 {
319 $atts = '';
320
321 foreach ($attributes as $attKey => $att) {
322 $atts .= $attKey . '="' . $att . '" ';
323 }
324
325 return $atts;
326 }
327
328 /**
329 * getSignupFields method will return the list of fields that will be used for sign up form
330 * @return mixed
331 */
332 public static function getSignupFields()
333 {
334 /*
335 * Filter signup form field
336 *
337 * @since v1.0.0
338 *
339 * @param array $fields Form fields
340 */
341 return apply_filters('fluent_support/registration_form_fields', [
342 'first_name' => [
343 'required' => true,
344 'type' => 'text',
345 'label' => __('First name', 'fluent-support'),
346 'id' => 'fst_first_name',
347 'placeholder' => __('First name', 'fluent-support')
348 ],
349 'last_name' => [
350 'type' => 'text',
351 'label' => __('Last Name', 'fluent-support'),
352 'id' => 'fst_last_name',
353 'placeholder' => __('Last name', 'fluent-support')
354 ],
355 'username' => [
356 'required' => true,
357 'type' => 'text',
358 'label' => __('Username', 'fluent-support'),
359 'id' => 'fst_username',
360 'placeholder' => __('Username', 'fluent-support')
361 ],
362 'email' => [
363 'required' => true,
364 'type' => 'email',
365 'label' => __('Email Address', 'fluent-support'),
366 'id' => 'fst_email',
367 'placeholder' => __('Your Email Address', 'fluent-support')
368 ],
369 'password' => [
370 'required' => true,
371 'type' => 'password',
372 'label' => __('Password', 'fluent-support'),
373 'id' => 'fst_password',
374 'placeholder' => __('Password', 'fluent-support')
375 ]
376 ]);
377 }
378
379 private function addCustomFieldsToRegistrationForm($fields, $customFieldsKey) {
380 $customFields = $this->allCustomFields();
381
382 foreach ($customFieldsKey as $key) {
383 $fields[$key] = $customFields[$key];
384 }
385
386 return $fields;
387 }
388
389 public static function allCustomFields(): array {
390 $countryList = CountryNames::get();
391
392 return apply_filters('fluent_support/custom_registration_form_fields', [
393 'address_line_1' => [
394 'required' => false,
395 'type' => 'text',
396 'label' => __('Address Line 1', 'fluent-support'),
397 'id' => 'fst_address_line_1',
398 'placeholder' => __('Address Line 1', 'fluent-support'),
399 ],
400 'address_line_2' => [
401 'required' => false,
402 'type' => 'text',
403 'label' => __('Address Line 2', 'fluent-support'),
404 'id' => 'fst_address_line_2',
405 'placeholder' => __('Address Line 2', 'fluent-support'),
406 ],
407 'city' => [
408 'required' => false,
409 'type' => 'text',
410 'label' => __('City', 'fluent-support'),
411 'id' => 'fst_city',
412 'placeholder' => __('City', 'fluent-support'),
413 ],
414 'zip' => [
415 'required' => false,
416 'type' => 'text',
417 'label' => __('Zip', 'fluent-support'),
418 'id' => 'fst_zip',
419 'placeholder' => __('Zip', 'fluent-support'),
420 ],
421 'state' => [
422 'required' => false,
423 'type' => 'text',
424 'label' => __('State', 'fluent-support'),
425 'id' => 'fst_state',
426 'placeholder' => __('State', 'fluent-support'),
427 ],
428 'country' => [
429 'required' => false,
430 'type' => 'select',
431 'label' => __('Country', 'fluent-support'),
432 'id' => 'fst_country',
433 'placeholder' => __('Select a Country', 'fluent-support'),
434 'options' => $countryList,
435 ],
436 ]);
437 }
438
439 public static function resetPasswordFields()
440 {
441 /*
442 * Filter reset password form field
443 *
444 * @since v1.5.7
445 *
446 * @param array $fields Form fields
447 */
448 return apply_filters('fluent_support/reset_password_form', [
449 'user_login' => [
450 'required' => true,
451 'type' => 'text',
452 'label' => __('Email Address', 'fluent-support'),
453 'id' => 'fst_reset_email',
454 'placeholder' => __('Your Email Address', 'fluent-support')
455 ]
456 ]);
457 }
458
459 protected function submitBtnLoadingSvg()
460 {
461 $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"
462 width="40px" height="20px" viewBox="0 0 50 50" style="enable-background:new 0 0 50 50;" xml:space="preserve">
463 <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">
464 <animateTransform attributeType="xml"
465 attributeName="transform"
466 type="rotate"
467 from="0 25 25"
468 to="360 25 25"
469 dur="0.6s"
470 repeatCount="indefinite"/>
471 </path>
472 </svg>';
473
474 /*
475 * Filter signup form loading icon
476 *
477 * @since v1.0.0
478 *
479 * @param string $loadingIcon this accepts html element
480 */
481 return apply_filters('fluent_support/signup_loading_icon', $loadingIcon);
482 }
483
484 protected function getShortcodes($attributes)
485 {
486 /*
487 * Filter shortcode behavior for agent
488 *
489 * @since v1.0.0
490 *
491 * @param array $shortCodeDefaults
492 */
493 $shortCodeDefaults = apply_filters('fluent_support/auth_shortcode_defaults', [
494 'auto-redirect' => false,
495 'redirect-to' => Helper::getPortalBaseUrl(),
496 'hide' => false,
497 'show-signup' => false,
498 'show-reset-password' => false,
499 ]);
500
501 $attributes = shortcode_atts($shortCodeDefaults, $attributes);
502
503 if (isset($attributes['redirect-to'])) {
504 $attributes['redirect_to'] = $attributes['redirect-to'];
505 }
506
507 return $attributes;
508
509 }
510
511 protected function handleAlreadyLoggedIn($attributes)
512 {
513 if (get_current_user_id() && !wp_is_json_request() && is_singular()) {
514 if ($attributes['auto-redirect'] === 'true') {
515 $redirect = $attributes['redirect-to'];
516 ?>
517 <script type="text/javascript">
518 document.addEventListener("DOMContentLoaded", function () {
519 var redirect = "<?php echo esc_url($redirect); ?>";
520 window.location.replace(redirect);
521 });
522 </script>
523 <?php
524 }
525 die();
526 }
527 }
528
529 public function loadAssets($hide = '')
530 {
531 if ($this->loaded) {
532 return false;
533 }
534
535 $app = App::getInstance();
536 $assets = $app['url.assets'];
537 wp_enqueue_style('fluent_support_login_style', $assets . 'admin/css/all_public.css', [], FLUENT_SUPPORT_VERSION);
538 wp_enqueue_script('fluent_support_login_helper', $assets . 'portal/js/login_helper.js', [], FLUENT_SUPPORT_VERSION);
539
540 //Get Recaptcha settings and enqueue recaptcha script
541 $reCaptchaSettingsData = Meta::where('object_type', '_fs_recaptcha_settings')->first();
542 $reCaptchaData = ($reCaptchaSettingsData) ? Helper::safeUnserialize($reCaptchaSettingsData->value, []) : '';
543
544 if (!empty($reCaptchaData) && isset($reCaptchaData['is_enabled']) && $reCaptchaData['is_enabled'] == "true") {
545 unset($reCaptchaData['secretKey']);
546 $recaptchaVersion = $reCaptchaData["reCaptcha_version"];
547 $reCaptchaApiUrl = 'https://www.google.com/recaptcha/api.js';
548
549 if ("recaptcha_v3" === $recaptchaVersion) {
550 $reCaptchaApiUrl .= '?render=' . $reCaptchaData["siteKey"];
551 }
552
553 wp_enqueue_script('recaptcha', $reCaptchaApiUrl);
554 }
555
556 wp_localize_script('fluent_support_login_helper', 'fluentSupportPublic', [
557 'signup' => rest_url($app->config->get('app.rest_namespace') . '/' . $app->config->get('app.rest_version')) . '/signup',
558 'login' => rest_url($app->config->get('app.rest_namespace') . '/' . $app->config->get('app.rest_version')) . '/login',
559 'nonce' => wp_create_nonce('wp_rest'),
560 'hide' => $hide,
561 'redirect_fallback' => Helper::getPortalBaseUrl(),
562 'fsupport_login_nonce' => wp_create_nonce('fsupport_login_nonce'),
563 'resetPass' => rest_url($app->config->get('app.rest_namespace') . '/' . $app->config->get('app.rest_version')) . '/reset_pass',
564 'reCaptchaSettingsData' => $reCaptchaData,
565 'fs_two_fa' => rest_url($app->config->get('app.rest_namespace') . '/' . $app->config->get('app.rest_version')) . '/two_fa'
566 ]);
567
568
569 $this->loaded = true;
570 }
571
572 public function maybeRenewNonce()
573 {
574 if (!PermissionManager::currentUserPermissions()) {
575 wp_send_json([
576 'error' => 'You do not have permission to do this'
577 ], 403);
578 }
579
580 wp_send_json([
581 'nonce' => wp_create_nonce('wp_rest')
582 ], 200);
583 }
584
585 private function authProvider()
586 {
587 return \FluentSupport\App\Services\Helper::getAuthProvider();
588 }
589 }
590