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
← All changes | app/Services/Integrations/FluentForm/FeedIntegration.php +202 -72 1.4.6 → 2.4.5 View file →
@@ -1,10 +1,9 @@
1 1 <?php
2 2
3 3 namespace FluentSupport\App\Services\Integrations\FluentForm;
4 4
5 -use FluentForm\App\Services\Integrations\IntegrationManager;
6 -use FluentForm\Framework\Foundation\Application;
5 +use FluentForm\App\Http\Controllers\IntegrationManagerController;
7 6 use FluentSupport\App\App;
8 7 use FluentSupport\App\Models\Attachment;
9 8 use FluentSupport\App\Models\Customer;
10 9 use FluentSupport\App\Models\MailBox;
@@ -11,9 +10,9 @@
11 10 use FluentSupport\App\Models\Product;
12 11 use FluentSupport\App\Models\Ticket;
13 12 use FluentSupport\Framework\Support\Arr;
14 13
15 -class FeedIntegration extends IntegrationManager
14 +class FeedIntegration extends IntegrationManagerController
16 15 {
17 16
18 17 public $hasGlobalMenu = false;
19 18
@@ -18,9 +17,9 @@
18 17 public $hasGlobalMenu = false;
19 18
20 19 public $disableGlobalSettings = 'yes';
21 20
22 - public function __construct(Application $app)
21 + public function __construct($app = null)
23 22 {
24 23 parent::__construct(
25 24 $app,
26 25 'FluentSupport',
@@ -33,15 +32,15 @@
33 32 $app = App::getInstance();
34 33
35 34 $assets = $app['url.assets'];
36 35
37 - $this->logo = $assets.'/images/fluent-support-logo.png';
36 + $this->logo = $assets.'/images/fluent-support-color-logo.png';
38 37
39 - $this->description = __('Create Support Ticket From Your Form Submission in FluentSupport', 'fluentform');
38 + $this->description = __('Create Support Ticket From Your Form Submission in FluentSupport', 'fluent-support');
40 39
41 40 $this->registerAdminHooks();
42 41
43 - add_filter('fluentform_notifying_async_fluent_support', '__return_false');
42 + add_filter('fluentform/notifying_async_fluent_support', '__return_false');
44 43
45 44 }
46 45
47 46 public function pushIntegration($integrations, $formId)
@@ -49,12 +48,12 @@
49 48 $integrations[$this->integrationKey] = [
50 49 'title' => $this->title . ' Integration',
51 50 'logo' => $this->logo,
52 51 'is_active' => $this->isConfigured(),
53 - 'configure_title' => __('Configuration required!', 'fluentform'),
52 + 'configure_title' => __('Configuration required!', 'fluent-support'),
54 53 'global_configure_url' => '#',
55 - 'configure_message' => __('FluentSupport is not configured yet! Please configure your FluentSupport api first', 'fluentform'),
56 - 'configure_button_text' => __('Set FluentSupport', 'fluentform')
54 + 'configure_message' => __('FluentSupport is not configured yet! Please configure your FluentSupport api first', 'fluent-support'),
55 + 'configure_button_text' => __('Set FluentSupport', 'fluent-support')
57 56 ];
58 57 return $integrations;
59 58 }
60 59
@@ -67,12 +66,19 @@
67 66 'email' => '',
68 67 'list_id' => '', // this is the business ID
69 68 'ticket_title' => '',
70 69 'ticket_body' => '',
70 + 'client_priority' => '',
71 71 'attachments' => '',
72 72 'product_id' => '',
73 73 'product_id_selection_type' => 'simple',
74 74 'product_routers' => [],
75 + 'customer_other_fields' => [
76 + [
77 + 'item_value' => '',
78 + 'label' => ''
79 + ]
80 + ],
75 81 'conditionals' => [
76 82 'conditions' => [],
77 83 'status' => false,
78 84 'type' => 'all'
@@ -82,22 +88,26 @@
82 88 }
83 89
84 90 public function getSettingsFields($settings, $formId)
85 91 {
92 + $fieldOptions = [];
93 + foreach (Customer::mappables() as $key => $column) {
94 + $fieldOptions[$key] = $column;
95 + }
86 96 return [
87 97 'fields' => [
88 98 [
89 99 'key' => 'name',
90 - 'label' => __('Feed Name', 'fluentform'),
100 + 'label' => __('Feed Name', 'fluent-support'),
91 101 'required' => true,
92 - 'placeholder' => __('Your Feed Name', 'fluentform'),
102 + 'placeholder' => __('Your Feed Name', 'fluent-support'),
93 103 'component' => 'text'
94 104 ],
95 105 [
96 106 'key' => 'list_id',
97 - 'label' => __('Business', 'fluentform'),
98 - 'placeholder' => __('Select Business', 'fluentform'),
99 - 'tips' => __('Select the Business you would like to add your Support Ticket to.', 'fluentform'),
107 + 'label' => __('Business', 'fluent-support'),
108 + 'placeholder' => __('Select Business', 'fluent-support'),
109 + 'tips' => __('Select the Business you would like to add your Support Ticket to.', 'fluent-support'),
100 110 'component' => 'select',
101 111 'required' => true,
102 112 'options' => $this->geMailBoxes(),
103 113 ],
@@ -103,10 +113,10 @@
103 113 ],
104 114 [
105 115 'key' => 'product_id',
106 116 'require_list' => false,
107 - 'label' => __('Product', 'fluentform'),
108 - 'placeholder' => __('Select Support Product', 'fluentform'),
117 + 'label' => __('Product', 'fluent-support'),
118 + 'placeholder' => __('Select Support Product', 'fluent-support'),
109 119 'component' => 'selection_routing',
110 120 'simple_component' => 'select',
111 121 'routing_input_type' => 'select',
112 122 'routing_key' => 'product_id_selection_type',
@@ -112,11 +122,11 @@
112 122 'routing_key' => 'product_id_selection_type',
113 123 'settings_key' => 'product_routers',
114 124 'is_multiple' => false,
115 125 'labels' => [
116 - 'choice_label' => __('Enable Dynamic Product Selection', 'fluentform'),
126 + 'choice_label' => __('Enable Dynamic Product Selection', 'fluent-support'),
117 127 'input_label' => '',
118 - 'input_placeholder' => __('Set Product', 'fluentform')
128 + 'input_placeholder' => __('Set Product', 'fluent-support')
119 129 ],
120 130 'options' => $this->getProducts()
121 131 ],
122 132 [
@@ -121,57 +131,75 @@
121 131 ],
122 132 [
123 133 'key' => 'ticket_title',
124 134 'require_list' => false,
125 - 'label' => __('Ticket Title', 'fluentform'),
126 - 'placeholder' => __('Ticket Title', 'fluentform'),
135 + 'label' => __('Ticket Title', 'fluent-support'),
136 + 'placeholder' => __('Ticket Title', 'fluent-support'),
127 137 'component' => 'value_text'
128 138 ],
129 139 [
130 140 'key' => 'ticket_content',
131 141 'require_list' => false,
132 - 'label' => __('Ticket Content', 'fluentform'),
133 - 'placeholder' => __('Ticket Content', 'fluentform'),
142 + 'label' => __('Ticket Content', 'fluent-support'),
143 + 'placeholder' => __('Ticket Content', 'fluent-support'),
134 144 'component' => 'value_textarea'
135 145 ],
136 146 [
137 147 'key' => 'ticket_attachments',
138 148 'require_list' => false,
139 - 'label' => __('Ticket Attachments', 'fluentform'),
140 - 'Placeholder' => __('Ticket Attachments', 'fluentform'),
141 - 'tips' => __('Please input your file upload or image upload field shortcode here', 'fluentform'),
149 + 'label' => __('Ticket Attachments', 'fluent-support'),
150 + 'Placeholder' => __('Ticket Attachments', 'fluent-support'),
151 + 'tips' => __('Please input your file upload or image upload field shortcode here', 'fluent-support'),
142 152 'component' => 'value_text'
143 153 ],
154 + [
155 + 'key' => 'client_priority',
156 + 'require_list' => false,
157 + 'label' => __('Ticket Priority', 'fluent-support'),
158 + 'Placeholder' => __('Ticket Priority', 'fluent-support'),
159 + 'tips' => __('Make sure form field values match with Fluent Support priorities', 'fluent-support'),
160 + 'component' => 'value_text'
161 + ],
144 162 $this->getCustomField(), //Getting Fluent Support Custom Field To Map
145 163 [
146 164 'component' => 'html_info',
147 - 'html_info' => __('<h4>Please provide the ticket provider info. If user is logged in then it will use that info. For Public users you can set your customer info</h4>', 'fluentform')
165 + 'html_info' => '<h4>' . __('Please provide the ticket provider info. If user is logged in then it will use that info. For Public users you can set your customer info', 'fluent-support') . '</h4>'
148 166 ],
149 167 [
150 - 'key' => 'CustomFields',
168 + 'key' => 'CustomerFields',
151 169 'require_list' => false,
152 - 'label' => __('Customer Data', 'fluentform'),
153 - 'tips' => __('Please Map Your Customer Data for this form. If your customer already logged in you can leave this', 'fluentform'),
170 + 'label' => __('Customer Data', 'fluent-support'),
171 + 'tips' => __('Please Map Your Customer Data for this form. If your customer already logged in you can leave this', 'fluent-support'),
154 172 'component' => 'map_fields',
155 - 'field_label_remote' => __('Support Customer Field', 'fluentform'),
156 - 'field_label_local' => __('Form Field', 'fluentform'),
173 + 'field_label_remote' => __('Support Customer Field', 'fluent-support'),
174 + 'field_label_local' => __('Form Field', 'fluent-support'),
157 175 'primary_fileds' => [
158 176 [
159 177 'key' => 'email',
160 - 'label' => __('Email Address', 'fluentform'),
178 + 'label' => __('Email Address', 'fluent-support'),
161 179 'required' => true,
162 180 'input_options' => 'emails'
163 181 ],
164 182 [
165 183 'key' => 'first_name',
166 - 'label' => __('First Name', 'fluentform')
184 + 'label' => __('First Name', 'fluent-support')
167 185 ],
168 186 [
169 187 'key' => 'last_name',
170 - 'label' => __('Last Name', 'fluentform')
188 + 'label' => __('Last Name', 'fluent-support')
171 189 ]
172 190 ]
173 - ]
191 + ],
192 + [
193 + 'key' => 'customer_other_fields',
194 + 'require_list' => false,
195 + 'label' => __('Customer Other Fields', 'fluent-support'),
196 + 'tips' => __('Select which Fluent Forms fields pair with their<br /> respective Fluent Support fields.', 'fluent-support'),
197 + 'field_label_remote' => __('Fluent Support Field', 'fluent-support'),
198 + 'field_label_local' => __('Form Field', 'fluent-support'),
199 + 'component' => 'dropdown_many_fields',
200 + 'options' => $fieldOptions
201 + ],
174 202 ],
175 203 'button_require_list' => false,
176 204 'integration_title' => $this->title
177 205 ];
@@ -199,13 +227,14 @@
199 227
200 228 private function getCustomField()
201 229 {
202 230 $customFields = apply_filters('fluent_support/ticket_custom_fields', []);
231 + $fieldToExclude = apply_filters('fluent_support/custom_field_types', []);
203 232
204 233 if(empty($customFields)){
205 234 return[
206 235 'component' => 'html_info',
207 - 'html_info' => __('', 'fluentform')
236 + 'html_info' => ''
208 237 ];
209 238 }
210 239
211 240 $fields = [];
@@ -210,23 +239,30 @@
210 239
211 240 $fields = [];
212 241 foreach ($customFields as $customFieldKey=>$customFieldValue) {
213 242
243 + if(in_array($customFieldValue['type'], array_keys($fieldToExclude))){
244 + unset($customFieldValue);
245 + }
246 +
214 247 $fields[] = [
215 248 'key' => $customFieldValue['slug'],
216 - 'label' => __($customFieldValue['label'], 'fluentform')
249 + 'label' => $customFieldValue['label']
217 250 ];
218 251 }
219 252
220 - return $customFieldsMapping = [
253 + $fields = array_map('array_filter', $fields);
254 + $fields = array_filter( $fields );
255 +
256 + return [
221 257 'key' => 'TicketCustomFields',
222 258 'require_list' => false,
223 - 'label' => __('Ticket Customer Fields', 'fluentform'),
224 - 'tips' => __('Please Map Your Ticket Custom Field Data for this form.', 'fluentform'),
259 + 'label' => __('Ticket Custom Fields', 'fluent-support'),
260 + 'tips' => __('Please Map Your Ticket Custom Field Data for this form.', 'fluent-support'),
225 261 'component' => 'map_fields',
226 - 'field_label_remote' => __('Fluent Support Custom Field', 'fluentform'),
227 - 'field_label_local' => __('Form Field', 'fluentform'),
228 - 'primary_fileds' => $fields
262 + 'field_label_remote' => __('Fluent Support Custom Field', 'fluent-support'),
263 + 'field_label_local' => __('Form Field', 'fluent-support'),
264 + 'primary_fileds' => $fields
229 265 ];
230 266 }
231 267
232 268 public function getMergeFields($list, $listId, $formId)
@@ -237,9 +273,8 @@
237 273 public function notify($feed, $formData, $entry, $form)
238 274 {
239 275 $data = $feed['processedValues'];
240 276
241 -
242 277 if (!empty($data['email']) && !is_email($data['email'])) {
243 278 $data['email'] = Arr::get($formData, $data['email']);
244 279 }
245 280
@@ -246,23 +281,35 @@
246 281 $ticketCustomField = array_filter($data, function($key) {
247 282 return strpos($key, 'cf_') === 0;
248 283 }, ARRAY_FILTER_USE_KEY);
249 284
285 +
250 286 $customFields = apply_filters('fluent_support/ticket_custom_fields', []);
251 287
252 - foreach($customFields as $key=>$value){
253 - $type = [$key=>$value][$key]['type'];
254 - $slug = [$key=>$value][$key]['slug'];
255 - $type=='checkbox' && array_key_exists($slug, $ticketCustomField) ?
256 - $ticketCustomField[$slug] = explode(',', Arr::get($ticketCustomField, $slug)) : $ticketCustomField;
288 + foreach($customFields as $key => $field){
289 + $type = $field['type'];
290 + $slug = $field['slug'];
291 + if(!isset($ticketCustomField[$slug])) {
292 + continue;
293 + }
294 +
295 + if( $type=='checkbox' && array_key_exists($slug, $ticketCustomField)) {
296 + $value = Arr::get($ticketCustomField, $slug, '');
297 + $ticketCustomField[$slug] = $value ? explode(',', (string)$value) : [];
298 + } else {
299 + $ticketCustomField[$slug] = Arr::get($ticketCustomField, $slug);
300 + }
257 301 }
258 302
259 303 $ticketData = [
260 304 'product_source' => 'local',
261 305 'mailbox_id' => Arr::get($data, 'list_id'),
262 - 'title' => sanitize_text_field(wp_unslash(Arr::get($data, 'ticket_title'))),
263 - 'content' => wp_unslash(wp_kses_post(Arr::get($data, 'ticket_content'))),
306 + // Feed settings and entry values are stored unslashed, so sanitize only.
307 + 'title' => sanitize_text_field(Arr::get($data, 'ticket_title')),
308 + 'content' => wp_kses_post(Arr::get($data, 'ticket_content')),
264 309 'attachments' => sanitize_text_field(Arr::get($data, 'ticket_attachments')),
310 + 'client_priority' => strtolower(sanitize_text_field(Arr::get($data, 'client_priority', ''))),
311 + 'priority' => strtolower(sanitize_text_field(Arr::get($data, 'client_priority', ''))),
265 312 'custom_fields' => $ticketCustomField,
266 313 'source' => 'web'
267 314 ];
268 315
@@ -274,8 +321,15 @@
274 321 }
275 322
276 323 $customerData = Arr::only($data, ['first_name', 'last_name', 'email']);
277 324
325 + foreach (Arr::get($data, 'customer_other_fields') as $field) {
326 + if ($field['item_value']) {
327 + $customerData[$field['label']] = $field['item_value'];
328 + }
329 + }
330 +
331 +
278 332 $user = get_user_by('ID', get_current_user_id());
279 333
280 334 if(!$user) {
281 335 $user = get_user_by('email', $customerData['email']);
@@ -286,12 +340,12 @@
286 340 $customerData['user_id'] = $user->ID;
287 341 }
288 342
289 343 if(empty($customerData['email'])) {
290 - do_action('ff_log_data', [
344 + do_action('fluentform/log_data', [
291 345 'title' => $feed['settings']['name'],
292 346 'status' => 'failed',
293 - 'description' => __('Support ticket creation failed, because no valid customer email found', 'fluentform'),
347 + 'description' => __('Support ticket creation failed, because no valid customer email found', 'fluent-support'),
294 348 'parent_source_id' => $form->id,
295 349 'source_id' => $entry->id,
296 350 'component' => $this->integrationKey,
297 351 'source_type' => 'submission_item'
@@ -302,8 +356,22 @@
302 356 $customerData['last_ip_address'] = $entry->ip;
303 357
304 358 $customer = Customer::maybeCreateCustomer($customerData);
305 359
360 + // Don't create a ticket if customer is blocked
361 + if($this->isBlockedCustomer($customerData['email'])) {
362 + do_action('fluentform/log_data', [
363 + 'title' => $feed['settings']['name'],
364 + 'status' => 'failed',
365 + 'description' => __('Support ticket creation failed, because customer email is blocked', 'fluent-support'),
366 + 'parent_source_id' => $form->id,
367 + 'source_id' => $entry->id,
368 + 'component' => $this->integrationKey,
369 + 'source_type' => 'submission_item'
370 + ]);
371 + return false;
372 + }
373 +
306 374 $ticketData['customer_id'] = $customer->id;
307 375
308 376 $ticketData = apply_filters('fluent_support/create_ticket_data', $ticketData, $customer);
309 377 do_action('fluent_support/before_ticket_create', $ticketData, $customer);
@@ -311,17 +379,23 @@
311 379 $ticket = Ticket::create($ticketData);
312 380
313 381 if(defined('FLUENTSUPPORTPRO') && !empty($ticketData['custom_fields'])) {
314 382 $ticket->syncCustomFields($ticketData['custom_fields']);
315 - $ticket->custom_fields = $ticket->customData();
383 + /*
384 + * This custom_fields is causing issues in all webhook to where we called $ticket->save()
385 + * As we are calling $ticket->syncCustomFields() above, we don't need this anymore
386 + * TODO: need to remove this line in future
387 + */
388 + //$ticket->custom_fields = $ticket->customData();
316 389 }
317 390
318 391 do_action('fluent_support/ticket_created', $ticket, $customer);
319 392
320 - do_action('ff_log_data', [
393 + do_action('fluentform/log_data', [
321 394 'title' => $feed['settings']['name'],
322 395 'status' => 'success',
323 - 'description' => __('Support ticket has been created at Fluent Support. Ticket ID: '.$ticket->id, 'fluentform'),
396 + // translators: %d is the ticket ID number
397 + 'description' => sprintf(__('Support ticket has been created at Fluent Support. Ticket ID: %d', 'fluent-support'), $ticket->id),
324 398 'parent_source_id' => $form->id,
325 399 'source_id' => $entry->id,
326 400 'component' => $this->integrationKey,
327 401 'source_type' => 'submission_item'
@@ -326,25 +400,69 @@
326 400 'component' => $this->integrationKey,
327 401 'source_type' => 'submission_item'
328 402 ]);
329 403
330 - if($ticketData['attachments']) {
331 - $attachments = explode(',', $ticketData['attachments']);
404 + if ($ticketData['attachments']) {
405 + $attachments = is_array($ticketData['attachments'])
406 + ? $ticketData['attachments']
407 + : explode(',', $ticketData['attachments']);
332 408
333 - foreach ($attachments as $attachment){
334 - $fileName = explode('/', $attachment);
335 - $fileName = end($fileName);
336 - Attachment::create(
337 - [
338 - 'ticket_id' => $ticket->id,
339 - 'file_path' => esc_url($attachment),
340 - 'full_url' => esc_url($attachment),
341 - 'title' => $fileName,
342 - 'person_id' => $customer->id
343 - ]
344 - );
409 + $uploadDir = wp_upload_dir();
410 +
411 + foreach ($attachments as $attachment) {
412 + $attachment = trim($attachment);
413 + if (empty($attachment)) {
414 + continue;
415 + }
416 +
417 + $isMediaLibrary = false;
418 + $attachmentId = null;
419 +
420 + // Try to get attachment ID if it's a media library URL
421 + $attachmentId = attachment_url_to_postid($attachment);
422 + if ($attachmentId) {
423 + $isMediaLibrary = true;
424 + }
425 +
426 + try {
427 + if ($isMediaLibrary) {
428 + $filePath = get_attached_file($attachmentId);
429 + if (!$filePath) {
430 + continue;
431 + }
432 + $fileName = basename($filePath);
433 + $fileType = get_post_mime_type($attachmentId);
434 + $fullUrl = wp_get_attachment_url($attachmentId);
435 + } else {
436 + $fileName = basename($attachment);
437 + $filePath = $uploadDir['basedir'] . FLUENTFORM_UPLOAD_DIR . '/' . $fileName;
438 + $fileInfo = wp_check_filetype($filePath);
439 + $fileType = (!empty($fileInfo['type'])) ? $fileInfo['type'] : '';
440 + $fullUrl = $attachment;
441 + }
442 +
443 + if (file_exists($filePath)) {
444 + try {
445 + Attachment::create([
446 + 'ticket_id' => $ticket->id,
447 + 'file_path' => sanitize_text_field($filePath),
448 + 'full_url' => sanitize_url($fullUrl),
449 + 'title' => sanitize_text_field($fileName),
450 + 'person_id' => $customer->id,
451 + 'file_type' => sanitize_text_field($fileType),
452 + 'is_media_library' => $isMediaLibrary,
453 + 'attachment_id' => $attachmentId
454 + ]);
455 + } catch (\Exception $e) {
456 +
457 + }
458 + }
459 + } catch (\Exception $e) {
460 + continue;
461 + }
345 462 }
346 - $ticket->load($attachments);
463 +
464 + $ticket->load('attachments');
347 465 }
348 466
349 467 return true;
350 468 }
@@ -356,7 +474,19 @@
356 474
357 475 public function isEnabled()
358 476 {
359 477 return true;
478 + }
479 +
480 + // check if customer is blocked or not
481 + private function isBlockedCustomer($customerEmail)
482 + {
483 + $customer = Customer::where('email', $customerEmail)->first();
484 +
485 + if (!$customer) {
486 + return false;
487 + }
488 +
489 + return $customer->isBlocked();
360 490 }
361 491
362 492 }