Нет описания

SubscriberSaveController.php 8.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. <?php
  2. namespace MailPoet\Subscribers;
  3. if (!defined('ABSPATH')) exit;
  4. use MailPoet\CustomFields\CustomFieldsRepository;
  5. use MailPoet\Entities\StatisticsUnsubscribeEntity;
  6. use MailPoet\Entities\SubscriberEntity;
  7. use MailPoet\Entities\SubscriberSegmentEntity;
  8. use MailPoet\Newsletter\Scheduler\WelcomeScheduler;
  9. use MailPoet\Segments\SegmentsRepository;
  10. use MailPoet\Settings\SettingsController;
  11. use MailPoet\Statistics\Track\Unsubscribes;
  12. use MailPoet\Util\Security;
  13. use MailPoet\WP\Functions as WPFunctions;
  14. use MailPoetVendor\Carbon\Carbon;
  15. class SubscriberSaveController {
  16. /** @var CustomFieldsRepository */
  17. private $customFieldsRepository;
  18. /** @var Security */
  19. private $security;
  20. /** @var SettingsController */
  21. private $settings;
  22. /** @var SegmentsRepository */
  23. private $segmentsRepository;
  24. /** @var SubscriberCustomFieldRepository */
  25. private $subscriberCustomFieldRepository;
  26. /** @var SubscribersRepository */
  27. private $subscribersRepository;
  28. /** @var SubscriberSegmentRepository */
  29. private $subscriberSegmentRepository;
  30. /** @var Unsubscribes */
  31. private $unsubscribesTracker;
  32. /** @var WelcomeScheduler */
  33. private $welcomeScheduler;
  34. /** @var WPFunctions */
  35. private $wp;
  36. public function __construct(
  37. CustomFieldsRepository $customFieldsRepository,
  38. Security $security,
  39. SettingsController $settings,
  40. SegmentsRepository $segmentsRepository,
  41. SubscriberCustomFieldRepository $subscriberCustomFieldRepository,
  42. SubscribersRepository $subscribersRepository,
  43. SubscriberSegmentRepository $subscriberSegmentRepository,
  44. Unsubscribes $unsubscribesTracker,
  45. WelcomeScheduler $welcomeScheduler,
  46. WPFunctions $wp
  47. ) {
  48. $this->customFieldsRepository = $customFieldsRepository;
  49. $this->security = $security;
  50. $this->settings = $settings;
  51. $this->segmentsRepository = $segmentsRepository;
  52. $this->subscriberSegmentRepository = $subscriberSegmentRepository;
  53. $this->subscribersRepository = $subscribersRepository;
  54. $this->subscriberCustomFieldRepository = $subscriberCustomFieldRepository;
  55. $this->unsubscribesTracker = $unsubscribesTracker;
  56. $this->welcomeScheduler = $welcomeScheduler;
  57. $this->wp = $wp;
  58. }
  59. public function save(array $data): SubscriberEntity {
  60. if (is_array($data) && !empty($data)) {
  61. $data = $this->wp->stripslashesDeep($data);
  62. }
  63. if (empty($data['segments'])) {
  64. $data['segments'] = [];
  65. }
  66. $data['segments'] = array_merge($data['segments'], $this->getNonDefaultSubscribedSegments($data));
  67. $newSegments = $this->findNewSegments($data);
  68. $oldSubscriber = $this->findSubscriber($data);
  69. $oldStatus = $oldSubscriber ? $oldSubscriber->getStatus() : null;
  70. if (
  71. $oldSubscriber instanceof SubscriberEntity
  72. && isset($data['status'])
  73. && ($data['status'] === SubscriberEntity::STATUS_UNSUBSCRIBED)
  74. && ($oldSubscriber->getStatus() !== SubscriberEntity::STATUS_UNSUBSCRIBED)
  75. ) {
  76. $currentUser = $this->wp->wpGetCurrentUser();
  77. $this->unsubscribesTracker->track(
  78. (int)$oldSubscriber->getId(),
  79. StatisticsUnsubscribeEntity::SOURCE_ADMINISTRATOR,
  80. null,
  81. $currentUser->display_name // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps
  82. );
  83. }
  84. $subscriber = $this->createOrUpdate($data, $oldSubscriber);
  85. $this->updateCustomFields($data, $subscriber);
  86. $segments = isset($data['segments']) ? $this->findSegments($data['segments']) : null;
  87. // check for status change
  88. if (
  89. $oldStatus === SubscriberEntity::STATUS_SUBSCRIBED
  90. && $subscriber->getStatus() === SubscriberEntity::STATUS_UNSUBSCRIBED
  91. ) {
  92. // make sure we unsubscribe the user from all segments
  93. $this->subscriberSegmentRepository->unsubscribeFromSegments($subscriber);
  94. } elseif ($segments !== null) {
  95. $this->subscriberSegmentRepository->resetSubscriptions($subscriber, $segments);
  96. }
  97. if (!empty($newSegments)) {
  98. $this->welcomeScheduler->scheduleSubscriberWelcomeNotification($subscriber->getId(), $newSegments);
  99. }
  100. return $subscriber;
  101. }
  102. public function filterOutReservedColumns(array $subscriberData): array {
  103. $reservedColumns = [
  104. 'id',
  105. 'wp_user_id',
  106. 'is_woocommerce_user',
  107. 'status',
  108. 'subscribed_ip',
  109. 'confirmed_ip',
  110. 'confirmed_at',
  111. 'created_at',
  112. 'updated_at',
  113. 'deleted_at',
  114. 'unconfirmed_data',
  115. ];
  116. return array_diff_key(
  117. $subscriberData,
  118. array_flip($reservedColumns)
  119. );
  120. }
  121. private function getNonDefaultSubscribedSegments(array $data): array {
  122. if (!isset($data['id']) || (int)$data['id'] <= 0) {
  123. return [];
  124. }
  125. $subscribedSegments = $this->subscriberSegmentRepository->getNonDefaultSubscribedSegments($data['id']);
  126. return array_filter(array_map(function(SubscriberSegmentEntity $subscriberSegment): int {
  127. $segment = $subscriberSegment->getSegment();
  128. if (!$segment) {
  129. return 0;
  130. }
  131. return (int)$segment->getId();
  132. }, $subscribedSegments));
  133. }
  134. private function findSegments(array $segmentIds): array {
  135. return $this->segmentsRepository->findBy(['id' => $segmentIds]);
  136. }
  137. private function findNewSegments(array $data): array {
  138. $oldSegmentIds = [];
  139. if (isset($data['id']) && (int)$data['id'] > 0) {
  140. $subscribersSegments = $this->subscriberSegmentRepository->findBy(['subscriber' => $data['id']]);
  141. foreach ($subscribersSegments as $subscribersSegment) {
  142. $segment = $subscribersSegment->getSegment();
  143. if (!$segment) {
  144. continue;
  145. }
  146. $oldSegmentIds[] = (int)$segment->getId();
  147. }
  148. }
  149. return array_diff($data['segments'], $oldSegmentIds);
  150. }
  151. public function createOrUpdate(array $data, ?SubscriberEntity $subscriber): SubscriberEntity {
  152. if (!$subscriber) {
  153. $subscriber = $this->createSubscriber();
  154. if (!isset($data['source'])) $data['source'] = Source::ADMINISTRATOR;
  155. }
  156. if (isset($data['email'])) $subscriber->setEmail($data['email']);
  157. if (isset($data['first_name'])) $subscriber->setFirstName($data['first_name']);
  158. if (isset($data['last_name'])) $subscriber->setLastName($data['last_name']);
  159. if (isset($data['status'])) $subscriber->setStatus($data['status']);
  160. if (isset($data['source'])) $subscriber->setSource($data['source']);
  161. if (isset($data['wp_user_id'])) $subscriber->setWpUserId($data['wp_user_id']);
  162. if (isset($data['subscribed_ip'])) $subscriber->setSubscribedIp($data['subscribed_ip']);
  163. if (isset($data['confirmed_ip'])) $subscriber->setConfirmedIp($data['confirmed_ip']);
  164. $createdAt = isset($data['created_at']) ? Carbon::createFromFormat('Y-m-d H:i:s', $data['created_at']) : null;
  165. if ($createdAt) $subscriber->setCreatedAt($createdAt);
  166. $confirmedAt = isset($data['confirmed_at']) ? Carbon::createFromFormat('Y-m-d H:i:s', $data['confirmed_at']) : null;
  167. if ($confirmedAt) $subscriber->setConfirmedAt($confirmedAt);
  168. // wipe any unconfirmed data at this point
  169. $subscriber->setUnconfirmedData(null);
  170. $this->subscribersRepository->persist($subscriber);
  171. $this->subscribersRepository->flush();
  172. return $subscriber;
  173. }
  174. private function createSubscriber(): SubscriberEntity {
  175. $subscriber = new SubscriberEntity();
  176. $subscriber->setUnsubscribeToken($this->security->generateUnsubscribeTokenByEntity($subscriber));
  177. $subscriber->setLinkToken(Security::generateHash(SubscriberEntity::LINK_TOKEN_LENGTH));
  178. $subscriber->setStatus(!$this->settings->get('signup_confirmation.enabled') ? SubscriberEntity::STATUS_SUBSCRIBED : SubscriberEntity::STATUS_UNCONFIRMED);
  179. return $subscriber;
  180. }
  181. private function findSubscriber(array &$data): ?SubscriberEntity {
  182. $subscriber = null;
  183. if (isset($data['id']) && (int)$data['id'] > 0) {
  184. $subscriber = $this->subscribersRepository->findOneById(((int)$data['id']));
  185. unset($data['id']);
  186. }
  187. if (!$subscriber && !empty($data['email'])) {
  188. $subscriber = $this->subscribersRepository->findOneBy(['email' => $data['email']]);
  189. if ($subscriber) {
  190. unset($data['email']);
  191. }
  192. }
  193. return $subscriber;
  194. }
  195. public function updateCustomFields(array $data, SubscriberEntity $subscriber): void {
  196. $customFieldsMap = [];
  197. foreach ($data as $key => $value) {
  198. if (strpos($key, 'cf_') === 0) {
  199. $customFieldsMap[(int)substr($key, 3)] = $value;
  200. }
  201. }
  202. if (empty($customFieldsMap)) {
  203. return;
  204. }
  205. $customFields = $this->customFieldsRepository->findBy(['id' => array_keys($customFieldsMap)]);
  206. foreach ($customFields as $customField) {
  207. $this->subscriberCustomFieldRepository->createOrUpdate($subscriber, $customField, $customFieldsMap[$customField->getId()]);
  208. }
  209. }
  210. }