Passed
Push — master ( bf0024...c8136b )
by Thomas
02:31
created

BetterEmail::setBody()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 7
c 1
b 0
f 0
nc 2
nop 1
dl 0
loc 12
rs 10
1
<?php
2
3
namespace LeKoala\EmailTemplates\Email;
4
5
use Exception;
6
use Swift_MimePart;
7
use BadMethodCallException;
8
use SilverStripe\i18n\i18n;
9
use SilverStripe\Control\HTTP;
10
use SilverStripe\View\SSViewer;
11
use SilverStripe\ORM\DataObject;
12
use SilverStripe\Security\Member;
13
use SilverStripe\Control\Director;
14
use SilverStripe\Security\Security;
15
use SilverStripe\View\Requirements;
16
use SilverStripe\Control\Controller;
17
use SilverStripe\Core\Config\Config;
18
use SilverStripe\Control\Email\Email;
19
use SilverStripe\SiteConfig\SiteConfig;
20
use LeKoala\EmailTemplates\Models\SentEmail;
21
use LeKoala\EmailTemplates\Helpers\EmailUtils;
22
use LeKoala\EmailTemplates\Models\EmailTemplate;
23
use LeKoala\EmailTemplates\Helpers\SubsiteHelper;
24
25
/**
26
 * An improved and more pleasant base Email class to use on your project
27
 *
28
 * This class is fully decoupled from the EmailTemplate class and keep be used
29
 * independantly
30
 *
31
 * Improvements are:
32
 *
33
 * - URL safe rewriting
34
 * - Configurable base template (base system use Email class with setHTMLTemplate to provide content)
35
 * - Send email according to member locale
36
 * - Check for subject
37
 * - Send to member or admin
38
 * - Persist emails
39
 * - Parse body (multi part body is supported)
40
 * - Plaintext takes template into account
41
 * - Disable emails
42
 * - Unified send methods that support hooks
43
 *
44
 * @author lekoala
45
 */
