Implement Show sender domain warning for settings

This would show the sender domain warning message for
domains that are not verified and have a Retricted DMARC Policy

MAILPOET-4302
This commit is contained in:
Oluwaseun Olorunsola
2022-07-21 00:16:49 +01:00
committed by Veljko V
parent 370de8050a
commit 9e5f1d0ff0
5 changed files with 115 additions and 2 deletions

View File

@ -11,6 +11,7 @@ function SenderEmailAddressWarning({
emailAddress,
mssActive,
isEmailAuthorized,
showSenderDomainWarning,
}) {
const [showAuthorizedEmailModal, setShowAuthorizedEmailModal] =
useState(false);
@ -21,6 +22,8 @@ function SenderEmailAddressWarning({
setShowAuthorizedEmailModal(true);
};
const emailAddressDomain = emailAddress.split('@').pop().toLowerCase();
if (mssActive) {
if (!isEmailAuthorized) {
return (
@ -56,9 +59,42 @@ function SenderEmailAddressWarning({
</>
);
}
if (showSenderDomainWarning) {
return (
<>
{showAuthorizedEmailModal && (
// TODO: Change me. This should open the sender domain modal
<AuthorizeSenderEmailModal
senderEmail={emailAddress}
onRequestClose={() => {
setShowAuthorizedEmailModal(false);
}}
setAuthorizedAddress={setAuthorizedEmailAddress}
/>
)}
<p className="sender_email_address_warning">
{ReactStringReplace(
MailPoet.I18n.t('authorizeSenderDomain'),
/\[link](.*?)\[\/link]/g,
(match) => (
<a
key={match}
className="mailpoet-link"
href="https://kb.mailpoet.com/article/328-set-up-dkim-for-your-sender-domain"
target="_blank"
rel="noopener noreferrer"
onClick={loadModal}
>
{match}
</a>
),
)}
</p>
</>
);
}
return null;
}
const emailAddressDomain = emailAddress.split('@').pop().toLowerCase();
if (window.mailpoet_free_domains.indexOf(emailAddressDomain) > -1) {
return (
<>
@ -100,10 +136,12 @@ SenderEmailAddressWarning.propTypes = {
emailAddress: PropTypes.string.isRequired,
mssActive: PropTypes.bool.isRequired,
isEmailAuthorized: PropTypes.bool,
showSenderDomainWarning: PropTypes.bool,
};
SenderEmailAddressWarning.defaultProps = {
isEmailAuthorized: true, // don't show error message by default
showSenderDomainWarning: false,
};
export { SenderEmailAddressWarning };

View File

@ -1,15 +1,30 @@
import { useEffect, useState } from 'react';
import { MailPoet } from 'mailpoet';
import { Label, Inputs } from 'settings/components';
import { isEmail, t, onChange, setLowercaseValue } from 'common/functions';
import { Input } from 'common/form/input/input';
import { useSetting, useSelector, useAction } from 'settings/store/hooks';
import { SenderEmailAddressWarning } from 'common/sender_email_address_warning.jsx';
/**
* @param {string} email - Email address
* @param {ApiActionType} type - action type
* @returns {Promise}
*/
const makeApiRequest = (domain: string) =>
MailPoet.Ajax.post({
api_version: MailPoet.apiVersion,
endpoint: 'settings',
action: 'checkDomainDmarcPolicy',
data: { domain },
});
export function DefaultSender() {
const isMssActive = useSelector('isMssActive')();
const [senderName, setSenderName] = useSetting('sender', 'name');
const [senderEmail, setSenderEmail] = useSetting('sender', 'address');
const [isAuthorized, setIsAuthorized] = useState(true);
const [showSenderDomainWarning, setShowSenderDomainWarning] = useState(false);
const [replyToName, setReplyToName] = useSetting('reply_to', 'name');
const [replyToEmail, setReplyToEmail] = useSetting('reply_to', 'address');
const setErrorFlag = useAction('setErrorFlag');
@ -22,6 +37,36 @@ export function DefaultSender() {
}
setIsAuthorized(window.mailpoet_authorized_emails.includes(email));
};
const checkSenderEmailDomain = (email: string) => {
const emailAddressDomain = email.split('@').pop().toLowerCase();
const isDomainVerified =
window.mailpoet_verified_sender_domains.includes(emailAddressDomain);
if (isDomainVerified) {
// do nothing if the email domain is verified
return;
}
// check domain DMARC policy
makeApiRequest(emailAddressDomain)
.then((res) => {
const isDmarcPolicyRetricted = Boolean(
res?.data?.isDmarcPolicyRetricted,
);
setShowSenderDomainWarning(isDmarcPolicyRetricted);
})
.catch(() => {
// do nothing for now when the request fails
});
};
const performActionOnBlur = (data: string) => {
isAuthorizedEmail(data);
checkSenderEmailDomain(data);
};
const updateSenderEmailController = (email: string) => {
setIsAuthorized(true);
setSenderEmail(email);
@ -66,7 +111,7 @@ export function DefaultSender() {
data-automation-id="from-email-field"
value={senderEmail}
onChange={onChange(setLowercaseValue(updateSenderEmailController))}
onBlur={onChange(isAuthorizedEmail)}
onBlur={onChange(performActionOnBlur)}
/>
<br />
{invalidSenderEmail && (
@ -79,6 +124,7 @@ export function DefaultSender() {
emailAddress={senderEmail}
mssActive={isMssActive}
isEmailAuthorized={isAuthorized}
showSenderDomainWarning={showSenderDomainWarning}
/>
</div>
<label className="mailpoet-settings-inputs-row" htmlFor="reply_to-name">

View File

@ -15,6 +15,7 @@ use MailPoet\Newsletter\NewslettersRepository;
use MailPoet\Newsletter\Sending\ScheduledTasksRepository;
use MailPoet\Segments\SegmentsRepository;
use MailPoet\Services\AuthorizedEmailsController;
use MailPoet\Services\AuthorizedSenderDomainController;
use MailPoet\Services\Bridge;
use MailPoet\Settings\SettingsChangeHandler;
use MailPoet\Settings\SettingsController;
@ -37,6 +38,9 @@ class Settings extends APIEndpoint {
/** @var AuthorizedEmailsController */
private $authorizedEmailsController;
/** @var AuthorizedSenderDomainController */
private $senderDomainController;
/** @var TransactionalEmails */
private $wcTransactionalEmails;
@ -80,6 +84,7 @@ class Settings extends APIEndpoint {
SettingsController $settings,
Bridge $bridge,
AuthorizedEmailsController $authorizedEmailsController,
AuthorizedSenderDomainController $senderDomainController,
TransactionalEmails $wcTransactionalEmails,
WPFunctions $wp,
EntityManager $entityManager,
@ -96,6 +101,7 @@ class Settings extends APIEndpoint {
$this->settings = $settings;
$this->bridge = $bridge;
$this->authorizedEmailsController = $authorizedEmailsController;
$this->senderDomainController = $senderDomainController;
$this->wcTransactionalEmails = $wcTransactionalEmails;
$this->servicesChecker = $servicesChecker;
$this->wp = $wp;
@ -246,6 +252,22 @@ class Settings extends APIEndpoint {
return $this->successResponse($response);
}
public function checkDomainDmarcPolicy($data = []) {
$domain = $data['domain'] ?? null;
if (!$domain) {
return $this->badRequest([
APIError::BAD_REQUEST => WPFunctions::get()->__('No sender domain specified.', 'mailpoet'),
]);
}
$domain = trim($domain);
$response = ['isDmarcPolicyRetricted' => $this->senderDomainController->isDomainDmarcRetricted($domain)];
return $this->successResponse($response);
}
private function onSettingsChange($oldSettings, $newSettings) {
// Recalculate inactive subscribers
$oldInactivationInterval = $oldSettings['deactivate_subscriber_after_inactive_days'];

View File

@ -19,6 +19,7 @@ use MailPoet\Newsletter\NewslettersRepository;
use MailPoet\Newsletter\Sending\ScheduledTasksRepository;
use MailPoet\Segments\SegmentsRepository;
use MailPoet\Services\AuthorizedEmailsController;
use MailPoet\Services\AuthorizedSenderDomainController;
use MailPoet\Services\Bridge;
use MailPoet\Settings\SettingsChangeHandler;
use MailPoet\Settings\SettingsController;
@ -53,6 +54,7 @@ class SettingsTest extends \MailPoetTest {
$this->settings,
new Bridge,
$this->make(AuthorizedEmailsController::class, ['onSettingsSave' => null ]),
$this->diContainer->get(AuthorizedSenderDomainController::class),
$this->make(TransactionalEmails::class),
WPFunctions::get(),
$this->diContainer->get(EntityManager::class),
@ -96,6 +98,7 @@ class SettingsTest extends \MailPoetTest {
$this->settings,
$this->make(Bridge::class, ['onSettingsSave' => Expected::once()]),
$this->make(AuthorizedEmailsController::class, ['onSettingsSave' => Expected::once()]),
$this->diContainer->get(AuthorizedSenderDomainController::class),
$this->make(TransactionalEmails::class),
WPFunctions::get(),
$this->diContainer->get(EntityManager::class),
@ -130,6 +133,7 @@ class SettingsTest extends \MailPoetTest {
$this->settings,
$bridgeMock,
new AuthorizedEmailsController($this->settings, $bridgeMock, $this->diContainer->get(NewslettersRepository::class)),
$this->diContainer->get(AuthorizedSenderDomainController::class),
$this->make(TransactionalEmails::class),
WPFunctions::get(),
$this->diContainer->get(EntityManager::class),
@ -159,6 +163,7 @@ class SettingsTest extends \MailPoetTest {
$this->settings,
$bridgeMock,
new AuthorizedEmailsController($this->settings, $bridgeMock, $this->diContainer->get(NewslettersRepository::class)),
$this->diContainer->get(AuthorizedSenderDomainController::class),
$this->make(TransactionalEmails::class),
WPFunctions::get(),
$this->diContainer->get(EntityManager::class),
@ -190,6 +195,7 @@ class SettingsTest extends \MailPoetTest {
$this->settings,
$bridgeMock,
new AuthorizedEmailsController($this->settings, $bridgeMock, $this->diContainer->get(NewslettersRepository::class)),
$this->diContainer->get(AuthorizedSenderDomainController::class),
$this->make(TransactionalEmails::class),
WPFunctions::get(),
$this->diContainer->get(EntityManager::class),

View File

@ -103,6 +103,7 @@
'invalidEmail': __('Invalid email address'),
'youNeedToAuthorizeTheEmail': __('You need to authorize the email address [email] to be able to send with it.'),
'authorizeMyEmail': __('Authorize my email address'),
'authorizeSenderDomain': __('Email violates Sender Domains DMARC policy. Please set up [link]sender authentication[/link].'),
'enableSignupConfTitle': __('Enable sign-up confirmation'),
'enableSignupConfDescription': __("If you enable this option, your subscribers will first receive a confirmation email after they subscribe. Once they confirm their subscription (via this email), they will be marked as 'confirmed' and will begin to receive your email newsletters."),