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
← All changes | app/Http/Controllers/CustomerPortalController.php +298 -284 1.4.52.4.0 View file →
@@ -1,65 +1,99 @@
1 1 <?php
2 2
3 3 namespace FluentSupport\App\Http\Controllers;
4 4
5 -use FluentSupport\App\Models\Attachment;
6 -use FluentSupport\App\Models\Customer;
7 -use FluentSupport\App\Models\MailBox;
5 +use Exception;
6 +use FluentSupport\App\Hooks\Handlers\ReCaptchaHandler;
7 +use FluentSupport\App\Http\Requests\TicketResponseRequest;
8 8 use FluentSupport\App\Models\Product;
9 -use FluentSupport\App\Models\Conversation;
10 9 use FluentSupport\App\Models\Ticket;
10 +use FluentSupport\App\Services\CustomerPortalService;
11 11 use FluentSupport\App\Services\Helper;
12 -use FluentSupport\App\Services\Includes\FileSystem;
13 -use FluentSupport\App\Services\Tickets\ResponseService;
14 -use FluentSupport\App\Services\Tickets\TicketService;
15 -use FluentSupport\Framework\Request\Request;
12 +use FluentSupport\Framework\Http\Request\Request;
16 13 use FluentSupport\Framework\Support\Arr;
17 14
15 +/**
16 + * CustomerPortalController class for REST API
17 + * This class is responsible for getting data for all request related to customer and customer portal
18 + * @package FluentSupport\App\Http\Controllers
19 + *
20 + * @version 1.0.0
21 + */
18 22 class CustomerPortalController extends Controller
19 23 {
24 + /**
25 + * getTickets will generate ticket information with customer and agents by customer
26 + * @param Request $request
27 + * @return array|\WP_REST_Response
28 + */
20 29 public function getTickets(Request $request)
21 30 {
22 - $customer = $this->resolveCustomer($request);
23 31
32 + $onBehalf = $this->sanitizeOnBehalf($request);
33 +
34 + $userIP = $request->getIp();
35 + $requestedStatus = $request->getSafe('filter_type', 'sanitize_text_field');
36 + $ticketOptions = $request->getSafe([
37 + 'search' => 'sanitize_text_field',
38 + 'filters.product_id' => 'intval',
39 + 'sorting.sort_type' => 'sanitize_sql_orderby',
40 + 'sorting.sort_by' => 'sanitize_sql_orderby'
41 + ]);
42 +
43 + $customer = (new CustomerPortalService)->resolveCustomer($onBehalf, $userIP);
44 +
24 45 if (!$customer) {
46 + return [
47 + 'tickets' => [
48 + 'data' => [],
49 + 'current_page' => $request->getSafe('page', 'intval', 1),
50 + 'last_page' => 1,
51 + 'per_page' => $request->getSafe('per_page', 'intval', 10),
52 + 'total' => 0,
53 + 'from' => null,
54 + 'to' => null
55 + ]
56 + ];
57 + }
58 +
59 + if (!$customer->canAccessPortal()) {
25 60 return $this->sendError([
26 - 'message' => __('No Customer Found', 'fluent-support'),
27 - 'error_type' => 'no_customer'
28 - ]);
61 + 'message' => __('Your account is not active. Please contact support.', 'fluent-support'),
62 + 'error_type' => '403'
63 + ], 403);
29 64 }
30 65
31 - if($customer->status == 'inactive') {
66 + $canAccess = apply_filters('fluent_support/can_customer_access_portal', true, $customer);
67 +
68 + if (is_wp_error($canAccess)) {
32 69 return $this->sendError([
33 - 'message' => __('Sorry, You do not have access to customer portal', 'fluent-support'),
34 - 'error_type' => 'inactive_customer'
35 - ]);
70 + 'message' => $canAccess->get_error_message(),
71 + 'error_type' => $canAccess->get_error_code()
72 + ], 403);
36 73 }
37 74
38 - $statuses = Arr::get([
75 + $statuses = [
39 76 'open' => ['new', 'active', 'on-hold'],
40 77 'all' => [],
41 78 'closed' => ['closed']
42 - ], $request->get('filter_type', ''));
79 + ];
43 80
44 - $ticketsQuery = Ticket::with([
45 - 'customer' => function ($query) {
46 - $query->select(['first_name', 'last_name', 'id']);
47 - }, 'agent' => function ($query) {
48 - $query->select(['first_name', 'last_name', 'id']);
49 - }
50 - ])
51 - ->where('customer_id', $customer->id)
52 - ->orderBy('id', 'DESC');
81 + $statusFilter = $statuses[$requestedStatus] ?? $statuses['all'];
53 82
54 - $ticketsQuery->where('customer_id', $customer->id);
83 + $sortBy = Arr::get($ticketOptions, 'sorting.sort_by', 'created_at');
84 + $sortType = Arr::get($ticketOptions, 'sorting.sort_type', 'desc');
55 85
56 - if ($statuses) {
57 - $ticketsQuery->whereIn('status', $statuses);
58 - }
86 + $ticketsQuery = Ticket::where('customer_id', $customer->id)
87 + ->filterByStatues($statusFilter)
88 + ->searchBy(Arr::get($ticketOptions, 'search'))
89 + ->filterByProductId(Arr::get($ticketOptions, 'filters.product_id'))
90 + ->orderBy($sortBy, $sortType);
59 91
60 - $tickets = $ticketsQuery->paginate();
92 + do_action_ref_array('fluent_support/customer_portal/tickets_query', [&$ticketsQuery, $customer, $request]);
61 93
94 + $tickets = $ticketsQuery->paginate($request->getInt('per_page', 10));
95 +
62 96 foreach ($tickets as $ticket) {
63 97 $ticket->human_date = sprintf(__('%s ago', 'fluent-support'), human_time_diff(strtotime($ticket->created_at), current_time('timestamp')));
64 98 $ticket->preview_response = $ticket->getLastResponse();
65 99 }
@@ -68,324 +102,263 @@
68 102 'tickets' => $tickets
69 103 ];
70 104 }
71 105
106 + /**
107 + * createTicket method will create ticket submitted by customers
108 + * @param Request $request
109 + * @return array | \WP_REST_Response
110 + */
72 111 public function createTicket(Request $request)
73 112 {
74 - $data = $request->all();
113 + if (ReCaptchaHandler::isRecaptchaApplicable('ticket_form')) {
114 + $captchaResponse = $request->getSafe('g-recaptcha-response', 'sanitize_text_field');
115 + $isValidCaptcha = $captchaResponse && ReCaptchaHandler::validateRecaptcha(
116 + $captchaResponse, null, null, 'create_ticket'
117 + );
75 118
76 - $this->validate($data, [
77 - 'title' => 'required',
78 - 'content' => 'required'
119 + if (!$isValidCaptcha) {
120 + return $this->sendError([
121 + 'message' => __('Your recaptcha is not verified', 'fluent-support'),
122 + 'error_type' => '422'
123 + ], 422);
124 + }
125 + }
126 +
127 + $dataRules = $this->app->applyCustomFilters('custom_field_required_before_ticket_create', [
128 + 'required_fields' => [
129 + 'title' => 'required',
130 + 'content' => 'required'
131 + ],
132 + 'error_messages' => [
133 + 'title.required' => __('Title is required', 'fluent-support'),
134 + 'content.required' => __('Content is required', 'fluent-support')
135 + ]
79 136 ]);
80 137
81 - $data['title'] = sanitize_text_field(wp_unslash($data['title']));
138 + $settings = Helper::getOption('_ticket_form_settings', []);
82 139
83 - $data['content'] = wp_unslash(wp_kses_post($data['content']));
140 + if (!empty($settings['product_required_field']) && $settings['product_required_field'] === 'yes') {
141 + $productCount = Product::count();
142 + if ($productCount > 0) {
143 + $dataRules['required_fields']['product_id'] = 'required';
144 + $dataRules['error_messages']['product_id.required'] = __('Product is required', 'fluent-support');
145 + }
146 + }
84 147
85 - $customer = $this->resolveCustomer($request, true);
148 + $defaultData = [
149 + 'ticket_title' => $request->getSafe('title', 'sanitize_text_field'),
150 + 'ticket_content' => $request->getSafe('content', 'wp_kses_post')
151 + ];
86 152
87 - if($customer->status == 'inactive') {
88 - return $this->sendError([
89 - 'message' => __('Sorry, You do not have access to customer portal', 'fluent-support'),
90 - 'error_type' => 'inactive_customer'
91 - ]);
153 + if ($request->has('product_id')) {
154 + $defaultData['ticket_product_id'] = $request->getSafe('product_id', 'intval');
92 155 }
93 156
94 - if (!$customer) {
95 - return $this->sendError([
96 - 'message' => __('No Customer Found', 'fluent-support'),
97 - 'error_type' => 'no_customer'
98 - ]);
157 + if ($request->has('client_priority')) {
158 + $defaultData['ticket_client_priority'] = $request->getSafe('client_priority', 'sanitize_text_field');
99 159 }
100 160
101 - $data['customer_id'] = $customer->id;
102 - $data['product_source'] = 'local';
103 - $data['mailbox_id'] = $this->resolveMailboxId($request);
161 + $customData = $request->get('custom_data', []);
162 + $customData = is_array($customData) ? map_deep($customData, 'sanitize_text_field') : [];
104 163
105 - $disabledFields = apply_filters('fluent_support/disabled_ticket_fields', []);
164 + $dataRules = $this->app->applyCustomFilters('custom_field_required_by_conditions_before_ticket_create', [
165 + 'required_fields' => $dataRules['required_fields'],
166 + 'error_messages' => $dataRules['error_messages'],
167 + 'custom_data' => $customData,
168 + 'default_data' => $defaultData
169 + ]);
106 170
107 - if(!in_array('priority', $disabledFields)) {
108 - $data['priority'] = sanitize_text_field($data['client_priority']);
109 - $data['client_priority'] = sanitize_text_field($data['client_priority']);
171 + if (!isset($dataRules['required_fields']) && !isset($dataRules['error_messages'])) {
172 + return $this->sendError([
173 + 'message' => __('Invalid form data submitted', 'fluent-support'),
174 + 'error_type' => '400'
175 + ], 400);
110 176 }
111 177
112 - if(!in_array('product_services', $disabledFields)) {
113 - unset($data['product_id']);
114 - }
178 + $data = $this->validate($request->get(), $dataRules['required_fields'], $dataRules['error_messages']);
115 179
116 - $data['source'] = 'web';
180 + $data['title'] = sanitize_text_field($data['title']);
181 + $data['content'] = wp_kses_post($data['content']);
182 + $data['custom_data'] = $customData;
117 183
118 - $data = apply_filters('fluent_support/create_ticket_data', $data, $customer);
119 - do_action('fluent_support/before_ticket_create', $data, $customer);
120 - $ticket = Ticket::create($data);
184 + $onBehalf = $this->sanitizeOnBehalf($request);
185 + $userIP = $request->getIp();
121 186
122 - if ($attachments = Arr::get($data, 'attachments') && !in_array('file_upload', $disabledFields)) {
123 - Attachment::whereNull('ticket_id')
124 - ->whereIn('file_hash', $attachments)
125 - ->whereNull('ticket_id')
126 - ->update([
127 - 'ticket_id' => $ticket->id,
128 - 'person_id' => $customer->id,
129 - 'status' => 'active'
130 - ]);
131 - $ticket->load('attachments');
187 + if (!empty($onBehalf['last_ip_address'])) {
188 + $userIP = $onBehalf['last_ip_address'];
132 189 }
133 190
191 + try {
192 + $customer = (new CustomerPortalService())->resolveCustomer($onBehalf, $userIP, true);
134 193
135 - if(defined('FLUENTSUPPORTPRO')) {
136 - $customData = Arr::get($data, 'custom_data');
137 - if($customData) {
138 - $customData = wp_unslash($customData);
139 - $ticket->syncCustomFields($customData);
194 + if (!$customer) {
195 + return $this->sendError([
196 + 'message' => __('Unable to identify. Please make sure you have provided correct information.', 'fluent-support'),
197 + 'error_type' => '403'
198 + ], 403);
140 199 }
141 - }
142 200
143 - do_action('fluent_support/ticket_created', $ticket, $customer);
201 + $canCreateTicket = apply_filters('fluent_support/can_customer_create_ticket', true, $customer, $data);
144 202
145 - return [
146 - 'message' => __('Ticket has been created successfully', 'fluent-support'),
147 - 'ticket' => $ticket
148 - ];
149 - }
203 + if (!$canCreateTicket || is_wp_error($canCreateTicket)) {
204 + $isWpError = is_wp_error($canCreateTicket);
150 205
151 - public function getTicket(Request $request, $ticketId)
152 - {
153 - $ticket = Ticket::where('id', $ticketId)
154 - ->with([
155 - 'customer' => function ($query) {
156 - $query->select(['first_name', 'email', 'person_type', 'last_name', 'id']);
157 - }, 'agent' => function ($query) {
158 - $query->select(['first_name', 'email', 'person_type', 'last_name', 'id', 'title']);
159 - },
160 - 'product',
161 - 'attachments'
162 - ])
163 - ->first();
206 + $message = ($isWpError) ? $canCreateTicket->get_error_message() : __('Sorry you cannot create ticket', 'fluent-support');
207 + $errorCode = ($isWpError) ? $canCreateTicket->get_error_code() : 'general_error';
164 208
165 - if ($request->get('intended_ticket_hash') && Helper::isPublicSignedTicketEnabled()) {
166 - $customer = $ticket->customer;
167 - } else {
168 - $customer = $this->resolveCustomer($request);
169 - }
209 + return $this->sendError([
210 + 'message' => $message,
211 + 'error_type' => $errorCode
212 + ]);
213 + }
170 214
171 - if (!$customer) {
172 - return $this->sendError([
173 - 'message' => __('Sorry, You do not have permission to this support ticket', 'fluent-support'),
174 - 'error_type' => 'no_customer'
175 - ]);
176 - }
215 + if ($messageId = Helper::generateMessageID($customer->email)) {
216 + $data['message_id'] = $messageId;
217 + }
177 218
178 - if($customer->status == 'inactive') {
219 + $defaultMailbox = Helper::getDefaultMailBox();
220 + $defaultMailboxId = $defaultMailbox ? $defaultMailbox->id : null;
221 + $ticket = (new CustomerPortalService())->createTicket($customer, $data, $request->getSafe('mailbox_id', 'intval', $defaultMailboxId));
222 +
223 + return [
224 + 'message' => __('Ticket has been created successfully', 'fluent-support'),
225 + 'ticket' => $ticket
226 + ];
227 + } catch (\Exception $e) {
179 228 return $this->sendError([
180 - 'message' => __('Sorry, You do not have access to customer portal', 'fluent-support'),
181 - 'error_type' => 'inactive_customer'
229 + 'message' => Helper::getSafeErrorMessage($e),
230 + 'error_type' => $e->getCode()
182 231 ]);
183 232 }
233 + }
184 234
235 + /**
236 + * getTicket method will get the ticket information with customer and agent as well as response in a ticket by ticket id
237 + * @param Request $request
238 + * @param $ticket_id
239 + * @return array|\WP_REST_Response
240 + */
241 + public function getTicket(Request $request, $ticket_id)
242 + {
243 + $customerAdditionalData = $this->getCustomerAdditionalData($request);
185 244
186 - if ($ticket->privacy == 'private' && $customer->id != $ticket->customer_id) {
245 + try {
246 + return (new CustomerPortalService())->getTicket($customerAdditionalData, $ticket_id);
247 + } catch (\Exception $e) {
187 248 return $this->sendError([
188 - 'message' => __('You do not have permission to view this support ticket', 'fluent-support'),
189 - 'error_type' => 'permission_error'
249 + 'message' => Helper::getSafeErrorMessage($e),
250 + 'error_type' => $e->getCode()
190 251 ]);
191 252 }
192 -
193 - $responses = Conversation::where('ticket_id', $ticketId)
194 - ->with([
195 - 'person' => function ($query) {
196 - $query->select(['first_name', 'email', 'person_type', 'last_name', 'id', 'title']);
197 - },
198 - 'attachments'
199 - ])
200 - ->filterByType('response')
201 - ->orderBy('id', 'DESC')
202 - ->get();
203 -
204 - foreach ($responses as $response) {
205 - $response->content = make_clickable($response->content);
206 - if ($response->person) {
207 - $response->person->setHidden(['email']);
208 - }
209 - }
210 -
211 - $ticket->content = make_clickable($ticket->content);
212 -
213 - if ($ticket->customer) {
214 - $ticket->customer->setHidden(['email']);
215 - }
216 -
217 - if ($ticket->agent) {
218 - $ticket->agent->setHidden(['email']);
219 - }
220 -
221 - if ($ticket->status == 'closed') {
222 - $ticket->load('closed_by_person');
223 - if ($ticket->closed_by_person) {
224 - $ticket->closed_by_person->setVisible(['first_name', 'last_name', 'id', 'full_name', 'photo']);
225 - }
226 - }
227 -
228 - if(defined('FLUENTSUPPORTPRO')) {
229 - $ticket->custom_fields = $ticket->customData('public', true);
230 - }
231 -
232 - return [
233 - 'ticket' => $ticket,
234 - 'responses' => $responses,
235 - 'sign_on_id' => $ticket->customer_id
236 - ];
237 253 }
238 254
239 - public function createResponse(Request $request, $ticketId)
255 + /**
256 + * createResponse method will create response by customer in a ticket by ticket id
257 + * @param Request $request
258 + * @param $ticket_id
259 + * @return array|\WP_REST_Response
260 + * @throws \FluentSupport\Framework\Validator\ValidationException
261 + */
262 + public function createResponse(TicketResponseRequest $request, $ticket_id)
240 263 {
241 - $data = $request->all();
242 - $this->validate($data, [
243 - 'content' => 'required'
244 - ]);
245 264
246 - $data['content'] = wp_unslash(wp_kses_post($data['content']));
265 + $customerAdditionalData = $this->getCustomerAdditionalData($request);
247 266
267 + $ticket = Ticket::wherePublicIdentifier($ticket_id)->firstOrFail();
248 268
249 - $ticket = Ticket::with(['customer'])->findOrFail($ticketId);
269 + $data = $request->sanitize();
250 270
251 - if ($request->get('intended_ticket_hash') && Helper::isPublicSignedTicketEnabled()) {
252 - $customer = $ticket->customer;
253 - } else {
254 - $customer = $this->resolveCustomer($request);
255 - }
271 + $canCreateResponse = apply_filters('fluent_support/can_customer_create_response', true, $ticket->customer, $ticket, $data);
256 272
257 - if (!$customer) {
258 - return $this->sendError([
259 - 'message' => __('Sorry! No customer found', 'fluent-support')
260 - ]);
273 + if (!$canCreateResponse || is_wp_error($canCreateResponse)) {
274 + return [
275 + 'type' => 'error',
276 + 'message' => (is_wp_error($canCreateResponse)) ? $canCreateResponse->get_error_message() : __('Sorry you cannot create response', 'fluent-support')
277 + ];
261 278 }
262 279
263 - if($customer->status == 'inactive') {
280 + try {
281 + return (new CustomerPortalService())->createResponse($customerAdditionalData, $ticket_id, $data);
282 + } catch (\Exception $e) {
264 283 return $this->sendError([
265 - 'message' => __('Sorry, You do not have access to customer portal', 'fluent-support'),
266 - 'error_type' => 'inactive_customer'
284 + 'message' => Helper::getSafeErrorMessage($e),
285 + 'error_type' => $e->getCode()
267 286 ]);
268 287 }
288 + }
269 289
290 + /**
291 + * This `closeTicket` is responsible for closing ticket by ticket id
292 + * @param Request $request
293 + * @param $ticket_id
294 + * @return array
295 + */
296 + public function closeTicket(Request $request, $ticket_id)
297 + {
298 + $customerAdditionalData = $this->getCustomerAdditionalData($request);
270 299
271 - if ($ticket->privacy == 'private' && $customer->id != $ticket->customer_id) {
300 + try {
301 + return (new CustomerPortalService())->closeTicket($customerAdditionalData, $ticket_id);
302 + } catch (Exception $e) {
272 303 return $this->sendError([
273 - 'message' => __('Sorry! You can not reply to this ticket', 'fluent-support')
304 + 'message' => Helper::getSafeErrorMessage($e),
305 + 'error_type' => $e->getCode()
274 306 ]);
275 307 }
276 -
277 - $data['conversation_type'] = 'response';
278 - $responseData = (new ResponseService())->createResponse($data, $customer, $ticket);
279 -
280 - return [
281 - 'message' => __('Reply has been added', 'fluent-support'),
282 - 'response' => $responseData['response'],
283 - 'ticket' => $responseData['ticket']
284 - ];
285 308 }
286 309
287 - public function closeTicket(Request $request, $ticketId)
310 + /**
311 + * closeTicket method will re-open a ticket by customer using ticket id
312 + * @param Request $request
313 + * @param $ticket_id
314 + * @return array
315 + */
316 + public function reOpenTicket(Request $request, $ticket_id)
288 317 {
289 - $ticket = Ticket::with(['customer'])->findOrFail($ticketId);
318 + $customerAdditionalData = $this->getCustomerAdditionalData($request);
290 319
291 - if ($request->get('intended_ticket_hash') && Helper::isPublicSignedTicketEnabled()) {
292 - $customer = $ticket->customer;
293 - } else {
294 - $customer = $this->resolveCustomer($request);
295 - }
296 -
297 - if (!$customer) {
320 + try {
321 + return (new CustomerPortalService())->reOpenTicket($customerAdditionalData, $ticket_id);
322 + } catch (Exception $e) {
298 323 return $this->sendError([
299 - 'message' => __('Sorry! No customer found', 'fluent-support')
324 + 'message' => Helper::getSafeErrorMessage($e),
325 + 'error_type' => $e->getCode()
300 326 ]);
301 327 }
302 -
303 - if($customer->status == 'inactive') {
304 - return $this->sendError([
305 - 'message' => __('Sorry, You do not have access to customer portal', 'fluent-support'),
306 - 'error_type' => 'inactive_customer'
307 - ]);
308 - }
309 -
310 -
311 - if ($customer->id != $ticket->customer_id) {
312 - return $this->sendError([
313 - 'message' => __('Sorry! You can not close this ticket', 'fluent-support')
314 - ]);
315 - }
316 -
317 - return [
318 - 'message' => __('Ticket has been closed', 'fluent_support'),
319 - 'ticket' => (new TicketService())->close($ticket, $customer)
320 - ];
321 328 }
322 329
323 - public function reOpenTicket(Request $request, $ticketId)
330 + public function agentFeedbackRating(Request $request, $ticketId)
324 331 {
325 - $ticket = Ticket::with(['customer'])->findOrFail($ticketId);
326 332
327 - if ($request->get('intended_ticket_hash') && Helper::isPublicSignedTicketEnabled()) {
328 - $customer = $ticket->customer;
329 - } else {
330 - $customer = $this->resolveCustomer($request);
331 - }
333 + $customerPortalService = new CustomerPortalService();
332 334
333 - if($customer->status == 'inactive') {
334 - return $this->sendError([
335 - 'message' => __('Sorry, You do not have access to customer portal', 'fluent-support'),
336 - 'error_type' => 'inactive_customer'
337 - ]);
338 - }
335 + // just for validation
336 + $ticket = Ticket::with(['customer'])->wherePublicIdentifier($ticketId)->firstOrFail();
337 + $customerAdditionalData = $this->getCustomerAdditionalData($request);
338 + $customer = $customerPortalService->getCustomer($customerAdditionalData, $ticket);
339 + $customerPortalService->checkCustomerTicketAccess($customer, $ticket, 'feedback');
339 340
341 + $conversationID = $request->getSafe('conversation_id', 'intval');
342 + $approvalStatus = $request->getSafe('approval_status', 'sanitize_text_field');
340 343
341 - if (!$customer) {
344 + try {
345 + return $customerPortalService->addUserFeedback($approvalStatus, $conversationID, $ticket->id);
346 + } catch (Exception $e) {
342 347 return $this->sendError([
343 - 'message' => __('Sorry! No customer found', 'fluent-support')
348 + 'message' => Helper::getSafeErrorMessage($e),
349 + 'error_type' => $e->getCode()
344 350 ]);
345 351 }
346 -
347 - if ($customer->id != $ticket->customer_id) {
348 - return $this->sendError([
349 - 'message' => __('Sorry! You can not close this ticket', 'fluent-support')
350 - ]);
351 - }
352 -
353 -
354 - return [
355 - 'message' => __('Ticket has been opened again', 'fluent_support'),
356 - 'ticket' => (new TicketService())->reopen($ticket, $customer)
357 - ];
358 -
359 -
360 352 }
361 353
362 - private function resolveCustomer($request, $forceCreate = false)
363 - {
364 - $onBehalf = $request->get('on_behalf');
365 -
366 - if (!$onBehalf) {
367 - $user = get_user_by('ID', get_current_user_id());
368 - if (!$user) {
369 - return false;
370 - }
371 - $onBehalf = [
372 - 'user_id' => $user->id,
373 - 'email' => $user->user_email,
374 - 'last_ip_address' => $request->getIp()
375 - ];
376 - }
377 -
378 - if ($forceCreate) {
379 - return Customer::maybeCreateCustomer($onBehalf);
380 - }
381 -
382 - return Customer::getCustomerFromData($onBehalf);
383 - }
384 -
354 + /**
355 + * getPublicOptions method will return the list of product and customer priorities
356 + * @return array
357 + */
385 358 public function getPublicOptions()
386 359 {
387 - $products = Product::select(['id', 'title'])->get();
360 + $products = Product::select(['id', 'title'])->orderedByTitle()->get();
388 361
389 362 return [
390 363 'support_products' => $products,
391 364 'customer_ticket_priorities' => Helper::customerTicketPriorities()
@@ -391,11 +364,15 @@
391 364 'customer_ticket_priorities' => Helper::customerTicketPriorities()
392 365 ];
393 366 }
394 367
368 + /**
369 + * getCustomFieldsRender method will return the list of custom fields
370 + * @return array|array[]
371 + */
395 372 public function getCustomFieldsRender()
396 373 {
397 - if(!defined('FLUENTSUPPORTPRO')) {
374 + if (!defined('FLUENTSUPPORTPRO')) {
398 375 return [
399 376 'custom_fields_rendered' => []
400 377 ];
401 378 }
@@ -400,26 +377,63 @@
400 377 ];
401 378 }
402 379
403 380 return [
404 - 'custom_fields_rendered' => \FluentSupportPro\App\Services\CustomFieldsService::getRenderedPublicFields()
381 + 'custom_fields_rendered' => \FluentSupportPro\App\Services\CustomFieldsService::getRenderedPublicFields()
405 382 ];
383 + }
406 384
385 +
386 + /**
387 + * logout method will logout the customer
388 + * @return mixed
389 + */
390 + public function logout()
391 + {
392 + wp_logout();
393 +
394 + return $this->sendSuccess([
395 + 'message' => __('You have been logged out', 'fluent-support')
396 + ]);
407 397 }
408 398
409 - private function resolveMailboxId($request)
399 + private function getCustomerAdditionalData($request)
410 400 {
411 - if ($mailboxId = $request->get('mailbox_id')) {
412 - $mailbox = MailBox::find($mailboxId);
413 - if ($mailbox) {
414 - return $mailbox->id;
415 - }
401 +
402 + $customerAdditionalData = [
403 + 'intended_ticket_hash' => $request->getSafe('intended_ticket_hash', 'sanitize_text_field'),
404 + 'on_behalf' => $this->sanitizeOnBehalf($request),
405 + 'user_ip' => $request->getIp()
406 + ];
407 +
408 + return $customerAdditionalData;
409 + }
410 +
411 + /**
412 + * Read the `on_behalf` identity payload from the request in a predictable shape.
413 + * Only an array of scalars is accepted; anything else (a scalar, or nested
414 + * arrays) is discarded so callers can never hand a non-string to
415 + * sanitize_text_field(). An empty result makes resolveCustomer() fall back to
416 + * the logged-in user instead of a request-supplied identity.
417 + *
418 + * @param Request $request
419 + * @return array
420 + */
421 + private function sanitizeOnBehalf($request)
422 + {
423 + $onBehalf = $request->get('on_behalf', []);
424 +
425 + if (!is_array($onBehalf)) {
426 + return [];
416 427 }
417 428
418 - $mailbox = Helper::getDefaultMailBox();
429 + $sanitized = [];
419 430
420 - if ($mailbox) {
421 - return $mailbox->id;
431 + foreach ($onBehalf as $key => $value) {
432 + if (is_scalar($value)) {
433 + $sanitized[sanitize_text_field($key)] = sanitize_text_field($value);
434 + }
422 435 }
423 - return null;
436 +
437 + return $sanitized;
424 438 }
425 439 }