Completed
Push — 4.9 ( 2841e1...018457 )
by Maxime
34s queued 22s
created

Email::getDefaultFrom()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 11
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 7
nc 3
nop 0
dl 0
loc 11
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace SilverStripe\Control\Email;
4
5
use DateTime;
6
use RuntimeException;
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 RuntimeException('Host not defined');
297
            }
298
            $defaultFrom = sprintf('no-reply@%s', $host);
299
        }
300
        return $defaultFrom;
301
    }
302
303
    /**
304
     * @return string[]
305
     */
306
    public function getFrom()
307
    {
308
        return $this->getSwiftMessage()->getFrom();
309
    }
310
311
    /**
312
     * @param string|array $address
313
     * @return string|array
314
     */
315
    private function sanitiseAddress($address)
316
    {
317
        if (is_array($address)) {
318
            return array_map('trim', $address);
319
        }
320
        return trim($address);
321
    }
322
323
    /**
324
     * @param string|array $address
325
     * @param string|null $name
326
     * @return $this
327
     */
328
    public function setFrom($address, $name = null)
329
    {
330
        $address = $this->sanitiseAddress($address);
331
        $this->getSwiftMessage()->setFrom($address, $name);
332
333
        return $this;
334
    }
335
336
    /**
337
     * @param string|array $address
338
     * @param string|null $name
339
     * @return $this
340
     */
341
    public function addFrom($address, $name = null)
342
    {
343
        $address = $this->sanitiseAddress($address);
344
        $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

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

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

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

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

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

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

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