Merge pull request #1085 from mailpoet/improve-user-sync
Improve user sync [MAILPOET-1073]
This commit is contained in:
@@ -172,7 +172,8 @@ class Migrator {
|
||||
'deleted_at TIMESTAMP NULL,',
|
||||
'unconfirmed_data longtext,',
|
||||
'PRIMARY KEY (id),',
|
||||
'UNIQUE KEY email (email)'
|
||||
'UNIQUE KEY email (email),',
|
||||
'KEY wp_user_id (wp_user_id)',
|
||||
);
|
||||
return $this->sqlify(__FUNCTION__, $attributes);
|
||||
}
|
||||
|
@@ -79,28 +79,109 @@ class WP {
|
||||
}
|
||||
|
||||
static function synchronizeUsers() {
|
||||
// get wordpress users list
|
||||
|
||||
self::updateSubscribersEmails();
|
||||
self::insertSubscribers();
|
||||
self::removeFromTrash();
|
||||
self::updateFirstNames();
|
||||
self::updateLastNames();
|
||||
self::updateFristNameIfMissing();
|
||||
self::insertUsersToSegment();
|
||||
self::removeOrphanedSubscribers();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static function updateSubscribersEmails() {
|
||||
global $wpdb;
|
||||
$subscribers_table = Subscriber::$_table;
|
||||
Subscriber::raw_execute(sprintf('
|
||||
UPDATE IGNORE %s
|
||||
JOIN %susers as wu ON %s.wp_user_id = wu.id
|
||||
SET email = user_email
|
||||
WHERE %s.wp_user_id IS NOT NULL
|
||||
', $subscribers_table, $wpdb->prefix, $subscribers_table, $subscribers_table));
|
||||
}
|
||||
|
||||
private static function insertSubscribers() {
|
||||
global $wpdb;
|
||||
$subscribers_table = Subscriber::$_table;
|
||||
Subscriber::raw_execute(sprintf('
|
||||
INSERT IGNORE INTO %s(wp_user_id, email, status, created_at)
|
||||
SELECT wu.id, wu.user_email, "subscribed", CURRENT_TIMESTAMP() FROM %susers wu
|
||||
LEFT JOIN %s mps ON wu.id = mps.wp_user_id
|
||||
WHERE mps.wp_user_id IS NULL
|
||||
', $subscribers_table, $wpdb->prefix, $subscribers_table));
|
||||
}
|
||||
|
||||
private static function updateFirstNames() {
|
||||
global $wpdb;
|
||||
$subscribers_table = Subscriber::$_table;
|
||||
Subscriber::raw_execute(sprintf('
|
||||
UPDATE %s
|
||||
JOIN %susermeta as wpum ON %s.wp_user_id = wpum.user_id AND meta_key = "first_name"
|
||||
SET first_name = meta_value
|
||||
WHERE %s.first_name = ""
|
||||
AND %s.wp_user_id IS NOT NULL
|
||||
', $subscribers_table, $wpdb->prefix, $subscribers_table, $subscribers_table, $subscribers_table));
|
||||
}
|
||||
|
||||
private static function updateLastNames() {
|
||||
global $wpdb;
|
||||
$subscribers_table = Subscriber::$_table;
|
||||
Subscriber::raw_execute(sprintf('
|
||||
UPDATE %s
|
||||
JOIN %susermeta as wpum ON %s.wp_user_id = wpum.user_id AND meta_key = "last_name"
|
||||
SET last_name = meta_value
|
||||
WHERE %s.last_name = ""
|
||||
AND %s.wp_user_id IS NOT NULL
|
||||
', $subscribers_table, $wpdb->prefix, $subscribers_table, $subscribers_table, $subscribers_table));
|
||||
}
|
||||
|
||||
private static function updateFristNameIfMissing() {
|
||||
global $wpdb;
|
||||
$subscribers_table = Subscriber::$_table;
|
||||
Subscriber::raw_execute(sprintf('
|
||||
UPDATE %s
|
||||
JOIN %susers wu ON %s.wp_user_id = wu.id
|
||||
SET first_name = display_name
|
||||
WHERE %s.first_name = ""
|
||||
AND %s.wp_user_id IS NOT NULL
|
||||
', $subscribers_table, $wpdb->prefix, $subscribers_table, $subscribers_table, $subscribers_table));
|
||||
}
|
||||
|
||||
private static function insertUsersToSegment() {
|
||||
$wp_segment = Segment::getWPSegment();
|
||||
$subscribers_table = Subscriber::$_table;
|
||||
$wp_mailpoet_subscriber_segment_table = SubscriberSegment::$_table;
|
||||
Subscriber::raw_execute(sprintf('
|
||||
INSERT IGNORE INTO %s(subscriber_id, segment_id, created_at)
|
||||
SELECT mps.id, "%s", CURRENT_TIMESTAMP() FROM %s mps
|
||||
WHERE mps.wp_user_id IS NOT NULL
|
||||
', $wp_mailpoet_subscriber_segment_table, $wp_segment->id, $subscribers_table));
|
||||
}
|
||||
|
||||
// fetch all wp users id
|
||||
$wp_users = \get_users(array(
|
||||
'count_total' => false,
|
||||
'fields' => 'ID'
|
||||
));
|
||||
|
||||
// update data for each wp user
|
||||
foreach($wp_users as $wp_user_id) {
|
||||
static::synchronizeUser($wp_user_id);
|
||||
}
|
||||
private static function removeFromTrash() {
|
||||
$subscribers_table = Subscriber::$_table;
|
||||
Subscriber::raw_execute(sprintf('
|
||||
UPDATE %s
|
||||
SET deleted_at = NULL
|
||||
WHERE %s.wp_user_id IS NOT NULL
|
||||
', $subscribers_table, $subscribers_table));
|
||||
}
|
||||
|
||||
private static function removeOrphanedSubscribers() {
|
||||
// remove orphaned wp segment subscribers (not having a matching wp user id),
|
||||
// e.g. if wp users were deleted directly from the database
|
||||
$wp_segment->subscribers()
|
||||
->whereNotIn('wp_user_id', $wp_users)
|
||||
global $wpdb;
|
||||
|
||||
Subscriber::table_alias('wpms')
|
||||
->leftOuterJoin($wpdb->prefix . 'users', array('wpms.wp_user_id', '=', 'wu.id'), 'wu')
|
||||
->whereNull('wu.id')
|
||||
->findResultSet()
|
||||
->set('wp_user_id', null)
|
||||
->delete();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,55 +1,194 @@
|
||||
<?php
|
||||
namespace MailPoet\Test\Segments;
|
||||
|
||||
use MailPoet\Models\Segment;
|
||||
use MailPoet\Segments\WP;
|
||||
namespace MailPoet\Test\Segments;
|
||||
|
||||
require_once(ABSPATH . 'wp-admin/includes/user.php');
|
||||
|
||||
class WPTest extends \MailPoetTest {
|
||||
function _before() {
|
||||
$this->wp_user_1 = $this->createWPUser('phoenix_test_user');
|
||||
$this->wp_user_2 = $this->createWPUser('phoenix_test_user2');
|
||||
$this->wp_segment = Segment::getWPSegment();
|
||||
use Carbon\Carbon;
|
||||
use MailPoet\Models\Segment;
|
||||
use MailPoet\Models\Subscriber;
|
||||
use MailPoet\Segments\WP;
|
||||
|
||||
class WPTest extends \MailPoetTest {
|
||||
|
||||
private $userIds = array();
|
||||
|
||||
function testItSynchronizeUsers() {
|
||||
$this->insertUser();
|
||||
$this->insertUser();
|
||||
WP::synchronizeUsers();
|
||||
$subscribersCount = $this->getSubscribersCount();
|
||||
expect($subscribersCount)->equals(2);
|
||||
}
|
||||
|
||||
function testItSynchronizeNewUsers() {
|
||||
$this->insertUser();
|
||||
$this->insertUser();
|
||||
WP::synchronizeUsers();
|
||||
$this->insertUser();
|
||||
WP::synchronizeUsers();
|
||||
$subscribersCount = $this->getSubscribersCount();
|
||||
expect($subscribersCount)->equals(3);
|
||||
}
|
||||
|
||||
function testItSynchronizeEmails() {
|
||||
$id = $this->insertUser();
|
||||
WP::synchronizeUsers();
|
||||
$this->updateWPUserEmail($id, 'user-sync-test-xx@email.com');
|
||||
WP::synchronizeUsers();
|
||||
$subscriber = Subscriber::where('wp_user_id', $id)->findOne();
|
||||
expect($subscriber->email)->equals('user-sync-test-xx@email.com');
|
||||
}
|
||||
|
||||
function testItSynchronizeFirstNames() {
|
||||
$id = $this->insertUser();
|
||||
WP::synchronizeUsers();
|
||||
update_user_meta($id, 'first_name', 'First name');
|
||||
WP::synchronizeUsers();
|
||||
$subscriber = Subscriber::where('wp_user_id', $id)->findOne();
|
||||
expect($subscriber->first_name)->equals('First name');
|
||||
}
|
||||
|
||||
function testItSynchronizeLastNames() {
|
||||
$id = $this->insertUser();
|
||||
WP::synchronizeUsers();
|
||||
update_user_meta($id, 'last_name', 'Last name');
|
||||
WP::synchronizeUsers();
|
||||
$subscriber = Subscriber::where('wp_user_id', $id)->findOne();
|
||||
expect($subscriber->last_name)->equals('Last name');
|
||||
}
|
||||
|
||||
function testItSynchronizeFirstNamesUsingDisplayName() {
|
||||
$id = $this->insertUser();
|
||||
WP::synchronizeUsers();
|
||||
$this->updateWPUserDisplayName($id, 'First name');
|
||||
WP::synchronizeUsers();
|
||||
$subscriber = Subscriber::where('wp_user_id', $id)->findOne();
|
||||
expect($subscriber->first_name)->equals('First name');
|
||||
}
|
||||
|
||||
function testItSynchronizeSegment() {
|
||||
$this->insertUser();
|
||||
$this->insertUser();
|
||||
$this->insertUser();
|
||||
WP::synchronizeUsers();
|
||||
$subscribers = Segment::getWPSegment()->subscribers()->whereIn('wp_user_id', $this->userIds);
|
||||
expect($subscribers->count())->equals(3);
|
||||
}
|
||||
|
||||
function testItRemovesUsersFromTrash() {
|
||||
$id = $this->insertUser();
|
||||
WP::synchronizeUsers();
|
||||
$subscriber = Subscriber::where("wp_user_id", $id)->findOne();
|
||||
$subscriber->deleted_at = Carbon::now();
|
||||
$subscriber->save();
|
||||
WP::synchronizeUsers();
|
||||
$subscriber = Subscriber::where("wp_user_id", $id)->findOne();
|
||||
expect($subscriber->deleted_at)->null();
|
||||
}
|
||||
|
||||
function testItSynchronizesDeletedWPUsersUsingHooks() {
|
||||
expect($this->getWPSegmentSubscribers()->count())->equals(2);
|
||||
wp_delete_user($this->wp_user_1->ID);
|
||||
expect($this->getWPSegmentSubscribers()->count())->equals(1);
|
||||
}
|
||||
|
||||
function testItForciblySynchronizesDeletedWPUsers() {
|
||||
global $wpdb;
|
||||
expect($this->getWPSegmentSubscribers()->count())->equals(2);
|
||||
// Remove a WP user directly from the database
|
||||
\ORM::for_table($wpdb->prefix . 'users')
|
||||
->where('id', $this->wp_user_2->ID)
|
||||
->deleteMany();
|
||||
$id = $this->insertUser();
|
||||
$this->insertUser();
|
||||
WP::synchronizeUsers();
|
||||
expect($this->getWPSegmentSubscribers()->count())->equals(1);
|
||||
$subscribersCount = $this->getSubscribersCount();
|
||||
expect($subscribersCount)->equals(2);
|
||||
wp_delete_user($id);
|
||||
$subscribersCount = $this->getSubscribersCount();
|
||||
expect($subscribersCount)->equals(1);
|
||||
}
|
||||
|
||||
private function getWPSegmentSubscribers() {
|
||||
return $this->wp_segment->subscribers()
|
||||
->whereIn(
|
||||
'wp_user_id',
|
||||
array(
|
||||
$this->wp_user_1->ID,
|
||||
$this->wp_user_2->ID
|
||||
)
|
||||
);
|
||||
function testItRemovesOrphanedSubscribers() {
|
||||
$this->insertUser();
|
||||
$id = $this->insertUser();
|
||||
WP::synchronizeUsers();
|
||||
$this->deleteWPUser($id);
|
||||
WP::synchronizeUsers();
|
||||
$subscribers = Segment::getWPSegment()->subscribers()->whereIn('wp_user_id', $this->userIds);
|
||||
expect($subscribers->count())->equals(1);
|
||||
}
|
||||
|
||||
private function createWPUser($login) {
|
||||
$WP_user = wp_create_user($login, 'pass', $login . '@example.com');
|
||||
$WP_user = get_user_by('login', $login);
|
||||
return $WP_user;
|
||||
function _before() {
|
||||
$this->cleanData();
|
||||
}
|
||||
|
||||
function _after() {
|
||||
\ORM::raw_execute('TRUNCATE ' . Segment::$_table);
|
||||
wp_delete_user($this->wp_user_1->ID);
|
||||
wp_delete_user($this->wp_user_2->ID);
|
||||
$this->cleanData();
|
||||
}
|
||||
}
|
||||
|
||||
private function cleanData() {
|
||||
\ORM::raw_execute('TRUNCATE ' . Segment::$_table);
|
||||
global $wpdb;
|
||||
$db = \ORM::getDb();
|
||||
$db->exec(sprintf('
|
||||
DELETE FROM
|
||||
%susers
|
||||
WHERE
|
||||
user_email LIKE "user-sync-test%%"
|
||||
', $wpdb->prefix));
|
||||
}
|
||||
|
||||
private function getSubscribersCount() {
|
||||
return Subscriber::whereIn("wp_user_id", $this->userIds)->count();
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a user without invoking wp hooks.
|
||||
* Those tests are testing user synchronisation, so we need data in wp_users table which has not been synchronised to
|
||||
* mailpoet database yet. We cannot use wp_insert_user functions because they would do the sync on insert.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function insertUser() {
|
||||
global $wpdb;
|
||||
$db = \ORM::getDb();
|
||||
$db->exec(sprintf('
|
||||
INSERT INTO
|
||||
%susers(user_login, user_email, user_registered)
|
||||
VALUES
|
||||
(
|
||||
CONCAT("user-sync-test", rand()),
|
||||
CONCAT("user", rand(), "@example.com"),
|
||||
"2017-01-02 12:31:12"
|
||||
)', $wpdb->prefix));
|
||||
$id = $db->lastInsertId();
|
||||
$this->userIds[] = $id;
|
||||
return $id;
|
||||
}
|
||||
|
||||
private function updateWPUserEmail($id, $email) {
|
||||
global $wpdb;
|
||||
$db = \ORM::getDb();
|
||||
$db->exec(sprintf('
|
||||
UPDATE
|
||||
%susers
|
||||
SET user_email = "%s"
|
||||
WHERE
|
||||
id = %s
|
||||
', $wpdb->prefix, $email, $id));
|
||||
}
|
||||
|
||||
private function updateWPUserDisplayName($id, $name) {
|
||||
global $wpdb;
|
||||
$db = \ORM::getDb();
|
||||
$db->exec(sprintf('
|
||||
UPDATE
|
||||
%susers
|
||||
SET display_name = "%s"
|
||||
WHERE
|
||||
id = %s
|
||||
', $wpdb->prefix, $name, $id));
|
||||
}
|
||||
|
||||
private function deleteWPUser($id) {
|
||||
global $wpdb;
|
||||
$db = \ORM::getDb();
|
||||
$db->exec(sprintf('
|
||||
DELETE FROM
|
||||
%susers
|
||||
WHERE
|
||||
id = %s
|
||||
', $wpdb->prefix, $id));
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user