Email   F
last analyzed

Complexity

Total Complexity 115

Size/Duplication

Total Lines 870
Duplicated Lines 0 %

Importance

Changes 2
Bugs 1 Features 0
Metric Value
eloc 222
c 2
b 1
f 0
dl 0
loc 870
rs 2
wmc 115

56 Methods

Rating   Name   Duplication   Size   Complexity  
A getCC() 0 3 1
A getSendAllEmailsFrom() 0 3 1
A setCC() 0 5 1
A setTo() 0 5 1
A setFailedRecipients() 0 5 1
A getHTMLTemplate() 0 9 2
A removeData() 0 9 2
A is_valid_address() 0 7 2
B __construct() 0 32 8
A setReplyTo() 0 5 1
A setData() 0 5 1
A setFrom() 0 5 1
A setHTMLTemplate() 0 8 2
A addReplyTo() 0 5 1
A findPlainPart() 0 8 4
A getPriority() 0 3 1
A setSender() 0 5 1
A mergeConfiguredEmails() 0 17 6
A debug() 0 6 1
A addBCC() 0 5 1
A obfuscate() 0 26 5
A setReturnPath() 0 4 1
A getReplyTo() 0 3 1
A getTo() 0 3 1
A getReturnPath() 0 3 1
A addFrom() 0 5 1
A setSwiftMessage() 0 9 3
A setPriority() 0 5 1
A getPlainTemplate() 0 3 1
A setBCC() 0 5 1
A send() 0 9 3
A getFrom() 0 3 1
A sendPlain() 0 6 2
A getCCAllEmailsTo() 0 3 1
A IsEmail() 0 3 1
A addAttachmentFromData() 0 9 2
A getSendAllEmailsTo() 0 3 1
A setBody() 0 12 2
A addAttachment() 0 12 3
A addTo() 0 5 1
A addCC() 0 5 1
A setPlainTemplate() 0 8 2
A getData() 0 3 1
A BaseURL() 0 3 1
A addData() 0 11 3
A getFailedRecipients() 0 3 1
A hasPlainPart() 0 6 2
A getSender() 0 3 1
A setSubject() 0 5 1
A getBCC() 0 3 1
A getBody() 0 3 1
F render() 0 63 22
A getBCCAllEmailsTo() 0 3 1
A getSwiftMessage() 0 7 2
A generatePlainPartFromBody() 0 15 2
A getSubject() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like Email often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Email, and based on these observations, apply Extract Interface, too.

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

317
        $this->getSwiftMessage()->addFrom(/** @scrutinizer ignore-type */ $address, $name);
Loading history...
318
319
        return $this;
320
    }
321
322
    /**
323
     * @return string
324
     */
325
    public function getSender()
326
    {
327
        return $this->getSwiftMessage()->getSender();
328
    }
329
330
    /**
331
     * @param string $address
332
     * @param string|null $name
333
     * @return $this
334
     */
335
    public function setSender($address, $name = null)
336
    {
337
        $this->getSwiftMessage()->setSender($address, $name);
338
339
        return $this;
340
    }
341
342
    /**
343
     * @return string
344
     */
345
    public function getReturnPath()
346
    {
347
        return $this->getSwiftMessage()->getReturnPath();
348
    }
349
350
    /**
351
     * The bounce handler address
352
     *
353
     * @param string $address Email address where bounce notifications should be sent
354
     * @return $this
355
     */
356
    public function setReturnPath($address)
357
    {
358
        $this->getSwiftMessage()->setReturnPath($address);
359
        return $this;
360
    }
361
362
    /**
363
     * @return array
364
     */
365
    public function getTo()
366
    {
367
        return $this->getSwiftMessage()->getTo();
368
    }
369
370
    /**
371
     * Set recipient(s) of the email
372
     *
373
     * To send to many, pass an array:
374
     * array('[email protected]' => 'My Name', '[email protected]');
375
     *
376
     * @param string|array $address The message recipient(s) - if sending to multiple, use an array of address => name
377
     * @param string|null $name The name of the recipient (if one)
378
     * @return $this
379
     */
380
    public function setTo($address, $name = null)
381
    {
382
        $this->getSwiftMessage()->setTo($address, $name);
383
384
        return $this;
385
    }
