PluginProbe
Fluent Support – Helpdesk & Customer Support Ticket System / 2.3.0
Fluent Support – Helpdesk & Customer Support Ticket System v2.3.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 1.5.6 All 67 releases
fluent-support / app / Services / TicketHelper.php

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

470 lines 15.5 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;
4
5 use FluentSupport\App\Models\Agent;
6 use FluentSupport\App\Models\Conversation;
7 use FluentSupport\App\Models\MailBox;
8 use FluentSupport\App\Models\Meta;
9 use FluentSupport\App\Models\Product;
10 use FluentSupport\App\Models\TagPivot;
11 use FluentSupport\App\Models\Ticket;
12 use FluentSupport\App\Models\TicketTag;
13 use FluentSupport\App\Modules\PermissionManager;
14 use FluentSupport\App\Services\Helper;
15
16 class TicketHelper
17 {
18 /**
19 * getActivity method will return the activity in a ticket by agent
20 * @param $ticketId
21 * @param false $currentAgentId
22 * @return array
23 */
24 public static function getActivity($ticketId, $currentAgentId = false)
25 {
26 //Get the ticket meta information
27 $meta = Meta::where('object_type', 'ticket_meta')
28 ->where('key', '_live_activity')
29 ->where('object_id', $ticketId)
30 ->first();
31
32 $activities = [];
33 if ($meta) {
34 $activities = Helper::safeUnserialize($meta->value);
35 }
36
37 foreach ($activities as $index => $activity) {
38 if ((time() - $activity) > 60) {
39 unset($activities[$index]);
40 }
41 }
42
43 if (!$currentAgentId) {
44 return self::getAgentsInfoFromActivities($activities);
45 }
46
47 $activities[$currentAgentId] = time();
48
49 if ($meta) {
50 $meta->value = maybe_serialize($activities);
51 $meta->save();
52 } else {
53 Meta::insert([
54 'object_type' => 'ticket_meta',
55 'key' => '_live_activity',
56 'object_id' => $ticketId,
57 'value' => maybe_serialize($activities)
58 ]);
59 }
60
61 return self::getAgentsInfoFromActivities($activities);
62 }
63
64 private static function getAgentsInfoFromActivities($activities)
65 {
66 if (!$activities) {
67 return [];
68 }
69
70 return Agent::select(['id', 'email', 'first_name', 'last_name'])
71 ->whereIn('id', array_keys($activities))
72 ->get();
73 }
74
75 /**
76 * Batch-load live activity for a collection of tickets.
77 * Uses 2 queries total: one for activity metas, one for all agents.
78 *
79 * @param \Iterable $tickets Collection of Ticket models
80 * @return void
81 */
82 public static function loadBatchLiveActivities($tickets)
83 {
84 $activeTicketIds = $tickets->where('status', '!=', 'closed')->pluck('id')->toArray();
85
86 if (!$activeTicketIds) {
87 foreach ($tickets as $ticket) {
88 $ticket->live_activity = [];
89 }
90 return;
91 }
92
93 $activityMetas = Meta::where('object_type', 'ticket_meta')
94 ->where('key', '_live_activity')
95 ->whereIn('object_id', $activeTicketIds)
96 ->get()
97 ->keyBy('object_id');
98
99 $allAgentIds = [];
100 $ticketActivities = [];
101
102 foreach ($activityMetas as $ticketId => $meta) {
103 $activities = Helper::safeUnserialize($meta->value);
104 if (!is_array($activities)) {
105 continue;
106 }
107
108 $activities = array_filter($activities, function ($time) {
109 return (time() - $time) <= 60;
110 });
111
112 $ticketActivities[$ticketId] = $activities;
113 $allAgentIds = array_merge($allAgentIds, array_keys($activities));
114 }
115
116 $allAgentIds = array_unique(array_filter($allAgentIds));
117 $agents = $allAgentIds
118 ? Agent::select(['id', 'email', 'first_name', 'last_name'])
119 ->whereIn('id', $allAgentIds)
120 ->get()
121 ->keyBy('id')
122 : [];
123
124 $activitiesByTicket = [];
125 foreach ($ticketActivities as $ticketId => $activities) {
126 $ticketAgents = [];
127 foreach (array_keys($activities) as $agentId) {
128 if (isset($agents[$agentId])) {
129 $ticketAgents[] = $agents[$agentId];
130 }
131 }
132 $activitiesByTicket[$ticketId] = $ticketAgents;
133 }
134
135 foreach ($tickets as $ticket) {
136 $ticket->live_activity = $activitiesByTicket[$ticket->id] ?? [];
137 }
138 }
139
140 /**
141 * removeFromActivities method will remove the old live activity by agent and ticket id
142 * @param $ticketId
143 * @param $agentId
144 * @return bool
145 */
146 public static function removeFromActivities($ticketId, $agentId)
147 {
148 $meta = Meta::where('object_type', 'ticket_meta')
149 ->where('key', '_live_activity')
150 ->where('object_id', $ticketId)
151 ->first();
152
153 $activities = [];
154 if ($meta) {
155 $activities = Helper::safeUnserialize($meta->value);
156 }
157
158 if (!$activities) {
159 return false;
160 }
161
162 unset($activities[$agentId]);
163 foreach ($activities as $index => $activity) {
164 if ((time() - $activity) > 60) {
165 unset($activities[$index]);
166 }
167 }
168
169 $meta->value = maybe_serialize($activities);
170 $meta->save();
171
172 return true;
173 }
174
175 /**
176 * getSuggestedTickets method will return the list of Suggested tickets
177 * This method will get the agent id as parameter and fetch ticket information that are waiting to reply or unassigned
178 * @param $agentId
179 * @param int $limit
180 * @return mixed
181 */
182 public static function getSuggestedTickets($agentId, $limit = 5)
183 {
184 $restrictedBusinessBoxes = PermissionManager::getRestrictedMailboxIds();
185
186 //Get lis of tickets which are waiting for reply
187 $tickets = Ticket::where('agent_id', $agentId)
188 ->where(function ($q) use ($restrictedBusinessBoxes) {
189 $q->whereNotIn('mailbox_id', $restrictedBusinessBoxes);
190 $q->orWhereNull('mailbox_id');
191 })
192 ->where('status', '!=', 'closed')
193 ->applyFilters([
194 'waiting_for_reply' => 'yes'
195 ])
196 ->oldest('last_customer_response')
197 ->limit($limit)
198 ->with(
199 [
200 'customer',
201 'mailbox',
202 'tags',
203 'agent'
204 ])
205 ->get();
206
207 //If no ticket is available for reply and logged-in user has permission to manage unassigned tickets
208 if($tickets->isEmpty() && PermissionManager::currentUserCan('fst_manage_unassigned_tickets')) {
209 //Get the ticket list which status is not closed and agent id is null or 0
210 $tickets = Ticket::where('status', '!=', 'closed')
211 ->oldest('id')
212 ->where(function ($q) use ($restrictedBusinessBoxes) {
213 $q->whereNotIn('mailbox_id', $restrictedBusinessBoxes);
214 $q->orWhereNull('mailbox_id');
215 })
216 ->where(function ($q) {
217 $q->whereNull('agent_id');
218 $q->orWhere('agent_id', '0');
219 })
220 ->with(
221 [
222 'customer',
223 'mailbox',
224 'tags',
225 ])
226 ->limit($limit)
227 ->get();
228 }
229
230
231 return $tickets;
232
233 }
234
235 // This method will return all tagged/mentioned/watcher ticket's ids for filtering
236 public static function getWatcherTicketIds($agentId)
237 {
238 $mentioned = TagPivot::where('source_type', 'ticket_watcher')
239 ->where('tag_id', $agentId)
240 ->latest('id')
241 ->get(['source_id']);
242
243 $ticketIds = array_column($mentioned->toArray(), 'source_id');
244 return $ticketIds;
245 }
246
247 // This method will return all tagged/mentioned/watcher tickets of logged in agent
248 public static function getTicketsToWatch()
249 {
250 $agent = Helper::getCurrentAgent();
251 $restrictedBusinessBoxes = PermissionManager::getRestrictedMailboxIds();
252
253 $tickets = Ticket::with(
254 [
255 'customer',
256 'mailbox',
257 'tags',
258 'agent'
259 ])
260 ->limit(5)
261 ->where(function ($q) use ($restrictedBusinessBoxes) {
262 $q->whereNotIn('mailbox_id', $restrictedBusinessBoxes);
263 $q->orWhereNull('mailbox_id');
264 })
265 ->join('fs_tag_pivot', 'fs_tag_pivot.source_id', '=', 'fs_tickets.id')
266 ->where('fs_tag_pivot.source_type', '=', 'ticket_watcher')
267 ->where('fs_tag_pivot.tag_id', '=', $agent->id)
268 ->select(['fs_tickets.*'])
269 ->get();
270
271 return $tickets;
272 }
273
274 // This method will return all ticket watcher inside a ticket
275 public static function getWatchers($watchers)
276 {
277 $watcherAgents = [];
278
279 foreach ($watchers as $watcher) {
280 $watcherAgents[] = Agent::where('id', absint($watcher->tag_id))->select(['id', 'first_name', 'last_name'])->first();
281 }
282
283 return $watcherAgents;
284 }
285
286 /** @internal Not called from core controllers — available for Pro/hook usage. */
287 public static function getCarbonCopyCustomerInfo($ticketId){
288 $existing = Meta::where('object_type', 'beginning_cc_info')->where('object_id', $ticketId)->first();
289 if($existing){
290 return Helper::safeUnserialize($existing->value, []);
291 }
292
293 return [];
294 }
295
296 // This method will count total tickets
297 public static function countAllTickets()
298 {
299 return (new Ticket())->count();
300 }
301 // This method will count all un-assigned tickets
302 public static function countUnassignedTickets()
303 {
304 return Ticket::whereNull('agent_id')->count();
305 }
306 // This method will count all closed tickets
307 public static function countClosedTickets()
308 {
309 return Ticket::where('status', 'closed')->count();
310 }
311
312 // This method will count all New tickets
313 public static function countNewTickets()
314 {
315 return Ticket::where('status', 'new')->count();
316 }
317
318 // This method will count all Active tickets
319 public static function countActiveTickets()
320 {
321 return Ticket::where('status', 'active')->count();
322 }
323
324 public static function getTicketEssentials($type = '')
325 {
326 $agents = Agent::select(['id', 'first_name', 'last_name'])
327 ->where('person_type', 'agent')
328 ->get()->toArray();
329
330 $data = [
331 'client_priorities' => Helper::customerTicketPriorities(),
332 'ticket_statuses_group' => Helper::ticketStatusGroups(),
333 'agents' => $agents,
334 'changeable_ticket_statuses' => Helper::changeableTicketStatuses(),
335 'admin_priorities' => Helper::adminTicketPriorities(),
336 'enable_draft_mode' => Helper::getBusinessSettings('enable_draft_mode', 'no'),
337 'max_file_upload' => Helper::getBusinessSettings('max_file_upload', 3),
338 'support_products' => Product::select(['id', 'title'])->get(),
339 'ticket_tags' => TicketTag::select(['id', 'title'])->get()->toArray(),
340 'mailboxes' => MailBox::select(['id', 'name', 'settings'])->get(),
341 'ticket_statuses' => Helper::ticketStatuses(),
342 ];
343
344 return $type ? $data[$type] : $data;
345 }
346
347 public static function getLabelSearch($agentId)
348 {
349 $lists = Meta::where('object_id', $agentId)
350 ->where('object_type', 'search_meta')
351 ->where('key', 'label_search')
352 ->first();
353 $unserialize = [];
354 if ($lists) {
355 $unserialize = Helper::safeUnserialize($lists->value);
356 }
357
358 return $unserialize;
359 }
360
361 public static function saveSearchLabel($agent_id, $searchData, $filterType) {
362
363 $agent_id = get_current_user_id();
364 $existingRecord = Meta::where('object_id', $agent_id)
365 ->where('object_type', 'search_meta')
366 ->where('key', 'label_search')
367 ->first();
368
369 // If record exists, unserialize the data or initialize an empty array
370 $existingData = $existingRecord ? Helper::safeUnserialize($existingRecord->value) ?: [] : [];
371
372 // Check if it's an update or new entry
373 $isUpdate = isset($searchData['id']) && array_filter($existingData, function ($item) use ($searchData) {
374 return isset($item['id']) && $item['id'] == $searchData['id'];
375 });
376
377 // Handle adding or updating
378 $updatedData = self::addOrUpdateSearchData($existingData, $searchData);
379
380 // Save the updated data back to the database
381 if ($existingRecord) {
382 $existingRecord->update([
383 'value' => maybe_serialize($updatedData)
384 ]);
385 } else {
386 Meta::insert([
387 'object_type' => 'search_meta',
388 'key' => 'label_search',
389 'object_id' => $agent_id,
390 'value' => maybe_serialize($updatedData)
391 ]);
392 }
393
394 $message = $isUpdate
395 ? __('Your saved search has been updated successfully!', 'fluent-support')
396 : __('Your search has been saved successfully!', 'fluent-support');
397
398 return [
399 'message' => $message,
400 ];
401 }
402
403 private static function addOrUpdateSearchData(array $existingData, array $searchData): array
404 {
405 if (isset($searchData['id'])) {
406 $updated = false;
407
408 // Update the existing record with the matching ID
409 foreach ($existingData as &$record) {
410 if (isset($record['id']) && $record['id'] == $searchData['id']) {
411 $record = array_merge($record, $searchData);
412 $updated = true;
413 break;
414 }
415 }
416
417 // If no matching record found, append the new data
418 if (!$updated) {
419 $existingData[] = $searchData;
420 }
421 } else {
422 // If no ID provided, generate a new one and add the data
423 $newId = self::generateNewId($existingData);
424 $searchData['id'] = $newId;
425 $existingData[] = $searchData;
426 }
427
428 return $existingData;
429 }
430
431 public static function deleteSavedSearch($search_id) {
432 $agent_id = get_current_user_id();
433 $existingRecord = Meta::where('object_id', $agent_id)
434 ->where('object_type', 'search_meta')
435 ->where('key', 'label_search')
436 ->first();
437
438 if ($existingRecord) {
439 $existingData = Helper::safeUnserialize($existingRecord->value) ?: [];
440
441 $updatedData = array_filter($existingData, function ($item) use ($search_id) {
442 return isset($item['id']) && $item['id'] != $search_id;
443 });
444
445 if (count($existingData) !== count($updatedData)) {
446 $existingRecord->update([
447 'value' => maybe_serialize(array_values($updatedData))
448 ]);
449 return [
450 'message' => __('Search deleted successfully.', 'fluent-support'),
451 ];
452 }
453
454 return [
455 'message' => __('Search ID not found.', 'fluent-support'),
456 ];
457 }
458 }
459
460 private static function generateNewId(array $existingData): int
461 {
462 if (empty($existingData)) {
463 return 1;
464 }
465
466 $maxId = max(array_column($existingData, 'id'));
467 return $maxId + 1;
468 }
469 }
470