Checking invalid emails on the input [MAILPOET-1288]

This commit is contained in:
Rostislav Wolny
2018-03-06 19:54:23 +01:00
parent 2f05eaf528
commit 46493b991c
11 changed files with 82 additions and 18 deletions

View File

@ -92,6 +92,9 @@ define(
name: 'reply_to_address', name: 'reply_to_address',
type: 'text', type: 'text',
placeholder: MailPoet.I18n.t('replyToAddressPlaceholder'), placeholder: MailPoet.I18n.t('replyToAddressPlaceholder'),
validation: {
'data-parsley-type': 'email',
},
}, },
], ],
}, },

View File

@ -398,6 +398,9 @@ define(
name: 'reply_to_address', name: 'reply_to_address',
type: 'text', type: 'text',
placeholder: MailPoet.I18n.t('replyToAddressPlaceholder'), placeholder: MailPoet.I18n.t('replyToAddressPlaceholder'),
validation: {
'data-parsley-type': 'email',
},
}, },
], ],
}, },

View File

@ -65,6 +65,9 @@ define(
name: 'reply_to_address', name: 'reply_to_address',
type: 'text', type: 'text',
placeholder: MailPoet.I18n.t('replyToAddressPlaceholder'), placeholder: MailPoet.I18n.t('replyToAddressPlaceholder'),
validation: {
'data-parsley-type': 'email',
},
}, },
], ],
}, },

View File

