jub / craft-newsletter
Craft CMS 新闻通讯插件
Requires
- php: ^8.0.2
- craftcms/cms: ^4.0
- getbrevo/brevo-php: ^1.0
- mailchimp/marketing: ^3.0
- mailjet/mailjet-apiv3-php: ^1.5
Requires (Dev)
- codeception/codeception: ^4.0.0
- codeception/module-asserts: ^1.0.0
- codeception/module-db: ^1.1
- codeception/module-yii2: ^1.0.0
- craftcms/ecs: dev-main
- craftcms/phpstan: dev-main
- jub/craft-google-recaptcha: ^2.1
- vlucas/phpdotenv: ^3.0
This package is auto-updated.
Last update: 2024-09-11 19:42:27 UTC
README
Craft CMS 插件使得最终用户可以通过前端表单订阅各种电子邮件服务。
目前支持以下服务
- Mailchimp
- Mailjet
- Brevo (前 Sendinblue)
此插件符合 GDPR 规定,并在订阅新闻通讯时要求用户给予其同意。
💡 类似于 Craft Mailer 适配器,您甚至可以 创建自己的适配器 来连接不支持的服务。
要求
此插件需要 Craft CMS 4.0.0 或更高版本和 PHP 8.0.2 或更高版本。
为了支持自动 Google reCAPTCHA 验证,您需要安装
jub/craft-google-recaptcha
插件。
安装
- 通过 composer 在项目目录中运行
composer require jub/craft-newsletter
来安装。 - 在 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