46
class BetterEmail extends Email
47
{
48
    const STATE_CANCELLED = 'cancelled';
49
    const STATE_NOT_SENT = 'not_sent';
50
    const STATE_SENT = 'sent';
51
    const STATE_FAILED = 'failed';
52
53
    /**
54
     * @var EmailTemplate
55
     */
56
    protected $emailTemplate;
57
58
    /**
59
     * @var string
60
     */
61
    protected $locale;
62
63
    /**
64
     * @var Member
65
     */
66
    protected $to_member;
67
68
    /**
69
     * @var Member
70
     */
71
    protected $from_member;
72
73
    /**
74
     * @var boolean
75
     */
76
    protected $disabled = false;
77
78
    /**
79
     * @var SentMail
0 ignored issues
show
Bug introduced by
The type LeKoala\EmailTemplates\Email\SentMail was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
80
     */
81
    protected $sentMail = null;
82
83
    /**
84
     * @var boolean
85
     */
86
    protected $sendingCancelled = false;
87
88
    /**
89
     * Email constructor.
90
     * @param string|array|null $from
91
     * @param string|array|null $to
92
     * @param string|null $subject
93
     * @param string|null $body
94
     * @param string|array|null $cc
95
     * @param string|array|null $bcc
96
     * @param string|null $returnPath
97
     */
98
    public function __construct(
99
        $from = null,
100
        $to = null,
101
        $subject = null,
102
        $body = null,
103
        $cc = null,
104
        $bcc = null,
105
        $returnPath = null
106
    ) {
107
        parent::__construct($from, $to, $subject, $body, $cc, $bcc, $returnPath);
108
109
        // Use template as a layout
110
        if ($defaultTemplate = self::config()->template) {
111
            // Call method because variable is private
112
            parent::setHTMLTemplate($defaultTemplate);
113
        }
114
    }
115
116
    /**
117
     * Persists the email to the database
118
     *
119
     * @param bool|array $results
120
     * @return SentEmail
121
     */
122
    protected function persist($results)
123
    {
124
        $record = SentEmail::create(array(
125
            'To' => EmailUtils::format_email_addresses($this->getTo()),
126
            'From' => EmailUtils::format_email_addresses($this->getFrom()),
127
            'ReplyTo' => $this->getReplyTo(),
128
            'Subject' => $this->getSubject(),
129
            'Body' => $this->getRenderedBody(),
130
            'Headers' => $this->getSwiftMessage()->getHeaders()->toString(),
131
            'CC' => EmailUtils::format_email_addresses($this->getCC()),
132
            'BCC' => EmailUtils::format_email_addresses($this->getBCC()),
133
            'Results' => json_encode($results),
134
        ));
135
        $record->write();
136
137
        // TODO: migrate this to a cron task
138
        SentEmail::cleanup();
139
140
        return $record;
141
    }
142
143
144
    /**
145
     * Get body of message after rendering
146
     * Useful for previews
147
     *
148
     * @return string
149
     */
150
    public function getRenderedBody()
151
    {
152
        $this->render();
153
        return $this->getSwiftMessage()->getBody();
154
    }
155
156
    /**
157
     * Don't forget that setBody will erase content of html template
158
     * Prefer to use this instead. Basically you can replace setBody calls with this method
159
     * URLs are rewritten by render process
160
     *
161
     * @param string $body
162
     * @return $this
163
     */
164
    public function addBody($body)
165
    {
166
        return $this->addData("EmailContent", $body);
167
    }
168
169
    /**
170
     * @param string $body The email body
171
     * @return $this
172
     */
173
    public function setBody($body)
174
    {
175
        $plainPart = $this->findPlainPart();
176
        if ($plainPart) {
177
            $this->getSwiftMessage()->detach($plainPart);
178
        }
179
        unset($plainPart);
180
181
        $body = self::rewriteURLs($body);
182
        $this->getSwiftMessage()->setBody($body);
183
184
        return $this;
185
    }
186
187
    /**
188
     * @param array|ViewableData $data The template data to set
0 ignored issues
show
Bug introduced by
The type LeKoala\EmailTemplates\Email\ViewableData was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
189
     * @return $this
190
     */
191
    public function setData($data)
192
    {
193
        // Merge data!
194
        if ($this->emailTemplate) {
195
            if (is_array($data)) {
196
                parent::addData($data);
197
            } elseif ($data instanceof DataObject) {
198
                parent::addData($data->toMap());
199
            } else {
200
                parent::setData($data);
201
            }
202
        } else {
203
            parent::setData($data);
204
        }
205
        return $this;
206
    }
207
208
    /**
209
     * Sends a HTML email
210
     *
211
     * @return bool true if successful or array of failed recipients
212
     */
213
    public function send()
214
    {
215
        return $this->doSend(false);
216
    }
217
218
    /**
219
     * Sends a plain text email
220
     *
221
     * @return bool true if successful or array of failed recipients
222
     */
223
    public function sendPlain()
224
    {
225
        return $this->doSend(true);
226
    }
227
228
    /**
229
     * Send this email
230
     *
231
     * @param bool $plain
232
     * @return bool true if successful or array of failed recipients
233
     * @throws Exception
234
     */
235
    public function doSend($plain = false)
236
    {
237
        if ($this->disabled) {
238
            $this->sendingCancelled = true;
239
            return false;
240
        }
241
242
        // Check for Subject
243
        if (!$this->getSubject()) {
244
            throw new BadMethodCallException('You must set a subject');
245
        }
246
247
        // This hook can prevent email from being sent
248
        $result = $this->extend('onBeforeDoSend', $this);
249
        if ($result === false) {
0 ignored issues
show
introduced by
The condition $result === false is always false.
Loading history...
250
            $this->sendingCancelled = true;
251
            return false;
252
        }
253
254
        $SiteConfig = SiteConfig::current_site_config();
255
256
        // Check for Sender and use default if necessary
257
        $from = $this->getFrom();
258
        if (empty($from)) {
259
            $this->setFrom($SiteConfig->EmailDefaultSender());
260
        }
261
262
        // Check for Recipient and use default if necessary
263
        $to = $this->getTo();
264
        if (empty($to)) {
265
            $this->addTo($SiteConfig->EmailDefaultRecipient());
266
        }
267
268
        // Set language to use for the email
269
        $restore_locale = null;
270
        if ($this->locale) {
271
            $restore_locale = i18n::get_locale();
272
            i18n::set_locale($this->locale);
273
        }
274
275
        $member = $this->to_member;
276
        if ($member) {
277
            // Maybe this member doesn't want to receive emails?
278
            if ($member->hasMethod('canReceiveEmails') && !$member->canReceiveEmails()) {
279
                return false;
280
            }
281
        }
282
283
        // Make sure we have a full render with current locale
284
        if ($this->emailTemplate) {
285
            $this->clearBody();
286
        }
287
288
        if ($plain) {
289
            // sendPlain will trigger our updated generatePlainPartFromBody
290
            $res = parent::sendPlain();
291
        } else {
292
            $res = parent::send();
293
        }
294
295
        if ($restore_locale) {
296
            i18n::set_locale($restore_locale);
297
        }
298
299
        $this->extend('onAfterDoSend', $this, $res);
300
        $this->sentMail = $this->persist($res);
301
302
        return $res;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $res also could return the type array which is incompatible with the documented return type boolean.
Loading history...
303
    }
304
305
    /**
306
     * Returns one of the STATE_xxxx constant
307
     *
308
     * @return string
309
     */
310
    public function getSendStatus()
311
    {
312
        if ($this->sendingCancelled) {
313
            return self::STATE_CANCELLED;
314
        }
315
        if ($this->sentMail) {
316
            if ($this->sentMail->IsSuccess()) {
317
                return self::STATE_SENT;
318
            }
319
            return self::STATE_FAILED;
320
        }
321
        return self::STATE_NOT_SENT;
322
    }
323
324
    /**
325
     * Was sending cancelled ?
326
     *
327
     * @return bool
328
     */
329
    public function getSendingCancelled()
330
    {
331
        return $this->sendingCancelled;
332
    }
333
334
    /**
335
     * The last result from "send" method. Null if not sent yet or sending was cancelled
336
     *
337
     * @return SentMail
338
     */
339
    public function getSentMail()
340
    {
341
        return $this->sentMail;
342
    }
343
344
    /**
345
     * Automatically adds a plain part to the email generated from the current Body
346
     *
347
     * @return $this
348
     */
349
    public function generatePlainPartFromBody()
350
    {
351
        $plainPart = $this->findPlainPart();
352
        if ($plainPart) {
353
            $this->getSwiftMessage()->detach($plainPart);
354
        }
355
        unset($plainPart);
356
357
        $this->getSwiftMessage()->addPart(
358
            EmailUtils::convert_html_to_text($this->getBody()),
359
            'text/plain',
360
            'utf-8'
361
        );
362
363
        return $this;
364
    }
365
366
    /**
367
     * @return $this
368
     */
369
    public function clearBody()
370
    {
371
        $this->getSwiftMessage()->setBody(null);
372
        return $this;
373
    }
374
375
    /**
376
     * Set the template to render the email with
377
     *
378
     * This method is overidden in order to look for email templates to provide
379
     * content to
380
     *
381
     * @param string $template
382
     * @return $this
383
     */
384
    public function setHTMLTemplate($template)
385
    {
386
        if (substr($template, -3) == '.ss') {
387
            $template = substr($template, 0, -3);
388
        }
389
390
        // Do we have a custom template matching this code?
391
        $code = self::makeTemplateCode($template);
392
        $emailTemplate = EmailTemplate::getByCode($code, false);
393
        if ($emailTemplate) {
0 ignored issues
show
introduced by
$emailTemplate is of type LeKoala\EmailTemplates\Models\EmailTemplate, thus it always evaluated to true.
Loading history...
394
            $emailTemplate->applyTemplate($this);
395
            return $this;
396
        }
397
398
        // If not, keep default behaviour (call method because var is private)
399
        return parent::setHTMLTemplate($template);
400
    }
401
402
    /**
403
     * Make a template code
404
     *
405
     * @param string $str
406
     * @return string
407
     */
408
    public static function makeTemplateCode($str)
409
    {
410
        // If we get a class name
411
        $parts = explode('\\', $str);
412
        $str = end($parts);
413
        $code = preg_replace('/Email$/', '', $str);
414
        return $code;
415
    }
416
417
    /**
418
     * Helper method to render string with data
419
     *
420
     * @param string $content
421
     * @return string
422
     */
423
    public function renderWithData($content)
424
    {
425
        $viewer = SSViewer::fromString($content);
426
        $data = $this->getData();
427
        // SSViewer_DataPresenter requires array
428
        if (is_object($data)) {
429
            if (method_exists($data, 'toArray')) {
430
                $data = $data->toArray();
431
            } else {
432
                $data = (array) $data;
433
            }
434
        }
435
        $result = (string) $viewer->process($this, $data);
436
        $result = self::rewriteURLs($result);
437
        return $result;
438
    }
439
440
    /**
441
     * Render the email
442
     * @param bool $plainOnly Only render the message as plain text
443
     * @return $this
444
     */
445
    public function render($plainOnly = false)
446
    {
447
        if ($existingPlainPart = $this->findPlainPart()) {
448
            $this->getSwiftMessage()->detach($existingPlainPart);
449
        }
450
        unset($existingPlainPart);
451
452
        // Respect explicitly set body
453
        $htmlPart = $plainPart = null;
454
455
        // Only respect if we don't have an email template
456
        if ($this->emailTemplate) {
457
            $htmlPart = $plainOnly ? null : $this->getBody();
458
            $plainPart = $plainOnly ? $this->getBody() : null;
459
        }
460
461
        // Ensure we can at least render something
462
        $htmlTemplate = $this->getHTMLTemplate();
463
        $plainTemplate = $this->getPlainTemplate();
464
        if (!$htmlTemplate && !$plainTemplate && !$plainPart && !$htmlPart) {
465
            return $this;
466
        }
467
468
        // Do not interfere with emails styles
469
        Requirements::clear();
470
471
        // Render plain part
472
        if ($plainTemplate && !$plainPart) {
473
            $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

473
            $plainPart = $this->renderWith($plainTemplate, /** @scrutinizer ignore-type */ $this->getData())->Plain();
Loading history...
474
            // Do another round of rendering to render our variables inside
475
            $plainPart = $this->renderWithData($plainPart);
476
        }
477
478
        // Render HTML part, either if sending html email, or a plain part is lacking
479
        if (!$htmlPart && $htmlTemplate && (!$plainOnly || empty($plainPart))) {
480
            $htmlPart = $this->renderWith($htmlTemplate, $this->getData());
481
            // Do another round of rendering to render our variables inside
482
            $htmlPart = $this->renderWithData($htmlPart);
483
        }
484
485
        // Render subject with data as well
486
        $subject = $this->renderWithData($this->getSubject());
487
        // Html entities in email titles is not a good idea
488
        $subject = html_entity_decode($subject, ENT_QUOTES | ENT_XML1, 'UTF-8');
489
        // Avoid crazy template name in email
490
        $subject = preg_replace("/<!--(.)+-->/", "", $subject);
491
        parent::setSubject($subject);
492
493
        // Plain part fails over to generated from html
494
        if (!$plainPart && $htmlPart) {
495
            $plainPart = EmailUtils::convert_html_to_text($htmlPart);
496
        }
497
498
        // Rendering is finished
499
        Requirements::restore();
500
501
        // Fail if no email to send
502
        if (!$plainPart && !$htmlPart) {
503
            return $this;
504
        }
505
506
        // Build HTML / Plain components
507
        if ($htmlPart && !$plainOnly) {
508
            $this->setBody($htmlPart);
509
            $this->getSwiftMessage()->setContentType('text/html');
510
            $this->getSwiftMessage()->setCharset('utf-8');
511
            if ($plainPart) {
512
                $this->getSwiftMessage()->addPart($plainPart, 'text/plain', 'utf-8');
513
            }
514
        } else {
515
            if ($plainPart) {
516
                $this->setBody($plainPart);
517
            }
518
            $this->getSwiftMessage()->setContentType('text/plain');
519
            $this->getSwiftMessage()->setCharset('utf-8');
520
        }
521
522
        return $this;
523
    }
524
525
    /**
526
     * Get locale set before email is sent
527
     *
528
     * @return string
529
     */
530
    public function getLocale()
531
    {
532
        return $this->locale;
533
    }
534
535
    /**
536
     *  Set locale to set before email is sent
537
     *
538
     * @param string $val
539
     */
540
    public function setLocale($val)
541
    {
542
        $this->locale = $val;
543
    }
544
545
    /**
546
     * Is this email disabled ?
547
     *
548
     * @return boolean
549
     */
550
    public function getDisabled()
551
    {
552
        return $this->disabled;
553
    }
554
555
    /**
556
     * Disable this email (sending will have no effect)
557
     *
558
     * @param bool $disabled
559
     * @return $this
560
     */
561
    public function setDisabled($disabled)
562
    {
563
        $this->disabled = (bool) $disabled;
564
        return $this;
565
    }
566
567
    /**
568
     * Get recipient as member
569
     *
570
     * @return Member
571
     */
572
    public function getToMember()
573
    {
574
        if (!$this->to_member && $this->to) {
0 ignored issues
show
Bug Best Practice introduced by
The property to does not exist on LeKoala\EmailTemplates\Email\BetterEmail. Since you implemented __get, consider adding a @property annotation.
Loading history...
575
            $email = EmailUtils::get_email_from_rfc_email($this->to);
576
            $member = Member::get()->filter(array('Email' => $email))->first();
577
            if ($member) {
0 ignored issues
show
introduced by
$member is of type SilverStripe\ORM\DataObject, thus it always evaluated to true.
Loading history...
578
                $this->setToMember($member);
579
            }
580
        }
581
        return $this->to_member;
582
    }
583
584
    /**
585
     * Set recipient(s) of the email
586
     *
587
     * To send to many, pass an array:
588
     * array('[email protected]' => 'My Name', '[email protected]');
589
     *
590
     * @param string|array $address The message recipient(s) - if sending to multiple, use an array of address => name
591
     * @param string|null $name The name of the recipient (if one)
592
     * @return $this
593
     */
594
    public function setTo($address, $name = null)
595
    {
596
        // Allow Name <[email protected]>
597
        if (!$name && is_string($address)) {
598
            $name = EmailUtils::get_displayname_from_rfc_email($address);
599
            $address = EmailUtils::get_email_from_rfc_email($address);
600
        }
601
        // Make sure this doesn't conflict with to_member property
602
        if ($this->to_member) {
603
            if (is_string($address)) {
604
                // We passed an email that doesn't match to member
605
                if ($this->to_member->Email != $address) {
606
                    $this->to_member = null;
607
                }
608
            } else {
609
                $this->to_member = null;
610
            }
611
        }
612
        return parent::setTo($address, $name);
613
    }
614
615
616
617
    /**
618
     * @param string $subject The Subject line for the email
619
     * @return $this
620
     */
621
    public function setSubject($subject)
622
    {
623
        // Do not allow changing subject if a template is set
624
        if ($this->emailTemplate && $this->getSubject()) {
625
            return $this;
626
        }
627
        return parent::setSubject($subject);
628
    }
629
630
    /**
631
     * Send to admin
632
     *
633
     * @return Email
634
     */
635
    public function setToAdmin()
636
    {
637
        $admin = Security::findAnAdministrator();
0 ignored issues
show
Deprecated Code introduced by
The function SilverStripe\Security\Se...::findAnAdministrator() has been deprecated: 4.0.0:5.0.0 Please use DefaultAdminService::findOrCreateDefaultAdmin() ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

637
        $admin = /** @scrutinizer ignore-deprecated */ Security::findAnAdministrator();

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
638
        return $this->setToMember($admin);
639
    }
640
641
    /**
642
     * Set to
643
     *
644
     * @return Email
645
     */
646
    public function setToContact()
647
    {
648
        $email = SiteConfig::current_site_config()->EmailDefaultRecipient();
649
        return $this->setTo($email);
650
    }
651
652
    /**
653
     * Add in bcc admin
654
     *
655
     * @return Email
656
     */
657
    public function bccToAdmin()
658
    {
659
        $admin = Security::findAnAdministrator();
0 ignored issues
show
Deprecated Code introduced by
The function SilverStripe\Security\Se...::findAnAdministrator() has been deprecated: 4.0.0:5.0.0 Please use DefaultAdminService::findOrCreateDefaultAdmin() ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

659
        $admin = /** @scrutinizer ignore-deprecated */ Security::findAnAdministrator();

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
660
        return $this->addBCC($admin->Email);
661
    }
662
663
    /**
664
     * Add in bcc admin
665
     *
666
     * @return Email
667
     */
668
    public function bccToContact()
669
    {
670
        $email = SiteConfig::current_site_config()->EmailDefaultRecipient();
671
        return $this->addBCC($email);
672
    }
673
674
    /**
675
     * Set a member as a recipient.
676
     *
677
     * It will also set the $Recipient variable in the template
678
     *
679
     * @param Member $member
680
     * @param string $locale Locale to use, set to false to keep current locale
681
     * @return BetterEmail
682
     */
683
    public function setToMember(Member $member, $locale = null)
684
    {
685
        if ($locale === null) {
686
            $this->locale = $member->Locale;
687
        } else {
688
            $this->locale = $locale;
689
        }
690
        $this->to_member = $member;
691
692
        $this->addData(array('Recipient' => $member));
693
694
        return $this->setTo($member->Email, $member->getTitle());
695
    }
696
697
    /**
698
     * Get sender as member
699
     *
700
     * @return Member
701
     */
702
    public function getFromMember()
703
    {
704
        if (!$this->from_member && $this->from) {
0 ignored issues
show
Bug Best Practice introduced by
The property from does not exist on LeKoala\EmailTemplates\Email\BetterEmail. Since you implemented __get, consider adding a @property annotation.
Loading history...
705
            $email = EmailUtils::get_email_from_rfc_email($this->from);
706
            $member = Member::get()->filter(array('Email' => $email))->first();
707
            if ($member) {
0 ignored issues
show
introduced by
$member is of type SilverStripe\ORM\DataObject, thus it always evaluated to true.
Loading history...
708
                $this->setFromMember($member);
709
            }
710
        }
711
        return $this->from_member;
712
    }
713
714
    /**
715
     * Set From Member
716
     *
717
     * It will also set the $Sender variable in the template
718
     *
719
     * @param Member $member
720
     * @return BetterEmail
721
     */
722
    public function setFromMember(Member $member)
723
    {
724
        $this->from_member = $member;
725
726
        $this->addData(array('Sender' => $member));
727
728
        return $this->setFrom($member->Email, $member->getTitle());
729
    }
730
731
    /**
732
     * Improved set from that supports Name <[email protected]> notation
733
     *
734
     * @param string|array $address
735
     * @param string|null $name
736
     * @return $this
737
     */
738
    public function setFrom($address, $name = null)
739
    {
740
        if (!$name && is_string($address)) {
741
            $name = EmailUtils::get_displayname_from_rfc_email($address);
742
            $address = EmailUtils::get_email_from_rfc_email($address);
743
        }
744
        return parent::setFrom($address, $name);
745
    }
746
747
    /**
748
     * Bug safe absolute url that support subsites
749
     *
750
     * @param string $url
751
     * @param bool $relativeToSiteBase
752
     * @return string
753
     */
754
    protected static function safeAbsoluteURL($url, $relativeToSiteBase = false)
755
    {
756
        if (empty($url)) {
757
            $absUrl = Director::baseURL();
758
        } else {
759
            $firstCharacter = substr($url, 0, 1);
760
761
            // It's a merge tag, don't touch it because we don't know what kind of url it contains
762
            if (in_array($firstCharacter, ['*', '$', '%'])) {
763
                return $url;
764
            }
765
766
            $absUrl = Director::absoluteURL($url, $relativeToSiteBase);
767
        }
768
769
        // If we use subsite, absolute url may not use the proper url
770
        if (SubsiteHelper::usesSubsite()) {
771
            $subsite = SubsiteHelper::currentSubsite();
772
            if ($subsite->hasMethod('getPrimarySubsiteDomain')) {
773
                $domain = $subsite->getPrimarySubsiteDomain();
774
                $link = $subsite->domain();
775
                $protocol = $domain->getFullProtocol();
776
            } else {
777
                $protocol = Director::protocol();
778
                $link = $subsite->domain();
779
            }
780
            $absUrl = preg_replace('/\/\/[^\/]+\//', '//' . $link . '/', $absUrl);
781
            $absUrl = preg_replace('/http(s)?:\/\//', $protocol, $absUrl);
782
        }
783
784
        return $absUrl;
785
    }
786
787
    /**
788
     * Turn all relative URLs in the content to absolute URLs
789
     */
790
    protected static function rewriteURLs($html)
791
    {
792
        if (isset($_SERVER['REQUEST_URI'])) {
793
            $html = str_replace('$CurrentPageURL', $_SERVER['REQUEST_URI'], $html);
794
        }
795
        return HTTP::urlRewriter($html, function ($url) {
796
            //no need to rewrite, if uri has a protocol (determined here by existence of reserved URI character ":")
797
            if (preg_match('/^\w+:/', $url)) {
798
                return $url;
799
            }
800
            return self::safeAbsoluteURL($url, true);
801
        });
802
    }
803
804
    /**
805
     * Get the value of emailTemplate
806
     * @return EmailTemplate
807
     */
808
    public function getEmailTemplate()
809
    {
810
        return $this->emailTemplate;
811
    }
812
813
    /**
814
     * Set the value of emailTemplate
815
     *
816
     * @param EmailTemplate $emailTemplate
817
     * @return $this
818
     */
819
    public function setEmailTemplate(EmailTemplate $emailTemplate)
820
    {
821
        $this->emailTemplate = $emailTemplate;
822
        return $this;
823
    }
824
}
825