Emails


有多种方法可以扩展 Mautic 与电子邮件的交互方式。本文档描述了以下扩展 Mautic 电子邮件功能的选项:

  • 邮件令牌

  • A/B 测试

  • 监控收件箱集成

  • 邮件传输或邮件提供商

  • 邮件统计助手

Note

通常,扩展是通过使用事件监听器或订阅者来钩入事件来实现的。更多信息请阅读 listeners and subscribers 部分。

邮件令牌

邮件令牌是您可以插入到电子邮件中的占位符。动态生成的的内容会替换这些令牌,当 Mautic 发送电子邮件或用户在浏览器中查看它时。

邮件令牌功能由两部分组成:

  • 注册自定义令牌

  • 渲染自定义令牌

在构建器中注册自定义令牌

注册令牌利用 \Mautic\EmailBundle\EmailEvents::EMAIL_ON_BUILD 事件。 在显示电子邮件构建表单之前会触发此事件,以便添加令牌。

一个事件监听器接收到 Mautic\EmailBundle\Event\EmailBuilderEvent。 使用其 $event->addToken($token, $htmlContent) 方法来添加您的令牌。

Note

您可以将令牌的文本描述硬编码在 $htmlContent 中,也可以使用可翻译的字符串。

渲染自定义令牌

要渲染自定义令牌,请在使用 \Mautic\EmailBundle\EmailEvents::EMAIL_ON_SEND 事件时,当 Mautic 发送电子邮件时,或在使用 \Mautic\EmailBundle\EmailEvents::EMAIL_ON_DISPLAY 事件时,当电子邮件在浏览器中显示时(例如,联系人点击 {webview_url} 链接后),进行操作。

一个事件监听器在两种情况下都会接收到 Mautic\EmailBundle\Event\EmailSendEvent。 您可以使用事件的 $event->addToken($token, $contentToReplaceToken) 方法来替换自定义令牌。

基本令牌替换

<?php

// plugins/HelloWorldBundle/EventListener/EmailSubscriber.php
class EmailSubscriber implements EventSubscriberInterface
{

  public static function getSubscribedEvents(): array
  {
    return [
      EmailEvents::EMAIL_ON_BUILD => ['onEmailBuild', 0],
      EmailEvents::EMAIL_ON_SEND => ['onEmailGenerate', 0],
      EmailEvents::EMAIL_ON_DISPLAY => ['onEmailGenerate', 0],
    ];
  }

  public function onEmailBuild(EmailBuilderEvent $event): void
  {
    $event->addToken('{my_custom_token}', 'My Custom Token');
  }

  public function onEmailGenerate(EmailSendEvent $event): void
  {
    $event->addToken('{my_custom_token}', 'Hello <b>World!</b>');
  }
}

Note

对于更复杂的替换,请使用事件的 $event->getContent()$event->setContent() 方法。

邮件 A/B 测试

While Mautic supports A/B testing out of the box, you might have more complex needs to determine A/B test winner criteria.

  • Use $event->addAbTestWinnerCriteria() to apply your custom logic when deciding the winner based on specific criteria.

  • Set the actual A/B test results with $event->setAbTestResults().

A/B testing examples

<?php
// plugins/HelloWorldBundle/EventListener/EmailSubscriber.php

declare(strict_types=1);

namespace MauticPlugin\HelloWorldBundle\EventListener;

use Mautic\CoreBundle\Helper\TemplatingHelper;
use Mautic\EmailBundle\EmailEvents;
use Mautic\EmailBundle\Event\EmailBuilderEvent;
use Mautic\EmailBundle\Event\EmailSendEvent;
use MauticPlugin\HelloWorldBundle\HelloWorldEvents;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

final class EmailSubscriber implements EventSubscriberInterface
{
    private TemplatingHelper $templating;

    public function __construct(TemplatingHelper $templating)
    {
        $this->templating = $templating;
    }

    public static function getSubscribedEvents(): array
    {
        return [
            EmailEvents::EMAIL_ON_BUILD   => ['onEmailBuild', 0],
            EmailEvents::EMAIL_ON_SEND    => ['onEmailGenerate', 0],
            EmailEvents::EMAIL_ON_DISPLAY => ['onEmailGenerate', 0],
        ];
    }

