PluginProbe
Fluent Support – Helpdesk & Customer Support Ticket System / 2.3.0
Fluent Support – Helpdesk & Customer Support Ticket System v2.3.0
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 / Services / Tickets / Importer / BaseImporter.php

BaseImporter.php in Fluent Support – Helpdesk & Customer Support Ticket System 2.3.0, at app/Services/Tickets/Importer/BaseImporter.php

418 lines 13.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\Services\Tickets\Importer;
4
5 use FluentSupport\App\Models\Agent;
6 use FluentSupport\App\Models\Attachment;
7 use FluentSupport\App\Models\Customer;
8 use FluentSupport\App\Models\Person;
9 use FluentSupport\App\Models\Ticket;
10 use FluentSupport\App\Services\Helper;
11 use FluentSupport\Framework\Support\Arr;
12
13 abstract class BaseImporter
14 {
15 protected $db;
16 protected $handler = '';
17 protected $mailbox;
18 protected $limit = 20;
19 protected $ticketUpdatedSince;
20
21 public function __construct()
22 {
23 $this->db = Helper::FluentSupport('db');
24 $this->mailbox = Helper::getDefaultMailBox();
25 $this->limit = apply_filters('fluent_support/ticket_import_chunk_limit', 20);
26 $this->ticketUpdatedSince = apply_filters('fluent_support/fs_tickets_updated_since', '2010-01-19T02:00:00Z');
27 }
28 /**
29 * This method `stats` returns an array of ticket stats of targeted helpdesk system
30 *
31 * @return array
32 */
33 abstract public function stats();
34
35 /**
36 * This method `doMigration` is responsible for migrating tickets from targeted helpdesk system
37 * and it returns an array with migration response
38 *
39 * @param int $page
40 * @param string $handler
41 * @return array
42 */
43 abstract public function doMigration($page, $handler);
44
45 /**
46 * This method `deleteTickets` delete tickets and related data from targeted helpdesk once the
47 * migration process done successfully
48 *
49 * @param int $page // this will be migrated ticket page number
50 * @return array
51 */
52 abstract public function deleteTickets($page);
53
54 /**
55 * This method `migrateTickets` migrate tickets on Fluent Support with the help of `migrateTicket` method
56 *
57 * @param array $tickets // array of tickets with all required data
58 * @return array
59 */
60 public function migrateTickets($tickets)
61 {
62 $insertIds = [];
63 $skips = [];
64 foreach ($tickets as $ticket) {
65 $createdTicket = $this->migrateTicket($ticket);
66 if ($createdTicket) {
67 $insertIds[] = $createdTicket->id;
68 } else {
69 $skips[] = $ticket;
70 }
71 }
72
73 return [
74 'inserts' => $insertIds,
75 'skips' => $skips
76 ];
77 }
78
79 /**
80 * This method `migrateTicket` is responsible for migrating a single ticket in Fluent Support
81 *
82 * @param array $ticketData
83 * @return \FluentSupport\App\Models\Ticket $createdTicket
84 */
85 public function migrateTicket($ticketData)
86 {
87 if ($this->isMigrated($ticketData['origin_id'])) {
88 return false;
89 }
90
91 $fillables = (new Ticket())->getFillable();
92 $fillables[] = 'created_at';
93 $fillables[] = 'updated_at';
94
95 $data = array_filter(Arr::only($ticketData, $fillables));
96
97 if (!isset($data['content'])) {
98 $data['content'] = '';
99 }
100
101 $data['customer_id'] = $ticketData['customer']['id'];
102
103 if (!empty($ticketData['agent'])) {
104 $data['agent_id'] = $ticketData['agent']['id'];
105 }
106
107 if (empty($ticketData['mailbox_id']) && $this->mailbox) {
108 $data['mailbox_id'] = $this->mailbox->id;
109 }
110
111 $data = $this->addMetaData($data);
112
113 $createdTicketId = $this->db->table('fs_tickets')
114 ->insertGetId($data);
115
116 if (!$createdTicketId) {
117 return false;
118 }
119
120 $createdTicket = Ticket::find($createdTicketId);
121
122
123 if (!empty($ticketData['attachments'])) {
124 foreach ($ticketData['attachments'] as $attachmentData) {
125 $attachmentData['person_id'] = $createdTicket->customer_id;
126 $attachmentData['ticket_id'] = $createdTicket->id;
127 Attachment::create($attachmentData);
128 }
129 }
130
131 if (!$createdTicket) {
132 return false;
133 }
134
135 Helper::updateTicketMeta($createdTicket->id, '_' . $this->handler . '_origin_id', $ticketData['origin_id']);
136
137 if (empty($ticketData['replies'])) {
138 return $createdTicket;
139 }
140
141 // Let's migrate the replies
142 $lastAgentResponseTime = $createdTicket->last_customer_response;
143 $lastCustomerResponseTime = $createdTicket->waiting_since;
144 $waitingSince = $createdTicket->waiting_since;
145 $responseCount = 0;
146 $firstResponseTimestamp = false;
147
148 $defualtAgentId = false;
149
150 foreach ($ticketData['replies'] as $reply) {
151
152 $replyData = Arr::only($reply, [
153 'content',
154 'conversation_type',
155 'created_at',
156 'updated_at'
157 ]);
158
159 if (!isset($replyData['content']) || empty($replyData['content'])) {
160 continue;
161 }
162
163 $replyData['ticket_id'] = $createdTicket->id;
164 $replyData['source'] = $this->handler;
165 $replyData['content_hash'] = md5($replyData['content']);
166
167 $person = ($reply['is_customer_reply']) ? $this->getPerson($reply['user']) : $this->getPerson($reply['user'], 'agent');
168
169 if (!$person) {
170 continue; // person could not be found
171 }
172
173 $replyData['person_id'] = intval($person->id);
174
175 if ($replyData['conversation_type'] == 'response') {
176 $responseCount++;
177 }
178
179 if ($reply['is_customer_reply']) {
180 $replyCreatedAt = $replyData['created_at'] ?? null;
181 if (strtotime($lastCustomerResponseTime) < strtotime($replyCreatedAt)) {
182 $lastCustomerResponseTime = $replyCreatedAt;
183 }
184 } else if ($replyData['conversation_type'] == 'response') {
185 $replyCreatedAt = $replyData['created_at'] ?? null;
186
187 if (!$firstResponseTimestamp && $replyCreatedAt) {
188 $firstResponseTimestamp = $replyCreatedAt;
189 }
190
191 if (strtotime($lastAgentResponseTime) < strtotime($replyCreatedAt)) {
192 $lastAgentResponseTime = $replyCreatedAt;
193 $waitingSince = $replyCreatedAt;
194 }
195
196 $defualtAgentId = $person->id;
197 }
198
199 $conversionId = $this->db->table('fs_conversations')
200 ->insertGetId($replyData);
201
202 if (!empty($reply['attachments'])) {
203 foreach ($reply['attachments'] as $attachmentData) {
204 $attachmentData['ticket_id'] = $createdTicket->id;
205 $attachmentData['person_id'] = $person->id;
206 $attachmentData['conversation_id'] = $conversionId;
207 Attachment::create($attachmentData);
208 }
209 }
210 }
211
212 $ticketUpdateData = array_filter([
213 'last_customer_response' => $lastAgentResponseTime,
214 'waiting_since' => $waitingSince,
215 'response_count' => $responseCount
216 ]);
217
218 if(!$createdTicket->agent_id && $defualtAgentId) {
219 $ticketUpdateData['agent_id'] = $defualtAgentId;
220 }
221
222 if ($firstResponseTimestamp && !empty($createdTicket->crerated_at)) {
223 $ticketUpdateData['first_response_time'] = strtotime($firstResponseTimestamp) - strtotime($createdTicket->crerated_at);
224 }
225
226 if ($createdTicket->status == 'closed' && !empty($createdTicket->resolved_at) && !empty($createdTicket->crerated_at)) {
227 $ticketUpdateData['total_close_time'] = strtotime($createdTicket->resolved_at) - strtotime($createdTicket->crerated_at);
228 }
229
230 if ($ticketUpdateData) {
231 $createdTicket->fill($ticketUpdateData);
232 $createdTicket->save();
233 }
234
235 return $createdTicket;
236 }
237
238 /**
239 * This `getPerson` method will get associated person with a tickets or conversation
240 * @param array $personData
241 * @param string $type user type default is `customer`
242 * @return object|false $person
243 */
244 protected function getPerson($personData, $type = 'customer')
245 {
246
247 static $cachedPerson = [];
248
249 $personData = (array) $personData;
250
251 if (!empty($personData['user_id'])) {
252 if (isset($personData[$personData['user_id'] . '_' . $type])) {
253 return $personData[$personData['user_id'] . '_' . $type];
254 }
255 }
256
257 if ($type == 'customer') {
258 if (!empty($personData['user_id']) || !empty($personData['email'])) {
259 $person = Customer::maybeCreateCustomer($personData);
260 if ($person) {
261 $cachedPerson[$person->user_id . '_' . $type] = $person;
262 }
263 return $person;
264 }
265 return false;
266 }
267
268 if (!empty($personData['user_id'])) {
269 $person = Person::where('user_id', $personData['user_id'])
270 ->where('person_type', $type)
271 ->first();
272 if ($person) {
273 $cachedPerson[$person->user_id . '_' . $type] = $person;
274
275 return $person;
276 }
277 }
278
279 if (!empty($personData['email'])) {
280 $person = Person::where('email', $personData['email'])
281 ->where('person_type', $type)
282 ->first();
283 if ($person) {
284 $cachedPerson[$person->user_id . '_' . $type] = $person;
285 return $person;
286 }
287 }
288
289 $user = false;
290
291 if (!empty($personData['user_id'])) {
292 $user = get_user_by('ID', $personData['user_id']);
293 } else if (!empty($personData['email'])) {
294 $user = get_user_by('email', $personData['email']);
295 }
296
297 if (!$user && 'agent' == $type) {
298 $person = Agent::updateOrCreate(
299 [
300 'email' => Arr::get($personData, 'email'),
301 ],
302 [
303 'first_name' => $personData['first_name'] ?? '',
304 'last_name' => $personData['last_name'] ?? '',
305 'email' => $personData['email'] ?? '',
306 'type' => $type
307 ]
308 );
309
310 return $person;
311 }
312
313
314 if ($user){
315 $personData['user_id'] = $user->ID;
316 $personData['first_name'] = $user->first_name;
317 $personData['last_name'] = $user->last_name;
318 $personData['last_name'] = $user->last_name;
319 $personData['email'] = $user->user_email;
320
321 if (empty($personData['full_name'])) {
322 $personData['full_name'] = $user->display_name;
323 }
324
325 }
326
327 if (empty($personData['first_name']) && $personData['last_name'] && !empty($personData['name'])) {
328 $fullNameArray = explode(' ', $personData['name']);
329 $personData['first_name'] = array_shift($fullNameArray);
330 if ($fullNameArray) {
331 $personData['last_name'] = implode(' ', $fullNameArray);
332 }
333 unset($personData['name']);
334 }
335
336
337 $personData['person_type'] = $type;
338
339 $person = Person::create($personData);
340 $cachedPerson[$person->user_id . '_' . $type] = $person;
341 return $person;
342 }
343
344 protected function fallbackAgentId()
345 {
346 $agent = Person::where('person_type', 'agent')
347 ->oldest('id')
348 ->select(['id'])
349 ->first();
350
351 return (int)$agent->id;
352 }
353
354 protected function isMigrated($originId)
355 {
356 $exist = $this->db->table('fs_meta')
357 ->where('object_type', 'ticket_meta')
358 ->where('key', '_' . $this->handler . '_origin_id')
359 ->where('value', $originId)
360 ->first();
361
362 return !!$exist;
363 }
364
365 protected function addMetaData($ticketData)
366 {
367 if (empty($ticketData['slug'])) {
368 $ticketData['slug'] = Ticket::slugify($ticketData['title']);
369 }
370
371 $ticketData['hash'] = substr(md5(time() . wp_generate_uuid4()), 0, 8) . wp_rand(1, 99);
372
373 $ticketData['content_hash'] = md5($ticketData['content']);
374
375 if (empty($ticketData['last_customer_response'])) {
376 $ticketData['last_customer_response'] = current_time('mysql');
377 }
378
379 if (empty($ticketData['created_at'])) {
380 $ticketData['created_at'] = current_time('mysql');
381 }
382 if (empty($ticketData['updated_at'])) {
383 $ticketData['updated_at'] = current_time('mysql');
384 }
385
386 if (empty($ticketData['waiting_since'])) {
387 $ticketData['waiting_since'] = current_time('mysql');
388 }
389
390 return $ticketData;
391 }
392
393
394 public function resolvePerson($personUserId)
395 {
396 $person = get_user_by('ID', $personUserId);
397
398 if (!$person) {
399 return false;
400 }
401
402 return [
403 'user_id' => $personUserId,
404 'full_name' => $person->first_name . ' ' . $person->last_name,
405 'email' => $person->user_email
406 ];
407 }
408
409 public function getUnknownCustomerData($authorId): array
410 {
411 return [
412 'user_id' => 0,
413 'full_name' => 'Unknown User',
414 'email' => 'unknown-' . $authorId . '@example.com',
415 ];
416 }
417 }
418