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

Email::getDefaultFrom()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 12
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 8
c 0
b 0
f 0
nc 3
nop 0
dl 0
loc 12
rs 10
1
<?php
2
3
namespace SilverStripe\Control\Email;
4
5
use DateTime;
6
use Exception;
7
use Egulias\EmailValidator\EmailValidator;
8
use Egulias\EmailValidator\Validation\RFCValidation;
9
use SilverStripe\Control\Director;
10
use SilverStripe\Control\HTTP;
11
use SilverStripe\Core\Convert;
12
use SilverStripe\Core\Environment;
13
use SilverStripe\Core\Injector\Injector;
14
use SilverStripe\ORM\FieldType\DBDatetime;
15
use SilverStripe\ORM\FieldType\DBField;
16
use SilverStripe\ORM\FieldType\DBHTMLText;
17
use SilverStripe\View\Requirements;
18
use SilverStripe\View\SSViewer;
19
use SilverStripe\View\ThemeResourceLoader;
20
use SilverStripe\View\ViewableData;
21
use Swift_Message;
22
use Swift_MimePart;
23
24
/**
25
 * Class to support sending emails.
26
 */
27
class Email extends ViewableData
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
        $dateTime = new DateTime();
277
        $dateTime->setTimestamp(DBDatetime::now()->getTimestamp());
278
        $swiftMessage->setDate($dateTime);
279
        if (!$swiftMessage->getFrom()) {
280
            $swiftMessage->setFrom($this->getDefaultFrom());
281
        }
282
        $this->swiftMessage = $swiftMessage;
283
284
        return $this;
285
    }
286
287
    /**
288
     * @return string
289
     */
290
    private function getDefaultFrom(): string
291
    {
292
        $defaultFrom = $this->config()->get('admin_email');
293
        if (!$defaultFrom) {
294
            $host = Director::host();
295
            if (empty($host)) {
296
                throw new Exception('Host not defined');
297
            }
298
            $defaultFrom = sprintf('no-reply@%s', $host);
299
        }
300
        $this->extend('updateDefaultFrom', $defaultFrom);
301
        return $defaultFrom;
302
    }
303
304
    /**
305
     * @return string[]
306
     */
307
    public function getFrom()
308
    {
309
        return $this->getSwiftMessage()->getFrom();
310
    }
311
312
    /**
313
     * @param string|array $address
314
     * @return string|array
315
     */
316
    private function sanitiseAddress($address)
317
    {
318
        if (is_array($address)) {
319
            return array_map('trim', $address);
320
        }
321
        return trim($address);
322
    }
323
324
    /**
325
     * @param string|array $address
326
     * @param string|null $name
327
     * @return $this
328
     */
329
    public function setFrom($address, $name = null)
330
    {
331
        $address = $this->sanitiseAddress($address);
332
        $this->getSwiftMessage()->setFrom($address, $name);
333
334
        return $this;
335
    }
336
337
    /**
338
     * @param string|array $address
339
     * @param string|null $name
340
     * @return $this
341
     */
342
    public function addFrom($address, $name = null)
343
    {
344
        $address = $this->sanitiseAddress($address);
345
        $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

345
        $this->getSwiftMessage()->addFrom(/** @scrutinizer ignore-type */ $address, $name);
Loading history...
346
347
        return $this;
348
    }
349
350
    /**
351
     * @return string
352
     */
353
    public function getSender()
354
    {
355
        return $this->getSwiftMessage()->getSender();
356
    }
357
358
    /**
359
     * @param string $address
360
     * @param string|null $name
361
     * @return $this
362
     */
363
    public function setSender($address, $name = null)
364
    {
365
        $address = $this->sanitiseAddress($address);
366
        $this->getSwiftMessage()->setSender($address, $name);
367
368
        return $this;
369
    }
370
371
    /**
372
     * @return string
373
     */
374
    public function getReturnPath()
375
    {
376
        return $this->getSwiftMessage()->getReturnPath();
377
    }
378
379
    /**
380
     * The bounce handler address
381
     *
382
     * @param string $address Email address where bounce notifications should be sent
383
     * @return $this
384
     */
385
    public function setReturnPath($address)
386
    {
387
        $address = $this->sanitiseAddress($address);
388
        $this->getSwiftMessage()->setReturnPath($address);
389
        return $this;
390
    }
391
392
    /**
393
     * @return array
394
     */
395
    public function getTo()
396
    {
397
        return $this->getSwiftMessage()->getTo();
398
    }
399
400
    /**
401
     * Set recipient(s) of the email
402
     *
403
     * To send to many, pass an array:
404
     * ['[email protected]' => 'My Name', '[email protected]'];
405
     *
406
     * @param string|array $address The message recipient(s) - if sending to multiple, use an array of address => name
407
     * @param string|null $name The name of the recipient (if one)
408
     * @return $this
409
     */
410
    public function setTo($address, $name = null)
411
    {
412
        $address = $this->sanitiseAddress($address);
413
        $this->getSwiftMessage()->setTo($address, $name);
414
415
        return $this;
416
    }
417
418
    /**
419
     * @param string|array $address
420
     * @param string|null $name
421
     * @return $this
422
     */
423
    public function addTo($address, $name = null)
424
    {
425
        $address = $this->sanitiseAddress($address);
426
        $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

426
        $this->getSwiftMessage()->addTo(/** @scrutinizer ignore-type */ $address, $name);
Loading history...
427
428
        return $this;
429
    }
