| @@ -2,35 +2,260 @@ | ||
| 2 | 2 | |
| 3 | 3 | namespace FluentSupport\App\Hooks\Handlers; |
| 4 | 4 | |
| 5 | 5 | use FluentSupport\App\App; |
| 6 | -use FluentSupport\App\Models\Agent; | |
| 7 | 6 | use FluentSupport\App\Models\Customer; |
| 8 | 7 | use FluentSupport\App\Models\Product; |
| 9 | 8 | use FluentSupport\App\Modules\PermissionManager; |
| 10 | -use FluentSupport\App\Services\EmailNotification\Settings; | |
| 9 | +use FluentSupport\App\Services\Blocks\BlockHelper; | |
| 10 | +use FluentSupport\App\Services\EmailClaimService; | |
| 11 | 11 | use FluentSupport\App\Services\Helper; |
| 12 | -use FluentSupport\App\Services\TransStrings; | |
| 12 | +use FluentSupport\App\Services\TranslationStrings; | |
| 13 | +use FluentSupport\App\Vite; | |
| 13 | 14 | use FluentSupport\Framework\Support\Arr; |
| 15 | +use FluentSupportPro\App\Services\ProHelper; | |
| 14 | 16 | |
| 15 | 17 | class CustomerPortalHandler |
| 16 | 18 | { |
| 17 | - public function renderPortal() | |
| 19 | + public function renderPortal($args = []) | |
| 18 | 20 | { |
| 19 | - if (static::customerStatus() && static::customerStatus()->status === 'inactive') { | |
| 20 | - return '<div id="fluent_support_client_app" style="text-align: center;"><h3 class="fs_customer_restriction">' . __('You don’t have permission to view the tickets', 'fluent-support') . '</h3></div>'; | |
| 21 | - } else if (PermissionManager::currentUserPermissions()) { | |
| 21 | + /** | |
| 22 | + * Filter the message shown when a customer can't access the portal. | |
| 23 | + * @param string $invalidPermissionMessage | |
| 24 | + * @return string | |
| 25 | + * @since 1.6.0 | |
| 26 | + */ | |
| 27 | + $invalidPermissionMessage = apply_filters( | |
| 28 | + 'fluent_support/customer_portal_invalid_permission_message', | |
| 29 | + esc_html__('You don\'t have permission to access customer support portal', 'fluent-support') | |
| 30 | + ); | |
| 31 | + | |
| 32 | + $person = Helper::getCurrentCustomer(); | |
| 33 | + | |
| 34 | + if (!$person && PermissionManager::currentUserPermissions()) { | |
| 22 | 35 | $adminPortalUrl = Helper::getPortalAdminBaseUrl(); |
| 23 | - return '<div style="text-align: center;"><h3>' . __('Customer Portal is only accessible by Customers. Looks like you are a support staff', 'fluent-support') . '</h3><a href="' . $adminPortalUrl . '">' . __('Go to Support Admin Page', 'fluent-support') . '</a></div>'; | |
| 36 | + | |
| 37 | + /** | |
| 38 | + * Filter the message shown when support staff open the customer portal. | |
| 39 | + * @param string $agentPermissionErrMessage | |
| 40 | + * @return string | |
| 41 | + * @since 1.6.0 | |
| 42 | + */ | |
| 43 | + $msg = __('Customer Portal is only accessible by Customers. Looks like you are a support staff', 'fluent-support'); | |
| 44 | + $agentPermissionErrMessage = apply_filters( | |
| 45 | + 'fluent_support/customer_portal_agent_permission_error_message', | |
| 46 | + $msg | |
| 47 | + ); | |
| 48 | + return '<div style="text-align: center;"><h3>' . esc_html($agentPermissionErrMessage) . '</h3><a href="' . esc_url($adminPortalUrl) . '">' . esc_html__('Go to Support Admin Page', 'fluent-support') . '</a></div>'; | |
| 24 | 49 | } else if ($this->hasCustomerPortalAccess()) { |
| 50 | + | |
| 51 | + /* | |
| 52 | + * Filter customer portal access settings | |
| 53 | + * | |
| 54 | + * @since v1.0.0 | |
| 55 | + * | |
| 56 | + * @param array $canAccess | |
| 57 | + */ | |
| 58 | + $canAccess = apply_filters('fluent_support/user_portal_access_config', [ | |
| 59 | + 'status' => true, | |
| 60 | + 'message' => $invalidPermissionMessage | |
| 61 | + ]); | |
| 62 | + | |
| 63 | + if (empty($canAccess['status'])) { | |
| 64 | + $invalidPermissionMessage = Arr::get($canAccess, 'message', $invalidPermissionMessage); | |
| 65 | + return '<div id="fluent_support_client_app" style="text-align: center;"><h3 class="fs_customer_restriction">' . esc_html($invalidPermissionMessage) . '</h3></div>'; | |
| 66 | + } | |
| 67 | + | |
| 68 | + if (!$person) { | |
| 69 | + $this->maybeCreateCustomer(); | |
| 70 | + } | |
| 71 | + | |
| 72 | + if (isset($args['attributes']) && !empty($args['attributes'])) { | |
| 73 | + BlockHelper::processAttributesAndPrepareStyle($args['attributes']); | |
| 74 | + } | |
| 75 | + | |
| 25 | 76 | $this->enqueueScripts(); |
| 26 | - return '<div id="fluent_support_client_app"><h3 class="fs_loading_text">' . __('Loading Customer Portal. Please wait...', 'fluent-support') . '</h3></div>'; | |
| 77 | + | |
| 78 | + return $this->renderEmailClaimNotice($person) | |
| 79 | + . '<div id="fluent_support_client_app"><h3 class="fs_loading_text">' . __('Loading Customer Portal. Please wait...', 'fluent-support') . '</h3></div>'; | |
| 27 | 80 | } else { |
| 81 | + | |
| 28 | 82 | $businessSettings = Helper::getBusinessSettings(); |
| 29 | - return do_shortcode(Arr::get($businessSettings, 'login_message', '')); | |
| 83 | + $loggedInMessage = Arr::get($businessSettings, 'login_message', ''); | |
| 84 | + | |
| 85 | + $loggedInMessage = str_replace('[fluent_support_portal]', '', $loggedInMessage); | |
| 86 | + | |
| 87 | + // Pass the portal's show-signup / show-reset-password through to the | |
| 88 | + // login shortcodes rendered inside the message. | |
| 89 | + $overrideDefaults = function ($defaults) use ($args) { | |
| 90 | + $defaults['show-signup'] = Arr::get($args, 'show-signup', 'true'); | |
| 91 | + $defaults['show-reset-password'] = Arr::get($args, 'show-reset-password', 'true'); | |
| 92 | + return $defaults; | |
| 93 | + }; | |
| 94 | + | |
| 95 | + $loggedInMessage = wp_kses_post($loggedInMessage); | |
| 96 | + | |
| 97 | + add_filter('fluent_support/auth_shortcode_defaults', $overrideDefaults); | |
| 98 | + $result = do_shortcode($loggedInMessage); | |
| 99 | + | |
| 100 | + remove_filter('fluent_support/auth_shortcode_defaults', $overrideDefaults); | |
| 101 | + | |
| 102 | + return $result; | |
| 30 | 103 | } |
| 31 | 104 | } |
| 32 | 105 | |
| 106 | + /** | |
| 107 | + * Notice for when the support address no longer matches the WP account email. | |
| 108 | + * Rendered outside the Vue mount point so the app doesn't replace it. | |
| 109 | + * | |
| 110 | + * It says nothing about tickets on the other address, since anyone can point | |
| 111 | + * their account at an address they don't own and would learn who is a customer. | |
| 112 | + * | |
| 113 | + * @param \FluentSupport\App\Models\Customer|null $customer | |
| 114 | + * @return string | |
| 115 | + */ | |
| 116 | + protected function renderEmailClaimNotice($customer = null) | |
| 117 | + { | |
| 118 | + $html = $this->renderEmailClaimResult(); | |
| 119 | + | |
| 120 | + $divergence = EmailClaimService::getDivergence($customer); | |
| 121 | + | |
| 122 | + if (!$divergence) { | |
| 123 | + return $html; | |
| 124 | + } | |
| 125 | + | |
| 126 | + $requestUrl = EmailClaimService::buildRequestUrl(); | |
| 127 | + | |
| 128 | + if (!$requestUrl) { | |
| 129 | + return $html; | |
| 130 | + } | |
| 131 | + | |
| 132 | + $message = sprintf( | |
| 133 | + // translators: 1: address support mail currently goes to, 2: the account's current address | |
| 134 | + __('Support messages are being sent to %1$s, but your account email is now %2$s.', 'fluent-support'), | |
| 135 | + '<strong>' . esc_html($divergence['from']) . '</strong>', | |
| 136 | + '<strong>' . esc_html($divergence['to']) . '</strong>' | |
| 137 | + ); | |
| 138 | + | |
| 139 | + return $html | |
| 140 | + . '<div class="fs_email_claim_notice" style="border: 1px solid #dcdcde; border-left: 4px solid #2271b1; background: #fff; padding: 12px 16px; margin-bottom: 16px;">' | |
| 141 | + . '<p style="margin: 0 0 8px;">' . wp_kses($message, ['strong' => []]) . '</p>' | |
| 142 | + . '<p style="margin: 0 0 12px; opacity: .8;">' . esc_html__('Confirm the new address to move your support messages and bring across any tickets you opened from it. We will email a link to that address.', 'fluent-support') . '</p>' | |
| 143 | + . '<a class="fs_email_claim_button" style="display: inline-block; background: #2271b1; color: #fff; text-decoration: none; padding: 8px 20px; border-radius: 3px;" href="' . esc_url($requestUrl) . '">' | |
| 144 | + . esc_html__('Send confirmation email', 'fluent-support') | |
| 145 | + . '</a></div>'; | |
| 146 | + } | |
| 147 | + | |
| 148 | + /** | |
| 149 | + * @return string | |
| 150 | + */ | |
| 151 | + protected function renderEmailClaimResult() | |
| 152 | + { | |
| 153 | + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- selects a fixed message to display; performs no action | |
| 154 | + $result = isset($_GET['fs_claim_result']) ? sanitize_key(wp_unslash($_GET['fs_claim_result'])) : ''; | |
| 155 | + | |
| 156 | + if (!$result) { | |
| 157 | + return ''; | |
| 158 | + } | |
| 159 | + | |
| 160 | + $user = wp_get_current_user(); | |
| 161 | + $accountEmail = $user ? $user->user_email : ''; | |
| 162 | + | |
| 163 | + $messages = [ | |
| 164 | + 'sent' => sprintf( | |
| 165 | + // translators: %s is the email address the confirmation was sent to | |
| 166 | + __('Confirmation email sent to %s. Open the link in that inbox to finish.', 'fluent-support'), | |
| 167 | + $accountEmail | |
| 168 | + ), | |
| 169 | + 'confirmed' => __('Your support email address has been updated.', 'fluent-support'), | |
| 170 | + 'throttled' => __('Too many confirmation emails have been requested for this address. Please try again later.', 'fluent-support'), | |
| 171 | + 'expired' => __('That confirmation link has expired. You can request a new one below.', 'fluent-support'), | |
| 172 | + 'stale' => __('That confirmation link is no longer valid, usually because it was already used or the address changed again. You can request a new one below.', 'fluent-support'), | |
| 173 | + 'wrong_account' => __('That confirmation link belongs to a different account. Sign in as that account and open the link again.', 'fluent-support'), | |
| 174 | + 'conflict' => __('Another support profile already uses that address, so it cannot be moved automatically. Please contact support.', 'fluent-support'), | |
| 175 | + 'invalid' => __('That confirmation link could not be read. You can request a new one below.', 'fluent-support'), | |
| 176 | + 'send_failed' => __('We could not send the confirmation email just now. Please try again in a few minutes.', 'fluent-support') | |
| 177 | + ]; | |
| 178 | + | |
| 179 | + if (empty($messages[$result])) { | |
| 180 | + return ''; | |
| 181 | + } | |
| 182 | + | |
| 183 | + $isGood = in_array($result, ['sent', 'confirmed'], true); | |
| 184 | + | |
| 185 | + return '<div class="fs_email_claim_result" style="border: 1px solid #dcdcde; border-left: 4px solid ' . ($isGood ? '#00a32a' : '#d63638') . '; background: #fff; padding: 12px 16px; margin-bottom: 16px;">' | |
| 186 | + . '<p style="margin: 0;">' . esc_html($messages[$result]) . '</p></div>'; | |
| 187 | + } | |
| 188 | + | |
| 189 | + public function hasCustomerPortalAccess() | |
| 190 | + { | |
| 191 | + $userId = get_current_user_id(); | |
| 192 | + | |
| 193 | + if ($userId) { | |
| 194 | + return true; | |
| 195 | + } | |
| 196 | + | |
| 197 | + return $this->isSignedTicketView(); | |
| 198 | + } | |
| 199 | + | |
| 200 | + protected function isSignedTicketView() | |
| 201 | + { | |
| 202 | + if (!Helper::isPublicSignedTicketEnabled()) { | |
| 203 | + return false; | |
| 204 | + } | |
| 205 | + | |
| 206 | + return isset($_REQUEST['fs_view']) && $_REQUEST['fs_view'] == 'ticket' && isset($_REQUEST['support_hash']) && isset($_REQUEST['ticket_id']); | |
| 207 | + } | |
| 208 | + | |
| 209 | + private function maybeCreateCustomer() | |
| 210 | + { | |
| 211 | + $userId = get_current_user_id(); | |
| 212 | + if (!$userId) { | |
| 213 | + return false; | |
| 214 | + } | |
| 215 | + | |
| 216 | + $person = Helper::getCurrentPerson(); | |
| 217 | + if ($person) { | |
| 218 | + return $person; | |
| 219 | + } | |
| 220 | + | |
| 221 | + $user = get_user_by('ID', $userId); | |
| 222 | + | |
| 223 | + $request = App::request(); | |
| 224 | + | |
| 225 | + $onBehalf = [ | |
| 226 | + 'user_id' => $user->ID, | |
| 227 | + 'email' => $user->user_email, | |
| 228 | + 'last_ip_address' => $request->getIp() | |
| 229 | + ]; | |
| 230 | + | |
| 231 | + $customFields = Helper::getBusinessSettings('custom_registration_form_field'); | |
| 232 | + | |
| 233 | + if (!empty($customFields)) { | |
| 234 | + $onBehalf = $this->processCustomFields($customFields, $onBehalf); | |
| 235 | + } | |
| 236 | + | |
| 237 | + return Customer::maybeCreateCustomer($onBehalf); | |
| 238 | + } | |
| 239 | + | |
| 240 | + private function processCustomFields($customFields, $onBehalf) | |
| 241 | + { | |
| 242 | + $userMeta = get_user_meta(get_current_user_id()); | |
| 243 | + $customData = []; | |
| 244 | + | |
| 245 | + foreach ($customFields as $field) { | |
| 246 | + if (isset($userMeta[$field])) { | |
| 247 | + $customData[$field] = is_array($userMeta[$field]) ? $userMeta[$field][0] : $userMeta[$field]; | |
| 248 | + } | |
| 249 | + } | |
| 250 | + | |
| 251 | + if ($customData) { | |
| 252 | + $onBehalf = array_merge($onBehalf, $customData); | |
| 253 | + } | |
| 254 | + | |
| 255 | + return $onBehalf; | |
| 256 | + } | |
| 257 | + | |
| 33 | 258 | public function enqueueScripts() |
| 34 | 259 | { |
| 35 | 260 | $app = App::getInstance(); |
| 36 | 261 | |
| @@ -38,49 +263,43 @@ | ||
| 38 | 263 | $v = $app->config->get('app.rest_version'); |
| 39 | 264 | $slug = $app->config->get('app.slug'); |
| 40 | 265 | |
| 41 | 266 | $restInfo = [ |
| 42 | - 'base_url' => esc_url_raw(rest_url()), | |
| 43 | - 'url' => rest_url($ns . '/' . $v . '/customer-portal'), | |
| 44 | - 'nonce' => wp_create_nonce('wp_rest'), | |
| 267 | + 'base_url' => esc_url_raw(rest_url()), | |
| 268 | + 'url' => rest_url($ns . '/' . $v . '/customer-portal'), | |
| 269 | + 'nonce' => wp_create_nonce('wp_rest'), | |
| 45 | 270 | 'namespace' => $ns, |
| 46 | - 'version' => $v, | |
| 271 | + 'version' => $v, | |
| 47 | 272 | ]; |
| 48 | 273 | |
| 49 | 274 | $assets = $app['url.assets']; |
| 50 | 275 | |
| 51 | - wp_enqueue_script('fs_tk_customer_portal', $assets . 'portal/js/app.js', ['jquery'], FLUENT_SUPPORT_VERSION); | |
| 52 | - wp_enqueue_style('fs_tk_customer_portal', $assets . 'portal/css/app.css', [], FLUENT_SUPPORT_VERSION); | |
| 53 | 276 | |
| 54 | - $i18ns = [ | |
| 55 | - 'Create a New Ticket' => __('Create a New Ticket', 'fluent-support'), | |
| 56 | - 'Conversation' => __('Conversation', 'fluent-support'), | |
| 57 | - 'Click Here to Write a reply' => __('Click Here to Write a reply', 'fluent-support'), | |
| 58 | - 'All' => __('All', 'fluent-support'), | |
| 59 | - 'Open' => __('Open', 'fluent-support'), | |
| 60 | - 'Closed' => __('Closed', 'fluent-support'), | |
| 61 | - 'Date' => __('Date', 'fluent-support'), | |
| 62 | - 'Status' => __('Status', 'fluent-support'), | |
| 63 | - 'Next' => __('Next', 'fluent-support'), | |
| 64 | - 'Prev' => __('Prev', 'fluent-support'), | |
| 65 | - 'Subject' => __('Subject', 'fluent-support'), | |
| 66 | - 'Ticket Details' => __('Ticket Details', 'fluent-support'), | |
| 67 | - 'Priority' => __('Priority', 'fluent-support'), | |
| 68 | - 'Related Product/Service' => __('Related Product/Service', 'fluent-support'), | |
| 69 | - 'agent_and_officials_can_see' => __('Only you and official support agents can view this conversations', 'fluent-support'), | |
| 70 | - ]; | |
| 277 | + $i18ns = TranslationStrings::getPortalStrings(); | |
| 71 | 278 | |
| 72 | 279 | $i18ns['allowed_files_and_size'] = Helper::getFileUploadMessage(); |
| 73 | 280 | |
| 74 | 281 | $data = [ |
| 75 | - 'rest' => $restInfo, | |
| 76 | - 'nonce' => wp_create_nonce($slug), | |
| 77 | - 'support_products' => Product::select(['id', 'title'])->get(), | |
| 282 | + 'rest' => $restInfo, | |
| 283 | + 'nonce' => wp_create_nonce($slug), | |
| 284 | + 'ticket_statuses' => Helper::ticketStatuses(), | |
| 285 | + 'support_products' => Product::select(['id', 'title'])->orderedByTitle()->get(), | |
| 286 | + 'product_field_required' => Helper::isProductRequired(), | |
| 78 | 287 | 'customer_ticket_priorities' => Helper::customerTicketPriorities(), |
| 79 | - 'view_tickets_url' => '#/', | |
| 80 | - 'i18n' => $i18ns, | |
| 81 | - 'fallback_image' => $assets . 'images/file.png', | |
| 82 | - 'has_file_upload' => !!Helper::ticketAcceptedFileMiles() | |
| 288 | + 'view_tickets_url' => '#/', | |
| 289 | + 'i18n' => $i18ns, | |
| 290 | + 'fallback_image' => $assets . 'images/icons/file.svg', | |
| 291 | + 'has_file_upload' => !!Helper::ticketAcceptedFileMiles(), | |
| 292 | + 'has_rich_text_editor' => true, | |
| 293 | + 'customer_status' => static::customerStatus()->status ?? static::customerStatus(), | |
| 294 | + 'max_file_upload' => Helper::getBusinessSettings('max_file_upload', 3), | |
| 295 | + 'agent_feedback_rating' => Helper::getBusinessSettings('agent_feedback_rating', 'no'), | |
| 296 | + 'can_view_private_ticket_number' => current_user_can('manage_options') || PermissionManager::currentUserCan([ | |
| 297 | + 'fst_view_tickets', | |
| 298 | + 'fst_manage_own_tickets', | |
| 299 | + 'fst_manage_unassigned_tickets', | |
| 300 | + 'fst_manage_other_tickets' | |
| 301 | + ]), | |
| 83 | 302 | ]; |
| 84 | 303 | |
| 85 | 304 | if ($this->isSignedTicketView()) { |
| 86 | 305 | $data['intended_ticket_hash'] = sanitize_text_field($_REQUEST['support_hash']); |
| @@ -88,38 +307,59 @@ | ||
| 88 | 307 | } else { |
| 89 | 308 | add_filter('user_can_richedit', '__return_true'); |
| 90 | 309 | } |
| 91 | 310 | |
| 92 | - wp_tinymce_inline_scripts(); | |
| 93 | - wp_enqueue_editor(); | |
| 311 | + $reCaptchaSettings = ReCaptchaHandler::getSettings(); | |
| 312 | + $data['recaptcha'] = ['enabled' => false]; | |
| 94 | 313 | |
| 314 | + if (ReCaptchaHandler::isRecaptchaApplicable('ticket_form', $reCaptchaSettings)) { | |
| 315 | + $recaptchaVersion = $reCaptchaSettings['reCaptcha_version'] ?? 'recaptcha_v2'; | |
| 316 | + $siteKey = $reCaptchaSettings['siteKey'] ?? ''; | |
| 317 | + | |
| 318 | + $data['recaptcha'] = [ | |
| 319 | + 'enabled' => true, | |
| 320 | + 'version' => $recaptchaVersion, | |
| 321 | + 'site_key' => $siteKey, | |
| 322 | + ]; | |
| 323 | + } | |
| 324 | + | |
| 325 | + /* | |
| 326 | + * Filter customer portal localize javascript data | |
| 327 | + * | |
| 328 | + * @since v1.0.0 | |
| 329 | + * | |
| 330 | + * @param array $data | |
| 331 | + */ | |
| 95 | 332 | $data = apply_filters('fluent_support/customer_portal_vars', $data); |
| 96 | 333 | |
| 97 | - wp_localize_script('fs_tk_customer_portal', 'fs_customer_portal', $data); | |
| 98 | - } | |
| 334 | + if (!empty($data['has_rich_text_editor'])) { | |
| 335 | + wp_tinymce_inline_scripts(); | |
| 336 | + wp_enqueue_editor(); | |
| 337 | + } | |
| 99 | 338 | |
| 100 | - public function hasCustomerPortalAccess() | |
| 101 | - { | |
| 102 | - $userId = get_current_user_id(); | |
| 339 | + add_action('wp_head', function () { | |
| 340 | + Vite::injectViteClient(); | |
| 341 | + }, 1); | |
| 103 | 342 | |
| 104 | - if ($userId) { | |
| 105 | - return true; | |
| 343 | + wp_enqueue_script('dompurify', $assets . 'libs/purify/purify.min.js', [], '3.4.16'); | |
| 344 | + wp_enqueue_script('fs_tk_customer_portal', Vite::getEnqueuePath('portal/js/app.js'), ['jquery'], FLUENT_SUPPORT_VERSION, true); | |
| 345 | + | |
| 346 | + if (is_rtl()) { | |
| 347 | + wp_enqueue_style('fs_tk_customer_portal_rtl', $assets . 'portal/css/app-rtl.css', [], FLUENT_SUPPORT_VERSION); | |
| 348 | + } else { | |
| 349 | + wp_enqueue_style('fs_tk_customer_portal', Vite::getEnqueuePath('portal/css/app.css'), [], FLUENT_SUPPORT_VERSION); | |
| 106 | 350 | } |
| 107 | 351 | |
| 108 | - return $this->isSignedTicketView(); | |
| 352 | + wp_localize_script('fs_tk_customer_portal', 'fs_customer_portal', $data); | |
| 109 | 353 | } |
| 110 | 354 | |
| 111 | 355 | protected static function customerStatus() |
| 112 | 356 | { |
| 113 | 357 | $user = get_current_user_id(); |
| 114 | - return Customer::where('user_id', $user)->select(['status'])->first(); | |
| 115 | - } | |
| 116 | 358 | |
| 117 | - protected function isSignedTicketView() | |
| 118 | - { | |
| 119 | - if (!Helper::isPublicSignedTicketEnabled()) { | |
| 120 | - return false; | |
| 359 | + if (!$user && isset($_REQUEST['support_hash']) && isset($_REQUEST['ticket_id']) && isset($_REQUEST['fs_view']) && $_REQUEST['fs_view'] == 'ticket') { | |
| 360 | + return true; | |
| 121 | 361 | } |
| 122 | 362 | |
| 123 | - return isset($_REQUEST['fs_view']) && $_REQUEST['fs_view'] == 'ticket' && isset($_REQUEST['support_hash']) && isset($_REQUEST['ticket_id']); | |
| 363 | + return Customer::where('user_id', $user)->select(['status'])->first(); | |
| 124 | 364 | } |
| 125 | 365 | } |