简化版/craft-newsletter

此包已被废弃且不再维护。作者建议使用 jub/craft-newsletter 包。

Craft CMS 新闻通讯插件

2.0.0 2022-05-15 14:52 UTC

This package is auto-updated.

Last update: 2022-07-25 08:55:56 UTC


README

Craft CMS 新闻通讯插件允许最终用户通过前端表单订阅各种电子邮件服务。

当前支持以下服务

  • Mailchimp
  • Mailjet
  • Sendinblue

此插件符合 GDPR 规范,并在订阅新闻通讯时要求用户给予同意。

💡 类似于 Craft Mailer 适配器,您甚至可以 创建自己的适配器 以连接到不支持的服务。

要求

此插件需要 Craft CMS 4.0.0 或更高版本以及 PHP 8.0.2 或更高版本。

为了支持自动 Google reCAPTCHA 验证,您需要安装 simplonprod/craft-google-recaptcha 插件。

安装

  1. 使用 composer 在项目目录下通过 composer require simplonprod/craft-newsletter 安装。
  2. 在 Craft 控制面板的设置 → 插件下安装插件,或通过命令行使用 ./craft install/plugin newsletter

配置

控制面板

您可以通过访问设置 → 新闻通讯来通过控制面板管理配置设置。

  • 服务类型 下拉菜单中选择您希望用于处理新闻通讯用户订阅的服务类型。
  • 提供所需的 API 密钥和参数 如下所述
  • 如果已安装并启用 Google reCAPTCHA 插件,请使用 启用 Google reCAPTCHA 开关选择是否验证提交。

⚠️ 启用后,Google reCAPTCHA 功能将 不会 为您渲染前端小部件。您必须在表单视图中添加 {{ craft.googleRecaptcha.render() }}

服务配置

Mailjet 设置
  • API 密钥 (apiKey)
  • API 秘密 (apiSecret)
  • 列表 ID (可选) (listId)

您可以在 REST API 密钥部分找到这些信息,在您的 Mailjet 账户信息 中。

您可以为订阅最终用户提供一个 联系人列表 ID

如果没有提供列表 ID,则用户将仅作为联系人创建。

Sendinblue 设置
  • API 密钥 (apiKey)
  • 列表 ID (可选,如果 DOI 开启则必需) (listId)
  • 激活双重确认 (DOI) (可选) (doi)
  • 邮件模板 ID (如果 DOI 开启则必需) (doiTemplateId)
  • 重定向 URL (如果 DOI 开启则必需) (doiRedirectionUrl)

您可以在您的 Sendinblue 账户 中找到这些信息。

您可以通过提供 联系名单 ID 来订阅特定名单。

您还可以启用 Sendinblue 双重确认功能。您需要一个 Sendinblue 模板,具体请参考 这里 的说明。

如果没有提供列表 ID,则用户将仅作为联系人创建。

Mailchimp 设置
  • API 密钥 (apiKey):您可以从您的 Mailchimp 账户 中找到该信息。
  • 服务器前缀 (serverPrefix):您可以通过登录 Mailchimp 账户查看 URL 来找到该信息。例如,https://us4.admin.mailchimp.com/account/api/ 表示要使用的服务器前缀是 us4
  • 受众 ID (listId):您可以在“受众”>“所有联系人”>“设置”>“受众名称和活动默认值”中找到该信息。

配置文件

您可以在项目的 config 文件夹中创建一个 newsletter.php 文件,并按照以下方式提供设置(示例)

return [
    "adapterType"         => \simplonprod\newsletter\adapters\Mailjet::class,
    "adapterTypeSettings" => [
        'apiKey'    => '',
        'apiSecret' => '',
        'listId'    => ''
    ],
    "recaptchaEnabled"    => true
];

根据服务和其特定设置,将 adapterType 调整为相应服务适配器类的名称,并在 adapterTypeSettings 关联数组中提供所需参数(请参阅 服务配置)。

⚠️ 该文件中提供的任何值都将覆盖控制面板中的设置。

前端表单

您可以将以下模板用作注册表单的起点

