Completed
Push — master ( 32a670...32a37c )
by Damian
37s queued 21s
created

src/Control/Email/Email.php (7 issues)

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

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

392
        $this->getSwiftMessage()->addTo(/** @scrutinizer ignore-type */ $address, $name);
Loading history...
393
394
        return $this;
395
    }
396
397
    /**
398
     * @return array
399
     */
400
    public function getCC()
401
    {
402
        return $this->getSwiftMessage()->getCc();
403
    }
404
405
    /**
406
     * @param string|array $address
407
     * @param string|null $name
408
     * @return $this
409
     */
410
    public function setCC($address, $name = null)
411
    {
412
        $this->getSwiftMessage()->setCc($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 addCC($address, $name = null)
423
    {
424
        $this->getSwiftMessage()->addCc($address, $name);
0 ignored issues
show
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

424
        $this->getSwiftMessage()->addCc(/** @scrutinizer ignore-type */ $address, $name);
Loading history...
425
426
        return $this;
427
    }
428
429
    /**
430
     * @return array
431
     */
432
    public function getBCC()
433
    {
434
        return $this->getSwiftMessage()->getBcc();
435
    }
436
437
    /**
438
     * @param string|array $address
439
     * @param string|null $name
440
     * @return $this
441
     */
442
    public function setBCC($address, $name = null)
443
    {
444
        $this->getSwiftMessage()->setBcc($address, $name);
445
446
        return $this;
447
    }
448
449
    /**
450
     * @param string|array $address
451
     * @param string|null $name
452
     * @return $this
453
     */
454
    public function addBCC($address, $name = null)
455
    {
456
        $this->getSwiftMessage()->addBcc($address, $name);
0 ignored issues
show
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

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

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

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

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