PluginProbe
Fluent Support – Helpdesk & Customer Support Ticket System / 2.3.2
Fluent Support – Helpdesk & Customer Support Ticket System v2.3.2
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 1.5.6 All 67 releases
fluent-support / app / Http / Controllers / CustomerController.php

CustomerController.php in Fluent Support – Helpdesk & Customer Support Ticket System 2.3.2, at app/Http/Controllers/CustomerController.php

429 lines 14.9 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\Http\Controllers;
4
5 use FluentCrm\App\Models\Subscriber;
6 use FluentSupport\App\Models\Customer;
7 use FluentSupport\Framework\Http\Request\Request;
8 use FluentSupport\App\Services\AvatarUploder;
9 use FluentSupport\App\Services\Helper;
10 use FluentSupport\Framework\Support\Arr;
11
12 /**
13 * CustomerController class for REST API
14 * This class is responsible for getting data for all request related to customer
15 * @package FluentSupport\App\Http\Controllers
16 *
17 * @version 1.0.0
18 */
19 class CustomerController extends Controller
20 {
21 /**
22 * index method will return the list of customers
23 * @param Request $request
24 * @param Customer $customer
25 * @return array
26 */
27 public function index(Request $request, Customer $customer)
28 {
29 return [
30 'customers' => $customer->getCustomers($request->getSafe('search', 'sanitize_text_field'), $request->getSafe('status', 'sanitize_text_field')),
31 ];
32 }
33
34 public function customerField (Request $request,Customer $customer, $customer_id) {
35
36 $userID = $request->getSafe('user_id', 'intval');
37 return[
38 'customerField' => $customer->getCustomerField($customer_id,$userID)
39 ];
40 }
41
42
43 /**
44 * getCustomer method will return individual customer information by customer id
45 * This function will also get information about extra widgets, tickets and Fluent CRM
46 * @param Request $request
47 * @param Customer $customer
48 * @param $customer_id
49 * @return array
50 */
51 public function getCustomer(Request $request, Customer $customer, $customer_id)
52 {
53 $with = $request->get('with', null);
54 $with = is_array($with) ? array_map('sanitize_key', $with) : [];
55
56 return $customer->getCustomer($customer_id, $with);
57 }
58
59 /**
60 * Create method will create new customer
61 * @param Request $request
62 * @param Customer $customer
63 * @return array
64 * @throws \FluentSupport\Framework\Validator\ValidationException
65 */
66 public function create(Request $request, Customer $customer)
67 {
68 // Define expected fields with their sanitizers
69 $fields = [
70 'id' => 'intval',
71 'customer_id' => 'intval',
72 'avatar' => 'esc_url_raw',
73 'person_type' => 'sanitize_text_field',
74 'hash' => 'sanitize_text_field',
75 'description' => 'sanitize_text_field',
76 'photo' => 'esc_url_raw',
77 'email' => 'sanitize_email',
78 'first_name' => 'sanitize_text_field',
79 'last_name' => 'sanitize_text_field',
80 'title' => 'sanitize_text_field',
81 'user_id' => 'intval',
82 'remote_uid' => 'sanitize_text_field',
83 'status' => 'sanitize_text_field',
84 'address_line_1' => 'sanitize_textarea_field',
85 'address_line_2' => 'sanitize_textarea_field',
86 'city' => 'sanitize_text_field',
87 'state' => 'sanitize_text_field',
88 'zip' => 'sanitize_text_field',
89 'country' => 'sanitize_text_field',
90 'note' => 'sanitize_textarea_field',
91 'ip_address' => 'sanitize_text_field',
92 'last_ip_address' => 'sanitize_text_field',
93 ];
94
95 $data = $this->sanitizeRequestData($request, $fields);
96
97 $data = $this->validate($data, [
98 'email' => 'required|email|unique:fs_persons',
99 'first_name' => 'required',
100 'last_name' => 'nullable|string',
101 'title' => 'nullable|string',
102 'user_id' => 'nullable|integer',
103 'remote_uid' => 'nullable|string',
104 'status' => 'nullable|string',
105 'address_line_1' => 'nullable|string',
106 'address_line_2' => 'nullable|string',
107 'city' => 'nullable|string',
108 'state' => 'nullable|string',
109 'zip' => 'nullable|string',
110 'country' => 'nullable|string',
111 'note' => 'nullable|string',
112 'ip_address' => 'nullable|string',
113 'last_ip_address' => 'nullable|string',
114 ]);
115
116 return [
117 'message' => __('Customer has been added', 'fluent-support'),
118 'customer' => $customer->createCustomer($data)
119 ];
120 }
121
122 /**
123 * update method will update existing customer by customer id
124 * @param Request $request
125 * @param Customer $customer
126 * @param $customerId
127 * @return array
128 * @throws \FluentSupport\Framework\Validator\ValidationException
129 */
130 public function update(Request $request, Customer $customer, $customer_id)
131 {
132 // Sanitize only allowed fields and also sanitize any extra fields from hooks
133 $fields = [
134 'id' => 'intval',
135 'customer_id' => 'intval',
136 'avatar' => 'esc_url_raw',
137 'person_type' => 'sanitize_text_field',
138 'hash' => 'sanitize_text_field',
139 'description' => 'sanitize_text_field',
140 'photo' => 'esc_url_raw',
141 'email' => 'sanitize_email',
142 'first_name' => 'sanitize_text_field',
143 'last_name' => 'sanitize_text_field',
144 'title' => 'sanitize_text_field',
145 'user_id' => 'intval',
146 'remote_uid' => 'sanitize_text_field',
147 'status' => 'sanitize_text_field',
148 'address_line_1' => 'sanitize_textarea_field',
149 'address_line_2' => 'sanitize_textarea_field',
150 'city' => 'sanitize_text_field',
151 'state' => 'sanitize_text_field',
152 'zip' => 'sanitize_text_field',
153 'country' => 'sanitize_text_field',
154 'note' => 'sanitize_textarea_field',
155 'ip_address' => 'sanitize_text_field',
156 'last_ip_address' => 'sanitize_text_field',
157 ];
158
159 $data = $this->sanitizeRequestData($request, $fields);
160
161 $data = $this->validate($data, [
162 'email' => 'required|email',
163 'first_name' => 'required',
164 'last_name' => 'nullable|string',
165 'title' => 'nullable|string',
166 'user_id' => 'nullable|integer',
167 'remote_uid' => 'nullable|string',
168 'status' => 'nullable|string',
169 'address_line_1' => 'nullable|string',
170 'address_line_2' => 'nullable|string',
171 'city' => 'nullable|string',
172 'state' => 'nullable|string',
173 'zip' => 'nullable|string',
174 'country' => 'nullable|string',
175 'note' => 'nullable|string',
176 'ip_address' => 'nullable|string',
177 'last_ip_address' => 'nullable|string',
178 ]);
179
180 try {
181 return [
182 'message' => __('Customer has been updated', 'fluent-support'),
183 'customer' => $customer->updateCustomer($customer_id, $data)
184 ];
185 } catch (\Exception $e) {
186 return $this->sendError([
187 'message' => Helper::getSafeErrorMessage($e),
188 'errors' => [
189 'email' => [
190 'unique' => __('Email address has been assigned to other customer', 'fluent-support'),
191 ]
192 ]
193 ], 423);
194 }
195 }
196
197 /**
198 * delete method will delete a customer and all tickets by that customer
199 * @param Request $request
200 * @param Customer $customer
201 * @param int $customerId
202 * @return array
203 */
204 public function delete(Request $request, Customer $customer, $customer_id)
205 {
206 return $customer->deleteCustomer($customer_id);
207 }
208
209 /**
210 * bulkDelete method will delete multiple customers and all their tickets
211 * @param Request $request
212 * @param Customer $customer
213 * @return array
214 */
215 public function bulkDelete(Request $request, Customer $customer)
216 {
217 // Get and sanitize customer_ids before validation
218 $customerIds = $request->get('customer_ids', []);
219 $customerIds = is_array($customerIds) ? array_map('intval', $customerIds) : [];
220
221 // Filter out any zero values (from invalid input)
222 $customerIds = array_filter($customerIds, function ($id) {
223 return $id > 0;
224 });
225
226 $this->validate(['customer_ids' => $customerIds], [
227 'customer_ids' => 'required|array|min:1',
228 'customer_ids.*' => 'required|integer|exists:fs_persons,id'
229 ]);
230
231 return $customer->bulkDeleteCustomers($customerIds);
232 }
233
234 /**
235 * addOrUpdateProfileImage method will update a customer avatar
236 * For a successful upload it's required to send file object, customer id and the user type(customer)
237 * @param Request $request
238 * @return array
239 */
240 public function addOrUpdateProfileImage(Request $request, AvatarUploder $avatarUploder)
241 {
242 try {
243 return $avatarUploder->addOrUpdateProfileImage($request->files(), $request->getSafe('customer_id', 'intval'), 'customer');
244 } catch (\Exception $e) {
245 return $this->sendError([
246 'message' => Helper::getSafeErrorMessage($e),
247 ],
248 $e->getCode()
249 );
250 }
251 }
252
253 /**
254 * resetAvatar method will restore a customer avatar
255 * For a successful upload it's required to send file object, customer id and the user type(customer)
256 * @param Request $request
257 * @param $id
258 * @return array
259 */
260 public function resetAvatar(Customer $customer)
261 {
262 try {
263 $customer->restoreAvatar();
264
265 return [
266 'message' => __('Customer avatar reset to gravatar default', 'fluent-support'),
267 ];
268 } catch (\Exception $e) {
269 return [
270 'message' => Helper::getSafeErrorMessage($e)
271 ];
272 }
273 }
274
275 public function searchContact(Request $request)
276 {
277 $search = trim($request->getSafe('search', 'sanitize_text_field'));
278
279 // '*' is a WP_User_Query wildcard and survives sanitize_text_field, so a
280 // lone '*' would list every user on the site (FS-SEC-014). Stripping it
281 // leaves WP_User_Query doing an exact match.
282 $search = trim(str_replace('*', '', $search));
283
284 if (!$search) {
285 return $this->sendError([
286 'message' => __('Please provide search string', 'fluent-support')
287 ]);
288 }
289
290 $isEmail = is_email($search);
291
292 // Require a meaningful prefix so the endpoint can't be walked one letter
293 // at a time. Emails are matched exactly, so they need no minimum.
294 if (!$isEmail && mb_strlen($search) < 3) {
295 return $this->sendError([
296 'message' => __('Please provide at least 3 characters to search', 'fluent-support')
297 ]);
298 }
299
300 if (Helper::hitRateLimit('fs_contact_search_' . get_current_user_id(), 60, 5 * MINUTE_IN_SECONDS)) {
301 return $this->sendError([
302 'message' => __('Too many contact searches. Please try again in a few minutes.', 'fluent-support')
303 ], 429);
304 }
305
306 // '%' and '_' are LIKE wildcards for the customer and CRM scopes below.
307 // Escape rather than strip: underscores are legitimate in emails.
308 global $wpdb;
309 $likeSearch = $wpdb->esc_like($search);
310
311 // search the existing customers first
312 if ($isEmail) {
313 $customers = Customer::select(['first_name', 'last_name', 'email', 'id', 'user_id'])
314 ->where('email', $search)
315 ->get();
316 } else {
317 $customers = Customer::select(['first_name', 'last_name', 'email', 'id', 'user_id'])
318 ->searchBy($likeSearch)
319 ->limit(10)
320 ->get();
321 }
322
323 if (!$customers->isEmpty()) {
324 return [
325 'type' => 'search_result',
326 'provider' => 'fluent_support',
327 'data' => $customers,
328 'is_email' => $isEmail,
329 'search' => $search
330 ];
331 }
332
333 // If FluentCRM exist then let's search for
334 if (defined('FLUENTCRM')) {
335
336 if ($isEmail) {
337 $contacts = \FluentCrm\App\Models\Subscriber::where('email', $search)
338 ->select(['first_name', 'last_name', 'email', 'id', 'user_id'])
339 ->get();
340 } else {
341
342 $contacts = \FluentCrm\App\Models\Subscriber::searchBy($likeSearch)
343 ->select(['first_name', 'last_name', 'email', 'id', 'user_id'])
344 ->limit(10)
345 ->get();
346 }
347
348 if (!$contacts->isEmpty()) {
349 return [
350 'type' => 'search_result',
351 'provider' => 'fluent_crm',
352 'data' => $contacts,
353 'is_email' => $isEmail
354 ];
355 }
356 }
357
358 // let's search from user's database
359 $user_query = new \WP_User_Query(array('search' => $search, 'number' => 10));
360
361 $users = $user_query->get_results();
362
363 if ($users) {
364 $formattedUsers = [];
365
366 foreach ($users as $user) {
367 $formattedUsers[] = [
368 'id' => $user->ID,
369 'first_name' => $user->first_name,
370 'last_name' => $user->last_name,
371 'user_id' => $user->ID,
372 'email' => $user->user_email
373 ];
374 }
375
376 return [
377 'type' => 'search_result',
378 'provider' => 'wp_users',
379 'data' => $formattedUsers,
380 'is_email' => $isEmail
381 ];
382 }
383
384 return [
385 'type' => 'none',
386 'provider' => 'none',
387 'data' => [],
388 'is_email' => $isEmail
389 ];
390
391 }
392
393 /**
394 * Sanitize request data for given fields. Uses Request::getSafe for known fields
395 * and falls back to sanitize_text_field for any other keys present in the raw request
396 * (useful when hooks inject extra data).
397 *
398 * @param Request $request
399 * @param array $fieldsMap associative array field => sanitizer callable name
400 * @return array
401 */
402 private function sanitizeRequestData(Request $request, array $fieldsMap)
403 {
404 $sanitized = [];
405
406 // Use getSafe for known fields
407 foreach ($fieldsMap as $field => $sanitizer) {
408 $sanitized[$field] = $request->getSafe($field, $sanitizer);
409 }
410
411 // Now sanitize any other incoming keys to avoid unsanitized data
412 $raw = $request->get();
413 foreach ($raw as $key => $value) {
414 if (array_key_exists($key, $sanitized)) {
415 continue;
416 }
417
418 if (is_array($value)) {
419 $sanitized[$key] = array_map('sanitize_text_field', $value);
420 } else {
421 // Fallback sanitizer for unknown fields
422 $sanitized[$key] = is_string($value) ? sanitize_text_field($value) : $value;
423 }
424 }
425
426 return $sanitized;
427 }
428 }
429