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 +260 -338 1.5.52.4.0 View file →
@@ -1,19 +1,16 @@
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
18 15 /**
19 16 * CustomerPortalController class for REST API
@@ -21,60 +18,82 @@
21 18 * @package FluentSupport\App\Http\Controllers
22 19 *
23 20 * @version 1.0.0
24 21 */
25 -
26 22 class CustomerPortalController extends Controller
27 23 {
28 24 /**
29 25 * getTickets will generate ticket information with customer and agents by customer
30 26 * @param Request $request
31 - * @return array
27 + * @return array|\WP_REST_Response
32 28 */
33 29 public function getTickets(Request $request)
34 30 {
35 - //Get customer information
36 - $customer = $this->resolveCustomer($request);
37 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 +
38 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()) {
39 60 return $this->sendError([
40 - 'message' => __('No Customer Found', 'fluent-support'),
41 - 'error_type' => 'no_customer'
42 - ]);
61 + 'message' => __('Your account is not active. Please contact support.', 'fluent-support'),
62 + 'error_type' => '403'
63 + ], 403);
43 64 }
44 65
45 - if($customer->status == 'inactive') {
66 + $canAccess = apply_filters('fluent_support/can_customer_access_portal', true, $customer);
67 +
68 + if (is_wp_error($canAccess)) {
46 69 return $this->sendError([
47 - 'message' => __('Sorry, You do not have access to customer portal', 'fluent-support'),
48 - 'error_type' => 'inactive_customer'
49 - ]);
70 + 'message' => $canAccess->get_error_message(),
71 + 'error_type' => $canAccess->get_error_code()
72 + ], 403);
50 73 }
51 74
52 - $statuses = Arr::get([
75 + $statuses = [
53 76 'open' => ['new', 'active', 'on-hold'],
54 77 'all' => [],
55 78 'closed' => ['closed']
56 - ], $request->get('filter_type', ''));
79 + ];
57 80
58 - //get tickets with customer and agent
59 - $ticketsQuery = Ticket::with([
60 - 'customer' => function ($query) {
61 - $query->select(['first_name', 'last_name', 'id']);
62 - }, 'agent' => function ($query) {
63 - $query->select(['first_name', 'last_name', 'id']);
64 - }
65 - ])
66 - ->where('customer_id', $customer->id)
67 - ->orderBy('id', 'DESC');
81 + $statusFilter = $statuses[$requestedStatus] ?? $statuses['all'];
68 82
69 - $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');
70 85
71 - if ($statuses) {
72 - $ticketsQuery->whereIn('status', $statuses);
73 - }
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);
74 91
75 - $tickets = $ticketsQuery->paginate();
92 + do_action_ref_array('fluent_support/customer_portal/tickets_query', [&$ticketsQuery, $customer, $request]);
76 93
94 + $tickets = $ticketsQuery->paginate($request->getInt('per_page', 10));
95 +
77 96 foreach ($tickets as $ticket) {
78 97 $ticket->human_date = sprintf(__('%s ago', 'fluent-support'), human_time_diff(strtotime($ticket->created_at), current_time('timestamp')));
79 98 $ticket->preview_response = $ticket->getLastResponse();
80 99 }
@@ -86,389 +105,260 @@
86 105
87 106 /**
88 107 * createTicket method will create ticket submitted by customers
89 108 * @param Request $request
90 - * @return array
91 - * @throws \FluentSupport\Framework\Validator\ValidationException
109 + * @return array | \WP_REST_Response
92 110 */
93 111 public function createTicket(Request $request)
94 112 {
95 - $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 + );
96 118
97 - $this->validate($data, [
98 - 'title' => 'required',
99 - '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 + ]
100 136 ]);
101 137
102 - $data['title'] = sanitize_text_field(wp_unslash($data['title']));
138 + $settings = Helper::getOption('_ticket_form_settings', []);
103 139
104 - $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 + }
105 147
106 - $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 + ];
107 152
108 - if($customer->status == 'inactive') {
109 - return $this->sendError([
110 - 'message' => __('Sorry, You do not have access to customer portal', 'fluent-support'),
111 - 'error_type' => 'inactive_customer'
112 - ]);
153 + if ($request->has('product_id')) {
154 + $defaultData['ticket_product_id'] = $request->getSafe('product_id', 'intval');
113 155 }
114 156
115 - if (!$customer) {
157 + if ($request->has('client_priority')) {
158 + $defaultData['ticket_client_priority'] = $request->getSafe('client_priority', 'sanitize_text_field');
159 + }
160 +
161 + $customData = $request->get('custom_data', []);
162 + $customData = is_array($customData) ? map_deep($customData, 'sanitize_text_field') : [];
163 +
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 + ]);
170 +
171 + if (!isset($dataRules['required_fields']) && !isset($dataRules['error_messages'])) {
116 172 return $this->sendError([
117 - 'message' => __('No Customer Found', 'fluent-support'),
118 - 'error_type' => 'no_customer'
119 - ]);
173 + 'message' => __('Invalid form data submitted', 'fluent-support'),
174 + 'error_type' => '400'
175 + ], 400);
120 176 }
121 177
122 - $data['customer_id'] = $customer->id;
123 - $data['product_source'] = 'local';
124 - $data['mailbox_id'] = $this->resolveMailboxId($request);
178 + $data = $this->validate($request->get(), $dataRules['required_fields'], $dataRules['error_messages']);
125 179
126 - $disabledFields = apply_filters('fluent_support/disabled_ticket_fields', []);
180 + $data['title'] = sanitize_text_field($data['title']);
181 + $data['content'] = wp_kses_post($data['content']);
182 + $data['custom_data'] = $customData;
127 183
128 - if(!in_array('priority', $disabledFields)) {
129 - $data['priority'] = sanitize_text_field($data['client_priority']);
130 - $data['client_priority'] = sanitize_text_field($data['client_priority']);
184 + $onBehalf = $this->sanitizeOnBehalf($request);
185 + $userIP = $request->getIp();
186 +
187 + if (!empty($onBehalf['last_ip_address'])) {
188 + $userIP = $onBehalf['last_ip_address'];
131 189 }
132 190
133 - if(in_array('product_services', $disabledFields)) {
134 - unset($data['product_id']);
135 - }
191 + try {
192 + $customer = (new CustomerPortalService())->resolveCustomer($onBehalf, $userIP, true);
136 193
137 - $data['source'] = 'web';
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);
199 + }
138 200
139 - /*
140 - * Filter ticket data
141 - *
142 - * @since v1.0.0
143 - * @param array $data
144 - * @param object $customer
145 - */
146 - $data = apply_filters('fluent_support/create_ticket_data', $data, $customer);
201 + $canCreateTicket = apply_filters('fluent_support/can_customer_create_ticket', true, $customer, $data);
147 202
148 - /*
149 - * Action before ticket create
150 - *
151 - * @since v1.0.0
152 - * @param array $data
153 - * @param object $customer
154 - */
155 - do_action('fluent_support/before_ticket_create', $data, $customer);
156 - $ticket = Ticket::create($data);
203 + if (!$canCreateTicket || is_wp_error($canCreateTicket)) {
204 + $isWpError = is_wp_error($canCreateTicket);
157 205
158 - if (($attachments = Arr::get($data, 'attachments')) && !in_array('file_upload', $disabledFields)) {
206 + $message = ($isWpError) ? $canCreateTicket->get_error_message() : __('Sorry you cannot create ticket', 'fluent-support');
207 + $errorCode = ($isWpError) ? $canCreateTicket->get_error_code() : 'general_error';
159 208
160 - Attachment::whereNull('ticket_id')
161 - ->whereIn('file_hash', $attachments)
162 - ->whereNull('ticket_id')
163 - ->update([
164 - 'ticket_id' => $ticket->id,
165 - 'person_id' => $customer->id,
166 - 'status' => 'active'
209 + return $this->sendError([
210 + 'message' => $message,
211 + 'error_type' => $errorCode
167 212 ]);
213 + }
168 214
169 - $ticket->load('attachments');
170 - }
215 + if ($messageId = Helper::generateMessageID($customer->email)) {
216 + $data['message_id'] = $messageId;
217 + }
171 218
219 + $defaultMailbox = Helper::getDefaultMailBox();
220 + $defaultMailboxId = $defaultMailbox ? $defaultMailbox->id : null;
221 + $ticket = (new CustomerPortalService())->createTicket($customer, $data, $request->getSafe('mailbox_id', 'intval', $defaultMailboxId));
172 222
173 - if(defined('FLUENTSUPPORTPRO')) {
174 - $customData = Arr::get($data, 'custom_data');
175 - if($customData) {
176 - $customData = wp_unslash($customData);
177 - $ticket->syncCustomFields($customData);
178 - }
223 + return [
224 + 'message' => __('Ticket has been created successfully', 'fluent-support'),
225 + 'ticket' => $ticket
226 + ];
227 + } catch (\Exception $e) {
228 + return $this->sendError([
229 + 'message' => Helper::getSafeErrorMessage($e),
230 + 'error_type' => $e->getCode()
231 + ]);
179 232 }
180 -
181 - /*
182 - * Action on ticket create
183 - *
184 - * @since v1.0.0
185 - * @param object $ticket
186 - * @param object $customer
187 - */
188 - do_action('fluent_support/ticket_created', $ticket, $customer);
189 -
190 - return [
191 - 'message' => __('Ticket has been created successfully', 'fluent-support'),
192 - 'ticket' => $ticket
193 - ];
194 233 }
195 234
196 235 /**
197 236 * getTicket method will get the ticket information with customer and agent as well as response in a ticket by ticket id
198 237 * @param Request $request
199 - * @param $ticketId
200 - * @return array
238 + * @param $ticket_id
239 + * @return array|\WP_REST_Response
201 240 */
202 - public function getTicket(Request $request, $ticketId)
241 + public function getTicket(Request $request, $ticket_id)
203 242 {
204 - //Get ticket by id with customer, agent, product and attachments
205 - $ticket = Ticket::where('id', $ticketId)
206 - ->with([
207 - 'customer' => function ($query) {
208 - $query->select(['first_name', 'email', 'person_type', 'last_name', 'id']);
209 - }, 'agent' => function ($query) {
210 - $query->select(['first_name', 'email', 'person_type', 'last_name', 'id', 'title']);
211 - },
212 - 'product',
213 - 'attachments'
214 - ])
215 - ->first();
243 + $customerAdditionalData = $this->getCustomerAdditionalData($request);
216 244
217 - if ($request->get('intended_ticket_hash') && Helper::isPublicSignedTicketEnabled()) {
218 - $customer = $ticket->customer;
219 - } else {
220 - $customer = $this->resolveCustomer($request);
221 - }
222 -
223 - if (!$customer) {
245 + try {
246 + return (new CustomerPortalService())->getTicket($customerAdditionalData, $ticket_id);
247 + } catch (\Exception $e) {
224 248 return $this->sendError([
225 - 'message' => __('Sorry, You do not have permission to this support ticket', 'fluent-support'),
226 - 'error_type' => 'no_customer'
249 + 'message' => Helper::getSafeErrorMessage($e),
250 + 'error_type' => $e->getCode()
227 251 ]);
228 252 }
229 -
230 - if($customer->status == 'inactive') {
231 - return $this->sendError([
232 - 'message' => __('Sorry, You do not have access to customer portal', 'fluent-support'),
233 - 'error_type' => 'inactive_customer'
234 - ]);
235 - }
236 -
237 -
238 - if ($ticket->privacy == 'private' && $customer->id != $ticket->customer_id) {
239 - return $this->sendError([
240 - 'message' => __('You do not have permission to view this support ticket', 'fluent-support'),
241 - 'error_type' => 'permission_error'
242 - ]);
243 - }
244 -
245 - //Get responses in a ticket by
246 - $responses = Conversation::where('ticket_id', $ticketId)
247 - ->with([
248 - 'person' => function ($query) {
249 - $query->select(['first_name', 'email', 'person_type', 'last_name', 'id', 'title']);
250 - },
251 - 'attachments'
252 - ])
253 - ->filterByType('response')
254 - ->orderBy('id', 'DESC')
255 - ->get();
256 -
257 - foreach ($responses as $response) {
258 - $response->content = make_clickable($response->content);
259 - if ($response->person) {
260 - $response->person->setHidden(['email']);
261 - }
262 - }
263 -
264 - $ticket->content = make_clickable($ticket->content);
265 -
266 - if ($ticket->customer) {
267 - $ticket->customer->setHidden(['email']);
268 - }
269 -
270 - if ($ticket->agent) {
271 - $ticket->agent->setHidden(['email']);
272 - }
273 -
274 - if ($ticket->status == 'closed') {
275 - $ticket->load('closed_by_person');
276 - if ($ticket->closed_by_person) {
277 - $ticket->closed_by_person->setVisible(['first_name', 'last_name', 'id', 'full_name', 'photo']);
278 - }
279 - }
280 -
281 - if(defined('FLUENTSUPPORTPRO')) {
282 - $ticket->custom_fields = $ticket->customData('public', true);
283 - }
284 -
285 - return [
286 - 'ticket' => $ticket,
287 - 'responses' => $responses,
288 - 'sign_on_id' => $ticket->customer_id
289 - ];
290 253 }
291 254
292 255 /**
293 256 * createResponse method will create response by customer in a ticket by ticket id
294 257 * @param Request $request
295 - * @param $ticketId
296 - * @return array
258 + * @param $ticket_id
259 + * @return array|\WP_REST_Response
297 260 * @throws \FluentSupport\Framework\Validator\ValidationException
298 261 */
299 - public function createResponse(Request $request, $ticketId)
262 + public function createResponse(TicketResponseRequest $request, $ticket_id)
300 263 {
301 - $data = $request->all();
302 - $this->validate($data, [
303 - 'content' => 'required'
304 - ]);
305 264
306 - $data['content'] = wp_unslash(wp_kses_post($data['content']));
265 + $customerAdditionalData = $this->getCustomerAdditionalData($request);
307 266
267 + $ticket = Ticket::wherePublicIdentifier($ticket_id)->firstOrFail();
308 268
309 - $ticket = Ticket::with(['customer'])->findOrFail($ticketId);
269 + $data = $request->sanitize();
310 270
311 - if ($request->get('intended_ticket_hash') && Helper::isPublicSignedTicketEnabled()) {
312 - $customer = $ticket->customer;
313 - } else {
314 - $customer = $this->resolveCustomer($request);
315 - }
271 + $canCreateResponse = apply_filters('fluent_support/can_customer_create_response', true, $ticket->customer, $ticket, $data);
316 272
317 - if (!$customer) {
318 - return $this->sendError([
319 - 'message' => __('Sorry! No customer found', 'fluent-support')
320 - ]);
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 + ];
321 278 }
322 279
323 - if($customer->status == 'inactive') {
280 + try {
281 + return (new CustomerPortalService())->createResponse($customerAdditionalData, $ticket_id, $data);
282 + } catch (\Exception $e) {
324 283 return $this->sendError([
325 - 'message' => __('Sorry, You do not have access to customer portal', 'fluent-support'),
326 - 'error_type' => 'inactive_customer'
284 + 'message' => Helper::getSafeErrorMessage($e),
285 + 'error_type' => $e->getCode()
327 286 ]);
328 287 }
329 -
330 -
331 - if ($ticket->privacy == 'private' && $customer->id != $ticket->customer_id) {
332 - return $this->sendError([
333 - 'message' => __('Sorry! You can not reply to this ticket', 'fluent-support')
334 - ]);
335 - }
336 -
337 - $data['conversation_type'] = 'response';
338 - $responseData = (new ResponseService())->createResponse($data, $customer, $ticket);
339 -
340 - return [
341 - 'message' => __('Reply has been added', 'fluent-support'),
342 - 'response' => $responseData['response'],
343 - 'ticket' => $responseData['ticket']
344 - ];
345 288 }
346 289
347 290 /**
348 - * closeTicket method will close a ticket by customer using ticket id
291 + * This `closeTicket` is responsible for closing ticket by ticket id
349 292 * @param Request $request
350 - * @param $ticketId
293 + * @param $ticket_id
351 294 * @return array
352 295 */
353 - public function closeTicket(Request $request, $ticketId)
296 + public function closeTicket(Request $request, $ticket_id)
354 297 {
355 - $ticket = Ticket::with(['customer'])->findOrFail($ticketId);
298 + $customerAdditionalData = $this->getCustomerAdditionalData($request);
356 299
357 - if ($request->get('intended_ticket_hash') && Helper::isPublicSignedTicketEnabled()) {
358 - $customer = $ticket->customer;
359 - } else {
360 - $customer = $this->resolveCustomer($request);
361 - }
362 -
363 - if (!$customer) {
300 + try {
301 + return (new CustomerPortalService())->closeTicket($customerAdditionalData, $ticket_id);
302 + } catch (Exception $e) {
364 303 return $this->sendError([
365 - 'message' => __('Sorry! No customer found', 'fluent-support')
304 + 'message' => Helper::getSafeErrorMessage($e),
305 + 'error_type' => $e->getCode()
366 306 ]);
367 307 }
368 -
369 - if($customer->status == 'inactive') {
370 - return $this->sendError([
371 - 'message' => __('Sorry, You do not have access to customer portal', 'fluent-support'),
372 - 'error_type' => 'inactive_customer'
373 - ]);
374 - }
375 -
376 -
377 - if ($customer->id != $ticket->customer_id) {
378 - return $this->sendError([
379 - 'message' => __('Sorry! You can not close this ticket', 'fluent-support')
380 - ]);
381 - }
382 -
383 - return [
384 - 'message' => __('Ticket has been closed', 'fluent_support'),
385 - 'ticket' => (new TicketService())->close($ticket, $customer)
386 - ];
387 308 }
388 309
389 310 /**
390 311 * closeTicket method will re-open a ticket by customer using ticket id
391 312 * @param Request $request
392 - * @param $ticketId
313 + * @param $ticket_id
393 314 * @return array
394 315 */
395 - public function reOpenTicket(Request $request, $ticketId)
316 + public function reOpenTicket(Request $request, $ticket_id)
396 317 {
397 - $ticket = Ticket::with(['customer'])->findOrFail($ticketId);
318 + $customerAdditionalData = $this->getCustomerAdditionalData($request);
398 319
399 - if ($request->get('intended_ticket_hash') && Helper::isPublicSignedTicketEnabled()) {
400 - $customer = $ticket->customer;
401 - } else {
402 - $customer = $this->resolveCustomer($request);
403 - }
404 -
405 - if($customer->status == 'inactive') {
320 + try {
321 + return (new CustomerPortalService())->reOpenTicket($customerAdditionalData, $ticket_id);
322 + } catch (Exception $e) {
406 323 return $this->sendError([
407 - 'message' => __('Sorry, You do not have access to customer portal', 'fluent-support'),
408 - 'error_type' => 'inactive_customer'
324 + 'message' => Helper::getSafeErrorMessage($e),
325 + 'error_type' => $e->getCode()
409 326 ]);
410 327 }
328 + }
411 329
330 + public function agentFeedbackRating(Request $request, $ticketId)
331 + {
412 332
413 - if (!$customer) {
414 - return $this->sendError([
415 - 'message' => __('Sorry! No customer found', 'fluent-support')
416 - ]);
417 - }
333 + $customerPortalService = new CustomerPortalService();
418 334
419 - if ($customer->id != $ticket->customer_id) {
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');
340 +
341 + $conversationID = $request->getSafe('conversation_id', 'intval');
342 + $approvalStatus = $request->getSafe('approval_status', 'sanitize_text_field');
343 +
344 + try {
345 + return $customerPortalService->addUserFeedback($approvalStatus, $conversationID, $ticket->id);
346 + } catch (Exception $e) {
420 347 return $this->sendError([
421 - 'message' => __('Sorry! You can not close this ticket', 'fluent-support')
348 + 'message' => Helper::getSafeErrorMessage($e),
349 + 'error_type' => $e->getCode()
422 350 ]);
423 351 }
424 -
425 -
426 - return [
427 - 'message' => __('Ticket has been opened again', 'fluent_support'),
428 - 'ticket' => (new TicketService())->reopen($ticket, $customer)
429 - ];
430 -
431 -
432 352 }
433 353
434 354 /**
435 - * resolveCustomer method will create and return or only return existing customer
436 - * This method will het customer id or customer info or option to force create as parameter.
437 - * @param $request
438 - * @param false $forceCreate
439 - * @return false|Customer
440 - */
441 - private function resolveCustomer($request, $forceCreate = false)
442 - {
443 - $onBehalf = $request->get('on_behalf');
444 -
445 - if (!$onBehalf) {
446 - $user = get_user_by('ID', get_current_user_id());
447 - if (!$user) {
448 - return false;
449 - }
450 - $onBehalf = [
451 - 'user_id' => $user->ID,
452 - 'email' => $user->user_email,
453 - 'last_ip_address' => $request->getIp()
454 - ];
455 - }
456 -
457 - if ($forceCreate) {
458 - return Customer::maybeCreateCustomer($onBehalf);
459 - }
460 -
461 - return Customer::getCustomerFromData($onBehalf);
462 - }
463 -
464 - /**
465 355 * getPublicOptions method will return the list of product and customer priorities
466 356 * @return array
467 357 */
468 358 public function getPublicOptions()
469 359 {
470 - $products = Product::select(['id', 'title'])->get();
360 + $products = Product::select(['id', 'title'])->orderedByTitle()->get();
471 361
472 362 return [
473 363 'support_products' => $products,
474 364 'customer_ticket_priorities' => Helper::customerTicketPriorities()
@@ -480,9 +370,9 @@
480 370 * @return array|array[]
481 371 */
482 372 public function getCustomFieldsRender()
483 373 {
484 - if(!defined('FLUENTSUPPORTPRO')) {
374 + if (!defined('FLUENTSUPPORTPRO')) {
485 375 return [
486 376 'custom_fields_rendered' => []
487 377 ];
488 378 }
@@ -487,31 +377,63 @@
487 377 ];
488 378 }
489 379
490 380 return [
491 - 'custom_fields_rendered' => \FluentSupportPro\App\Services\CustomFieldsService::getRenderedPublicFields()
381 + 'custom_fields_rendered' => \FluentSupportPro\App\Services\CustomFieldsService::getRenderedPublicFields()
492 382 ];
383 + }
493 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 + ]);
494 397 }
495 398
399 + private function getCustomerAdditionalData($request)
400 + {
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 +
496 411 /**
497 - * resolveMailboxId method will either get information of the mailbox added by user or default and return the id
498 - * @param $request
499 - * @return null
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
500 420 */
501 - private function resolveMailboxId($request)
421 + private function sanitizeOnBehalf($request)
502 422 {
503 - if ($mailboxId = $request->get('mailbox_id')) {
504 - $mailbox = MailBox::find($mailboxId);
505 - if ($mailbox) {
506 - return $mailbox->id;
507 - }
423 + $onBehalf = $request->get('on_behalf', []);
424 +
425 + if (!is_array($onBehalf)) {
426 + return [];
508 427 }
509 428
510 - $mailbox = Helper::getDefaultMailBox();
429 + $sanitized = [];
511 430
512 - if ($mailbox) {
513 - 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 + }
514 435 }
515 - return null;
436 +
437 + return $sanitized;
516 438 }
517 439 }