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

Email::getDefaultFrom()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 4
c 0
b 0
f 0
nc 2
nop 0
dl 0
loc 7
rs 10
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
        $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
            return $defaultFrom;
295
        }
296
        return sprintf('no-reply@%s', parse_url(Director::host(), PHP_URL_HOST));
297
    }
298
299
    /**
300
     * @return string[]
301
     */
302
    public function getFrom()
303
    {
304
        return $this->getSwiftMessage()->getFrom();
305
    }
306
307
    /**
308
     * @param string|array $address
309
     * @return string|array
310
     */
311
    private function sanitiseAddress($address)
312
    {
313
        if (is_array($address)) {
314
            return array_map('trim', $address);
315
        }
316
        return trim($address);
317
    }
318
319
    /**
320
     * @param string|array $address
321
     * @param string|null $name
322
     * @return $this
323
     */
324
    public function setFrom($address, $name = null)
325
    {
326
        $address = $this->sanitiseAddress($address);
327
        $this->getSwiftMessage()->setFrom($address, $name);
328
329
        return $this;
330
    }
331
332
    /**
333
     * @param string|array $address
334
     * @param string|null $name
335
     * @return $this
336
     */
337
    public function addFrom($address, $name = null)
338
    {
339
        $address = $this->sanitiseAddress($address);
340
        $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

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

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

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

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

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

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

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