PluginProbe
Fluent Support – Helpdesk & Customer Support Ticket System / 2.4.0
Fluent Support – Helpdesk & Customer Support Ticket System v2.4.0
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 1.5.5 All 68 releases
← All changes | app/Services/TicketHelper.php +358 -8 1.4.52.4.0 View file →
@@ -2,16 +2,29 @@
2 2
3 3 namespace FluentSupport\App\Services;
4 4
5 5 use FluentSupport\App\Models\Agent;
6 +use FluentSupport\App\Models\Conversation;
7 +use FluentSupport\App\Models\MailBox;
6 8 use FluentSupport\App\Models\Meta;
9 +use FluentSupport\App\Models\Product;
10 +use FluentSupport\App\Models\TagPivot;
7 11 use FluentSupport\App\Models\Ticket;
12 +use FluentSupport\App\Models\TicketTag;
8 13 use FluentSupport\App\Modules\PermissionManager;
14 +use FluentSupport\App\Services\Helper;
9 15
10 16 class TicketHelper
11 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 + */
12 24 public static function getActivity($ticketId, $currentAgentId = false)
13 25 {
26 + //Get the ticket meta information
14 27 $meta = Meta::where('object_type', 'ticket_meta')
15 28 ->where('key', '_live_activity')
16 29 ->where('object_id', $ticketId)
17 30 ->first();
@@ -17,9 +30,9 @@
17 30 ->first();
18 31
19 32 $activities = [];
20 33 if ($meta) {
21 - $activities = maybe_unserialize($meta->value);
34 + $activities = Helper::safeUnserialize($meta->value);
22 35 }
23 36
24 37 foreach ($activities as $index => $activity) {
25 38 if ((time() - $activity) > 60) {
@@ -47,19 +60,90 @@
47 60
48 61 return self::getAgentsInfoFromActivities($activities);
49 62 }
50 63
51 - public static function getAgentsInfoFromActivities($activities)
64 + private static function getAgentsInfoFromActivities($activities)
52 65 {
53 66 if (!$activities) {
54 67 return [];
55 68 }
56 69
57 - return Agent::select(['email', 'first_name', 'last_name'])
70 + return Agent::select(['id', 'email', 'first_name', 'last_name'])
58 71 ->whereIn('id', array_keys($activities))
59 72 ->get();
60 73 }
61 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 + */
62 146 public static function removeFromActivities($ticketId, $agentId)
63 147 {
64 148 $meta = Meta::where('object_type', 'ticket_meta')
65 149 ->where('key', '_live_activity')
@@ -67,9 +151,9 @@
67 151 ->first();
68 152
69 153 $activities = [];
70 154 if ($meta) {
71 - $activities = maybe_unserialize($meta->value);
155 + $activities = Helper::safeUnserialize($meta->value);
72 156 }
73 157
74 158 if (!$activities) {
75 159 return false;
@@ -87,28 +171,59 @@
87 171
88 172 return true;
89 173 }
90 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 + */
91 182 public static function getSuggestedTickets($agentId, $limit = 5)
92 183 {
184 + $restrictedBusinessBoxes = PermissionManager::getRestrictedMailboxIds();
185 +
186 + //Get lis of tickets which are waiting for reply
93 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 + })
94 192 ->where('status', '!=', 'closed')
95 193 ->applyFilters([
96 194 'waiting_for_reply' => 'yes'
97 195 ])
98 - ->orderBy('last_customer_response', 'ASC')
196 + ->oldest('last_customer_response')
99 197 ->limit($limit)
100 - ->with('customer')
198 + ->with(
199 + [
200 + 'customer',
201 + 'mailbox',
202 + 'tags',
203 + 'agent'
204 + ])
101 205 ->get();
102 206
207 + //If no ticket is available for reply and logged-in user has permission to manage unassigned tickets
103 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
104 210 $tickets = Ticket::where('status', '!=', 'closed')
105 - ->orderBy('id', 'ASC')
211 + ->oldest('id')
212 + ->where(function ($q) use ($restrictedBusinessBoxes) {
213 + $q->whereNotIn('mailbox_id', $restrictedBusinessBoxes);
214 + $q->orWhereNull('mailbox_id');
215 + })
106 216 ->where(function ($q) {
107 217 $q->whereNull('agent_id');
108 218 $q->orWhere('agent_id', '0');
109 219 })
110 - ->with('customer')
220 + ->with(
221 + [
222 + 'customer',
223 + 'mailbox',
224 + 'tags',
225 + ])
111 226 ->limit($limit)
112 227 ->get();
113 228 }
114 229
@@ -114,6 +229,241 @@
114 229
115 230
116 231 return $tickets;
117 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'])->orderedByTitle()->get(),
339 + 'ticket_tags' => TicketTag::select(['id', 'title'])->get()->toArray(),
340 + 'mailboxes' => MailBox::getAccessibleBoxes(),
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;
118 468 }
119 469 }