430
431
    /**
432
     * @return array
433
     */
434
    public function getCC()
435
    {
436
        return $this->getSwiftMessage()->getCc();
437
    }
438
439
    /**
440
     * @param string|array $address
441
     * @param string|null $name
442
     * @return $this
443
     */
444
    public function setCC($address, $name = null)
445
    {
446
        $address = $this->sanitiseAddress($address);
447
        $this->getSwiftMessage()->setCc($address, $name);
448
449
        return $this;
450
    }
451
452
    /**
453
     * @param string|array $address
454
     * @param string|null $name
455
     * @return $this
456
     */
457
    public function addCC($address, $name = null)
458
    {
459
        $address = $this->sanitiseAddress($address);
460
        $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

460
        $this->getSwiftMessage()->addCc(/** @scrutinizer ignore-type */ $address, $name);
Loading history...
461
462
        return $this;
463
    }
464
465
    /**
466
     * @return array
467
     */
468
    public function getBCC()
469
    {
470
        return $this->getSwiftMessage()->getBcc();
471
    }
472
473
    /**
474
     * @param string|array $address
475
     * @param string|null $name
476
     * @return $this
477
     */
478
    public function setBCC($address, $name = null)
479
    {
480
        $address = $this->sanitiseAddress($address);
481
        $this->getSwiftMessage()->setBcc($address, $name);
482
483
        return $this;
484
    }
485
486
    /**
487
     * @param string|array $address
488
     * @param string|null $name
489
     * @return $this
490
     */
491
    public function addBCC($address, $name = null)
492
    {
493
        $address = $this->sanitiseAddress($address);
494
        $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

494
        $this->getSwiftMessage()->addBcc(/** @scrutinizer ignore-type */ $address, $name);
Loading history...
495
496
        return $this;
497
    }
498
499
    /**
500
     * @return mixed
501
     */
502
    public function getReplyTo()
503
    {
504
        return $this->getSwiftMessage()->getReplyTo();
505
    }
506
507
    /**
508
     * @param string|array $address
509
     * @param string|null $name
510
     * @return $this
511
     */
512
    public function setReplyTo($address, $name = null)
513
    {
514
        $address = $this->sanitiseAddress($address);
515
        $this->getSwiftMessage()->setReplyTo($address, $name);
516
517
        return $this;
518
    }
519
520
    /**
521
     * @param string|array $address
522
     * @param string|null $name
523
     * @return $this
524
     */
525
    public function addReplyTo($address, $name = null)
526
    {
527
        $address = $this->sanitiseAddress($address);
528
        $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

528
        $this->getSwiftMessage()->addReplyTo(/** @scrutinizer ignore-type */ $address, $name);
Loading history...
529
530
        return $this;
531
    }
532
533
    /**
534
     * @return string
535
     */
536
    public function getSubject()
537
    {
538
        return $this->getSwiftMessage()->getSubject();
539
    }
540
541
    /**
542
     * @param string $subject The Subject line for the email
543
     * @return $this
544
     */
545
    public function setSubject($subject)
546
    {
547
        $this->getSwiftMessage()->setSubject($subject);
548
549
        return $this;
550
    }
551
552
    /**
553
     * @return int
554
     */
555
    public function getPriority()
556
    {
557
        return $this->getSwiftMessage()->getPriority();
558
    }
559
560
    /**
561
     * @param int $priority
562
     * @return $this
563
     */
564
    public function setPriority($priority)
565
    {
566
        $this->getSwiftMessage()->setPriority($priority);
567
568
        return $this;
569
    }
570
571
    /**
572
     * @param string $path Path to file
573
     * @param string $alias An override for the name of the file
574
     * @param string $mime The mime type for the attachment
575
     * @return $this
576
     */
577
    public function addAttachment($path, $alias = null, $mime = null)
578
    {
579
        $attachment = \Swift_Attachment::fromPath($path);
580
        if ($alias) {
581
            $attachment->setFilename($alias);
582
        }
583
        if ($mime) {
584
            $attachment->setContentType($mime);
585
        }
586
        $this->getSwiftMessage()->attach($attachment);
587
588
        return $this;
589
    }
590
591
    /**
592
     * @param string $data
593
     * @param string $name
594
     * @param string $mime
595
     * @return $this
596
     */
597
    public function addAttachmentFromData($data, $name, $mime = null)
598
    {
599
        $attachment = new \Swift_Attachment($data, $name);
600
        if ($mime) {
601
            $attachment->setContentType($mime);
602
        }
603
        $this->getSwiftMessage()->attach($attachment);
604
605
        return $this;
606
    }
607
608
    /**
609
     * @return array|ViewableData The template data
610
     */
611
    public function getData()
612
    {
613
        return $this->data;
614
    }
615
616
    /**
617
     * @param array|ViewableData $data The template data to set
618
     * @return $this
619
     */
620
    public function setData($data)
621
    {
622
        $this->data = $data;
623
        $this->invalidateBody();
624
625
        return $this;
626
    }
627
628
    /**
629
     * @param string|array $name The data name to add or array to names => value
630
     * @param string|null $value The value of the data to add
631
     * @return $this
632
     */
633
    public function addData($name, $value = null)
634
    {
635
        if (is_array($name)) {
636
            $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

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

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