PluginProbe
Fluent Support – Helpdesk & Customer Support Ticket System / 2.4.5
Fluent Support – Helpdesk & Customer Support Ticket System v2.4.5
2.4.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 All 69 releases
fluent-support / app / Http / Controllers / CustomerController.php

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

424 lines 14.1 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 * Customer endpoints.
14 */
15 class CustomerController extends Controller
16 {
17 /**
18 * Each id cascades into tickets, conversations and attachments, so the batch is capped.
19 */
20 const BULK_DELETE_LIMIT = 100;
21
22 /**
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 * `with` can add widgets, tickets and FluentCRM data.
45 *
46 * @param Request $request
47 * @param Customer $customer
48 * @param int $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 * @param Request $request
61 * @param Customer $customer
62 * @return array
63 * @throws \FluentSupport\Framework\Validator\ValidationException
64 */
65 public function create(Request $request, Customer $customer)
66 {
67 // The WordPress user is linked through the email only, so a client user_id could
68 // otherwise make the new record another account's portal identity.
69 if ($request->get('user_id')) {
70 return $this->sendError([
71 'message' => __('user_id is not accepted when creating a customer. The WordPress user is linked by the customer email.', 'fluent-support')
72 ], 422);
73 }
74
75 $fields = [
76 'id' => 'intval',
77 'customer_id' => 'intval',
78 'avatar' => 'esc_url_raw',
79 'person_type' => 'sanitize_text_field',
80 'hash' => 'sanitize_text_field',
81 'description' => 'sanitize_text_field',
82 'photo' => 'esc_url_raw',
83 'email' => 'sanitize_email',
84 'first_name' => 'sanitize_text_field',
85 'last_name' => 'sanitize_text_field',
86 'title' => 'sanitize_text_field',
87 'remote_uid' => 'sanitize_text_field',
88 'status' => 'sanitize_text_field',
89 'address_line_1' => 'sanitize_textarea_field',
90 'address_line_2' => 'sanitize_textarea_field',
91 'city' => 'sanitize_text_field',
92 'state' => 'sanitize_text_field',
93 'zip' => 'sanitize_text_field',
94 'country' => 'sanitize_text_field',
95 'note' => 'sanitize_textarea_field',
96 'ip_address' => 'sanitize_text_field',
97 'last_ip_address' => 'sanitize_text_field',
98 ];
99
100 $data = $this->sanitizeRequestData($request, $fields);
101
102 $data = $this->validate($data, [
103 'email' => 'required|email|unique:fs_persons',
104 'first_name' => 'required',
105 'last_name' => 'nullable|string',
106 'title' => 'nullable|string',
107 'remote_uid' => 'nullable|string',
108 'status' => 'nullable|string',
109 'address_line_1' => 'nullable|string',
110 'address_line_2' => 'nullable|string',
111 'city' => 'nullable|string',
112 'state' => 'nullable|string',
113 'zip' => 'nullable|string',
114 'country' => 'nullable|string',
115 'note' => 'nullable|string',
116 'ip_address' => 'nullable|string',
117 'last_ip_address' => 'nullable|string',
118 ]);
119
120 return [
121 'message' => __('Customer has been added', 'fluent-support'),
122 'customer' => $customer->createCustomer($data)
123 ];
124 }
125
126 /**
127 * @param Request $request
128 * @param Customer $customer
129 * @param int $customer_id
130 * @return array
131 * @throws \FluentSupport\Framework\Validator\ValidationException
132 */
133 public function update(Request $request, Customer $customer, $customer_id)
134 {
135 $fields = [
136 'id' => 'intval',
137 'customer_id' => 'intval',
138 'avatar' => 'esc_url_raw',
139 'person_type' => 'sanitize_text_field',
140 'hash' => 'sanitize_text_field',
141 'description' => 'sanitize_text_field',
142 'photo' => 'esc_url_raw',
143 'email' => 'sanitize_email',
144 'first_name' => 'sanitize_text_field',
145 'last_name' => 'sanitize_text_field',
146 'title' => 'sanitize_text_field',
147 'user_id' => 'intval',
148 'remote_uid' => 'sanitize_text_field',
149 'status' => 'sanitize_text_field',
150 'address_line_1' => 'sanitize_textarea_field',
151 'address_line_2' => 'sanitize_textarea_field',
152 'city' => 'sanitize_text_field',
153 'state' => 'sanitize_text_field',
154 'zip' => 'sanitize_text_field',
155 'country' => 'sanitize_text_field',
156 'note' => 'sanitize_textarea_field',
157 'ip_address' => 'sanitize_text_field',
158 'last_ip_address' => 'sanitize_text_field',
159 ];
160
161 $data = $this->sanitizeRequestData($request, $fields);
162
163 $data = $this->validate($data, [
164 'email' => 'required|email',
165 'first_name' => 'required',
166 'last_name' => 'nullable|string',
167 'title' => 'nullable|string',
168 'user_id' => 'nullable|integer',
169 'remote_uid' => 'nullable|string',
170 'status' => 'nullable|string',
171 'address_line_1' => 'nullable|string',
172 'address_line_2' => 'nullable|string',
173 'city' => 'nullable|string',
174 'state' => 'nullable|string',
175 'zip' => 'nullable|string',
176 'country' => 'nullable|string',
177 'note' => 'nullable|string',
178 'ip_address' => 'nullable|string',
179 'last_ip_address' => 'nullable|string',
180 ]);
181
182 try {
183 return [
184 'message' => __('Customer has been updated', 'fluent-support'),
185 'customer' => $customer->updateCustomer($customer_id, $data)
186 ];
187 } catch (\Exception $e) {
188 return $this->sendError([
189 'message' => Helper::getSafeErrorMessage($e),
190 'errors' => [
191 'email' => [
192 'unique' => __('Email address has been assigned to other customer', 'fluent-support'),
193 ]
194 ]
195 ], 423);
196 }
197 }
198
199 /**
200 * Also deletes the customer's tickets.
201 *
202 * @param Request $request
203 * @param Customer $customer
204 * @param int $customer_id
205 * @return array
206 */
207 public function delete(Request $request, Customer $customer, $customer_id)
208 {
209 return $customer->deleteCustomer($customer_id);
210 }
211
212 /**
213 * Also deletes the customers' tickets.
214 *
215 * @param Request $request
216 * @param Customer $customer
217 * @return array
218 */
219 public function bulkDelete(Request $request, Customer $customer)
220 {
221 $customerIds = $request->get('customer_ids', []);
222 $customerIds = is_array($customerIds) ? array_map('intval', $customerIds) : [];
223
224 $customerIds = array_filter($customerIds, function ($id) {
225 return $id > 0;
226 });
227
228 $customerIds = array_values(array_unique($customerIds));
229
230 $this->validate(['customer_ids' => $customerIds], [
231 'customer_ids' => 'required|array|min:1|max:' . self::BULK_DELETE_LIMIT,
232 'customer_ids.*' => 'required|integer|exists:fs_persons,id'
233 ]);
234
235 return $customer->bulkDeleteCustomers($customerIds);
236 }
237
238 /**
239 * @param Request $request
240 * @param AvatarUploder $avatarUploder
241 * @return array
242 */
243 public function addOrUpdateProfileImage(Request $request, AvatarUploder $avatarUploder)
244 {
245 try {
246 return $avatarUploder->addOrUpdateProfileImage($request->files(), $request->getSafe('customer_id', 'intval'), 'customer');
247 } catch (\Exception $e) {
248 return $this->sendError([
249 'message' => Helper::getSafeErrorMessage($e),
250 ],
251 $e->getCode()
252 );
253 }
254 }
255
256 /**
257 * No Customer type-hint: route-model binding runs before the policy, which would
258 * let unauthenticated callers probe customer IDs. Resolve after auth.
259 *
260 * @param int|string $customer
261 * @return array
262 */
263 public function resetAvatar($customer)
264 {
265 try {
266 $customer = Customer::findOrFail((int) $customer);
267 $customer->restoreAvatar();
268
269 return [
270 'message' => __('Customer avatar reset to gravatar default', 'fluent-support'),
271 ];
272 } catch (\Exception $e) {
273 return [
274 'message' => Helper::getSafeErrorMessage($e)
275 ];
276 }
277 }
278
279 public function searchContact(Request $request)
280 {
281 $search = trim($request->getSafe('search', 'sanitize_text_field'));
282
283 // '*' is a WP_User_Query wildcard that survives sanitize_text_field.
284 // A lone '*' would list every user.
285 $search = trim(str_replace('*', '', $search));
286
287 if (!$search) {
288 return $this->sendError([
289 'message' => __('Please provide search string', 'fluent-support')
290 ]);
291 }
292
293 $isEmail = is_email($search);
294
295 // Stops walking the list one letter at a time. Emails match exactly, so no minimum.
296 if (!$isEmail && mb_strlen($search) < 3) {
297 return $this->sendError([
298 'message' => __('Please provide at least 3 characters to search', 'fluent-support')
299 ]);
300 }
301
302 if (Helper::hitRateLimit('fs_contact_search_' . get_current_user_id(), 60, 5 * MINUTE_IN_SECONDS)) {
303 return $this->sendError([
304 'message' => __('Too many contact searches. Please try again in a few minutes.', 'fluent-support')
305 ], 429);
306 }
307
308 // Escape LIKE wildcards rather than strip them: '_' is valid in emails.
309 global $wpdb;
310 $likeSearch = $wpdb->esc_like($search);
311
312 // Customers first, then FluentCRM contacts, then WP users.
313 if ($isEmail) {
314 $customers = Customer::select(['first_name', 'last_name', 'email', 'id', 'user_id'])
315 ->where('email', $search)
316 ->get();
317 } else {
318 $customers = Customer::select(['first_name', 'last_name', 'email', 'id', 'user_id'])
319 ->searchBy($likeSearch)
320 ->limit(10)
321 ->get();
322 }
323
324 if (!$customers->isEmpty()) {
325 return [
326 'type' => 'search_result',
327 'provider' => 'fluent_support',
328 'data' => $customers,
329 'is_email' => $isEmail,
330 'search' => $search
331 ];
332 }
333
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 $user_query = new \WP_User_Query(array('search' => $search, 'number' => 10));
359
360 $users = $user_query->get_results();
361
362 if ($users) {
363 $formattedUsers = [];
364
365 foreach ($users as $user) {
366 $formattedUsers[] = [
367 'id' => $user->ID,
368 'first_name' => $user->first_name,
369 'last_name' => $user->last_name,
370 'user_id' => $user->ID,
371 'email' => $user->user_email
372 ];
373 }
374
375 return [
376 'type' => 'search_result',
377 'provider' => 'wp_users',
378 'data' => $formattedUsers,
379 'is_email' => $isEmail
380 ];
381 }
382
383 return [
384 'type' => 'none',
385 'provider' => 'none',
386 'data' => [],
387 'is_email' => $isEmail
388 ];
389
390 }
391
392 /**
393 * Known fields use their mapped sanitizer. Any other keys (e.g. added by hooks)
394 * get sanitize_text_field.
395 *
396 * @param Request $request
397 * @param array $fieldsMap associative array field => sanitizer callable name
398 * @return array
399 */
400 private function sanitizeRequestData(Request $request, array $fieldsMap)
401 {
402 $sanitized = [];
403
404 foreach ($fieldsMap as $field => $sanitizer) {
405 $sanitized[$field] = $request->getSafe($field, $sanitizer);
406 }
407
408 $raw = $request->get();
409 foreach ($raw as $key => $value) {
410 if (array_key_exists($key, $sanitized)) {
411 continue;
412 }
413
414 if (is_array($value)) {
415 $sanitized[$key] = array_map('sanitize_text_field', $value);
416 } else {
417 $sanitized[$key] = is_string($value) ? sanitize_text_field($value) : $value;
418 }
419 }
420
421 return $sanitized;
422 }
423 }
424