    /**
    * Register the token and a custom A/B test winner
    */
    public function onEmailBuild(EmailBuilderEvent $event): void
    {
        // Displays the token in the email builder, so that users can easily find it and add it to their emails
        $event->addToken('helloworld.token', 'Hello world token');

        // Add AB Test Winner Criteria
        $event->addAbTestWinnerCriteria(
            'helloworld.planetvisits',
            [
                // Label to group by
                'group' => 'plugin.helloworld.header',

                // Label for this specific a/b test winning criteria
                'label' => 'plugin.helloworld.emailtokens.',

                // Event that will be used to determine the winner
                'event' => HelloWorldEvents::ON_DETERMINE_PLANET_VISIT_WINNER
            ]
        );
    }

    /**
    * Search and replace tokens with content
    */
    public function onEmailGenerate(EmailSendEvent $event): void
    {
        // Get content
        $content = $event->getContent();

        // Search and replace tokens
        $content = str_replace(
            '{helloworld.token}',
            $this->templating->getTemplating()->render('HelloWorldBundle:SubscribedEvents\EmailToken:token.html.php'),
            $content
        );
// 设置更新后的内容

$event->setContent($content);

}

}

<?php
// plugins/HelloWorldBundle/EventListener/PlanetVisitSubscriber.php

declare(strict_types=1);

namespace MauticPlugin\HelloWorldBundle\EventListener;

use Mautic\CoreBundle\Event\DetermineWinnerEvent;
use MauticPlugin\HelloWorldBundle\HelloWorldEvents;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

final class PlanetVisitSubscriber implements EventSubscriberInterface
{
    public static function getSubscribedEvents(): array
    {
        return [
            HelloWorldEvents::ON_DETERMINE_PLANET_VISIT_WINNER   => ['onDeterminePlanetVisitWinner', 0],
        ];
    }

    public function onDeterminePlanetVisitWinner(DetermineWinnerEvent $event): void
    {
        $event->setAbTestResults([
            'winners' => [],
            'support' => [
                'labels' => ['label1', 'label2'],
                'data'   => [
                    'label1' => [100,200],
                    'label2' => [200,300]
                ],
                'step_width' => 10
            ],
            'supportTemplate' => 'HelloWorldBundle:SubscribedEvents\AbTest:bargraph.html.php'
        ]);
    }
}
<?php
// plugins/HelloWorldBundle/Views/SubscribedEvents/AbTest/bargraph.html.php

declare(strict_types=1);

$support = $results['support'];
$label   = 'My chart label';
$chart   = new \Mautic\CoreBundle\Helper\Chart\BarChart($support['labels']);

if ($support['data']) {
    foreach ($support['data'] as $datasetLabel => $values) {
        $chart->setDataset($datasetLabel, $values);
    }
}
?>

<div class="panel ovf-h bg-auto bg-light-xs abtest-bar-chart">
    <div class="panel-body box-layout">
        <div class="col-xs-8 va-m">
            <h5 class="text-white dark-md fw-sb mb-xs">
                <?php echo $label; ?>
            </h5>
        </div>
        <div class="col-xs-4 va-t text-right">
            <h3 class="text-white dark-sm"><span class="fa fa-bar-chart"></span></h3>
        </div>
    </div>
    <?php echo $view->render(
        'MauticCoreBundle:Helper:chart.html.php',
        ['chartData' => $chart->render(), 'chartType' => 'bar', 'chartHeight' => 300]
    ); ?>
</div>

监控的收件箱集成

插件可以访问 mautic:email:fetch 命令,以从特定收件箱/文件夹中获取电子邮件并处理消息的内容。 该插件还可以注入用于处理的消息的特定搜索条件。

