PluginProbe
Fluent Support – Helpdesk & Customer Support Ticket System / trunk
Fluent Support – Helpdesk & Customer Support Ticket System vtrunk
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
fluent-support / app / Services / Tickets / ResponseService.php

ResponseService.php in Fluent Support – Helpdesk & Customer Support Ticket System trunk, at app/Services/Tickets/ResponseService.php

260 lines 9.6 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;
4
5 use FluentSupport\App\Models\Attachment;
6 use FluentSupport\App\Models\Conversation;
7 use FluentSupport\App\Services\Helper;
8 use FluentSupport\App\Services\Includes\UploadService;
9 use FluentSupport\Framework\Support\Arr;
10
11 class ResponseService
12 {
13 /**
14 * createResponse method is responsible for create responses to ticket by agent or customer
15 * @param $data
16 * @param $person
17 * @param $ticket
18 * @return array|false
19 */
20 public function createResponse($data, $person, $ticket, $silently = false)
21 {
22 if (empty($data['content'])) {
23 return false;
24 }
25
26 $cc_emails = Arr::get($data, 'cc_emails', []);
27
28 $informationalReply = Arr::get($data, 'informational_reply', false);
29 // Adding support for shortcode in agent response
30 if ($person->person_type == 'agent') {
31 $data['content'] = apply_filters('fluent_support/parse_smartcode_data', $data['content'], [
32 'customer' => $ticket->customer,
33 'agent' => $person
34 ]);
35 }
36
37 if ($person->person_type == 'agent' && Arr::get($data, 'conversation_type', 'response') == 'response') {
38 $data['content'] = $this->maybeAppendSignature($data['content'], $person);
39 }
40
41 $convoType = Arr::get($data, 'conversation_type', 'response');
42
43 // Request input is unslashed once at the boundary (the framework Request
44 // cleaner for web requests, WP core for REST params), and API, MCP and
45 // webhook callers pass decoded JSON that was never slashed. Sanitize only:
46 // unslashing again would strip literal backslashes out of Windows paths,
47 // regexes, JSON and shell snippets.
48 $content = wp_kses_post($data['content']);
49 $resetWaitingSince = apply_filters('fluent_support/reset_waiting_since', true, $content);
50 $content = apply_filters('fluent_support/response_content_before_use_anywhere', $content);
51 $response = [
52 'person_id' => $person->id,
53 'ticket_id' => $ticket->id,
54 'conversation_type' => $convoType,
55 'content' => $content,
56 'source' => Arr::get($data, 'source', 'web'),
57 'content_hash' => md5($content)
58 ];
59
60 if (!empty($data['message_id'])) {
61 $response['message_id'] = sanitize_text_field($data['message_id']);
62 }
63
64 $response = apply_filters('fluent_support/new_' . $person->person_type . '_' . $convoType, $response, $ticket, $person);
65
66 $createdResponse = \FluentSupport\App\Models\Conversation::create($response);
67
68 if (!empty($cc_emails)) {
69 //Store cc emails in meta settings for the response
70 $createdResponse->updateSettingsValue('cc_email', $cc_emails);
71
72 //Store all the carbon copy customers under the ticket
73 $existingCcEmails = $ticket->getSettingsValue('all_cc_email', []);
74 $newData = array_merge($existingCcEmails, $cc_emails);
75 $ticket->updateSettingsValue('all_cc_email', array_unique($newData));
76 }
77
78 $createdResponse->load('person');
79
80 if ($person->person_type == 'agent' && $ticket->status == 'new' && $convoType == 'response') {
81 $ticket->status = 'active';
82 if ($ticket->created_at) {
83 $ticket->first_response_time = strtotime(current_time('mysql')) - strtotime($ticket->created_at);
84 } else {
85 $ticket->first_response_time = 300;
86 }
87 }
88
89 $agentAdded = false;
90 $previousAgentId = (int) $ticket->agent_id;
91 $updateData = [];
92
93 if ($person->person_type == 'agent') {
94 if (!$ticket->agent_id && $convoType == 'response') {
95 $ticket->agent_id = $person->id;
96 $agentAdded = true;
97 $updateData = [
98 'agent_id' => $person->id
99 ];
100 }
101
102 if ($convoType == 'response') {
103 if ($resetWaitingSince && !$informationalReply) {
104 $ticket->last_agent_response = current_time('mysql');
105 $ticket->waiting_since = current_time('mysql');
106 }
107 }
108 } else {
109
110 if ($ticket->last_agent_response && strtotime($ticket->last_agent_response) > strtotime($ticket->last_customer_response)) {
111 $ticket->waiting_since = current_time('mysql');
112 }
113
114 $ticket->last_customer_response = current_time('mysql');
115 }
116
117 if ($convoType == 'response') {
118 $ticket->response_count += 1;
119 }
120
121 $closed = false;
122 if (Arr::get($data, 'close_ticket') == 'yes' && $ticket->status != 'closed') {
123 $ticket->status = 'closed';
124 $ticket->resolved_at = current_time('mysql');
125 $ticket->closed_by = $person->id;
126 $ticket->total_close_time = current_time('timestamp') - strtotime($ticket->created_at);
127 $closed = true;
128
129 $internalNote = __('Ticket has been closed', 'fluent-support');
130
131 $internalNote = apply_filters('fluent_support/ticket_close_internal_note', $internalNote, $ticket);
132
133 if ($internalNote) {
134 Conversation::create([
135 'ticket_id' => $ticket->id,
136 'person_id' => $person->id,
137 'conversation_type' => 'internal_info',
138 'content' => $internalNote
139 ]);
140 }
141 }
142
143 $ticket->save();
144
145 Helper::tempImageMoveUploadDir($ticket->id, 'conversation', $createdResponse->id, $person->id);
146
147 //If file upload failed to local during create response
148 if ($attachmentHashes = Arr::get($data, 'attachments', [])) {
149 $attachments = Attachment::where('ticket_id', $ticket->id)
150 ->whereIn('file_hash', $attachmentHashes)
151 ->where('status', 'in-active')
152 ->get();
153
154 if (!$attachments->isEmpty()) {
155 $storageDriver = Helper::getUploadDriverKey();
156 foreach ($attachments as $attachment) {
157 if ($storageDriver != 'local') {
158 do_action_ref_array('fluent_support/finalize_file_upload_' . $storageDriver, [&$attachment, $ticket->id]);
159 }
160
161 if ($attachment->driver == 'local') {
162 $newFileInfo = UploadService::copyFileTicketFolder($attachment->file_path, $ticket->id);
163 if ($newFileInfo && !empty($newFileInfo['file_path'])) {
164 $attachment->file_path = $newFileInfo['file_path'];
165 $attachment->full_url = $newFileInfo['url'];
166 }
167 }
168
169 $attachment->ticket_id = $ticket->id;
170 $attachment->person_id = $createdResponse->person_id;
171 $attachment->conversation_id = $createdResponse->id;
172 $attachment->status = 'active';
173 $attachment->save();
174 }
175 $createdResponse->load('attachments');
176 }
177 }
178
179 if ($silently) {
180 return [
181 'response' => $createdResponse,
182 'ticket' => $ticket,
183 'update_data' => $updateData
184 ];
185 }
186
187 if ($agentAdded) {
188 $assigner = Helper::getCurrentAgent();
189 $ticket->load('agent');
190
191 /*
192 * Action on ticket assign to an agent
193 *
194 * @since v1.0.0
195 * @param object $person
196 * @param object $ticket
197 * @param object $assigner
198 */
199 do_action('fluent_support/agent_assigned_to_ticket', $person, $ticket, $assigner, $previousAgentId);
200 $updateData['agent'] = $ticket->agent;
201 }
202
203 /*
204 * Action on conversation
205 *
206 * @since v1.0.0
207 * @param string $convoType
208 * @param string $personType
209 * @param object $createdResponse
210 * @param object $ticket
211 * @param object $person
212 */
213 do_action('fluent_support/' . $convoType . '_added_by_' . $person->person_type, $createdResponse, $ticket, $person);
214
215
216 if ($closed) {
217 /*
218 * Action on ticket close
219 *
220 * @since v1.0.0
221 * @param object $ticket
222 * @param object $person
223 */
224 do_action('fluent_support/ticket_closed', $ticket, $person);
225
226 /*
227 * Action on ticket close
228 *
229 * @since v1.0.0
230 * @param string $personType
231 * @param object $ticket
232 * @param object $person
233 */
234 do_action('fluent_support/ticket_closed_by_' . $person->person_type, $ticket, $person);
235 }
236
237 return [
238 'response' => $createdResponse,
239 'ticket' => $ticket,
240 'update_data' => $updateData
241 ];
242 }
243
244 public function maybeAppendSignature($content, $person)
245 {
246 if ($person->getMeta('agent_signature_enabled', 'no') !== 'yes') {
247 return $content;
248 }
249
250 $signature = $person->getMeta('agent_signature', '');
251 $hasText = !empty(trim(strip_tags($signature)));
252 $hasImage = (bool) preg_match('/<img[\s>]/i', $signature);
253 if (!$hasText && !$hasImage) {
254 return $content;
255 }
256
257 return $content . '<div class="fs_agent_signature">' . $signature . '</div>';
258 }
259 }
260