Test Failed
Push — master ( 9b3943...8924b8 )
by Mathieu
02:13
created

Email::logEnabled()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 7
rs 10
c 0
b 0
f 0
cc 2
nc 2
nop 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Charcoal\Email;
6
7
use Exception;
8
use InvalidArgumentException;
9
10
// From 'psr/log' (PSR-3)
11
use Psr\Log\LoggerAwareInterface;
12
use Psr\Log\LoggerAwareTrait;
13
14
// From 'phpmailer/phpmailer'
15
use PHPMailer\PHPMailer\PHPMailer;
16
17
// From 'locomotivemtl/charcoal-config'
18
use Charcoal\Config\AbstractEntity;
19
use Charcoal\Config\ConfigurableInterface;
20
use Charcoal\Config\ConfigurableTrait;
21
22
// From 'locomotivemtl/charcoal-factory'
23
use Charcoal\Factory\FactoryInterface;
24
25
// From 'locomotivemtl/charcoal-view'
26
use Charcoal\View\GenericView;
27
use Charcoal\View\ViewableInterface;
28
use Charcoal\View\ViewableTrait;
29
30
// From 'locomotivemtl/charcoal-queue'
31
use Charcoal\Queue\QueueableInterface;
32
use Charcoal\Queue\QueueableTrait;
33
34
/**
35
 * Default implementation of the `EmailInterface`.
36
 */
37
class Email extends AbstractEntity implements
38
    ConfigurableInterface,
39
    EmailInterface,
40
    LoggerAwareInterface,
41
    QueueableInterface,
42
    ViewableInterface