{# @var craft \craft\web\twig\variables\CraftVariable #}
{# @var newsletterForm \simplonprod\newsletter\models\NewsletterForm #}

{% if craft.app.plugins.plugin('newsletter') is not null %}
    {# If there were any validation errors, a `newsletterForm` variable will be passed to the template, which contains the posted values and validation errors. If that’s not set, we’ll default to a new newsletterForm. #}
    {% set newsletterForm = newsletterForm ?? create('simplonprod\\newsletter\\models\\NewsletterForm') %}

    {# success notification #}
    {% if success is defined and success %}
        <div role="alert">
            <p>{{ 'Your newsletter subscription has been taken into account. Thank you.'|t }}</p>
        </div>
    {% endif %}
    
    <form action="" method="post" accept-charset="UTF-8">
        {{ csrfInput() }}
        
        {# Subscription process is handled by the newsletter plugin controller #}
        {{ actionInput('newsletter/newsletter/subscribe') }}
        
        {# User will be redirected to the redirect input url upon successful subscription #}
        {{ redirectInput('thank-you') }}
        
        <label for="newsletter-consent">
        	<input type="checkbox" value="check" name="consent" id="newsletter-consent" required {% if newsletterForm.hasErrors('consent') %}aria-invalid="true" aria-describedby="consent-error"{% endif %}>
            {{'I agree to receive your emails and confirm that I have read your privacy policy.'|t}}
        </label>
        {% if newsletterForm.hasErrors('consent') %}
            <div id="consent-error" role="alert" class="text-sm text-error font-bold">{{ newsletterForm.getFirstError('consent') }}</div>
        {% endif %}
        
        <label for="newsletter-email">{{ 'Votre email'|t }}<span aria-hidden="true">*</span></label>
        <input id="newsletter-email" required name="email" type="email" placeholder="j.dupont@gmail.com" value="{{ newsletterForm.email }}" {% if newsletterForm.hasErrors('email') %}aria-invalid="true" aria-describedby="email-error"{% endif %}>

        {% if newsletterForm.hasErrors('email') %}
            <div id="email-error" role="alert" class="text-sm text-error font-bold">{{ newsletterForm.getFirstError('email') }}</div>
        {% endif %}
        
        <button type="submit">{{ 'Subscribe'|t }}</button>

    </form>
{% endif %}

newsletter/newsletter/subscribe 动作期望以下以 POST 方式提交的输入

  • email:要订阅的用户电子邮件
  • consent:任何表示用户同意接收通讯录的值的值

⚠️ 如果启用了 Google reCAPTCHA 验证,别忘了在表单视图中添加 {{ craft.googleRecaptcha.render() }}

XHR / AJAX 表单

或者,您可以使用 JavaScript 提交通讯录表单。
这为您提供了更多向用户提供视觉效果的自由,并防止页面重新加载和滚动到页面顶部。
缺点是您需要编写 ajax-请求。
以下是一个参考示例,请注意所需的 application/json 标头

var data = new FormData();
data.append("consent", consent);
data.append("email", email);

var xhr = new XMLHttpRequest();
xhr.withCredentials = true;

xhr.addEventListener("readystatechange", function() {
  if (this.readyState === 4) {
    console.log(this.responseText);
  }
});

xhr.open("POST", "https://{YOUR_DOMAIN}/actions/newsletter/newsletter/subscribe/");
xhr.setRequestHeader("Accept", "application/json");
xhr.send(data);

⚠️ 如果启用了 Google reCAPTCHA 验证,请将 data.append("g-recaptcha-response", ""); 也添加到请求中!

自定义验证

您可以在项目模块或插件中使用 NewsletterForm 模型的 afterValidate 事件在客户端表单提交时提供自己的验证。

use yii\base\Event;
use simplonprod\googlerecaptcha\GoogleRecaptcha;

Event::on(
	NewsletterForm::class,
	NewsletterForm::EVENT_AFTER_VALIDATE,
	static function (Event $event) {
	    $form = $event->sender;	    
	    $isSpam = // custom spam detection logic...
	    if($isSpam) {
	    	$this->addError('email', 'Spam detected!');
	    }
	}
);

如何创建适配器

要添加对新服务的支持

创建一个扩展 simplonprod\newsletter\adapters\BaseNewsletterAdapter 类的适配器类。

一些小的示例

namespace simplonprod\newsletter\adapters;

use Craft;

class MySuperNewsletterAdapter extends BaseNewsletterAdapter
{

	// Declare every attributes required by the service (ie API keys and secrets, etc...)
    public $apiKey;
    // Store any error occuring in the subscribe method here
    private $_errorMessage;
    
    /**
     * @inheritdoc
     */
    public function behaviors(): array
    {
        // Support for setting defined in environment variables or aliases
        $behaviors = parent::behaviors();
        $behaviors['parser'] = [
            'class'      => EnvAttributeParserBehavior::class,
            'attributes' => [
                'apiKey'
            ],
        ];
        return $behaviors;
    }

    /**
     * @inheritdoc
     */
    public static function displayName(): string
    {
        return 'My Service Name'; // Service name as shown in the adapter type dropdown
    }

    /**
     * @inheritdoc
     */
    public function getSettingsHtml()
    {
        // Render the adapter settings templates
        // Adapt the path according to your module / plugin
        return Craft::$app->getView()->renderTemplate('newsletter/newsletterAdapters/MySuperNewsletterAdapter/settings', [
            'adapter' => $this
        ]);
	    }
	
	/**
	 * Try to subscribe the given email into the newsletter mailing list service
	 * @param string $email
	 * @return bool
	 */
    public function subscribe(string $email): bool
    {
        // Call the service API here
        $this->_errorMessage = null;
        if(!MySuperNewsletterService::subscribe($email)) {
            // If something goes wrong, store the error message
            $this->_errorMessage = MySuperNewsletterService::getError();
            return false;
        }
        return true;
    }

    /**
     * Return the latest error message after a call to the subscribe method
     * @return null|string
     */
    public function getSubscriptionError(): string
    {
        return $this->_errorMessage;
    }

    /**
     * @inheritdoc
     */
    protected function defineRules(): array
    {
        // Validation rules for the adapter settings should be defined here
        $rules = parent::defineRules();
        $rules[] = [['apiKey'], 'trim'];
        $rules[] = [['apiKey'], 'required'];
        return $rules;
    }
}

模板设置视图可能如下所示

{% import "_includes/forms" as forms %}

{{ forms.autosuggestField({
    label: "API Key"|t('newsletter'),
    id: 'apiKey',
    name: 'apiKey',
    required: true,
    suggestEnvVars: true,
    value: adapter.apiKey,
    errors: adapter.getErrors('apiKey')
}) }}

适配器模型可以在twig视图中通过 adapter 变量访问。

最后,在一个模块或插件中,按照以下方式注册适配器

use craft\events\RegisterComponentTypesEvent;
use simplonprod\newsletter\Newsletter;

Event::on(
    Newsletter::class,
    Newsletter::EVENT_REGISTER_NEWSLETTER_ADAPTER_TYPES,
    static function (RegisterComponentTypesEvent $event) {
        $event->types[] = MySuperNewsletterAdapter::class;
    }
);

路线图

  • 支持额外的表单字段
  • 支持多个列表

基本插件图标使用了Font Awesome Free

Simplon.Prod创建。