説明なし

WP.php 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. <?php
  2. namespace MailPoet\Segments;
  3. if (!defined('ABSPATH')) exit;
  4. use MailPoet\DI\ContainerWrapper;
  5. use MailPoet\Entities\SegmentEntity;
  6. use MailPoet\Entities\SubscriberEntity;
  7. use MailPoet\Models\ModelValidator;
  8. use MailPoet\Models\Segment;
  9. use MailPoet\Models\Subscriber;
  10. use MailPoet\Models\SubscriberSegment;
  11. use MailPoet\Newsletter\Scheduler\WelcomeScheduler;
  12. use MailPoet\Settings\SettingsController;
  13. use MailPoet\Subscribers\ConfirmationEmailMailer;
  14. use MailPoet\Subscribers\Source;
  15. use MailPoet\WooCommerce\Helper as WooCommerceHelper;
  16. use MailPoet\WooCommerce\Subscription as WooCommerceSubscription;
  17. use MailPoet\WP\Functions as WPFunctions;
  18. use MailPoetVendor\Carbon\Carbon;
  19. use MailPoetVendor\Idiorm\ORM;
  20. class WP {
  21. /** @var WPFunctions */
  22. private $wp;
  23. /** @var WelcomeScheduler */
  24. private $welcomeScheduler;
  25. /** @var WooCommerceHelper */
  26. private $wooHelper;
  27. public function __construct(
  28. WPFunctions $wp,
  29. WelcomeScheduler $welcomeScheduler,
  30. WooCommerceHelper $wooHelper
  31. ) {
  32. $this->wp = $wp;
  33. $this->welcomeScheduler = $welcomeScheduler;
  34. $this->wooHelper = $wooHelper;
  35. }
  36. public function synchronizeUser($wpUserId, $oldWpUserData = false) {
  37. $wpUser = \get_userdata($wpUserId);
  38. if ($wpUser === false) return;
  39. $subscriber = Subscriber::where('wp_user_id', $wpUser->ID)
  40. ->findOne();
  41. $currentFilter = $this->wp->currentFilter();
  42. // Delete
  43. if (in_array($currentFilter, ['delete_user', 'deleted_user', 'remove_user_from_blog'])) {
  44. return $this->deleteSubscriber($subscriber);
  45. }
  46. return $this->createOrUpdateSubscriber($currentFilter, $wpUser, $subscriber, $oldWpUserData);
  47. }
  48. private function deleteSubscriber($subscriber) {
  49. if ($subscriber !== false) {
  50. // unlink subscriber from wp user and delete
  51. $subscriber->set('wp_user_id', null);
  52. $subscriber->delete();
  53. }
  54. }
  55. private function createOrUpdateSubscriber($currentFilter, $wpUser, $subscriber = false, $oldWpUserData = false) {
  56. // Add or update
  57. $wpSegment = Segment::getWPSegment();
  58. if (!$wpSegment) return;
  59. // find subscriber by email when is false
  60. if (!$subscriber) {
  61. $subscriber = Subscriber::where('email', $wpUser->user_email)->findOne(); // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps
  62. }
  63. // get first name & last name
  64. $firstName = $wpUser->first_name; // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps
  65. $lastName = $wpUser->last_name; // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps
  66. if (empty($wpUser->first_name) && empty($wpUser->last_name)) { // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps
  67. $firstName = $wpUser->display_name; // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps
  68. }
  69. $signupConfirmationEnabled = SettingsController::getInstance()->get('signup_confirmation.enabled');
  70. $status = $signupConfirmationEnabled ? Subscriber::STATUS_UNCONFIRMED : Subscriber::STATUS_SUBSCRIBED;
  71. // we want to mark a new subscriber as unsubscribe when the checkbox from registration is unchecked
  72. if (isset($_POST['mailpoet']['subscribe_on_register_active']) && (bool)$_POST['mailpoet']['subscribe_on_register_active'] === true) {
  73. $status = SubscriberEntity::STATUS_UNSUBSCRIBED;
  74. }
  75. // we want to mark a new subscriber as unsubscribed when the checkbox on Woo checkout is unchecked
  76. if (isset($_POST[WooCommerceSubscription::CHECKOUT_OPTIN_PRESENCE_CHECK_INPUT_NAME])
  77. && !isset($_POST[WooCommerceSubscription::CHECKOUT_OPTIN_INPUT_NAME])
  78. ) {
  79. $status = SubscriberEntity::STATUS_UNSUBSCRIBED;
  80. }
  81. // subscriber data
  82. $data = [
  83. 'wp_user_id' => $wpUser->ID,
  84. 'email' => $wpUser->user_email, // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps
  85. 'first_name' => $firstName,
  86. 'last_name' => $lastName,
  87. 'status' => $status,
  88. 'source' => Source::WORDPRESS_USER,
  89. ];
  90. if ($subscriber !== false) {
  91. $data['id'] = $subscriber->id();
  92. unset($data['status']); // don't override status for existing users
  93. unset($data['source']); // don't override status for existing users
  94. }
  95. $addingNewUserToDisabledWPSegment = $wpSegment->deletedAt !== null && $currentFilter === 'user_register';
  96. $otherActiveSegments = [];
  97. if ($subscriber) {
  98. $subscriber = $subscriber->withSegments();
  99. $otherActiveSegments = array_filter($subscriber->segments ?? [], function ($segment) {
  100. return $segment['type'] !== SegmentEntity::TYPE_WP_USERS && $segment['deleted_at'] === null;
  101. });
  102. }
  103. $isWooCustomer = $this->wooHelper->isWooCommerceActive() && in_array('customer', $wpUser->roles ?? [], true);
  104. // When WP Segment is disabled force trashed state and unconfirmed status for new WPUsers without active segment
  105. // or who are not WooCommerce customers at the same time since customers are added to the WooCommerce list
  106. if ($addingNewUserToDisabledWPSegment && !$otherActiveSegments && !$isWooCustomer) {
  107. $data['deleted_at'] = Carbon::createFromTimestamp($this->wp->currentTime('timestamp'));
  108. $data['status'] = SubscriberEntity::STATUS_UNCONFIRMED;
  109. }
  110. $subscriber = Subscriber::createOrUpdate($data);
  111. if ($subscriber->getErrors() === false && $subscriber->id > 0) {
  112. // add subscriber to the WP Users segment
  113. SubscriberSegment::subscribeToSegments(
  114. $subscriber,
  115. [$wpSegment->id]
  116. );
  117. $subscribeOnRegisterEnabled = SettingsController::getInstance()->get('subscribe.on_register.enabled');
  118. $sendConfirmationEmail =
  119. $signupConfirmationEnabled
  120. && $subscribeOnRegisterEnabled
  121. && $currentFilter !== 'profile_update'
  122. && !$addingNewUserToDisabledWPSegment;
  123. if ($sendConfirmationEmail && ($subscriber->status === Subscriber::STATUS_UNCONFIRMED)) {
  124. /** @var ConfirmationEmailMailer $confirmationEmailMailer */
  125. $confirmationEmailMailer = ContainerWrapper::getInstance()->get(ConfirmationEmailMailer::class);
  126. $confirmationEmailMailer->sendConfirmationEmailOnce($subscriber);
  127. }
  128. // welcome email
  129. $scheduleWelcomeNewsletter = false;
  130. if (in_array($currentFilter, ['profile_update', 'user_register'])) {
  131. $scheduleWelcomeNewsletter = true;
  132. }
  133. if ($scheduleWelcomeNewsletter === true) {
  134. $this->welcomeScheduler->scheduleWPUserWelcomeNotification(
  135. $subscriber->id,
  136. (array)$wpUser,
  137. (array)$oldWpUserData
  138. );
  139. }
  140. }
  141. }
  142. public function synchronizeUsers() {
  143. $updatedUsersEmails = $this->updateSubscribersEmails();
  144. $insertedUsersEmails = $this->insertSubscribers();
  145. $this->removeUpdatedSubscribersWithInvalidEmail(array_merge($updatedUsersEmails, $insertedUsersEmails));
  146. unset($updatedUsersEmails);
  147. unset($insertedUsersEmails);
  148. $this->updateFirstNames();
  149. $this->updateLastNames();
  150. $this->updateFirstNameIfMissing();
  151. $this->insertUsersToSegment();
  152. $this->removeOrphanedSubscribers();
  153. return true;
  154. }
  155. private function removeUpdatedSubscribersWithInvalidEmail($updatedEmails) {
  156. $validator = new ModelValidator();
  157. $invalidWpUserIds = array_map(function($item) {
  158. return $item['id'];
  159. },
  160. array_filter($updatedEmails, function($updatedEmail) use($validator) {
  161. return !$validator->validateEmail($updatedEmail['email']);
  162. }));
  163. if (!$invalidWpUserIds) {
  164. return;
  165. }
  166. ORM::for_table(Subscriber::$_table)->whereIn('wp_user_id', $invalidWpUserIds)->delete_many();
  167. }
  168. private function updateSubscribersEmails() {
  169. global $wpdb;
  170. Subscriber::rawExecute('SELECT NOW();');
  171. $startTime = Subscriber::getLastStatement()->fetch(\PDO::FETCH_COLUMN);
  172. $subscribersTable = Subscriber::$_table;
  173. Subscriber::rawExecute(sprintf('
  174. UPDATE IGNORE %1$s
  175. INNER JOIN %2$s as wu ON %1$s.wp_user_id = wu.id
  176. SET %1$s.email = wu.user_email;
  177. ', $subscribersTable, $wpdb->users));
  178. return ORM::for_table(Subscriber::$_table)->raw_query(sprintf(
  179. 'SELECT wp_user_id as id, email FROM %s
  180. WHERE updated_at >= \'%s\';
  181. ', $subscribersTable, $startTime))->findArray();
  182. }
  183. private function insertSubscribers() {
  184. global $wpdb;
  185. $wpSegment = Segment::getWPSegment();
  186. if (!$wpSegment) return;
  187. if ($wpSegment->deletedAt !== null) {
  188. $subscriberStatus = SubscriberEntity::STATUS_UNCONFIRMED;
  189. $deletedAt = 'CURRENT_TIMESTAMP()';
  190. } else {
  191. $signupConfirmationEnabled = SettingsController::getInstance()->get('signup_confirmation.enabled');
  192. $subscriberStatus = $signupConfirmationEnabled ? SubscriberEntity::STATUS_UNCONFIRMED : SubscriberEntity::STATUS_SUBSCRIBED;
  193. $deletedAt = 'null';
  194. }
  195. $subscribersTable = Subscriber::$_table;
  196. $inserterdUserIds = ORM::for_table($wpdb->users)->raw_query(sprintf(
  197. 'SELECT %2$s.id, %2$s.user_email as email FROM %2$s
  198. LEFT JOIN %1$s AS mps ON mps.wp_user_id = %2$s.id
  199. WHERE mps.wp_user_id IS NULL AND %2$s.user_email != ""
  200. ', $subscribersTable, $wpdb->users))->findArray();
  201. Subscriber::rawExecute(sprintf(
  202. '
  203. INSERT IGNORE INTO %1$s(wp_user_id, email, status, created_at, `source`, deleted_at)
  204. SELECT wu.id, wu.user_email, "%4$s", CURRENT_TIMESTAMP(), "%3$s", %5$s FROM %2$s wu
  205. LEFT JOIN %1$s mps ON wu.id = mps.wp_user_id
  206. WHERE mps.wp_user_id IS NULL AND wu.user_email != ""
  207. ON DUPLICATE KEY UPDATE wp_user_id = wu.id
  208. ',
  209. $subscribersTable,
  210. $wpdb->users,
  211. Source::WORDPRESS_USER,
  212. $subscriberStatus,
  213. $deletedAt
  214. ));
  215. return $inserterdUserIds;
  216. }
  217. private function updateFirstNames() {
  218. global $wpdb;
  219. $subscribersTable = Subscriber::$_table;
  220. Subscriber::rawExecute(sprintf('
  221. UPDATE %1$s
  222. JOIN %2$s as wpum ON %1$s.wp_user_id = wpum.user_id AND wpum.meta_key = "first_name"
  223. SET %1$s.first_name = SUBSTRING(wpum.meta_value, 1, 255)
  224. WHERE %1$s.first_name = ""
  225. AND %1$s.wp_user_id IS NOT NULL
  226. AND wpum.meta_value IS NOT NULL
  227. ', $subscribersTable, $wpdb->usermeta));
  228. }
  229. private function updateLastNames() {
  230. global $wpdb;
  231. $subscribersTable = Subscriber::$_table;
  232. Subscriber::rawExecute(sprintf('
  233. UPDATE %1$s
  234. JOIN %2$s as wpum ON %1$s.wp_user_id = wpum.user_id AND wpum.meta_key = "last_name"
  235. SET %1$s.last_name = SUBSTRING(wpum.meta_value, 1, 255)
  236. WHERE %1$s.last_name = ""
  237. AND %1$s.wp_user_id IS NOT NULL
  238. AND wpum.meta_value IS NOT NULL
  239. ', $subscribersTable, $wpdb->usermeta));
  240. }
  241. private function updateFirstNameIfMissing() {
  242. global $wpdb;
  243. $subscribersTable = Subscriber::$_table;
  244. Subscriber::rawExecute(sprintf('
  245. UPDATE %1$s
  246. JOIN %2$s wu ON %1$s.wp_user_id = wu.id
  247. SET %1$s.first_name = wu.display_name
  248. WHERE %1$s.first_name = ""
  249. AND %1$s.wp_user_id IS NOT NULL
  250. ', $subscribersTable, $wpdb->users));
  251. }
  252. private function insertUsersToSegment() {
  253. $wpSegment = Segment::getWPSegment();
  254. $subscribersTable = Subscriber::$_table;
  255. $wpMailpoetSubscriberSegmentTable = SubscriberSegment::$_table;
  256. Subscriber::rawExecute(sprintf('
  257. INSERT IGNORE INTO %s(subscriber_id, segment_id, created_at)
  258. SELECT mps.id, "%s", CURRENT_TIMESTAMP() FROM %s mps
  259. WHERE mps.wp_user_id > 0
  260. ', $wpMailpoetSubscriberSegmentTable, $wpSegment->id, $subscribersTable));
  261. }
  262. private function removeOrphanedSubscribers() {
  263. // remove orphaned wp segment subscribers (not having a matching wp user id),
  264. // e.g. if wp users were deleted directly from the database
  265. global $wpdb;
  266. $wpSegment = Segment::getWPSegment();
  267. $wpSegment->subscribers()
  268. ->leftOuterJoin($wpdb->users, [MP_SUBSCRIBERS_TABLE . '.wp_user_id', '=', 'wu.id'], 'wu')
  269. ->whereRaw('(wu.id IS NULL OR ' . MP_SUBSCRIBERS_TABLE . '.email = "")')
  270. ->findResultSet()
  271. ->set('wp_user_id', null)
  272. ->delete();
  273. }
  274. }