386
387
    /**
388
     * @param string|array $address
389
     * @param string|null $name
390
     * @return $this
391
     */
392
    public function addTo($address, $name = null)
393
    {
394
        $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

394
        $this->getSwiftMessage()->addTo(/** @scrutinizer ignore-type */ $address, $name);
Loading history...
395
396
        return $this;
397
    }
398
399
    /**
400
     * @return array
401
     */
402
    public function getCC()
403
    {
404
        return $this->getSwiftMessage()->getCc();
405
    }
406
407
    /**
408
     * @param string|array $address
409
     * @param string|null $name
410
     * @return $this
411
     */
412
    public function setCC($address, $name = null)
413
    {
414
        $this->getSwiftMessage()->setCc($address, $name);
415
416
        return $this;
417
    }
418
419
    /**
420
     * @param string|array $address
421
     * @param string|null $name
422
     * @return $this
423
     */
424
    public function addCC($address, $name = null)
425
    {
426
        $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

426
        $this->getSwiftMessage()->addCc(/** @scrutinizer ignore-type */ $address, $name);
Loading history...
427
428
        return $this;
429
    }
430
431
    /**
432
     * @return array
433
     */
434
    public function getBCC()
435
    {
436
        return $this->getSwiftMessage()->getBcc();
437
    }
438
439
    /**
440
     * @param string|array $address
441
     * @param string|null $name
442
     * @return $this
443
     */
444
    public function setBCC($address, $name = null)