为此,插件需要为三个事件添加一个事件监听器:

  1. EmailEvents::MONITORED_EMAIL_CONFIG 此事件用于向 Mautic 的配置中注入字段,以配置应监视的 IMAP 收件箱和文件夹。

  2. EmailEvents::EMAIL_PRE_FETCH 此事件在执行 mautic:email:fetch 命令时触发。它用于注入用于指定所需消息的搜索条件。

  3. EmailEvents::EMAIL_PARSE 此事件解析由命令获取的消息。

<?php
// plugins/HelloWorldBundle/EventListener/MonitoredInboxSubscriber.php

declare(strict_types=1);

namespace MauticPlugin\HelloWorldBundle\EventListener;

use Mautic\EmailBundle\EmailEvents;
use Mautic\EmailBundle\Event\MonitoredEmailEvent;
use Mautic\EmailBundle\Event\ParseEmailEvent;
use Mautic\EmailBundle\MonitoredEmail\Mailbox;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

final class MonitoredInboxSubscriber implements EventSubscriberInterface
{
    private $bundle = 'HelloWorldBundle';
    private $monitor =  'deep_space_emails';

    static public function getSubscribedEvents(): array
    {
        return [
            EmailEvents::MONITORED_EMAIL_CONFIG => ['onConfig', 0],
            EmailEvents::EMAIL_PRE_FETCH        => ['onPreFetch', 0],
            EmailEvents::EMAIL_PARSE            => ['onParse', 0],
        ];
    }

    /**
    * Inject the IMAP folder settings into the Configuration
    */
    public function onConfig(MonitoredEmailEvent $event): void
    {
        /**
        * The first argument is something unique to recognize this plugin.
        * The second argument should be something unique to identify this monitored inbox.
        * The third argument is the label for this monitored inbox.
        */
        $event->addFolder($this->bundle, $this->monitor, 'mautic.world.monitored_deep_space_emails');
    }

    /**
    * Inject search criteria for which messages to fetch from the configured folder.
    */
    public function onPreFetch(ParseEmailEvent $event): void
    {
        $event->setCriteriaRequest($this->bundle, $this->monitor, Mailbox::CRITERIA_UNSEEN. " " . Mailbox::CRITERIA_FROM ." aliens@andromeda");
    }

    /**
    * Parse the messages
    */
    public function onParse(ParseEmailEvent $event): void
    {
        if ($event->isApplicable($this->bundle, $this->monitor)) {
            $messages = $event->getMessages();

            foreach ($messages as $message) {
                // Do something
            }
        }
    }
}

Email transports

Mautic 默认支持多种电子邮件提供商(Amazon Simple Email Service、SendGrid 等)。 如果您想添加自己的电子邮件传输方式,这当然也是可以的。

最重要的一点是创建一个带有 mautic.email.transport_type 标签的服务,以便 Mautic 将其识别为一种传输类型。

<?php
// plugins/HelloWorldBundle/Config/config.php

declare(strict_types=1);

return [

    ...

    'services'    => [

        ...

        'other' => [
            'mautic.transport.helloworld_api' => [
                'class'        => \MauticPlugin\HelloWorldBundle\Swiftmailer\Transport\HelloWorldApiTransport::class,
                'serviceAlias' => 'swiftmailer.mailer.transport.%s',
                'arguments'    => [
                    'mautic.helper.core_parameters',
                ],
                'tag'          => 'mautic.email_transport',
                'tagArguments' => [
                    # 可翻译的别名,用作传输类型的内部键,也用作翻译键。
                    \Mautic\EmailBundle\Model\TransportType::TRANSPORT_ALIAS => 'mautic.email.config.mailer_transport.helloworld_api',
                    # 确定在 Mautic 的配置屏幕(电子邮件设置下)中显示哪些字段
                    \Mautic\EmailBundle\Model\TransportType::FIELD_HOST      => true,
                    \Mautic\EmailBundle\Model\TransportType::FIELD_API_KEY   => true,
                    \Mautic\EmailBundle\Model\TransportType::FIELD_PASSWORD  => true,
                    \Mautic\EmailBundle\Model\TransportType::FIELD_PORT      => true,
                    \Mautic\EmailBundle\Model\TransportType::FIELD_USER      => true
                ],
            ],
        ],
    ],
];