43
{
44
    use ConfigurableTrait;
45
    use LoggerAwareTrait;
46
    use QueueableTrait;
47
    use ViewableTrait;
48
    use EmailAwareTrait;
49
50
    /**
51
     * The campaign ID.
52
     *
53
     * @var string
54
     */
55
    private $campaign;
56
57
    /**
58
     * The recipient email address(es).
59
     *
60
     * @var array
61
     */
62
    private $to = [];
63
64
    /**
65
     * The CC recipient email address(es).
66
     *
67
     * @var array
68
     */
69
    private $cc = [];
70
71
    /**
72
     * The BCC recipient email address(es).
73
     *
74
     * @var array
75
     */
76
    private $bcc = [];
77
78
    /**
79
     * The sender's email address.
80
     *
81
     * @var string
82
     */
83
    private $from;
84
85
    /**
86
     * The email address to reply to the message.
87
     *
88
     * @var string
89
     */
90
    private $replyTo;
91
92
    /**
93
     * The email subject.
94
     *
95
     * @var string
96
     */
97
    private $subject;
98
99
    /**
100
     * The HTML message body.
101
     *
102
     * @var string
103
     */
104
    private $msgHtml;
105
106
    /**
107
     * The plain-text message body.
108
     *
109
     * @var string
110
     */
111
    private $msgTxt;
112
113
    /**
114
     * @var array
115
     */
116
    private $attachments = [];
117
118
    /**
119
     * Whether the email should be logged.
120
     *
121
     * @var boolean
122
     */
123
    private $logEnabled;
124
125
    /**
126
     * Whether the email should be tracked.
127
     *
128
     * @var boolean
129
     */
130
    private $trackOpenEnabled;
131
132
    /**
133
     * @var boolean
134
     */
135
    private $trackLinksEnabled;
136
137
    /**
138
     * The data to pass onto the view controller.
139
     *
140
     * @var array
141
     */
142
    private $templateData = [];
143
144
    /**
145
     * @var PHPMailer
146
     */
147
    private $phpMailer;
148
149
    /**
150
     * @var FactoryInterface
151
     */
152
    private $templateFactory;
153
154
    /**
155
     * @var FactoryInterface
156
     */
157
    private $queueItemFactory;
158
159
    /**
160
     * @var FactoryInterface
161
     */
162
    private $logFactory;
163
164
    /**
165
     * Construct a new Email object with the given dependencies.
166
     *
167
     * - `logger` a PSR-3 logger.
168
     * - `view` a charcoal view for template rendering.
169
     * - `config` a charcoal config containing email settings.
170
     * - `template_factory` a charcoal model factory to create templates.
171
     * - `queue_item_factory` a charcoal model factory to create queue item.
172
     * - `log_factory` a charcoal model factory to create email logs.
173
     *
174
     * @param array $data Dependencies and settings.
175
     */
176
    public function __construct(array $data)
177
    {
178
        $this->phpMailer = new PHPMailer(true);
179
        $this->setLogger($data['logger']);
180
        $this->setView($data['view']);
181
        $this->setConfig($data['config']);
182
        $this->setTemplateFactory($data['template_factory']);
183
        $this->setQueueItemFactory($data['queue_item_factory']);
184
        $this->setLogFactory($data['log_factory']);
185
    }
186
187
    /**
188
     * Set the campaign ID.
189
     *
190
     * @param  string $campaign The campaign identifier.
191
     * @return self
192
     */
193
    public function setCampaign(string $campaign)
194
    {
195
        $this->campaign = $campaign;
196
        return $this;
197
    }
198
199
    /**
200
     * Get the campaign identifier.
201
     *
202
     * If it has not been explicitely set, it will be auto-generated (with uniqid).
203
     *
204
     * @return string
205
     */
206
    public function campaign()
207
    {
208
        if ($this->campaign === null) {
209
            $this->campaign = $this->generateCampaign();
210
        }
211
        return $this->campaign;
212
    }
213
214
    /**
215
     * Set the recipient email address(es).
216
     *
217
     * @param string|array $email The recipient email address(es).
218
     * @throws InvalidArgumentException If the email address is invalid.
219
     * @return self
220
     */
221 View Code Duplication
    public function setTo($email)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
222
    {
223
        if (is_string($email)) {
224
            $email = [ $email ];
225
        }
226
227
        if (!is_array($email)) {
228
            throw new InvalidArgumentException(
229
                'Must be an array of recipients.'
230
            );
231
        }
232
233
        $this->to = [];
234
235
        // At this point, `$email` can be an _email array_ or an _array of emails_...
236
        if (isset($email['email'])) {
237
            // Means we're not dealing with multiple emails
238
            $this->addTo($email);
239
        } else {
240
            foreach ($email as $recipient) {
241
                $this->addTo($recipient);
242
            }
243
        }
244
245
        return $this;
246
    }
247
248
    /**
249
     * Add a recipient email address.
250
     *
251
     * @param  mixed $email The recipient email address to add.
252
     * @throws InvalidArgumentException If the email address is invalid.
253
     * @return self
254
     */
255
    public function addTo($email)
256
    {
257
        $this->to[] = $this->parseEmail($email);
258
        return $this;
259
    }
260
261
    /**
262
     * Get the recipient's email addresses.
263
     *
264
     * @return string[]
265
     */
266
    public function to()
267
    {
268
        return $this->to;
269
    }
270
271
    /**
272
     * Set the carbon copy (CC) recipient email address(es).
273
     *
274
     * @param string|array $email The CC recipient email address(es).
275
     * @throws InvalidArgumentException If the email address is invalid.
276
     * @return self
277
     */
278 View Code Duplication
    public function setCc($email)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
279
    {
280
        if (is_string($email)) {
281
            $email = [ $email ];
282
        }
283
284
        if (!is_array($email)) {
285
            throw new InvalidArgumentException(
286
                'Must be an array of CC recipients.'
287
            );
288
        }
289
290
        $this->cc = [];
291
292
        // At this point, `$email` can be an _email array_ or an _array of emails_...
293
        if (isset($email['email'])) {
294
            // Means we're not dealing with multiple emails
295
            $this->addCc($email);
296
        } else {
297
            foreach ($email as $recipient) {
298
                $this->addCc($recipient);
299
            }
300
        }
301
302
        return $this;
303
    }
304
305
    /**
306
     * Add a CC recipient email address.
307
     *
308
     * @param mixed $email The CC recipient email address to add.
309
     * @throws InvalidArgumentException If the email address is invalid.
310
     * @return self
311
     */
312
    public function addCc($email)
313
    {
314
        $this->cc[] = $this->parseEmail($email);
315
        return $this;
316
    }
317
318
    /**
319
     * Get the CC recipient's email address.
320
     *
321
     * @return string[]
322
     */
323
    public function cc()
324
    {
325
        return $this->cc;
326
    }
327
328
    /**
329
     * Set the blind carbon copy (BCC) recipient email address(es).
330
     *
331
     * @param string|array $email The BCC recipient email address(es).
332
     * @throws InvalidArgumentException If the email address is invalid.
333
     * @return self
334
     */
335 View Code Duplication
    public function setBcc($email)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
336
    {
337
        if (is_string($email)) {
338
            // Means we have a straight email
339
            $email = [ $email ];
340
        }
341
342
        if (!is_array($email)) {
343
            throw new InvalidArgumentException(
344
                'Must be an array of BCC recipients.'
345
            );
346
        }
347
348
        $this->bcc = [];
349
350
        // At this point, `$email` can be an _email array_ or an _array of emails_...
351
        if (isset($email['email'])) {
352
            // Means we're not dealing with multiple emails
353
            $this->addBcc($email);
354
        } else {
355
            foreach ($email as $recipient) {
356
                $this->addBcc($recipient);
357
            }
358
        }
359
360
        return $this;
361
    }
362
363
    /**
364
     * Add a BCC recipient email address.
365
     *
366
     * @param mixed $email The BCC recipient email address to add.
367
     * @throws InvalidArgumentException If the email address is invalid.
368
     * @return self
369
     */
370
    public function addBcc($email)
371
    {
372
        $this->bcc[] = $this->parseEmail($email);
373
        return $this;
374
    }
375
376
    /**
377
     * Get the BCC recipient's email address.
378
     *
379
     * @return string[]
380
     */
381
    public function bcc()
382
    {
383
        return $this->bcc;
384
    }
385
386
    /**
387
     * Set the sender's email address.
388
     *
389
     * @param  string|array $email An email address.
390
     * @throws InvalidArgumentException If the email is not a string or an array.
391
     * @return self
392
     * @todo   Implement optional "Sender" field.
393
     */
394
    public function setFrom($email)
395
    {
396
        $this->from = $this->parseEmail($email);
397
        return $this;
398
    }
399
400
    /**
401
     * Get the sender's email address.
402
     *
403
     * @return string
404
     */
405
    public function from()
406
    {
407
        if ($this->from === null) {
408
            $this->setFrom($this->config()->defaultFrom());
409
        }
410
        return $this->from;
411
    }
412
413
    /**
414
     * Set email address to reply to the message.
415
     *
416
     * @param  mixed $email The sender's "Reply-To" email address.
417
     * @throws InvalidArgumentException If the email is not a string or an array.
418
     * @return self
419
     */
420
    public function setReplyTo($email)
421
    {
422
        $this->replyTo = $this->parseEmail($email);
423
        return $this;
424
    }
425
426
    /**
427
     * Get email address to reply to the message.
428
     *
429
     * @return string
430
     */
431
    public function replyTo()
432
    {
433
        if ($this->replyTo === null) {
434
            $this->replyTo = $this->config()->defaultReplyTo();
435
        }
436
        return $this->replyTo;
437
    }
438
439
    /**
440
     * Set the email subject.
441
     *
442
     * @param  string $subject The email subject.
443
     * @return self
444
     */
445
    public function setSubject(string $subject)
446
    {
447
        $this->subject = $subject;
448
        return $this;
449
    }
450
451
    /**
452
     * Get the email subject.
453
     *
454
     * @return string The emails' subject.
455
     */
456
    public function subject(): string
457
    {
458
        return $this->subject;
459
    }
460
461
    /**
462
     * Set the email's HTML message body.
463
     *
464
     * @param  string $body The HTML message body.
465
     * @return self
466
     */
467
    public function setMsgHtml(string $body)
468
    {
469
        $this->msgHtml = $body;
470
        return $this;
471
    }
472
473
    /**
474
     * Get the email's HTML message body.
475
     *
476
     * If the message is not explitely set, it will be
477
     * auto-generated from a template view.
478
     *
479
     * @return string
480
     */
481
    public function msgHtml(): string
482
    {
483
        if ($this->msgHtml === null) {
484
            $this->msgHtml = $this->generateMsgHtml();
485
        }
486
        return $this->msgHtml;
487
    }
488
489
    /**
490
     * Set the email's plain-text message body.
491
     *
492
     * @param string $body The message's text body.
493
     * @return self
494
     */
495
    public function setMsgTxt(string $body)
496
    {
497
        $this->msgTxt = $body;
498
        return $this;
499
    }
500
501
    /**
502
     * Get the email's plain-text message body.
503
     *
504
     * If the plain-text message is not explitely set,
505
     * it will be auto-generated from the HTML message.
506
     *
507
     * @return string
508
     */
509
    public function msgTxt(): string
510
    {
511
        if ($this->msgTxt === null) {
512
            $this->msgTxt = $this->stripHtml($this->msgHtml());
513
        }
514
        return $this->msgTxt;
515
    }
516
517
    /**
518
     * Set the email's attachments.
519
     *
520
     * @param  array $attachments The file attachments.
521
     * @return self
522
     */
523
    public function setAttachments(array $attachments)
524
    {
525
        foreach ($attachments as $att) {
526
            $this->addAttachment($att);
527
        }
528
        return $this;
529
    }
530
531
    /**
532
     * Add an attachment to the email.
533
     *
534
     * @param  mixed $attachment A single file attachment.
535
     * @return self
536
     */
537
    public function addAttachment($attachment)
538
    {
539
        $this->attachments[] = $attachment;
540
        return $this;
541
    }
542
543
    /**
544
     * Get the email's attachments.
545
     *
546
     * @return array
547
     */
548
    public function attachments()
549
    {
550
        return $this->attachments;
551
    }
552
553
    /**
554
     * Enable or disable logging for this particular email.
555
     *
556
     * @param  boolean $log The log-enabled flag.
557
     * @return self
558
     */
559
    public function setLogEnabled($log)
560
    {
561
        $this->logEnabled = !!$log;
562
        return $this;
563
    }
564
565
    /**
566
     * Determine if logging is enabled for this particular email.
567
     *
568
     * @return boolean
569
     */
570
    public function logEnabled(): bool
571
    {
572
        if ($this->logEnabled === null) {
573
            $this->logEnabled = $this->config()->defaultLogEnabled();
574
        }
575
        return $this->logEnabled;
576
    }
577
578
    /**
579
     * Enable or disable email open tracking for this particular email.
580
     *
581
     * @param boolean $track The track flag.
582
     * @return self
583
     */
584
    public function setTrackOpenEnabled($track)
585
    {
586
        $this->trackOpenEnabled = !!$track;
587
        return $this;
588
    }
589
590
    /**
591
     * Determine if email open tracking is enabled for this particular email.
592
     *
593
     * @return boolean
594
     */
595
    public function trackOpenEnabled(): bool
596
    {
597
        if ($this->trackOpenEnabled === null) {
598
            $this->trackOpenEnabled = $this->config()->defaultTrackOpenEnabled();
599
        }
600
        return $this->trackOpenEnabled;
601
    }
602
603
    /**
604
     * Enable or disable email links tracking for this particular email.
605
     *
606
     * @param boolean $track The track flag.
607
     * @return self
608
     */
609
    public function setTrackLinksEnabled($track)
610
    {
611
        $this->trackLinksEnabled = !!$track;
612
        return $this;
613
    }
614
615
    /**
616
     * Determine if email links tracking is enabled for this particular email.
617
     *
618
     * @return boolean
619
     */
620
    public function trackLinksEnabled(): bool
621
    {
622
        if ($this->trackLinksEnabled === null) {
623
            $this->trackLinksEnabled = $this->config()->defaultTrackLinksEnabled();
624
        }
625
        return $this->trackLinksEnabled;
626
    }
627
628
629
    /**
630
     * Send the email to all recipients
631
     *
632
     * @return boolean Success / Failure.
633
     * @todo Implement methods and property for toggling rich-text vs. plain-text
634
     *       emails (`$mail->isHTML(true)`).
635
     */
636
    public function send(): bool
637
    {
638
        $this->logger->debug(
639
            'Attempting to send an email',
640
            $this->to()
641
        );
642
643
        $mail = $this->phpMailer;
644
645
        try {
646
            $this->setSmtpOptions($mail);
647
648
            $mail->CharSet = 'UTF-8';
649
650
            // Setting reply-to field, if required.
651
            $replyTo = $this->replyTo();
652
            if ($replyTo) {
653
                $replyArr = $this->emailToArray($replyTo);
654
                $mail->addReplyTo($replyArr['email'], $replyArr['name']);
655
            }
656
657
            // Setting from (sender) field.
658
            $from = $this->from();
659
            $fromArr = $this->emailToArray($from);
660
            $mail->setFrom($fromArr['email'], $fromArr['name']);
661
662
            // Setting to (recipients) field(s).
663
            $to = $this->to();
664
            foreach ($to as $recipient) {
665
                $toArr = $this->emailToArray($recipient);
666
                $mail->addAddress($toArr['email'], $toArr['name']);
667
            }
668
669
            // Setting cc (carbon-copy) field(s).
670
            $cc = $this->cc();
671
            foreach ($cc as $ccRecipient) {
672
                $ccArr = $this->emailToArray($ccRecipient);
673
                $mail->addCC($ccArr['email'], $ccArr['name']);
674
            }
675
676
            // Setting bcc (black-carbon-copy) field(s).
677
            $bcc = $this->bcc();
678
            foreach ($bcc as $bccRecipient) {
679
                $bccArr = $this->emailToArray($bccRecipient);
680
                $mail->addBCC($bccArr['email'], $bccArr['name']);
681
            }
682
683
            // Setting attachment(s), if required.
684
            $attachments = $this->attachments();
685
            foreach ($attachments as $att) {
686
                $mail->addAttachment($att);
687
            }
688
689
            $mail->isHTML(true);
690
691
            $mail->Subject = $this->subject();
692
            $mail->Body    = $this->msgHtml();
693
            $mail->AltBody = $this->msgTxt();
694
695
            $ret = $mail->send();
696
697
            $this->logSend($ret, $mail);
698
        } catch (Exception $e) {
699
            $ret = false;
700
            $this->logger->error(
701
                sprintf('Error sending email: %s', $e->getMessage())
702
            );
703
        }
704
705
        return $ret;
706
    }
707
708
    /**
709
     * Set the SMTP's options for PHPMailer.
710
     *
711
     * @param PHPMailer $mail The PHPMailer to setup.
712
     * @return void
713
     */
714
    protected function setSmtpOptions(PHPMailer $mail)
715
    {
716
        $config = $this->config();
717
        if (!$config['smtp']) {
718
            return;
719
        }
720
721
        $this->logger->debug(
722
            sprintf('Using SMTP "%s" server to send email', $config['smtp_hostname'])
723
        );
724
725
        $mail->isSMTP();
726
        $mail->Host       = $config['smtp_hostname'];
727
        $mail->Port       = $config['smtp_port'];
728
        $mail->SMTPAuth   = $config['smtp_auth'];
729
        $mail->Username   = $config['smtp_username'];
730
        $mail->Password   = $config['smtp_password'];
731
        $mail->SMTPSecure = $config['smtp_security'];
732
    }
733
734
    /**
735
     * Enqueue the email for each recipient.
736
     *
737
     * @param mixed $ts A date/time to initiate the queue processing.
738
     * @return self
739
     */
740
    public function queue($ts = null)
741
    {
742
        $recipients = $this->to();
743
        $author     = $this->from();
744
        $subject    = $this->subject();
745
        $msgHtml    = $this->msgHtml();
746
        $msgTxt     = $this->msgTxt();
747
        $campaign   = $this->campaign();
748
        $queueId    = $this->queueId();
749
750
        foreach ($recipients as $to) {
751
            if (is_string($to) && !empty($to)) {
752
                $queueItem = $this->queueItemFactory()->create(EmailQueueItem::class);
753
754
                $queueItem->setTo($to);
755
                $queueItem->setFrom($author);
756
                $queueItem->setSubject($subject);
757
                $queueItem->setMsgHtml($msgHtml);
758
                $queueItem->setMsgTxt($msgTxt);
759
                $queueItem->setCampaign($campaign);
760
                $queueItem->setProcessingDate($ts);
761
                $queueItem->setQueueId($queueId);
762
763
                $res = $queueItem->save();
0 ignored issues
show
Unused Code introduced by
$res is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
764
            } else {
765
                $this->logger->warning('Could not queue email, null or empty value');
766
            }
767
        }
768
769
        return $this;
770
    }
771
772
    /**
773
     * Set the template data for the view.
774
     *
775
     * @param array $data The template data.
776
     * @return Email Chainable
777
     */
778
    public function setTemplateData(array $data)
779
    {
780
        $this->templateData = $data;
781
        return $this;
782
    }
783
784
    /**
785
     * Get the template data for the view.
786
     *
787
     * @return array
788
     */
789
    public function templateData(): array
790
    {
791
        return $this->templateData;
792
    }
793
794
    /**
795
     * Get the custom view controller for rendering
796
     * the email's HTML message.
797
     *
798
     * Unlike typical `ViewableInterface` objects, the view controller is not
799
     * the email itself but an external "email" template.
800
     *
801
     * @see    ViewableInterface::viewController()
802
     * @return \Charcoal\App\Template\TemplateInterface|array
803
     */
804
    public function viewController()
805
    {
806
        $templateIdent = $this->templateIdent();
807
808
        if (!$templateIdent) {
809
            return [];
810
        }
811
812
        $templateFactory = clone($this->templateFactory());
813
        $templateFactory->setDefaultClass(GenericEmailTemplate::class);
0 ignored issues
show
Bug introduced by
The method setDefaultClass() does not exist on Charcoal\Factory\FactoryInterface. Did you maybe mean defaultClass()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
814
        $template = $templateFactory->create($templateIdent);
815
816
        $template->setData($this->templateData());
817
818
        return $template;
819
    }
820
821
    /**
822
     * @param FactoryInterface $factory The factory to use to create email template objects.
823
     * @return Email Chainable
824
     */
825
    protected function setTemplateFactory(FactoryInterface $factory)
826
    {
827
        $this->templateFactory = $factory;
828
        return $this;
829
    }
830
831
    /**
832
     * @return FactoryInterface
833
     */
834
    protected function templateFactory(): FactoryInterface
835
    {
836
        return $this->templateFactory;
837
    }
838
839
    /**
840
     * @param FactoryInterface $factory The factory to use to create email queue item objects.
841
     * @return Email Chainable
842
     */
843
    protected function setQueueItemFactory(FactoryInterface $factory)
844
    {
845
        $this->queueItemFactory = $factory;
846
        return $this;
847
    }
848
849
    /**
850
     * @return FactoryInterface
851
     */
852
    protected function queueItemFactory(): FactoryInterface
853
    {
854
        return $this->queueItemFactory;
855
    }
856
857
    /**
858
     * @param FactoryInterface $factory The factory to use to create log objects.
859
     * @return Email Chainable
860
     */
861
    protected function setLogFactory(FactoryInterface $factory)
862
    {
863
        $this->logFactory = $factory;
864
        return $this;
865
    }
866
867
    /**
868
     * @return FactoryInterface
869
     */
870
    protected function logFactory(): FactoryInterface
871
    {
872
        return $this->logFactory;
873
    }
874
875
    /**
876
     * Get the email's HTML message from the template, if applicable.
877
     *
878
     * @see    ViewableInterface::renderTemplate()
879
     * @return string
880
     */
881
    protected function generateMsgHtml(): string
882
    {
883
        $templateIdent = $this->templateIdent();
884
885
        if (!$templateIdent) {
886
            $message = '';
887
        } else {
888
            $message = $this->renderTemplate($templateIdent);
889
        }
890
891
        return $message;
892
    }
893
894
    /**
895
     * Generates a unique identifier ideal for a campaign ID.
896
     *
897
     * @return string
898
     */
899
    protected function generateCampaign(): string
900
    {
901
        return uniqid();
902
    }
903
904
    /**
905
     * Allow an object to define how the key getter are called.
906
     *
907
     * @param string $key The key to get the getter from.
908
     * @return string The getter method name, for a given key.
909
     */
910
    protected function getter(string $key): string
911
    {
912
        $getter = $key;
913
        return $this->camelize($getter);
914
    }
915
916
    /**
917
     * Allow an object to define how the key setter are called.
918
     *
919
     * @param string $key The key to get the setter from.
920
     * @return string The setter method name, for a given key.
921
     */
922
    protected function setter(string $key): string
923
    {
924
        $setter = 'set_'.$key;
925
        return $this->camelize($setter);
926
    }
927
928
    /**
929
     * Convert an HTML string to plain-text.
930
     *
931
     * @param string $html The HTML string to convert.
932
     * @return string The resulting plain-text string.
933
     */
934
    protected function stripHtml(string $html): string
935
    {
936
        $str = html_entity_decode($html);
937
938
        // Strip HTML (Replace br with newline, remove "invisible" tags and strip other tags)
939
        $str = preg_replace('#<br[^>]*?>#siu', "\n", $str);
940
        $str = preg_replace(
941
            [
942
                '#<applet[^>]*?.*?</applet>#siu',
943
                '#<embed[^>]*?.*?</embed>#siu',
944
                '#<head[^>]*?>.*?</head>#siu',
945
                '#<noframes[^>]*?.*?</noframes>#siu',
946
                '#<noscript[^>]*?.*?</noscript>#siu',
947
                '#<noembed[^>]*?.*?</noembed>#siu',
948
                '#<object[^>]*?.*?</object>#siu',
949
                '#<script[^>]*?.*?</script>#siu',
950
                '#<style[^>]*?>.*?</style>#siu'
951
            ],
952
            '',
953
            $str
954
        );
955
        $str = strip_tags($str);
956
957
        // Trim whitespace
958
        $str = str_replace("\t", '', $str);
959
        $str = preg_replace('#\n\r|\r\n#', "\n", $str);
960
        $str = preg_replace('#\n{3,}#', "\n\n", $str);
961
        $str = preg_replace('/ {2,}/', ' ', $str);
962
        $str = implode("\n", array_map('trim', explode("\n", $str)));
963
        $str = trim($str)."\n";
964
        return $str;
965
    }
966
967
    /**
968
     * Log the send event for each recipient.
969
     *
970
     * @param  boolean   $result Success or failure.
971
     * @param  PHPMailer $mailer The raw mailer.
972
     * @return void
973
     */
974
    protected function logSend(bool $result, PHPMailer $mailer): void
975
    {
976
        if ($this->logEnabled() === false) {
977
            return;
978
        }
979
980
        if (!$result) {
981
            $this->logger->error('Email could not be sent.');
982
        } else {
983
            $this->logger->debug(
984
                sprintf('Email "%s" sent successfully.', $this->subject()),
985
                $this->to()
986
            );
987
        }
988
989
        $recipients = array_merge(
990
            $this->to(),
991
            $this->cc(),
992
            $this->bcc()
993
        );
994
995
        foreach ($recipients as $to) {
996
            $log = $this->logFactory()->create(EmailLog::class);
997
998
            $log->setQueueId($this->queueId());
999
            $log->setMessageId($mailer->getLastMessageId());
1000
            $log->setCampaign($this->campaign());
1001
            $log->setSendTs('now');
1002
            $log->setFrom($mailer->From);
1003
            $log->setTo($to);
1004
            $log->setSubject($this->subject());
1005
1006
            if (!empty($mailer->getSMTPInstance()->getError()['smtp_code'])) {
1007
                $log->setErrorCode($mailer->getSMTPInstance()->getError()['smtp_code']);
1008
            }
1009
1010
            $log->save();
1011
        }
1012
    }
1013
1014
    /**
1015
     * Temporary hack to fulfills the Configurable Interface.
1016
     *
1017
     * @return EmailConfig
1018
     */
1019
    public function createConfig()
1020
    {
1021
        // This should really be avoided.
1022
        $this->logger->warning('AbstractEmail::createConfig() was called, but should not.');
1023
        return new \Charcoal\Email\EmailConfig();
1024
    }
1025
}
1026