@ -209,7 +209,7 @@ define(
for (column in rowData) { for (column in rowData) {
emailAddress = detectAndCleanupEmail(rowData[column]); emailAddress = detectAndCleanupEmail(rowData[column]);
if (emailColumnPosition === null if (emailColumnPosition === null
&& window.emailRegex.test(emailAddress)) { && window.mailpoet_email_regex.test(emailAddress)) {
emailColumnPosition = column; emailColumnPosition = column;
// add current e-mail to an object index // add current e-mail to an object index
parsedEmails[emailAddress] = true; parsedEmails[emailAddress] = true;
@ -229,7 +229,7 @@ define(
if (_.has(parsedEmails, email)) { if (_.has(parsedEmails, email)) {
duplicateEmails.push(email); duplicateEmails.push(email);
} }
else if (!window.emailRegex.test(email)) { else if (!window.mailpoet_email_regex.test(email)) {
invalidEmails.push(rowData[emailColumnPosition]); invalidEmails.push(rowData[emailColumnPosition]);
} }
// if we haven't yet processed this e-mail and it passed // if we haven't yet processed this e-mail and it passed
@ -257,7 +257,7 @@ define(
// since we assume that the header line is always present, we need // since we assume that the header line is always present, we need
// to detect the header by checking if it contains a valid e-mail address // to detect the header by checking if it contains a valid e-mail address
window.importData.step1 = { window.importData.step1 = {
header: (!window.emailRegex.test( header: (!window.mailpoet_email_regex.test(
processedSubscribers[0][emailColumnPosition]) processedSubscribers[0][emailColumnPosition])
) ? processedSubscribers.shift() : null, ) ? processedSubscribers.shift() : null,
subscribers: processedSubscribers, subscribers: processedSubscribers,
@ -690,7 +690,7 @@ define(
columnData = helperSubscribers.subscribers[0][i]; columnData = helperSubscribers.subscribers[0][i];
columnId = 'ignore'; // set default column type columnId = 'ignore'; // set default column type
// if the column is not undefined and has a valid e-mail, set type as email // if the column is not undefined and has a valid e-mail, set type as email
if (columnData % 1 !== 0 && window.emailRegex.test(columnData)) { if (columnData % 1 !== 0 && window.mailpoet_email_regex.test(columnData)) {
columnId = 'email'; columnId = 'email';
} else if (helperSubscribers.header) { } else if (helperSubscribers.header) {
headerName = helperSubscribers.header[i]; headerName = helperSubscribers.header[i];
@ -791,7 +791,9 @@ define(
// EMAIL filter: if the first value in the column doesn't have a valid // EMAIL filter: if the first value in the column doesn't have a valid
// email, hide the next button // email, hide the next button
if (column.id === 'email') { if (column.id === 'email') {
if (!window.emailRegex.test(subscribersClone.subscribers[0][matchedColumn.index])) { if (!window.mailpoet_email_regex.test(
subscribersClone.subscribers[0][matchedColumn.index])
) {
preventNextStep = true; preventNextStep = true;
if (!jQuery('[data-id="notice_invalidEmail"]').length) { if (!jQuery('[data-id="notice_invalidEmail"]').length) {
MailPoet.Notice.error(MailPoet.I18n.t('columnContainsInvalidElement'), { MailPoet.Notice.error(MailPoet.I18n.t('columnContainsInvalidElement'), {

View File

@ -32,7 +32,7 @@ class ModelValidator extends \Sudzy\Engine {
function validateEmail($email) { function validateEmail($email) {
$permitted_length = (strlen($email) >= self::EMAIL_MIN_LENGTH && strlen($email) <= self::EMAIL_MAX_LENGTH); $permitted_length = (strlen($email) >= self::EMAIL_MIN_LENGTH && strlen($email) <= self::EMAIL_MAX_LENGTH);
$valid_email = (is_email($email) !== false); $valid_email = is_email($email) !== false && parent::_isEmail($email, null);
return ($permitted_length && $valid_email); return ($permitted_length && $valid_email);
} }

View File

@ -1,6 +1,7 @@
<?php <?php
namespace MailPoet\Segments; namespace MailPoet\Segments;
use MailPoet\Models\ModelValidator;
use MailPoet\Models\Subscriber; use MailPoet\Models\Subscriber;
use MailPoet\Models\Segment; use MailPoet\Models\Segment;
use MailPoet\Models\SubscriberSegment; use MailPoet\Models\SubscriberSegment;
@ -80,8 +81,9 @@ class WP {
static function synchronizeUsers() { static function synchronizeUsers() {
self::updateSubscribersEmails(); $updated_users_emails = self::updateSubscribersEmails();
self::insertSubscribers(); $inserted_users_emails = self::insertSubscribers();
self::removeUpdatedSubscribersWithInvalidEmail(array_merge($updated_users_emails, $inserted_users_emails));
self::removeFromTrash(); self::removeFromTrash();
self::updateFirstNames(); self::updateFirstNames();
self::updateLastNames(); self::updateLastNames();
@ -92,20 +94,48 @@ class WP {
return true; return true;
} }
private static function removeUpdatedSubscribersWithInvalidEmail($updated_emails) {
$validator = new ModelValidator();
$invalid_wp_user_ids = array_map(function($item) {
return $item['id'];
},
array_filter($updated_emails, function($updated_email) use($validator) {
return !$validator->validateEmail($updated_email['email']);
}));
if(!$invalid_wp_user_ids) {
return;
}
\ORM::for_table(Subscriber::$_table)->whereIn('wp_user_id', $invalid_wp_user_ids)->delete_many();
}
private static function updateSubscribersEmails() { private static function updateSubscribersEmails() {
global $wpdb; global $wpdb;
$subscribers_table = Subscriber::$_table; $subscribers_table = Subscriber::$_table;
$changed_email_user_ids = \ORM::for_table(Subscriber::$_table)->raw_query(sprintf(
'SELECT %1$s.wp_user_id as id, wu.user_email as email FROM %1$s
INNER JOIN %2$s AS wu ON %1$s.wp_user_id = wu.id
WHERE wu.user_email != %1$s.email
', $subscribers_table, $wpdb->users))->findArray();
Subscriber::raw_execute(sprintf(' Subscriber::raw_execute(sprintf('
UPDATE IGNORE %1$s UPDATE IGNORE %1$s
JOIN %2$s as wu ON %1$s.wp_user_id = wu.id INNER JOIN %2$s as wu ON %1$s.wp_user_id = wu.id
SET %1$s.email = wu.user_email SET %1$s.email = wu.user_email
WHERE %1$s.wp_user_id IS NOT NULL AND wu.user_email != "" WHERE wu.user_email != %1$s.email AND wu.user_email != ""
', $subscribers_table, $wpdb->users)); ', $subscribers_table, $wpdb->users));
return $changed_email_user_ids;
} }
private static function insertSubscribers() { private static function insertSubscribers() {
global $wpdb; global $wpdb;
$subscribers_table = Subscriber::$_table; $subscribers_table = Subscriber::$_table;
$inserterd_user_ids = \ORM::for_table($wpdb->users)->raw_query(sprintf(
'SELECT %2$s.id, %2$s.user_email as email FROM %2$s
LEFT JOIN %1$s AS mps ON mps.wp_user_id = %2$s.id
WHERE mps.wp_user_id IS NULL AND %2$s.user_email != ""
', $subscribers_table, $wpdb->users))->findArray();
Subscriber::raw_execute(sprintf(' Subscriber::raw_execute(sprintf('
INSERT IGNORE INTO %1$s(wp_user_id, email, status, created_at) INSERT IGNORE INTO %1$s(wp_user_id, email, status, created_at)
SELECT wu.id, wu.user_email, "subscribed", CURRENT_TIMESTAMP() FROM %2$s wu SELECT wu.id, wu.user_email, "subscribed", CURRENT_TIMESTAMP() FROM %2$s wu
@ -113,6 +143,8 @@ class WP {
WHERE mps.wp_user_id IS NULL AND wu.user_email != "" WHERE mps.wp_user_id IS NULL AND wu.user_email != ""
ON DUPLICATE KEY UPDATE wp_user_id = wu.id ON DUPLICATE KEY UPDATE wp_user_id = wu.id
', $subscribers_table, $wpdb->users)); ', $subscribers_table, $wpdb->users));
return $inserterd_user_ids;
} }
private static function updateFirstNames() { private static function updateFirstNames() {

View File

@ -3,6 +3,7 @@ namespace MailPoet\Subscribers\ImportExport\Import;
use MailPoet\Form\Block\Date; use MailPoet\Form\Block\Date;
use MailPoet\Models\CustomField; use MailPoet\Models\CustomField;
use MailPoet\Models\ModelValidator;
use MailPoet\Models\Newsletter; use MailPoet\Models\Newsletter;
use MailPoet\Models\Subscriber; use MailPoet\Models\Subscriber;
use MailPoet\Models\SubscriberCustomField; use MailPoet\Models\SubscriberCustomField;
@ -148,12 +149,13 @@ class Import {
function validateSubscribersData($subscribers_data, $validation_rules) { function validateSubscribersData($subscribers_data, $validation_rules) {
$invalid_records = array(); $invalid_records = array();
$validator = new ModelValidator();
foreach($subscribers_data as $column => &$data) { foreach($subscribers_data as $column => &$data) {
$validation_rule = $validation_rules[$column]; $validation_rule = $validation_rules[$column];
if($validation_rule === 'email') { if($validation_rule === 'email') {
$data = array_map( $data = array_map(
function($index, $email) use(&$invalid_records) { function($index, $email) use(&$invalid_records, $validator) {
if(!is_email($email)) { if(!$validator->validateEmail($email)) {
$invalid_records[] = $index; $invalid_records[] = $index;
} }
return strtolower($email); return strtolower($email);

View File

@ -54,13 +54,21 @@ class WPTest extends \MailPoetTest {
expect($subscriber->email)->equals('user-sync-test-xx@email.com'); expect($subscriber->email)->equals('user-sync-test-xx@email.com');
} }
function testItDoesNotSynchronizeEmptyEmailsForExistingUsers() { function testRemovesUsersWithEmptyEmailsFromSunscribersDuringSynchronization() {
$id = $this->insertUser(); $id = $this->insertUser();
WP::synchronizeUsers(); WP::synchronizeUsers();
$this->updateWPUserEmail($id, ''); $this->updateWPUserEmail($id, '');
WP::synchronizeUsers(); WP::synchronizeUsers();
$subscriber = Subscriber::where('wp_user_id', $id)->findOne(); expect(Subscriber::where('wp_user_id', $id)->count())->equals(0);
expect($subscriber->email)->notEmpty(); $this->deleteWPUser($id);
}
function testRemovesUsersWithInvalidEmailsFromSunscribersDuringSynchronization() {
$id = $this->insertUser();
WP::synchronizeUsers();
$this->updateWPUserEmail($id, 'ivalid.@email.com');
WP::synchronizeUsers();
expect(Subscriber::where('wp_user_id', $id)->count())->equals(0);
$this->deleteWPUser($id); $this->deleteWPUser($id);
} }

View File

@ -48,6 +48,8 @@ jQuery('.toplevel_page_mailpoet-newsletters.menu-top-last')
var mailpoet_premium_version = <%= json_encode(mailpoet_premium_version()) %>; var mailpoet_premium_version = <%= json_encode(mailpoet_premium_version()) %>;
var mailpoet_analytics_enabled = <%= is_analytics_enabled() | json_encode %>; var mailpoet_analytics_enabled = <%= is_analytics_enabled() | json_encode %>;
var mailpoet_analytics_data = <%= json_encode(get_analytics_data()) %>; var mailpoet_analytics_data = <%= json_encode(get_analytics_data()) %>;
// RFC 5322 standard; http://emailregex.com/ combined with https://google.github.io/closure-library/api/goog.format.EmailAddress.html#isValid
var mailpoet_email_regex = /(?=^[+a-zA-Z0-9_.!#$%&'*\/=?^`{|}~-]+@([a-zA-Z0-9-]+\.)+[a-zA-Z0-9]{2,63}$)(?=^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,})))/;
</script> </script>
<!-- javascripts --> <!-- javascripts -->

View File

@ -64,6 +64,17 @@
$(function() { $(function() {
// on form submission // on form submission
$('#mailpoet_settings_form').on('submit', function() { $('#mailpoet_settings_form').on('submit', function() {
// Check if filled emails are valid
var invalidEmails = $.map($('#mailpoet_settings_form')[0].elements, function(el) {
return el.type === 'email' && el.value && !window.mailpoet_email_regex.test(el.value) ? el.value : null;
}).filter(function(val) { return !!val; });
if (invalidEmails.length) {
MailPoet.Notice.error(
"<%= __('Invalid email addresses: ') | escape('js') %>" + invalidEmails.join(', '),
{ scroll: true }
);
return false;
}
// if reCAPTCHA is enabled but keys are emty, show error // if reCAPTCHA is enabled but keys are emty, show error
var enabled = $('input[name="re_captcha[enabled]"]:checked').val(), var enabled = $('input[name="re_captcha[enabled]"]:checked').val(),
site_key = $('input[name="re_captcha[site_token]"]').val().trim(), site_key = $('input[name="re_captcha[site_token]"]').val().trim(),

View File

@ -24,9 +24,7 @@
importData = {}, importData = {},
mailpoetColumnsSelect2 = <%= subscriberFieldsSelect2|raw %>, mailpoetColumnsSelect2 = <%= subscriberFieldsSelect2|raw %>,
mailpoetColumns = <%= subscriberFields|raw %>, mailpoetColumns = <%= subscriberFields|raw %>,
mailpoetSegments = <%= segments|raw %>, mailpoetSegments = <%= segments|raw %>
// RFC 5322 standard; http://emailregex.com/
emailRegex = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
</script> </script>
<% endblock %> <% endblock %>