服务的实际实现可能如下所示:

<?php
// plugin/HelloWorldBundle/Swiftmailer/Transport/HelloeWorldApiTransport.php

declare(strict_types=1);

namespace MauticPlugin\HelloWorldBundle\Swiftmailer\Transport;

use Mautic\CoreBundle\Helper\CoreParametersHelper;
use Mautic\EmailBundle\Swiftmailer\Transport\AbstractTokenArrayTransport;
use Mautic\EmailBundle\Swiftmailer\Transport\CallbackTransportInterface;
use Symfony\Component\HttpFoundation\Request;

class HelloWorldApiTransport extends AbstractTokenArrayTransport implements \Swift_Transport, CallbackTransportInterface
{
    private CoreParametersHelper $coreParametersHelper;

    public function __construct(CoreParametersHelper $coreParametersHelper)
    {
        $this->coreParametersHelper = $coreParametersHelper;
    }

    /**
    * @return int
    *
    * @throws \Exception
    */
    public function send(\Swift_Mime_SimpleMessage $message, &$failedRecipients = null)
    {
        $count            = 0;
        $failedRecipients = (array) $failedRecipients;

```markdown if ($event = $this->getDispatcher()->createSendEvent($this, $message)) {

$this->getDispatcher()->dispatchEvent($event, ‘beforeSendPerformed’); if ($event->bubbleCancelled()) {

return 0;

}

}

try {

// The message object contains all the email details (from/to/body/etc.) $from = $message->getFrom(); $to = $message->getTo(); $body = $message->getBody();

// Configuration values that were set by the user through Mautic’s Configuration screen $host = $this->coreParametersHelper->get(‘mautic.mailer_host’); $apiKey = $this->coreParametersHelper->get(‘mautic.mailer_api_key’);

// Do your magic for sending the email here // $myService->send(…)

// Return the number of recipients who were accepted for delivery return 1;

} catch (Exception $e) {

$this->triggerSendError($event, $failedRecipients); $message->generateId(); $this->throwException($e->getMessage());

}

// Return the number of recipients who were accepted for delivery return 0;

}

/** * @inheritdoc */ public function getMaxBatchLimit(): int {

return 50;

}

/** * @inheritdoc */ public function getBatchRecipientCount(Swift_Message $message, $toBeAdded = 1, $type = ‘to’): int {

$toCount = is_array($message->getTo()) ? count($message->getTo()) : 0; $ccCount = is_array($message->getCc()) ? count($message->getCc()) : 0; $bccCount = is_array($message->getBcc()) ? count($message->getBcc()) : 0;

return null === $this->batchRecipientCount ? $this->batchRecipientCount : $toCount + $ccCount + $bccCount + $toBeAdded;

}

/** * @inheritdoc */ public function getCallbackPath(): string {

return ‘helloworld_api’;

}

/** * @inheritdoc */ public function processCallbackRequest(Request $request) {

$postData = json_decode($request->getContent(), true);

// Handle the callback here

}

private function triggerSendError(Swift_Events_SendEvent $evt, array &$failedRecipients): void {

$failedRecipients = array_merge(

$failedRecipients, array_keys((array) $this->message->getTo()), array_keys((array) $this->message->getCc()), array_keys((array) $this->message->getBcc())

);

```

if ($evt) {

$evt->setResult(Swift_Events_SendEvent::RESULT_FAILED); $evt->setFailedRecipients($failedRecipients); $this->getDispatcher()->dispatchEvent($evt, ‘sendPerformed’);

}

}

}

Email stat helpers

This section is in progress. See \Mautic\EmailBundle\Stats\Helper\StatHelperInterface

Testing Email transports

This document targets software developers who write Email transports based on Symfony Mailer which is available to Mautic from Mautic 5.0.

This document describes Manual steps for testing and the items that you need to verify before submitting your PR for approval in case you want to add a new transport.

Email components

