jub/craft-newsletter

Craft CMS 新闻通讯插件

安装次数: 2,626

依赖项: 0

建议者: 0

安全: 0

星标: 5

关注者: 1

分支: 5

开放性问题: 2

类型:craft-plugin

2.2.0 2023-08-05 14:07 UTC

This package is auto-updated.

Last update: 2024-09-11 19:42:27 UTC


README

Stable Version Total Downloads Tests status

Craft CMS 插件使得最终用户可以通过前端表单订阅各种电子邮件服务。

目前支持以下服务

  • Mailchimp
  • Mailjet
  • Brevo (前 Sendinblue)

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

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

要求

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

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

安装

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

配置

控制面板

您可以通过前往设置 → 新闻通讯通过控制面板管理配置设置

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

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

服务配置

Mailjet 设置
  • API 密钥 (apiKey)
  • API 密钥 (apiSecret)
  • 列表 ID (可选,如果启用 DOI 则必须) (listId)

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

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

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

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

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

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

您还可以启用 Brevo 双重确认功能。您需要一个 Sendinblue 模板,如 此处 所述。

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

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

配置文件

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

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

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

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

前端表单

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

{# @var craft \craft\web\twig\variables\CraftVariable #}
{# @var newsletterForm \juban\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('juban\\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 juban\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!');
	    }
	}
);

如何创建适配器

为不支持的服务添加新适配器

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

一些简单的示例

namespace juban\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 juban\newsletter\Newsletter;

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

路线图

  • 支持更多表单字段
  • 支持多个列表

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