PluginProbe
Fluent Support – Helpdesk & Customer Support Ticket System / 2.1.1
Fluent Support – Helpdesk & Customer Support Ticket System v2.1.1
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.1.1, at app/Services/TicketHelper.php

461 lines 15.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;
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 ->whereNotIn('mailbox_id', $restrictedBusinessBoxes)
189 ->where('status', '!=', 'closed')
190 ->applyFilters([
191 'waiting_for_reply' => 'yes'
192 ])
193 ->oldest('last_customer_response')
194 ->limit($limit)
195 ->with(
196 [
197 'customer',
198 'mailbox',
199 'tags',
200 'agent'
201 ])
202 ->get();
203
204 //If no ticket is available for reply and logged-in user has permission to manage unassigned tickets
205 if($tickets->isEmpty() && PermissionManager::currentUserCan('fst_manage_unassigned_tickets')) {
206 //Get the ticket list which status is not closed and agent id is null or 0
207 $tickets = Ticket::where('status', '!=', 'closed')
208 ->oldest('id')
209 ->whereNotIn('mailbox_id', $restrictedBusinessBoxes)
210 ->where(function ($q) {
211 $q->whereNull('agent_id');
212 $q->orWhere('agent_id', '0');
213 })
214 ->with(
215 [
216 'customer',
217 'mailbox',
218 'tags',
219 ])
220 ->limit($limit)
221 ->get();
222 }
223
224
225 return $tickets;
226
227 }
228
229 // This method will return all tagged/mentioned/watcher ticket's ids for filtering
230 public static function getWatcherTicketIds($agentId)
231 {
232 $mentioned = TagPivot::where('source_type', 'ticket_watcher')
233 ->where('tag_id', $agentId)
234 ->latest('id')
235 ->get(['source_id']);
236
237 $ticketIds = array_column($mentioned->toArray(), 'source_id');
238 return $ticketIds;
239 }
240
241 // This method will return all tagged/mentioned/watcher tickets of logged in agent
242 public static function getTicketsToWatch()
243 {
244 $agent = Helper::getCurrentAgent();
245 $restrictedBusinessBoxes = PermissionManager::getRestrictedMailboxIds();
246
247 $tickets = Ticket::with(
248 [
249 'customer',
250 'mailbox',
251 'tags',
252 'agent'
253 ])
254 ->limit(5)
255 ->whereNotIn('mailbox_id', $restrictedBusinessBoxes)
256 ->join('fs_tag_pivot', 'fs_tag_pivot.source_id', '=', 'fs_tickets.id')
257 ->where('fs_tag_pivot.source_type', '=', 'ticket_watcher')
258 ->where('fs_tag_pivot.tag_id', '=', $agent->id)
259 ->select(['fs_tickets.*'])
260 ->get();
261
262 return $tickets;
263 }
264
265 // This method will return all ticket watcher inside a ticket
266 public static function getWatchers($watchers)
267 {
268 $watcherAgents = [];
269
270 foreach ($watchers as $watcher) {
271 $watcherAgents[] = Agent::where('id', absint($watcher->tag_id))->select(['id', 'first_name', 'last_name'])->first();
272 }
273
274 return $watcherAgents;
275 }
276
277 /** @internal Not called from core controllers — available for Pro/hook usage. */
278 public static function getCarbonCopyCustomerInfo($ticketId){
279 $existing = Meta::where('object_type', 'beginning_cc_info')->where('object_id', $ticketId)->first();
280 if($existing){
281 return Helper::safeUnserialize($existing->value, []);
282 }
283
284 return [];
285 }
286
287 // This method will count total tickets
288 public static function countAllTickets()
289 {
290 return (new Ticket())->count();
291 }
292 // This method will count all un-assigned tickets
293 public static function countUnassignedTickets()
294 {
295 return Ticket::whereNull('agent_id')->count();
296 }
297 // This method will count all closed tickets
298 public static function countClosedTickets()
299 {
300 return Ticket::where('status', 'closed')->count();
301 }
302
303 // This method will count all New tickets
304 public static function countNewTickets()
305 {
306 return Ticket::where('status', 'new')->count();
307 }
308
309 // This method will count all Active tickets
310 public static function countActiveTickets()
311 {
312 return Ticket::where('status', 'active')->count();
313 }
314
315 public static function getTicketEssentials($type = '')
316 {
317 $agents = Agent::select(['id', 'first_name', 'last_name'])
318 ->where('person_type', 'agent')
319 ->get()->toArray();
320
321 $data = [
322 'client_priorities' => Helper::customerTicketPriorities(),
323 'ticket_statuses_group' => Helper::ticketStatusGroups(),
324 'agents' => $agents,
325 'changeable_ticket_statuses' => Helper::changeableTicketStatuses(),
326 'admin_priorities' => Helper::adminTicketPriorities(),
327 'enable_draft_mode' => Helper::getBusinessSettings('enable_draft_mode', 'no'),
328 'max_file_upload' => Helper::getBusinessSettings('max_file_upload', 3),
329 'support_products' => Product::select(['id', 'title'])->get(),
330 'ticket_tags' => TicketTag::select(['id', 'title'])->get()->toArray(),
331 'mailboxes' => MailBox::select(['id', 'name', 'settings'])->get(),
332 'ticket_statuses' => Helper::ticketStatuses(),
333 ];
334
335 return $type ? $data[$type] : $data;
336 }
337
338 public static function getLabelSearch($agentId)
339 {
340 $lists = Meta::where('object_id', $agentId)
341 ->where('object_type', 'search_meta')
342 ->where('key', 'label_search')
343 ->first();
344 $unserialize = [];
345 if ($lists) {
346 $unserialize = Helper::safeUnserialize($lists->value);
347 }
348
349 return $unserialize;
350 }
351
352 public static function saveSearchLabel($agent_id, $searchData, $filterType) {
353
354 $agent_id = get_current_user_id();
355 $existingRecord = Meta::where('object_id', $agent_id)
356 ->where('object_type', 'search_meta')
357 ->where('key', 'label_search')
358 ->first();
359
360 // If record exists, unserialize the data or initialize an empty array
361 $existingData = $existingRecord ? Helper::safeUnserialize($existingRecord->value) ?: [] : [];
362
363 // Check if it's an update or new entry
364 $isUpdate = isset($searchData['id']) && array_filter($existingData, function ($item) use ($searchData) {
365 return isset($item['id']) && $item['id'] == $searchData['id'];
366 });
367
368 // Handle adding or updating
369 $updatedData = self::addOrUpdateSearchData($existingData, $searchData);
370
371 // Save the updated data back to the database
372 if ($existingRecord) {
373 $existingRecord->update([
374 'value' => maybe_serialize($updatedData)
375 ]);
376 } else {
377 Meta::insert([
378 'object_type' => 'search_meta',
379 'key' => 'label_search',
380 'object_id' => $agent_id,
381 'value' => maybe_serialize($updatedData)
382 ]);
383 }
384
385 $message = $isUpdate
386 ? __('Your saved search has been updated successfully!', 'fluent-support')
387 : __('Your search has been saved successfully!', 'fluent-support');
388
389 return [
390 'message' => $message,
391 ];
392 }
393
394 private static function addOrUpdateSearchData(array $existingData, array $searchData): array
395 {
396 if (isset($searchData['id'])) {
397 $updated = false;
398
399 // Update the existing record with the matching ID
400 foreach ($existingData as &$record) {
401 if (isset($record['id']) && $record['id'] == $searchData['id']) {
402 $record = array_merge($record, $searchData);
403 $updated = true;
404 break;
405 }
406 }
407
408 // If no matching record found, append the new data
409 if (!$updated) {
410 $existingData[] = $searchData;
411 }
412 } else {
413 // If no ID provided, generate a new one and add the data
414 $newId = self::generateNewId($existingData);
415 $searchData['id'] = $newId;
416 $existingData[] = $searchData;
417 }
418
419 return $existingData;
420 }
421
422 public static function deleteSavedSearch($search_id) {
423 $agent_id = get_current_user_id();
424 $existingRecord = Meta::where('object_id', $agent_id)
425 ->where('object_type', 'search_meta')
426 ->where('key', 'label_search')
427 ->first();
428
429 if ($existingRecord) {
430 $existingData = Helper::safeUnserialize($existingRecord->value) ?: [];
431
432 $updatedData = array_filter($existingData, function ($item) use ($search_id) {
433 return isset($item['id']) && $item['id'] != $search_id;
434 });
435
436 if (count($existingData) !== count($updatedData)) {
437 $existingRecord->update([
438 'value' => maybe_serialize(array_values($updatedData))
439 ]);
440 return [
441 'message' => __('Search deleted successfully.', 'fluent-support'),
442 ];
443 }
444
445 return [
446 'message' => __('Search ID not found.', 'fluent-support'),
447 ];
448 }
449 }
450
451 private static function generateNewId(array $existingData): int
452 {
453 if (empty($existingData)) {
454 return 1;
455 }
456
457 $maxId = max(array_column($existingData, 'id'));
458 return $maxId + 1;
459 }
460 }
461