Each Email sent out by Mautic includes the following components:

  1. Email Address: (FROM, TO, CC, BCC, REPLY-TO): Unicode Email address in this format email@example.com or email+test@example.com. Make sure that you always use the Unicode email address to accommodate special characters in languages like Arabic, Hebrew, or Chinese.

  2. Email Name: (FROM, TO, CC, BCC, REPLY-TO): Unicode Human-readable name, make sure that you always use Unicode Email address to accommodate special characters in languages like Arabic, Hebrew, or Chinese.

  3. Subject: Unicode string that might include emojis.

  4. Text: Unicode string that might include emojis.

  5. HTML: Unicode string that might include emojis, in HTML format.

  6. Headers: ANSI string pairs, Symfony/Mailer adds most of the headers, but for some transports, you need to add your own headers, so you can use the methods mentioned here: https://symfony.com/doc/current/mailer.html#message-headers, referenced in this file https://github.com/symfony/symfony/blob/HEAD/src/Symfony/Component/Mime/Header/Headers.php.

  7. Priority: sets the Email priority based on enum

  8. Attachments: a file with a variety of mime types, the file size shouldn’t exceed a specific size provided by the transport provider, usually nothing more than 10 MB and go up to 40 MB (for the whole message, including the text, HTML, and anything embedded within the HTML)

Preparing Mautic for testing

  1. Create 10 Contacts with any Email address you need

  2. Create a Segment that includes the 10 Contacts

Testing Email transport

In order to test the Email transport you need to go through the following steps:

Testing the connection

Go to Mautic Configuration -> Email Settings -> Click on Test Connection. If the connection works you should see success otherwise you should see an error

Screenshot showing testing the connection

发送示例电子邮件

从测试连接的相同屏幕上,您可以发送一个示例电子邮件。Mautic 将示例电子邮件发送到当前登录的 Mautic 用户地址。请检查该电子邮件是否已到达。

上传资产

转到“组件”->“资产”,然后上传一个示例文件,并确保文件名使用一种 Unicode 语言,例如阿拉伯语、俄语、德语等。

创建模板电子邮件

转到“渠道”->“电子邮件”->“新建”->“模板电子邮件”->选择“空白主题”。 使用构建器执行以下操作:

  • 嵌入图像

  • 添加 Unicode 文本,您可以使用此文本:”نحن نحب ان نقوم ببناء Mautic”

  • 关闭构建器

  • 转到“高级”选项卡

  • 填写“发件人名称”、“发件人地址”、“密送”、“回复至”、“添加附件”、“自定义标头”,然后单击“自动生成”以创建电子邮件的文本版本。

  • 保存电子邮件并发送一个示例测试,您应该收到所有已填写的字段。

创建分段电子邮件

转到“渠道”->“电子邮件”->“新建”->“分段电子邮件”->选择“空白主题”。 使用构建器执行以下操作:

  • 嵌入图像

  • 添加 Unicode 文本,您可以使用此文本:”نحن نحب ان نقوم ببناء Mautic”

  • 关闭构建器。

  • 转到“高级”选项卡

  • 填写“发件人名称”、“发件人地址”、“密送”、“回复至”、“添加附件”、“自定义标头”,然后单击“自动生成”以创建电子邮件的文本版本。

  • 保存电子邮件并发送一个示例测试,您应该收到所有已填写的字段。

发送单个电子邮件

转到“联系人”部分,选择一个联系人,然后单击“发送电子邮件”。您应该能够直接向该特定联系人的电子邮件地址发送电子邮件。

发送报告电子邮件

创建一个包含任何数据的报告,并将其设置为计划任务,它应以报告作为附件的形式发送电子邮件。

其他电子邮件功能

还有其他地方,例如“忘记密码”,它们也需要正常工作。请确保验证它们。

测试传输回调

每个传输都应该包括一个回调 URL,Webhook 应该通过 “POST” 方法发送到该 URL,以将跳出(bounce)的联系人标记为“不要联系”。

要测试这些回调,您需要执行以下操作:

  1. 配置一个电子邮件传输并将其设置为默认传输。

  2. 转到以下格式的 URL:“/mailer/{transport}/callback”。

  3. 您应该收到一条消息,其中显示“success”,并且应该有一个回调逻辑来处理 Webhook。