11

I want to replace all links in the HTML email with tracker. As far as I know there is this EVENT_BEFORE_SEND event. So I created some behavior that can be used like below

$mailer = \Yii::$app->mailer;
/* @var $mailer \yii\mail\BaseMailer */
$mailer->attachBehavior('archiver', [
   'class' => \app\MailTracker::class
]);

Here's the content of the MyTracker class.

class MailTracker extends Behavior {
    public function events() {
        return [
            \yii\mail\BaseMailer::EVENT_BEFORE_SEND => 'trackMail',
        ];
    }

    /**
     * @param \yii\mail\MailEvent $event
     */
     public function trackMail($event) {
        $message = $event->message;

        $htmlOutput = $this->how_do_i_get_the_html_output();
        $changedOutput = $this->changeLinkWithTracker($htmlOutput);
        $message->getHtmlBody($changedOutput);
     }
}

The problem now is \yii\mail\BaseMailer doesn't provide method to get the HTML output rendered before sending.

How to do this?

UPDATE

The only way I can get this is through this hacky way.

    /* @var $message \yii\swiftmailer\Message */
    if ($message instanceof \yii\swiftmailer\Message) {
        $swiftMessage = $message->getSwiftMessage();
        $r = new \ReflectionObject($swiftMessage);
        $parentClassThatHasBody = $r->getParentClass()
                ->getParentClass()
                ->getParentClass(); //\Swift_Mime_SimpleMimeEntity
        $body = $parentClassThatHasBody->getProperty('_immediateChildren');
        $body->setAccessible(true);
        $children = $body->getValue($swiftMessage);
        foreach ($children as $child) {
            if ($child instanceof \Swift_MimePart &&
                    $child->getContentType() == 'text/html') {
                $html = $child->getBody();
                break;
            }
        }
        print_r($html);
    }
robsch
  • 9,358
  • 9
  • 63
  • 104
Petra Barus
  • 3,815
  • 8
  • 48
  • 87
  • I've ended up having to do the same thing. Very frustrating. – Neil Bryson Aug 13 '15 at 12:59
  • 1
    To avoid using Reflection, I've managed to get the mail body (without encoding) by calling `$message->getChildren()[0]->getBody()` –  Mar 06 '17 at 18:35

3 Answers3

8

One approach that I've found is to use render() instead of compose(). So we need to render the message string before sending and then compose it again.

$string = Yii::$app->mailer->render('path/to/view', ['params' => 'foo'], 'path/to/layout');

Yii doc: yii\mail\BaseMailer::render()

Amoo Hesam
  • 461
  • 3
  • 13
0

Inorder to edit your email content, you can try editor in view with ckeditor . Ckeditor helps you to edit the contents as you desire.

https://github.com/2amigos/yii2-ckeditor-widget

Edit the contents before sending email.

0

I created a workaround with preg_match and substitutions, in case is helpful for anyone.

$message = \Yii::$app->mailer->compose('templateName', ['data' => $data])->toString();
// Workaround cause Swift_Mailer doesn't have getBody()
preg_match("/<body>(.*)<\/body>/si", $message, $matches);
$delimitedHtmlBody = $matches[1];
$htmlBody = str_replace("=\r\n", '', $delimitedHtmlBody);
$htmlBody = str_replace("=3D", '=', $htmlBody);
Uriel
  • 352
  • 5
  • 17