Passed
Pull Request — 4.9 (#10236)
by Steve
16:16
created

Email::setSubject()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 5
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace SilverStripe\Control\Email;
4
5
use DateTime;
6
use Egulias\EmailValidator\EmailValidator;
7
use Egulias\EmailValidator\Validation\RFCValidation;
8
use SilverStripe\Control\Director;
9
use SilverStripe\Control\HTTP;
10
use SilverStripe\Core\Convert;
11
use SilverStripe\Core\Environment;
12
use SilverStripe\Core\Injector\Injector;
13
use SilverStripe\ORM\FieldType\DBDatetime;
14
use SilverStripe\ORM\FieldType\DBField;
15
use SilverStripe\ORM\FieldType\DBHTMLText;
16
use SilverStripe\View\Requirements;
17
use SilverStripe\View\SSViewer;
18
use SilverStripe\View\ThemeResourceLoader;
19
use SilverStripe\View\ViewableData;
20
use Swift_Message;
21
use Swift_MimePart;
22
23
/**
24
 * Class to support sending emails.
25
 */
26
class Email extends ViewableData
27
{
28
29
    /**
30
     * @var array
31
     * @config
32
     */
33
    private static $send_all_emails_to = [];
34
35
    /**
36
     * @var array
37
     * @config
38
     */
39
    private static $cc_all_emails_to = [];
40
41
    /**
42
     * @var array
43
     * @config
44
     */
45
    private static $bcc_all_emails_to = [];
46
47
    /**
48
     * @var array
49
     * @config
50
     */
51
    private static $send_all_emails_from = [];
52
53
    /**
54
     * This will be set in the config on a site-by-site basis
55
     * @see https://docs.silverstripe.org/en/4/developer_guides/email/#administrator-emails
56
     *
57
     * @config
58
     * @var string|array The default administrator email address or array of [email => name]
59
     */
60
    private static $admin_email = null;
61
62
    /**
63
     * @var Swift_Message
64
     */
65
    private $swiftMessage;
66
67
    /**
68
     * @var string The name of the HTML template to render the email with (without *.ss extension)
69
     */
70
    private $HTMLTemplate = null;
71
72
    /**
73
     * @var string The name of the plain text template to render the plain part of the email with
74
     */
75
    private $plainTemplate = null;
76
77
    /**
78
     * @var Swift_MimePart
79
     */
80
    private $plainPart;
81
82
    /**
83
     * @var array|ViewableData Additional data available in a template.
84
     * Used in the same way than {@link ViewableData->customize()}.
85
     */
86
    private $data = [];
87
88
    /**
89
     * @var array
90
     */
91
    private $failedRecipients = [];
92
93
    /**
94
     * Checks for RFC822-valid email format.
95
     *
96
     * @param string $address
97
     * @return boolean
98
     *
99
     * @copyright Cal Henderson <[email protected]>
100
     *    This code is licensed under a Creative Commons Attribution-ShareAlike 2.5 License
101
     *    http://creativecommons.org/licenses/by-sa/2.5/
102
     */
103
    public static function is_valid_address($address)
104
    {
105
        $validator = new EmailValidator();
106
        return $validator->isValid($address, new RFCValidation());
107
    }
108
109
    /**
110
     * Get send_all_emails_to
111
     *
112
     * @return array Keys are addresses, values are names
113
     */
114
    public static function getSendAllEmailsTo()
115
    {
116
        return static::mergeConfiguredEmails('send_all_emails_to', 'SS_SEND_ALL_EMAILS_TO');
117
    }
118
119
    /**
120
     * Get cc_all_emails_to
121
     *
122
     * @return array
123
     */
124
    public static function getCCAllEmailsTo()
125
    {
126
        return static::mergeConfiguredEmails('cc_all_emails_to', 'SS_CC_ALL_EMAILS_TO');
127
    }
128
129
    /**
130
     * Get bcc_all_emails_to
131
     *
132
     * @return array
133
     */
134
    public static function getBCCAllEmailsTo()
135
    {
136
        return static::mergeConfiguredEmails('bcc_all_emails_to', 'SS_BCC_ALL_EMAILS_TO');
137
    }
138
139
    /**
140
     * Get send_all_emails_from
141
     *
142
     * @return array
143
     */
144
    public static function getSendAllEmailsFrom()
145
    {
146
        return static::mergeConfiguredEmails('send_all_emails_from', 'SS_SEND_ALL_EMAILS_FROM');
147
    }
148
149
    /**
150
     * Normalise email list from config merged with env vars
151
     *
152
     * @param string $config Config key
153
     * @param string $env Env variable key
154
     * @return array Array of email addresses
155
     */
156
    protected static function mergeConfiguredEmails($config, $env)
157
    {
158
        // Normalise config list
159
        $normalised = [];
160
        $source = (array)static::config()->get($config);
161
        foreach ($source as $address => $name) {
162
            if ($address && !is_numeric($address)) {
163
                $normalised[$address] = $name;
164
            } elseif ($name) {
165
                $normalised[$name] = null;
166
            }
167
        }
168
        $extra = Environment::getEnv($env);
169
        if ($extra) {
170
            $normalised[$extra] = null;
171
        }
172
        return $normalised;
173
    }
174
175
    /**
176
     * Encode an email-address to protect it from spambots.
177
     * At the moment only simple string substitutions,
178
     * which are not 100% safe from email harvesting.
179
     *
180
     * @param string $email Email-address
181
     * @param string $method Method for obfuscating/encoding the address
182
     *    - 'direction': Reverse the text and then use CSS to put the text direction back to normal
183
     *    - 'visible': Simple string substitution ('@' to '[at]', '.' to '[dot], '-' to [dash])
184
     *    - 'hex': Hexadecimal URL-Encoding - useful for mailto: links
185
     * @return string
186
     */
187
    public static function obfuscate($email, $method = 'visible')
188
    {
189
        switch ($method) {
190
            case 'direction':
191
                Requirements::customCSS('span.codedirection { unicode-bidi: bidi-override; direction: rtl; }', 'codedirectionCSS');
192
193
                return '<span class="codedirection">' . strrev($email) . '</span>';
194
            case 'visible':
195
                $obfuscated = ['@' => ' [at] ', '.' => ' [dot] ', '-' => ' [dash] '];
196
197
                return strtr($email, $obfuscated);
198
            case 'hex':
199
                $encoded = '';
200
                $emailLength = strlen($email);
201
                for ($x = 0; $x < $emailLength; $x++) {
202
                    $encoded .= '&#x' . bin2hex($email[$x]) . ';';
203
                }
204
205
                return $encoded;
206
            default:
207
                user_error('Email::obfuscate(): Unknown obfuscation method', E_USER_NOTICE);
208
209
                return $email;
210
        }
211
    }
212
213
    /**
214
     * Email constructor.
215
     * @param string|array|null $from
216
     * @param string|array|null $to
217
     * @param string|null $subject
218
     * @param string|null $body
219
     * @param string|array|null $cc
220
     * @param string|array|null $bcc
221
     * @param string|null $returnPath
222
     */
223
    public function __construct(
224
        $from = null,
225
        $to = null,
226
        $subject = null,
227
        $body = null,
228
        $cc = null,
229
        $bcc = null,
230
        $returnPath = null
231
    ) {
232
        if ($from) {
233
            $this->setFrom($from);
234
        }
235
        if ($to) {
236
            $this->setTo($to);
237
        }
238
        if ($subject) {
239
            $this->setSubject($subject);
240
        }
241
        if ($body) {
242
            $this->setBody($body);
243
        }
244
        if ($cc) {
245
            $this->setCC($cc);
246
        }
247
        if ($bcc) {
248
            $this->setBCC($bcc);
249
        }
250
        if ($returnPath) {
251
            $this->setReturnPath($returnPath);
252
        }
253
254
        parent::__construct();
255
    }
256
257
    /**
258
     * @return Swift_Message
259
     */
260
    public function getSwiftMessage()
261
    {
262
        if (!$this->swiftMessage) {
263
            $this->setSwiftMessage(new Swift_Message(null, null, 'text/html', 'utf-8'));
264
        }
265
266
        return $this->swiftMessage;
267
    }
268
269
    /**
270
     * @param Swift_Message $swiftMessage
271
     *
272
     * @return $this
273
     */
274
    public function setSwiftMessage($swiftMessage)
275
    {
276
        $swiftMessage->setDate(DBDatetime::now()->getTimestamp());
0 ignored issues
show
Bug introduced by
SilverStripe\ORM\FieldTy...::now()->getTimestamp() of type integer is incompatible with the type DateTimeInterface expected by parameter $dateTime of Swift_Mime_SimpleMessage::setDate(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

276
        $swiftMessage->setDate(/** @scrutinizer ignore-type */ DBDatetime::now()->getTimestamp());
Loading history...
277
        if (!$swiftMessage->getFrom()) {
278
            $swiftMessage->setFrom($this->getDefaultFrom());
279
        }
280
        $this->swiftMessage = $swiftMessage;
281
282
        return $this;
283
    }
284
285
    /**
286
     * @return string
287
     */
288
    private function getDefaultFrom(): string
289
    {
290
        $defaultFrom = $this->config()->get('admin_email');
291
        if ($defaultFrom) {
292
            return $defaultFrom;
293
        }
294
        return sprintf('no-reply@%s', Director::absoluteBaseURL());
295
    }
296
297
    /**
298
     * @return string[]
299
     */
300
    public function getFrom()
301
    {
302
        return $this->getSwiftMessage()->getFrom();
303
    }
304
305
    /**
306
     * @param string|array $address
307
     * @return string|array
308
     */
309
    private function sanitiseAddress($address)
310
    {
311
        if (is_array($address)) {
312
            return array_map('trim', $address);
313
        }
314
        return trim($address);
315
    }
316
317
    /**
318
     * @param string|array $address
319
     * @param string|null $name
320
     * @return $this
321
     */
322
    public function setFrom($address, $name = null)
323
    {
324
        $address = $this->sanitiseAddress($address);
325
        $this->getSwiftMessage()->setFrom($address, $name);
326
327
        return $this;
328
    }
329
330
    /**
331
     * @param string|array $address
332
     * @param string|null $name
333
     * @return $this
334
     */
335
    public function addFrom($address, $name = null)
336
    {
337
        $address = $this->sanitiseAddress($address);
338
        $this->getSwiftMessage()->addFrom($address, $name);
0 ignored issues
show
Bug introduced by
It seems like $address can also be of type array; however, parameter $address of Swift_Mime_SimpleMessage::addFrom() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

338
        $this->getSwiftMessage()->addFrom(/** @scrutinizer ignore-type */ $address, $name);
Loading history...
339
340
        return $this;
341
    }
342
343
    /**
344
     * @return string
345
     */
346
    public function getSender()
347
    {
348
        return $this->getSwiftMessage()->getSender();
349
    }
350
351
    /**
352
     * @param string $address
353
     * @param string|null $name
354
     * @return $this
355
     */
356
    public function setSender($address, $name = null)
357
    {
358
        $address = $this->sanitiseAddress($address);
359
        $this->getSwiftMessage()->setSender($address, $name);
360
361
        return $this;
362
    }
363
364
    /**
365
     * @return string
366
     */
367
    public function getReturnPath()
368
    {
369
        return $this->getSwiftMessage()->getReturnPath();
370
    }
371
372
    /**
373
     * The bounce handler address
374
     *
375
     * @param string $address Email address where bounce notifications should be sent
376
     * @return $this
377
     */
378
    public function setReturnPath($address)
379
    {
380
        $address = $this->sanitiseAddress($address);
381
        $this->getSwiftMessage()->setReturnPath($address);
382
        return $this;
383
    }
384
385
    /**
386
     * @return array
387
     */
388
    public function getTo()
389
    {
390
        return $this->getSwiftMessage()->getTo();
391
    }
392
393
    /**
394
     * Set recipient(s) of the email
395
     *
396
     * To send to many, pass an array:
397
     * ['[email protected]' => 'My Name', '[email protected]'];
398
     *
399
     * @param string|array $address The message recipient(s) - if sending to multiple, use an array of address => name
400
     * @param string|null $name The name of the recipient (if one)
401
     * @return $this
402
     */
403
    public function setTo($address, $name = null)
404
    {
405
        $address = $this->sanitiseAddress($address);
406
        $this->getSwiftMessage()->setTo($address, $name);
407
408
        return $this;
409
    }
410
411
    /**
412
     * @param string|array $address
413
     * @param string|null $name
414
     * @return $this
415
     */
416
    public function addTo($address, $name = null)
417
    {
418
        $address = $this->sanitiseAddress($address);
419
        $this->getSwiftMessage()->addTo($address, $name);
0 ignored issues
show
Bug introduced by
It seems like $address can also be of type array; however, parameter $address of Swift_Mime_SimpleMessage::addTo() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

419
        $this->getSwiftMessage()->addTo(/** @scrutinizer ignore-type */ $address, $name);
Loading history...
420
421
        return $this;
422
    }
423
424
    /**
425
     * @return array
426
     */
427
    public function getCC()
428
    {
429
        return $this->getSwiftMessage()->getCc();
430
    }
431
432
    /**
433
     * @param string|array $address
434
     * @param string|null $name
435
     * @return $this
436
     */
437
    public function setCC($address, $name = null)
438
    {
439
        $address = $this->sanitiseAddress($address);
440
        $this->getSwiftMessage()->setCc($address, $name);
441
442
        return $this;
443
    }
444
445
    /**
446
     * @param string|array $address
447
     * @param string|null $name
448
     * @return $this
449
     */
450
    public function addCC($address, $name = null)
451
    {
452
        $address = $this->sanitiseAddress($address);
453
        $this->getSwiftMessage()->addCc($address, $name);
0 ignored issues
show
Bug introduced by
It seems like $address can also be of type array; however, parameter $address of Swift_Mime_SimpleMessage::addCc() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

453
        $this->getSwiftMessage()->addCc(/** @scrutinizer ignore-type */ $address, $name);
Loading history...
454
455
        return $this;
456
    }
457
458
    /**
459
     * @return array
460
     */
461
    public function getBCC()
462
    {
463
        return $this->getSwiftMessage()->getBcc();
464
    }
465
466
    /**
467
     * @param string|array $address
468
     * @param string|null $name
469
     * @return $this
470
     */
471
    public function setBCC($address, $name = null)
472
    {
473
        $address = $this->sanitiseAddress($address);
474
        $this->getSwiftMessage()->setBcc($address, $name);
475
476
        return $this;
477
    }
478
479
    /**
480
     * @param string|array $address
481
     * @param string|null $name
482
     * @return $this
483
     */
484
    public function addBCC($address, $name = null)
485
    {
486
        $address = $this->sanitiseAddress($address);
487
        $this->getSwiftMessage()->addBcc($address, $name);
0 ignored issues
show
Bug introduced by
It seems like $address can also be of type array; however, parameter $address of Swift_Mime_SimpleMessage::addBcc() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

487
        $this->getSwiftMessage()->addBcc(/** @scrutinizer ignore-type */ $address, $name);
Loading history...
488
489
        return $this;
490
    }
491
492
    /**
493
     * @return mixed
494
     */
495
    public function getReplyTo()
496
    {
497
        return $this->getSwiftMessage()->getReplyTo();
498
    }
499
500
    /**
501
     * @param string|array $address
502
     * @param string|null $name
503
     * @return $this
504
     */
505
    public function setReplyTo($address, $name = null)
506
    {
507
        $address = $this->sanitiseAddress($address);
508
        $this->getSwiftMessage()->setReplyTo($address, $name);
509
510
        return $this;
511
    }
512
513
    /**
514
     * @param string|array $address
515
     * @param string|null $name
516
     * @return $this
517
     */
518
    public function addReplyTo($address, $name = null)
519
    {
520
        $address = $this->sanitiseAddress($address);
521
        $this->getSwiftMessage()->addReplyTo($address, $name);
0 ignored issues
show
Bug introduced by
It seems like $address can also be of type array; however, parameter $address of Swift_Mime_SimpleMessage::addReplyTo() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

521
        $this->getSwiftMessage()->addReplyTo(/** @scrutinizer ignore-type */ $address, $name);
Loading history...
522
523
        return $this;
524
    }
525
526
    /**
527
     * @return string
528
     */
529
    public function getSubject()
530
    {
531
        return $this->getSwiftMessage()->getSubject();
532
    }
533
534
    /**
535
     * @param string $subject The Subject line for the email
536
     * @return $this
537
     */
538
    public function setSubject($subject)
539
    {
540
        $this->getSwiftMessage()->setSubject($subject);
541
542
        return $this;
543
    }
544
545
    /**
546
     * @return int
547
     */
548
    public function getPriority()
549
    {
550
        return $this->getSwiftMessage()->getPriority();
551
    }
552
553
    /**
554
     * @param int $priority
555
     * @return $this
556
     */
557
    public function setPriority($priority)
558
    {
559
        $this->getSwiftMessage()->setPriority($priority);
560
561
        return $this;
562
    }
563
564
    /**
565
     * @param string $path Path to file
566
     * @param string $alias An override for the name of the file
567
     * @param string $mime The mime type for the attachment
568
     * @return $this
569
     */
570
    public function addAttachment($path, $alias = null, $mime = null)
571
    {
572
        $attachment = \Swift_Attachment::fromPath($path);
573
        if ($alias) {
574
            $attachment->setFilename($alias);
575
        }
576
        if ($mime) {
577
            $attachment->setContentType($mime);
578
        }
579
        $this->getSwiftMessage()->attach($attachment);
580
581
        return $this;
582
    }
583
584
    /**
585
     * @param string $data
586
     * @param string $name
587
     * @param string $mime
588
     * @return $this
589
     */
590
    public function addAttachmentFromData($data, $name, $mime = null)
591
    {
592
        $attachment = new \Swift_Attachment($data, $name);
593
        if ($mime) {
594
            $attachment->setContentType($mime);
595
        }
596
        $this->getSwiftMessage()->attach($attachment);
597
598
        return $this;
599
    }
600
601
    /**
602
     * @return array|ViewableData The template data
603
     */
604
    public function getData()
605
    {
606
        return $this->data;
607
    }
608
609
    /**
610
     * @param array|ViewableData $data The template data to set
611
     * @return $this
612
     */
613
    public function setData($data)
614
    {
615
        $this->data = $data;
616
        $this->invalidateBody();
617
618
        return $this;
619
    }
620
621
    /**
622
     * @param string|array $name The data name to add or array to names => value
623
     * @param string|null $value The value of the data to add
624
     * @return $this
625
     */
626
    public function addData($name, $value = null)
627
    {
628
        if (is_array($name)) {
629
            $this->data = array_merge($this->data, $name);
0 ignored issues
show
Bug introduced by
It seems like $this->data can also be of type SilverStripe\View\ViewableData; however, parameter $arrays of array_merge() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

629
            $this->data = array_merge(/** @scrutinizer ignore-type */ $this->data, $name);
Loading history...
630
        } elseif (is_array($this->data)) {
631
            $this->data[$name] = $value;
632
        } else {
633
            $this->data->$name = $value;
634
        }
635
636
        $this->invalidateBody();
637
638
        return $this;
639
    }
640
641
    /**
642
     * Remove a datum from the message
643
     *
644
     * @param string $name
645
     * @return $this
646
     */
647
    public function removeData($name)
648
    {
649
        if (is_array($this->data)) {
650
            unset($this->data[$name]);
651
        } else {
652
            $this->data->$name = null;
653
        }
654
655
        $this->invalidateBody();
656
657
        return $this;
658
    }
659
660
    /**
661
     * @return string
662
     */
663
    public function getBody()
664
    {
665
        return $this->getSwiftMessage()->getBody();
666
    }
667
668
    /**
669
     * @param string $body The email body
670
     * @return $this
671
     */
672
    public function setBody($body)
673
    {
674
        $plainPart = $this->findPlainPart();
675
        if ($plainPart) {
676
            $this->getSwiftMessage()->detach($plainPart);
677
        }
678
        unset($plainPart);
679
680
        $body = HTTP::absoluteURLs($body);
681
        $this->getSwiftMessage()->setBody($body);
682
683
        return $this;
684
    }
685
686
    /**
687
     * @return $this
688
     */
689
    public function invalidateBody()
690
    {
691
        $this->setBody(null);
692
693
        return $this;
694
    }
695
696
    /**
697
     * @return string The base URL for the email
698
     */
699
    public function BaseURL()
700
    {
701
        return Director::absoluteBaseURL();
702
    }
703
704
    /**
705
     * Debugging help
706
     *
707
     * @return string Debug info
708
     */
709
    public function debug()
710
    {
711
        $this->render();
712
713
        $class = static::class;
714
        return "<h2>Email template {$class}:</h2>\n" . '<pre>' . $this->getSwiftMessage()->toString() . '</pre>';
715
    }
716
717
    /**
718
     * @return string
719
     */
720
    public function getHTMLTemplate()
721
    {
722
        if ($this->HTMLTemplate) {
723
            return $this->HTMLTemplate;
724
        }
725
726
        return ThemeResourceLoader::inst()->findTemplate(
727
            SSViewer::get_templates_by_class(static::class, '', self::class),
728
            SSViewer::get_themes()
729
        );
730
    }
731
732
    /**
733
     * Set the template to render the email with
734
     *
735
     * @param string $template
736
     * @return $this
737
     */
738
    public function setHTMLTemplate($template)
739
    {
740
        if (substr($template, -3) == '.ss') {
741
            $template = substr($template, 0, -3);
742
        }
743
        $this->HTMLTemplate = $template;
744
745
        return $this;
746
    }
747
748
    /**
749
     * Get the template to render the plain part with
750
     *
751
     * @return string
752
     */
753
    public function getPlainTemplate()
754
    {
755
        return $this->plainTemplate;
756
    }
757
758
    /**
759
     * Set the template to render the plain part with
760
     *
761
     * @param string $template
762
     * @return $this
763
     */
764
    public function setPlainTemplate($template)
765
    {
766
        if (substr($template, -3) == '.ss') {
767
            $template = substr($template, 0, -3);
768
        }
769
        $this->plainTemplate = $template;
770
771
        return $this;
772
    }
773
774
    /**
775
     * @param array $recipients
776
     * @return $this
777
     */
778
    public function setFailedRecipients($recipients)
779
    {
780
        $this->failedRecipients = $recipients;
781
782
        return $this;
783
    }
784
785
    /**
786
     * @return array
787
     */
788
    public function getFailedRecipients()
789
    {
790
        return $this->failedRecipients;
791
    }
792
793
    /**
794
     * Used by {@link SSViewer} templates to detect if we're rendering an email template rather than a page template
795
     *
796
     * @return bool
797
     */
798
    public function IsEmail()
799
    {
800
        return true;
801
    }
802
803
    /**
804
     * Send the message to the recipients
805
     *
806
     * @return bool true if successful or array of failed recipients
807
     */
808
    public function send()
809
    {
810
        if (!$this->getBody()) {
811
            $this->render();
812
        }
813
        if (!$this->hasPlainPart()) {
814
            $this->generatePlainPartFromBody();
815
        }
816
        return Injector::inst()->get(Mailer::class)->send($this);
817
    }
818
819
    /**
820
     * @return array|bool
821
     */
822
    public function sendPlain()
823
    {
824
        if (!$this->hasPlainPart()) {
825
            $this->render(true);
826
        }
827
        return Injector::inst()->get(Mailer::class)->send($this);
828
    }
829
830
    /**
831
     * Render the email
832
     * @param bool $plainOnly Only render the message as plain text
833
     * @return $this
834
     */
835
    public function render($plainOnly = false)
836
    {
837
        if ($existingPlainPart = $this->findPlainPart()) {
838
            $this->getSwiftMessage()->detach($existingPlainPart);
839
        }
840
        unset($existingPlainPart);
841
842
        // Respect explicitly set body
843
        $htmlPart = $plainOnly ? null : $this->getBody();
844
        $plainPart = $plainOnly ? $this->getBody() : null;
845
846
        // Ensure we can at least render something
847
        $htmlTemplate = $this->getHTMLTemplate();
848
        $plainTemplate = $this->getPlainTemplate();
849
        if (!$htmlTemplate && !$plainTemplate && !$plainPart && !$htmlPart) {
850
            return $this;
851
        }
852
853
        // Do not interfere with emails styles
854
        Requirements::clear();
855
856
        // Render plain part
857
        if ($plainTemplate && !$plainPart) {
858
            $plainPart = $this->renderWith($plainTemplate, $this->getData())->Plain();
0 ignored issues
show
Bug introduced by
It seems like $this->getData() can also be of type SilverStripe\View\ViewableData; however, parameter $customFields of SilverStripe\View\ViewableData::renderWith() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

858
            $plainPart = $this->renderWith($plainTemplate, /** @scrutinizer ignore-type */ $this->getData())->Plain();
Loading history...
859
        }
860
861
        // Render HTML part, either if sending html email, or a plain part is lacking
862
        if (!$htmlPart && $htmlTemplate && (!$plainOnly || empty($plainPart))) {
863
            $htmlPart = $this->renderWith($htmlTemplate, $this->getData());
864
        }
865
866
        // Plain part fails over to generated from html
867
        if (!$plainPart && $htmlPart) {
868
            /** @var DBHTMLText $htmlPartObject */
869
            $htmlPartObject = DBField::create_field('HTMLFragment', $htmlPart);
870
            $plainPart = $htmlPartObject->Plain();
871
        }
872
873
        // Rendering is finished
874
        Requirements::restore();
875
876
        // Fail if no email to send
877
        if (!$plainPart && !$htmlPart) {
878
            return $this;
879
        }
880
881
        // Build HTML / Plain components
882
        if ($htmlPart && !$plainOnly) {
883
            $this->setBody($htmlPart);
884
            $this->getSwiftMessage()->setContentType('text/html');
885
            $this->getSwiftMessage()->setCharset('utf-8');
886
            if ($plainPart) {
887
                $this->getSwiftMessage()->addPart($plainPart, 'text/plain', 'utf-8');
888
            }
889
        } else {
890
            if ($plainPart) {
891
                $this->setBody($plainPart);
892
            }
893
            $this->getSwiftMessage()->setContentType('text/plain');
894
            $this->getSwiftMessage()->setCharset('utf-8');
895
        }
896
897
        return $this;
898
    }
899
900
    /**
901
     * @return Swift_MimePart|false
902
     */
903
    public function findPlainPart()
904
    {
905
        foreach ($this->getSwiftMessage()->getChildren() as $child) {
906
            if ($child instanceof Swift_MimePart && $child->getContentType() == 'text/plain') {
907
                return $child;
908
            }
909
        }
910
        return false;
911
    }
912
913
    /**
914
     * @return bool
915
     */
916
    public function hasPlainPart()
917
    {
918
        if ($this->getSwiftMessage()->getContentType() === 'text/plain') {
919
            return true;
920
        }
921
        return (bool) $this->findPlainPart();
922
    }
923
924
    /**
925
     * Automatically adds a plain part to the email generated from the current Body
926
     *
927
     * @return $this
928
     */
929
    public function generatePlainPartFromBody()
930
    {
931
        $plainPart = $this->findPlainPart();
932
        if ($plainPart) {
933
            $this->getSwiftMessage()->detach($plainPart);
934
        }
935
        unset($plainPart);
936
937
        $this->getSwiftMessage()->addPart(
938
            Convert::xml2raw($this->getBody()),
939
            'text/plain',
940
            'utf-8'
941
        );
942
943
        return $this;
944
    }
945
}
946