From dbb52a758ee4aef811b99331b1aea63d6fcbd77b Mon Sep 17 00:00:00 2001 From: wxa Date: Tue, 5 Oct 2021 13:38:22 +0300 Subject: [PATCH] Add deep links to email authorization notices [MAILPOET-3830] --- .../js/src/common/set_from_address_modal.tsx | 7 ++- assets/js/src/newsletters/send.jsx | 5 +- .../sending-paused-notices-resume-button.jsx | 60 +++++++++++++++++++ assets/js/src/webpack_admin_index.jsx | 1 + lib/Util/Notices/UnauthorizedEmailNotice.php | 27 +++++++-- views/layout.html | 1 + 6 files changed, 91 insertions(+), 10 deletions(-) create mode 100644 assets/js/src/sending-paused-notices-resume-button.jsx diff --git a/assets/js/src/common/set_from_address_modal.tsx b/assets/js/src/common/set_from_address_modal.tsx index 5ebfee9f90..2b1ce3750c 100644 --- a/assets/js/src/common/set_from_address_modal.tsx +++ b/assets/js/src/common/set_from_address_modal.tsx @@ -26,15 +26,16 @@ type error = { message: string; } -const getErrorMessage = (error: error | null): string => { +const getErrorMessage = (error: error | null, address: string | null): string => { if (!error) { return MailPoet.I18n.t('setFromAddressEmailUnknownError'); } if (error.error === 'unauthorized') { + const fromAddress = encodeURIComponent(address); return MailPoet.I18n.t('setFromAddressEmailNotAuthorized').replace( /\[link\](.*?)\[\/link\]/g, - '$1' + `$1` ); } @@ -143,7 +144,7 @@ const SetFromAddressModal = ({ onRequestClose, setAuthorizedAddress }: Props) => notices.success(getSuccessMessage(), { timeout: false }); } catch (e) { const error = e.errors && e.errors[0] ? e.errors[0] : null; - const message = getErrorMessage(error); + const message = getErrorMessage(error, address); addressValidator.addError('saveError', { message }); } }} diff --git a/assets/js/src/newsletters/send.jsx b/assets/js/src/newsletters/send.jsx index 15c118dab1..cfe0ec9f1b 100644 --- a/assets/js/src/newsletters/send.jsx +++ b/assets/js/src/newsletters/send.jsx @@ -95,15 +95,16 @@ class NewsletterSend extends React.Component { } showInvalidFromAddressError = () => { + const fromAddress = this.state.item.sender_address; let errorMessage = ReactStringReplace( MailPoet.I18n.t('newsletterInvalidFromAddress'), '%$1s', - () => this.state.item.sender_address + () => fromAddress ); errorMessage = ReactStringReplace( errorMessage, /\[link\](.*?)\[\/link\]/g, - (match) => `${match}` + (match) => `${match}` ); jQuery('#field_sender_address') .parsley() diff --git a/assets/js/src/sending-paused-notices-resume-button.jsx b/assets/js/src/sending-paused-notices-resume-button.jsx new file mode 100644 index 0000000000..87e3d673bc --- /dev/null +++ b/assets/js/src/sending-paused-notices-resume-button.jsx @@ -0,0 +1,60 @@ +import jQuery from 'jquery'; +import MailPoet from 'mailpoet'; + +const loadAuthorizedEmailAddresses = async () => { + if (window.mailpoet_mta_method !== 'MailPoet') { + return []; + } + const response = await MailPoet.Ajax.post({ + api_version: window.mailpoet_api_version, + endpoint: 'mailer', + action: 'getAuthorizedEmailAddresses', + }); + return response.data || []; +}; + +const isValidFromAddress = async (fromAddress) => { + if (window.mailpoet_mta_method !== 'MailPoet') { + return true; + } + const addresses = await loadAuthorizedEmailAddresses(); + return addresses.indexOf(fromAddress) !== -1; +}; + +const resumeMailerSending = () => { + MailPoet.Ajax.post({ + api_version: window.mailpoet_api_version, + endpoint: 'mailer', + action: 'resumeSending', + }).done(() => { + MailPoet.Notice.success(MailPoet.I18n.t('mailerSendingResumedNotice')); + window.mailpoet_listing.forceUpdate(); + }).fail((response) => { + if (response.errors.length > 0) { + MailPoet.Notice.error( + response.errors.map((error) => error.message), + { scroll: true } + ); + } + }); +}; + +const resumeSendingIfAuthorized = (fromAddress) => isValidFromAddress(fromAddress) + .then((valid) => { + if (!valid) { + MailPoet.Notice.error( + MailPoet.I18n.t('mailerSendingNotResumedUnauthorized'), + { scroll: true } + ); + return false; + } + return resumeMailerSending(); + }); + +// use jQuery since some of the targeted notices are added to the DOM using the old +// jQuery-based notice implementation which doesn't trigger pure-JS added listeners +jQuery(($) => { + $(document).on('click', '.notice .mailpoet-js-button-resume-sending', (e) => ( + resumeSendingIfAuthorized(e.target.value) + )); +}); diff --git a/assets/js/src/webpack_admin_index.jsx b/assets/js/src/webpack_admin_index.jsx index 13ae37bbcc..0bdab3841a 100644 --- a/assets/js/src/webpack_admin_index.jsx +++ b/assets/js/src/webpack_admin_index.jsx @@ -15,3 +15,4 @@ import 'wizard/wizard.jsx'; // side effect - renders ReactDOM to document import 'experimental_features/experimental_features.jsx'; // side effect - renders ReactDOM to document import 'logs/logs'; // side effect - renders ReactDOM to document import 'sending-paused-notices-fix-button.tsx'; // side effect - renders ReactDOM to document +import 'sending-paused-notices-resume-button.jsx'; // side effect - executes on doc ready, adds events diff --git a/lib/Util/Notices/UnauthorizedEmailNotice.php b/lib/Util/Notices/UnauthorizedEmailNotice.php index 7c5de77444..1709128daa 100644 --- a/lib/Util/Notices/UnauthorizedEmailNotice.php +++ b/lib/Util/Notices/UnauthorizedEmailNotice.php @@ -35,21 +35,38 @@ class UnauthorizedEmailNotice { public function display($validationError) { $message = $this->getMessageText($validationError); - $message .= $this->getFixThisButton(); + $message .= sprintf( + '

%s   %s   %s

', + $this->getAuthorizeEmailButton($validationError), + $this->getDifferentEmailButton(), + $this->getResumeSendingButton($validationError) + ); $extraClasses = 'mailpoet-js-error-unauthorized-emails-notice'; Notice::displayError($message, $extraClasses, self::OPTION_NAME, false, false); } private function getMessageText($validationError) { - $text = $this->wp->_x('Sending all of your emails has been paused because your email address %s hasn’t been authorized yet.', + $text = $this->wp->_x('Sending all of your emails has been paused because your email address %s hasn’t been authorized yet.', 'Email addresses have to be authorized to be used to send emails. %s will be replaced by an email address.', 'mailpoet'); $message = str_replace('%s', EscapeHelper::escapeHtmlText($validationError['invalid_sender_address']), $text); return "

$message

"; } - private function getFixThisButton() { - $button = ''; - return "

$button

"; + private function getAuthorizeEmailButton($validationError) { + $email = $this->wp->escAttr($validationError['invalid_sender_address']); + $button = '' . $this->wp->__('Authorize this email address', 'mailpoet') . ''; + return $button; + } + + private function getDifferentEmailButton() { + $button = ''; + return $button; + } + + private function getResumeSendingButton($validationError) { + $email = $this->wp->escAttr($validationError['invalid_sender_address']); + $button = ''; + return $button; } } diff --git a/views/layout.html b/views/layout.html index b714a134b7..fb3852afaf 100644 --- a/views/layout.html +++ b/views/layout.html @@ -88,6 +88,7 @@ jQuery('#adminmenu #toplevel_page_mailpoet-newsletters') 'senderEmailAddressWarning2': _x('Use an address like %1$s for the Sender and put %2$s in the Reply-to field below.', 'In the last step, before sending a newsletter. URL: ?page=mailpoet-newsletters#/send/2'), 'senderEmailAddressWarning3': _x('Read more.'), 'mailerSendingResumedNotice': __('Sending has been resumed.'), + 'mailerSendingNotResumedUnauthorized': __('Failed to resume sending because the email address is unauthorized. Please authorize it and try again.'), 'dismissNotice': __('Dismiss this notice.'), 'subscribersLimitNoticeTitle': __('Congratulations, you now have more than [subscribersLimit] subscribers!'),