PluginProbe
Fluent Support – Helpdesk & Customer Support Ticket System / 2.4.5
Fluent Support – Helpdesk & Customer Support Ticket System v2.4.5
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
← All changes | app/Hooks/Handlers/EmailNotificationHandler.php +166 -21 1.10.0 → 2.4.5 View file →
@@ -2,13 +2,16 @@
2 2
3 3 namespace FluentSupport\App\Hooks\Handlers;
4 4
5 5 use FluentSupport\App\App;
6 +use FluentSupport\App\Models\Agent;
6 7 use FluentSupport\App\Models\Meta;
8 +use FluentSupport\App\Models\Ticket;
7 9 use FluentSupport\App\Services\EmailNotification\Settings;
8 10 use FluentSupport\App\Services\Emogrifier;
9 11 use FluentSupport\App\Services\Helper;
10 12 use FluentSupport\App\Services\Mailer;
13 +use FluentSupport\App\Services\Tickets\AgentTicketAccess;
11 14 use FluentSupport\Framework\Support\Arr;
12 15
13 16 class EmailNotificationHandler
14 17 {
@@ -22,9 +25,11 @@
22 25
23 26 // Let's send welcome email to customer if enabled
24 27 $emailSettings = (new Settings())->getBoxEmailSettings($mailbox, 'ticket_created_email_to_customer');
25 28
26 - if ($emailSettings && $emailSettings['status'] == 'yes') {
29 + // Blocked customers get no email; the admin copy below still goes out.
30 + if ($customer && $customer->canReceiveNotifications()
31 + && $this->shouldSendEmail($emailSettings, 'ticket_created_email_to_customer', $ticket, $customer)) {
27 32 $subject = apply_filters('fluent_support/parse_smartcode_data', $emailSettings['email_subject'], [
28 33 'customer' => $customer,
29 34 'business' => $mailbox,
30 35 'ticket' => $ticket
@@ -29,8 +34,10 @@
29 34 'business' => $mailbox,
30 35 'ticket' => $ticket
31 36 ]);
32 37
38 + $subject = apply_filters('fluent_support/ticket_email_subject', $subject, $ticket, 'ticket_created_email_to_customer');
39 +
33 40 $emailBody = $this->parseEmailBody($emailSettings['email_body'], [
34 41 'customer' => $customer,
35 42 'business' => $mailbox,
36 43 'ticket' => $ticket,
@@ -36,13 +43,14 @@
36 43 'ticket' => $ticket,
37 44 'email_type' => 'ticket_created_email_to_customer'
38 45 ]);
39 46
40 - $headers = $mailbox->getMailerHeader();
47 + $headers = self::automatedHeaders($mailbox->getMailerHeader());
41 48
42 49 $headers = apply_filters('fluent_support/mail_to_customer_header', $headers, [
43 50 'cc_email' => $ticket->getSettingsValue('cc_email', []),
44 - 'hook_type' => 'ticket_created_email_to_customer'
51 + 'hook_type' => 'ticket_created_email_to_customer',
52 + 'ticket' => $ticket
45 53 ]);
46 54
47 55 $attachments = [];
48 56
@@ -63,9 +71,11 @@
63 71 }
64 72
65 73 // let's send email to admin if enabled
66 74 $emailSettings = (new Settings())->getBoxEmailSettings($mailbox, 'ticket_created_email_to_admin');
67 - if ($emailSettings && $emailSettings['status'] == 'yes' && is_email($mailbox->settings['admin_email_address'])) {
75 + if ($this->shouldSendEmail($emailSettings, 'ticket_created_email_to_admin', $ticket, $customer)
76 + && is_email($mailbox->settings['admin_email_address'])
77 + ) {
68 78
69 79 $mailTo = Arr::get($mailbox->settings, 'admin_email_address');
70 80
71 81 if (!$mailTo || !is_email($mailTo)) {
@@ -77,8 +87,10 @@
77 87 'business' => $mailbox,
78 88 'ticket' => $ticket
79 89 ]);
80 90
91 + $subject = apply_filters('fluent_support/ticket_email_subject', $subject, $ticket, 'ticket_created_email_to_admin');
92 +
81 93 $emailBody = $this->parseEmailBody($emailSettings['email_body'], [
82 94 'customer' => $customer,
83 95 'business' => $mailbox,
84 96 'ticket' => $ticket,
@@ -84,9 +96,9 @@
84 96 'ticket' => $ticket,
85 97 'email_type' => 'ticket_created_email_to_admin'
86 98 ]);
87 99
88 - $headers = $mailbox->getMailerHeader();
100 + $headers = self::automatedHeaders($mailbox->getMailerHeader());
89 101
90 102 $attachments = [];
91 103
92 104 if ($emailSettings['send_attachments'] == 'yes' && ($files = $ticket->attachments)) {
@@ -125,12 +137,16 @@
125 137
126 138 $ticket->load('customer');
127 139 $customer = $ticket->customer;
128 140
129 - if ($customer->status == 'inactive') {
141 + if (!$customer->canReceiveNotifications()) {
130 142 return false;
131 143 }
132 144
145 + if (!$this->shouldSendEmail($emailSettings, 'ticket_replied_by_agent_email_to_customer', $ticket, $customer)) {
146 + return false;
147 + }
148 +
133 149 $subject = apply_filters('fluent_support/parse_smartcode_data', $emailSettings['email_subject'], [
134 150 'customer' => $customer,
135 151 'business' => $mailbox,
136 152 'ticket' => $ticket,
@@ -137,8 +153,10 @@
137 153 'response' => $response,
138 154 'agent' => $agent
139 155 ]);
140 156
157 + $subject = apply_filters('fluent_support/ticket_email_subject', $subject, $ticket, 'ticket_replied_by_agent_email_to_customer');
158 +
141 159 $emailBody = $this->parseEmailBody($emailSettings['email_body'], [
142 160 'customer' => $customer,
143 161 'business' => $mailbox,
144 162 'ticket' => $ticket,
@@ -147,12 +165,14 @@
147 165 'email_type' => 'ticket_replied_by_agent_email_to_customer'
148 166 ]);
149 167
150 168
151 - $headers = $mailbox->getMailerHeader();
169 + // Written by the agent, so the From name follows the inbox's From name setting.
170 + $headers = $mailbox->getMailerHeader($agent);
152 171 $headers = apply_filters('fluent_support/mail_to_customer_header', $headers, [
153 172 'cc_email' => $response->getSettingsValue('cc_email', []),
154 - 'hook_type' => 'ticket_replied_by_agent_email_to_customer'
173 + 'hook_type' => 'ticket_replied_by_agent_email_to_customer',
174 + 'ticket' => $ticket
155 175 ]);
156 176 $attachments = [];
157 177
158 178 if ($emailSettings['send_attachments'] == 'yes' && ($files = $response->attachments)) {
@@ -185,12 +205,15 @@
185 205
186 206 $ticket->load('customer');
187 207 $customer = $ticket->customer;
188 208
189 - if ($customer->status == 'inactive') {
209 + if (!$customer->canReceiveNotifications()) {
190 210 return false;
191 211 }
192 212
213 + if (!$this->shouldSendEmail($emailSettings, 'ticket_closed_by_agent_email_to_customer', $ticket, $customer)) {
214 + return false;
215 + }
193 216
194 217 $subject = apply_filters('fluent_support/parse_smartcode_data', $emailSettings['email_subject'], [
195 218 'customer' => $customer,
196 219 'business' => $mailbox,
@@ -197,8 +220,10 @@
197 220 'ticket' => $ticket,
198 221 'agent' => $agent,
199 222 ]);
200 223
224 + $subject = apply_filters('fluent_support/ticket_email_subject', $subject, $ticket, 'ticket_closed_by_agent_email_to_customer');
225 +
201 226 $emailBody = $this->parseEmailBody($emailSettings['email_body'], [
202 227 'customer' => $customer,
203 228 'business' => $mailbox,
204 229 'ticket' => $ticket,
@@ -205,12 +230,13 @@
205 230 'agent' => $agent,
206 231 'email_type' => 'ticket_closed_by_agent_email_to_customer'
207 232 ]);
208 233
209 - $headers = $mailbox->getMailerHeader();
234 + $headers = self::automatedHeaders($mailbox->getMailerHeader());
210 235 $headers = apply_filters('fluent_support/mail_to_customer_header', $headers, [
211 236 'cc_email' => $ticket->getSettingsValue('cc_email', []),
212 - 'hook_type' => 'ticket_closed_by_agent_email_to_customer'
237 + 'hook_type' => 'ticket_closed_by_agent_email_to_customer',
238 + 'ticket' => $ticket
213 239 ]);
214 240 Mailer::send($customer->email, $subject, $emailBody, $headers);
215 241 }
216 242 }
@@ -223,9 +249,9 @@
223 249 }
224 250
225 251 // Let's send welcome email to customer if enabled
226 252 $emailSettings = (new Settings())->getBoxEmailSettings($mailbox, 'ticket_replied_by_customer_email_to_admin');
227 - if ($emailSettings && $emailSettings['status'] == 'yes') {
253 + if ($this->shouldSendEmail($emailSettings, 'ticket_replied_by_customer_email_to_admin', $ticket, $customer)) {
228 254
229 255 $ticket->load('agent');
230 256 $agent = $ticket->agent;
231 257
@@ -245,8 +271,10 @@
245 271 'response' => $response,
246 272 'agent' => $agent
247 273 ]);
248 274
275 + $subject = apply_filters('fluent_support/ticket_email_subject', $subject, $ticket, 'ticket_replied_by_customer_email_to_admin');
276 +
249 277 $emailBody = $this->parseEmailBody($emailSettings['email_body'], [
250 278 'customer' => $customer,
251 279 'business' => $mailbox,
252 280 'ticket' => $ticket,
@@ -254,9 +282,9 @@
254 282 'agent' => $agent,
255 283 'email_type' => 'ticket_replied_by_customer_email_to_admin'
256 284 ]);
257 285
258 - $headers = $mailbox->getMailerHeader();
286 + $headers = self::automatedHeaders($mailbox->getMailerHeader());
259 287 $attachments = [];
260 288
261 289 if ($emailSettings['send_attachments'] == 'yes' && ($files = $response->attachments)) {
262 290 foreach ($files as $file) {
@@ -274,10 +302,18 @@
274 302 Mailer::send($emailTo, $subject, $emailBody, $headers, $attachments);
275 303 }
276 304 }
277 305
278 - public function onAgentAssign($agent, $ticket, $assigner)
306 + public function onAgentAssign(Agent $agent, Ticket $ticket, ?Agent $assigner = null)
279 307 {
308 + if ($agent->status !== 'active' || (int) $ticket->agent_id !== (int) $agent->id) {
309 + return;
310 + }
311 +
312 + if (!(new AgentTicketAccess())->canAccess($agent, $ticket)) {
313 + return;
314 + }
315 +
280 316 $currentUser = Helper::getAgentByUserId();
281 317
282 318 if ($currentUser && $currentUser->user_id == $agent->user_id) {
283 319 return;
@@ -291,9 +327,9 @@
291 327
292 328 // Let's send notification email to agent if enabled
293 329 $emailSettings = (new Settings())->getBoxEmailSettings($mailbox, 'ticket_agent_on_change');
294 330
295 - if ($emailSettings && $emailSettings['status'] == 'yes') {
331 + if ($this->shouldSendEmail($emailSettings, 'ticket_agent_on_change', $ticket, $agent)) {
296 332
297 333 if ($agent) {
298 334 $emailTo = $agent->email;
299 335 }
@@ -308,8 +344,10 @@
308 344 'agent' => $agent,
309 345 'assigner' => $assigner
310 346 ]);
311 347
348 + $subject = apply_filters('fluent_support/ticket_email_subject', $subject, $ticket, 'ticket_agent_on_change');
349 +
312 350 $emailBody = $this->parseEmailBody($emailSettings['email_body'], [
313 351 'business' => $mailbox,
314 352 'ticket' => $ticket,
315 353 'agent' => $agent,
@@ -316,9 +354,9 @@
316 354 'assigner' => $assigner,
317 355 'email_type' => 'ticket_agent_on_change'
318 356 ]);
319 357
320 - $headers = $mailbox->getMailerHeader();
358 + $headers = self::automatedHeaders($mailbox->getMailerHeader());
321 359
322 360 Mailer::send($emailTo, $subject, $emailBody, $headers);
323 361 }
324 362 }
@@ -326,15 +364,15 @@
326 364 public function ticketCreatedByAgent($ticket, $customer, $agent)
327 365 {
328 366 $mailbox = $ticket->mailbox;
329 367
330 - if (!$mailbox) {
368 + if (!$mailbox || !$customer || !$customer->canReceiveNotifications()) {
331 369 return;
332 370 }
333 371
334 372 // Let's send welcome email to customer if enabled
335 373 $emailSettings = (new Settings())->getBoxEmailSettings($mailbox, 'ticket_created_by_agent_email_to_customer');
336 - if ($emailSettings && $emailSettings['status'] == 'yes') {
374 + if ($this->shouldSendEmail($emailSettings, 'ticket_created_by_agent_email_to_customer', $ticket, $customer)) {
337 375 $subject = apply_filters('fluent_support/parse_smartcode_data', $emailSettings['email_subject'], [
338 376 'customer' => $customer,
339 377 'agent' => $agent,
340 378 'business' => $mailbox,
@@ -340,8 +378,10 @@
340 378 'business' => $mailbox,
341 379 'ticket' => $ticket
342 380 ]);
343 381
382 + $subject = apply_filters('fluent_support/ticket_email_subject', $subject, $ticket, 'ticket_created_by_agent_email_to_customer');
383 +
344 384 $emailBody = $this->parseEmailBody($emailSettings['email_body'], [
345 385 'customer' => $customer,
346 386 'business' => $mailbox,
347 387 'agent' => $agent,
@@ -348,9 +388,13 @@
348 388 'ticket' => $ticket,
349 389 'email_type' => 'ticket_created_by_agent_email_to_customer'
350 390 ]);
351 391
352 - $headers = $mailbox->getMailerHeader();
392 + $headers = apply_filters('fluent_support/mail_to_customer_header', self::automatedHeaders($mailbox->getMailerHeader()), [
393 + 'hook_type' => 'ticket_created_by_agent_email_to_customer',
394 + 'cc_email' => [],
395 + 'ticket' => $ticket
396 + ]);
353 397
354 398 $attachments = [];
355 399
356 400 if ($emailSettings['send_attachments'] == 'yes' && ($files = $ticket->attachments)) {
@@ -369,8 +413,78 @@
369 413 Mailer::send($customer->email, $subject, $emailBody, $headers, $attachments);
370 414 }
371 415 }
372 416
417 + public function ticketCreatedByAgentOnBehalf($response, $ticket, $agent)
418 + {
419 + $mailbox = $ticket->mailbox;
420 +
421 + if (!$mailbox) {
422 + return;
423 + }
424 +
425 + $emailSettings = (new Settings())->getBoxEmailSettings($mailbox, 'ticket_created_by_agent_on_behalf_email_to_customer');
426 + if ($this->shouldSendEmail($emailSettings, 'ticket_created_by_agent_on_behalf_email_to_customer', $ticket)) {
427 +
428 + $ticket->load('customer');
429 + $customer = $ticket->customer;
430 +
431 + if (!$customer->canReceiveNotifications()) {
432 + return false;
433 + }
434 +
435 + $subject = apply_filters('fluent_support/parse_smartcode_data', $emailSettings['email_subject'], [
436 + 'customer' => $customer,
437 + 'agent' => $agent,
438 + 'business' => $mailbox,
439 + 'ticket' => $ticket,
440 + 'response' => $response
441 + ]);
442 +
443 + $subject = apply_filters('fluent_support/ticket_email_subject', $subject, $ticket, 'ticket_created_by_agent_on_behalf_email_to_customer');
444 +
445 + $emailBody = $this->parseEmailBody($emailSettings['email_body'], [
446 + 'customer' => $customer,
447 + 'business' => $mailbox,
448 + 'agent' => $agent,
449 + 'ticket' => $ticket,
450 + 'response' => $response,
451 + 'email_type' => 'ticket_created_by_agent_on_behalf_email_to_customer'
452 + ]);
453 +
454 + $headers = apply_filters('fluent_support/mail_to_customer_header', $mailbox->getMailerHeader($agent), [
455 + 'hook_type' => 'ticket_created_by_agent_on_behalf_email_to_customer',
456 + 'cc_email' => [],
457 + 'ticket' => $ticket
458 + ]);
459 +
460 + $attachments = [];
461 +
462 + if ($emailSettings['send_attachments'] == 'yes') {
463 + $files = $response->attachments;
464 +
465 + // Agent-initiated ticket uploads are stored on the ticket, while this email
466 + // is rendered from the synthetic first response created during ticket creation.
467 + if ((!$files || $files->isEmpty()) && $ticket->attachments) {
468 + $files = $ticket->attachments;
469 + }
470 +
471 + foreach ($files as $file) {
472 + if ($file->driver == 'local') {
473 + $filePath = $file->file_path;
474 + } else {
475 + $filePath = Arr::get($file->settings, 'local_temp_path');
476 + }
477 + if (file_exists($filePath)) {
478 + $attachments[] = $filePath;
479 + }
480 + }
481 + }
482 +
483 + Mailer::send($customer->email, $subject, $emailBody, $headers, $attachments);
484 + }
485 + }
486 +
373 487 private function emailTemplateCss()
374 488 {
375 489 $app = App::getInstance();
376 490 return $app->view->make('emails.styles');
@@ -387,13 +501,14 @@
387 501 } else {
388 502 $businessName = get_bloginfo('name');
389 503 }
390 504
391 - $footerText = apply_filters('fluent_support/email_footer_credit', 'This email is a service from ' . $businessName . '. Support Plugin is Powered by <a href="https://fluentsupport.com/?utm_source=user&utm_medium=wp&utm_campaign=mail_footer" style="color:#9e9e9e;" target="_new">FluentSupport</a>.');
505 + $footerCreditUrl = Helper::getUpgradeUrl('mail_footer', ['base_url' => 'https://fluentsupport.com/']);
506 + $footerText = apply_filters('fluent_support/email_footer_credit', 'This email is a service from ' . $businessName . '. Support Plugin is Powered by <a href="' . esc_url($footerCreditUrl) . '" style="color:#9e9e9e;" target="_new">FluentSupport</a>.');
392 507
393 508 $data['email_footer'] = $footerText;
394 509
395 - $emailTypeForCustomerFooter = ['ticket_created_email_to_customer', 'ticket_replied_by_agent_email_to_customer', 'ticket_closed_by_agent_email_to_customer'];
510 + $emailTypeForCustomerFooter = ['ticket_created_email_to_customer', 'ticket_replied_by_agent_email_to_customer', 'ticket_closed_by_agent_email_to_customer', 'ticket_created_by_agent_on_behalf_email_to_customer'];
396 511
397 512 if (defined('FLUENTSUPPORTPRO') && in_array($data['email_type'], $emailTypeForCustomerFooter)
398 513 && !is_null($data['business']->email_footer)) {
399 514 $data['email_footer'] = apply_filters('fluent_support/parse_smartcode_data', $data['business']->email_footer, $data);
@@ -402,8 +517,38 @@
402 517 $emailBody = $app->view->make('emails.ticket_template', $data);
403 518 $emogrifier = new Emogrifier($emailBody, $this->emailTemplateCss());
404 519 $emogrifier->disableInvisibleNodeRemoval();
405 520 return $emogrifier->emogrify();
521 + }
522 +
523 + protected function shouldSendEmail($emailSettings, $emailType, $ticket, $person = null)
524 + {
525 + if (!$emailSettings || $emailSettings['status'] != 'yes') {
526 + return false;
527 + }
528 +
529 + return apply_filters('fluent_support/should_send_notification', true, 'email', $emailType, $ticket, $person);
530 + }
531 +
532 + /**
533 + * Mark a system-generated email (confirmations, closed notices, agent alerts) so
534 + * autoresponders don't answer it (RFC 3834 Auto-Submitted, and Outlook's
535 + * X-Auto-Response-Suppress). An answer would come back through email piping and
536 + * could start a mail loop. Replies an agent writes are not marked.
537 + *
538 + * @param array $headers
539 + * @return array
540 + */
541 + public static function automatedHeaders($headers)
542 + {
543 + if (!apply_filters('fluent_support/mark_automated_emails', true)) {
544 + return $headers;
545 + }
546 +
547 + $headers[] = 'Auto-Submitted: auto-generated';
548 + $headers[] = 'X-Auto-Response-Suppress: All';
549 +
550 + return $headers;
406 551 }
407 552
408 553 public function getMailerHeaderWithCc($headers, $info)
409 554 {