445
    {
446
        $this->getSwiftMessage()->setBcc($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 addBCC($address, $name = null)
457
    {
458
        $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

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

487
        $this->getSwiftMessage()->addReplyTo(/** @scrutinizer ignore-type */ $address, $name);
Loading history...
488
489
        return $this;
490
    }
491
492
    /**
493
     * @return string
494
     */
495
    public function getSubject()
496
    {
497
        return $this->getSwiftMessage()->getSubject();
498
    }
499
500
    /**
501
     * @param string $subject The Subject line for the email
502
     * @return $this
503
     */
504
    public function setSubject($subject)
505
    {
506
        $this->getSwiftMessage()->setSubject($subject);
507
508
        return $this;
509
    }
510
511
    /**
512
     * @return int
513
     */
514
    public function getPriority()
515
    {
516
        return $this->getSwiftMessage()->getPriority();
517
    }
518
519
    /**
520
     * @param int $priority
521
     * @return $this
522
     */
523
    public function setPriority($priority)
524
    {
525
        $this->getSwiftMessage()->setPriority($priority);
526
527
        return $this;
528
    }
529
530
    /**
531
     * @param string $path Path to file
532
     * @param string $alias An override for the name of the file
533
     * @param string $mime The mime type for the attachment
534
     * @return $this
535
     */
536
    public function addAttachment($path, $alias = null, $mime = null)
537
    {
538
        $attachment = \Swift_Attachment::fromPath($path);
539
        if ($alias) {
540
            $attachment->setFilename($alias);
541
        }
542
        if ($mime) {
543
            $attachment->setContentType($mime);
544
        }
545
        $this->getSwiftMessage()->attach($attachment);
546
547
        return $this;
548
    }
549
550
    /**
551
     * @param string $data
552
     * @param string $name
553
     * @param string $mime
554
     * @return $this
555
     */
556
    public function addAttachmentFromData($data, $name, $mime = null)
557
    {
558
        $attachment = new \Swift_Attachment($data, $name);
559
        if ($mime) {
560
            $attachment->setContentType($mime);
561
        }
562
        $this->getSwiftMessage()->attach($attachment);
563
564
        return $this;
565
    }
566
567
    /**
568
     * @return array|ViewableData The template data
569
     */
570
    public function getData()
571
    {
572
        return $this->data;
573
    }
574
575
    /**
576
     * @param array|ViewableData $data The template data to set
577
     * @return $this
578
     */
579
    public function setData($data)
580
    {
581
        $this->data = $data;
582
583
        return $this;
584
    }
585
586
    /**
587
     * @param string|array $name The data name to add or array to names => value
588
     * @param string|null $value The value of the data to add
589
     * @return $this
590
     */
591
    public function addData($name, $value = null)
592
    {
593
        if (is_array($name)) {
594
            $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 $array1 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

594
            $this->data = array_merge(/** @scrutinizer ignore-type */ $this->data, $name);
Loading history...
595
        } elseif (is_array($this->data)) {
596
            $this->data[$name] = $value;
597
        } else {
598
            $this->data->$name = $value;
599
        }
600
601
        return $this;
602
    }
603
604
    /**
605
     * Remove a datum from the message
606
     *
607
     * @param string $name
608
     * @return $this
609
     */
610
    public function removeData($name)
611
    {
612
        if (is_array($this->data)) {
613
            unset($this->data[$name]);
614
        } else {
615
            $this->data->$name = null;
616
        }
617
618
        return $this;
619
    }
620
621
    /**
622
     * @return string
623
     */
624
    public function getBody()
625
    {
626
        return $this->getSwiftMessage()->getBody();
627
    }
628
629
    /**
630
     * @param string $body The email body
631
     * @return $this
632
     */
633
    public function setBody($body)
634
    {
635
        $plainPart = $this->findPlainPart();
636
        if ($plainPart) {
637
            $this->getSwiftMessage()->detach($plainPart);
638
        }
639
        unset($plainPart);
640
641
        $body = HTTP::absoluteURLs($body);
642
        $this->getSwiftMessage()->setBody($body);
643
644
        return $this;
645
    }
646
647
    /**
648
     * @return string The base URL for the email
649
     */
650
    public function BaseURL()
651
    {
652
        return Director::absoluteBaseURL();
653
    }
654
655
    /**
656
     * Debugging help
657
     *
658
     * @return string Debug info
659
     */
660
    public function debug()
661
    {
662
        $this->render();
663
664
        $class = static::class;
665
        return "<h2>Email template {$class}:</h2>\n" . '<pre>' . $this->getSwiftMessage()->toString() . '</pre>';
666
    }
667
668
    /**
669
     * @return string
670
     */
671
    public function getHTMLTemplate()
672
    {
673
        if ($this->HTMLTemplate) {
674
            return $this->HTMLTemplate;
675
        }
676
677
        return ThemeResourceLoader::inst()->findTemplate(
678
            SSViewer::get_templates_by_class(static::class, '', self::class),
679
            SSViewer::get_themes()
680
        );
681
    }
682
683
    /**
684
     * Set the template to render the email with
685
     *
686
     * @param string $template
687
     * @return $this
688
     */
689
    public function setHTMLTemplate($template)
690
    {
691
        if (substr($template, -3) == '.ss') {
692
            $template = substr($template, 0, -3);
693
        }
694
        $this->HTMLTemplate = $template;
695
696
        return $this;
697
    }
698
699
    /**
700
     * Get the template to render the plain part with
701
     *
702
     * @return string
703
     */
704
    public function getPlainTemplate()
705
    {
706
        return $this->plainTemplate;
707
    }
708
709
    /**
710
     * Set the template to render the plain part with
711
     *
712
     * @param string $template
713
     * @return $this
714
     */
715
    public function setPlainTemplate($template)
716
    {
717
        if (substr($template, -3) == '.ss') {
718
            $template = substr($template, 0, -3);
719
        }
720
        $this->plainTemplate = $template;
721
722
        return $this;
723
    }
724
725
    /**
726
     * @param array $recipients
727
     * @return $this
728
     */
729
    public function setFailedRecipients($recipients)
730
    {
731
        $this->failedRecipients = $recipients;
732
733
        return $this;
734
    }
735
736
    /**
737
     * @return array
738
     */
739
    public function getFailedRecipients()
740
    {
741
        return $this->failedRecipients;
742
    }
743
744
    /**
745
     * Used by {@link SSViewer} templates to detect if we're rendering an email template rather than a page template
746
     *
747
     * @return bool
748
     */
749
    public function IsEmail()
750
    {
751
        return true;
752
    }
753
754
    /**
755
     * Send the message to the recipients
756
     *
757
     * @return bool true if successful or array of failed recipients
758
     */
759
    public function send()
760
    {
761
        if (!$this->getBody()) {
762
            $this->render();
763
        }
764
        if (!$this->hasPlainPart()) {
765
            $this->generatePlainPartFromBody();
766
        }
767
        return Injector::inst()->get(Mailer::class)->send($this);
768
    }
769
770
    /**
771
     * @return array|bool
772
     */
773
    public function sendPlain()
774
    {
775
        if (!$this->hasPlainPart()) {
776
            $this->render(true);
777
        }
778
        return Injector::inst()->get(Mailer::class)->send($this);
779
    }
780
781
    /**
782
     * Render the email
783
     * @param bool $plainOnly Only render the message as plain text
784
     * @return $this
785
     */
786
    public function render($plainOnly = false)
787
    {
788
        if ($existingPlainPart = $this->findPlainPart()) {
789
            $this->getSwiftMessage()->detach($existingPlainPart);
790
        }
791
        unset($existingPlainPart);
792
793
        // Respect explicitly set body
794
        $htmlPart = $plainOnly ? null : $this->getBody();
795
        $plainPart = $plainOnly ? $this->getBody() : null;
796
797
        // Ensure we can at least render something
798
        $htmlTemplate = $this->getHTMLTemplate();
799
        $plainTemplate = $this->getPlainTemplate();
800
        if (!$htmlTemplate && !$plainTemplate && !$plainPart && !$htmlPart) {
801
            return $this;
802
        }
803
804
        // Do not interfere with emails styles
805
        Requirements::clear();
806
807
        // Render plain part
808
        if ($plainTemplate && !$plainPart) {
809
            $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

809
            $plainPart = $this->renderWith($plainTemplate, /** @scrutinizer ignore-type */ $this->getData())->Plain();
Loading history...
810
        }
811
812
        // Render HTML part, either if sending html email, or a plain part is lacking
813
        if (!$htmlPart && $htmlTemplate && (!$plainOnly || empty($plainPart))) {
814
            $htmlPart = $this->renderWith($htmlTemplate, $this->getData());
815
        }
816
817
        // Plain part fails over to generated from html
818
        if (!$plainPart && $htmlPart) {
819
            /** @var DBHTMLText $htmlPartObject */
820
            $htmlPartObject = DBField::create_field('HTMLFragment', $htmlPart);
821
            $plainPart = $htmlPartObject->Plain();
822
        }
823
824
        // Rendering is finished
825
        Requirements::restore();
826
827
        // Fail if no email to send
828
        if (!$plainPart && !$htmlPart) {
829
            return $this;
830
        }
831
832
        // Build HTML / Plain components
833
        if ($htmlPart && !$plainOnly) {
834
            $this->setBody($htmlPart);
835
            $this->getSwiftMessage()->setContentType('text/html');
836
            $this->getSwiftMessage()->setCharset('utf-8');
837
            if ($plainPart) {
838
                $this->getSwiftMessage()->addPart($plainPart, 'text/plain', 'utf-8');
839
            }
840
        } else {
841
            if ($plainPart) {
842
                $this->setBody($plainPart);
843
            }
844
            $this->getSwiftMessage()->setContentType('text/plain');
845
            $this->getSwiftMessage()->setCharset('utf-8');
846
        }
847
848
        return $this;
849
    }
850
851
    /**
852
     * @return Swift_MimePart|false
853
     */
854
    public function findPlainPart()
855
    {
856
        foreach ($this->getSwiftMessage()->getChildren() as $child) {
857
            if ($child instanceof Swift_MimePart && $child->getContentType() == 'text/plain') {
858
                return $child;
859
            }
860
        }
861
        return false;
862
    }
863
864
    /**
865
     * @return bool
866
     */
867
    public function hasPlainPart()
868
    {
869
        if ($this->getSwiftMessage()->getContentType() === 'text/plain') {
870
            return true;
871
        }
872
        return (bool) $this->findPlainPart();
873
    }
874
875
    /**
876
     * Automatically adds a plain part to the email generated from the current Body
877
     *
878
     * @return $this
879
     */
880
    public function generatePlainPartFromBody()
881
    {
882
        $plainPart = $this->findPlainPart();
883
        if ($plainPart) {
884
            $this->getSwiftMessage()->detach($plainPart);
885
        }
886
        unset($plainPart);
887
888
        $this->getSwiftMessage()->addPart(
889
            Convert::xml2raw($this->getBody()),
890
            'text/plain',
891
            'utf-8'
892
        );
893
894
        return $this;
895
    }
896
}
897