Нет описания

Subscribers.php 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. <?php
  2. namespace MailPoet\API\JSON\v1;
  3. if (!defined('ABSPATH')) exit;
  4. use MailPoet\API\JSON\Endpoint as APIEndpoint;
  5. use MailPoet\API\JSON\Error as APIError;
  6. use MailPoet\API\JSON\ResponseBuilders\SubscribersResponseBuilder;
  7. use MailPoet\Config\AccessControl;
  8. use MailPoet\Doctrine\Validator\ValidationException;
  9. use MailPoet\Entities\SegmentEntity;
  10. use MailPoet\Entities\SubscriberEntity;
  11. use MailPoet\Exception;
  12. use MailPoet\Listing;
  13. use MailPoet\Models\Subscriber;
  14. use MailPoet\Segments\SegmentsRepository;
  15. use MailPoet\Subscribers\ConfirmationEmailMailer;
  16. use MailPoet\Subscribers\SubscriberListingRepository;
  17. use MailPoet\Subscribers\SubscriberSaveController;
  18. use MailPoet\Subscribers\SubscribersRepository;
  19. use MailPoet\Subscribers\SubscriberSubscribeController;
  20. use MailPoet\UnexpectedValueException;
  21. use MailPoet\WP\Functions as WPFunctions;
  22. class Subscribers extends APIEndpoint {
  23. const SUBSCRIPTION_LIMIT_COOLDOWN = 60;
  24. public $permissions = [
  25. 'global' => AccessControl::PERMISSION_MANAGE_SUBSCRIBERS,
  26. 'methods' => ['subscribe' => AccessControl::NO_ACCESS_RESTRICTION],
  27. ];
  28. /** @var Listing\Handler */
  29. private $listingHandler;
  30. /** @var ConfirmationEmailMailer; */
  31. private $confirmationEmailMailer;
  32. /** @var SubscribersRepository */
  33. private $subscribersRepository;
  34. /** @var SubscribersResponseBuilder */
  35. private $subscribersResponseBuilder;
  36. /** @var SubscriberListingRepository */
  37. private $subscriberListingRepository;
  38. /** @var SegmentsRepository */
  39. private $segmentsRepository;
  40. /** @var SubscriberSaveController */
  41. private $saveController;
  42. /** @var SubscriberSubscribeController */
  43. private $subscribeController;
  44. public function __construct(
  45. Listing\Handler $listingHandler,
  46. ConfirmationEmailMailer $confirmationEmailMailer,
  47. SubscribersRepository $subscribersRepository,
  48. SubscribersResponseBuilder $subscribersResponseBuilder,
  49. SubscriberListingRepository $subscriberListingRepository,
  50. SegmentsRepository $segmentsRepository,
  51. SubscriberSaveController $saveController,
  52. SubscriberSubscribeController $subscribeController
  53. ) {
  54. $this->listingHandler = $listingHandler;
  55. $this->confirmationEmailMailer = $confirmationEmailMailer;
  56. $this->subscribersRepository = $subscribersRepository;
  57. $this->subscribersResponseBuilder = $subscribersResponseBuilder;
  58. $this->subscriberListingRepository = $subscriberListingRepository;
  59. $this->segmentsRepository = $segmentsRepository;
  60. $this->saveController = $saveController;
  61. $this->subscribeController = $subscribeController;
  62. }
  63. public function get($data = []) {
  64. $subscriber = $this->getSubscriber($data);
  65. if (!$subscriber instanceof SubscriberEntity) {
  66. return $this->errorResponse([
  67. APIError::NOT_FOUND => WPFunctions::get()->__('This subscriber does not exist.', 'mailpoet'),
  68. ]);
  69. }
  70. $result = $this->subscribersResponseBuilder->build($subscriber);
  71. return $this->successResponse($result);
  72. }
  73. public function listing($data = []) {
  74. $definition = $this->listingHandler->getListingDefinition($data);
  75. $items = $this->subscriberListingRepository->getData($definition);
  76. $count = $this->subscriberListingRepository->getCount($definition);
  77. $filters = $this->subscriberListingRepository->getFilters($definition);
  78. $groups = $this->subscriberListingRepository->getGroups($definition);
  79. $subscribers = $this->subscribersResponseBuilder->buildForListing($items);
  80. if ($data['filter']['segment'] ?? false) {
  81. foreach ($subscribers as $key => $subscriber) {
  82. $subscribers[$key] = $this->preferUnsubscribedStatusFromSegment($subscriber, $data['filter']['segment']);
  83. }
  84. }
  85. return $this->successResponse($subscribers, [
  86. 'count' => $count,
  87. 'filters' => $filters,
  88. 'groups' => $groups,
  89. ]);
  90. }
  91. private function preferUnsubscribedStatusFromSegment(array $subscriber, $segmentId) {
  92. $segmentStatus = $this->findSegmentStatus($subscriber, $segmentId);
  93. if ($segmentStatus === Subscriber::STATUS_UNSUBSCRIBED) {
  94. $subscriber['status'] = $segmentStatus;
  95. }
  96. return $subscriber;
  97. }
  98. private function findSegmentStatus(array $subscriber, $segmentId) {
  99. foreach ($subscriber['subscriptions'] as $segment) {
  100. if ($segment['segment_id'] === $segmentId) {
  101. return $segment['status'];
  102. }
  103. }
  104. }
  105. public function subscribe($data = []) {
  106. try {
  107. $meta = $this->subscribeController->subscribe($data);
  108. } catch (Exception $exception) {
  109. return $this->badRequest([$exception->getMessage()]);
  110. }
  111. if (!empty($meta['error'])) {
  112. $errorMessage = $meta['error'];
  113. unset($meta['error']);
  114. return $this->badRequest([APIError::BAD_REQUEST => $errorMessage], $meta);
  115. }
  116. return $this->successResponse(
  117. [],
  118. $meta
  119. );
  120. }
  121. public function save($data = []) {
  122. try {
  123. $subscriber = $this->saveController->save($data);
  124. } catch (ValidationException $validationException) {
  125. return $this->badRequest([$this->getErrorMessage($validationException)]);
  126. }
  127. return $this->successResponse(
  128. $this->subscribersResponseBuilder->build($subscriber)
  129. );
  130. }
  131. public function restore($data = []) {
  132. $subscriber = $this->getSubscriber($data);
  133. if ($subscriber instanceof SubscriberEntity) {
  134. $this->subscribersRepository->bulkRestore([$subscriber->getId()]);
  135. $this->subscribersRepository->refresh($subscriber);
  136. return $this->successResponse(
  137. $this->subscribersResponseBuilder->build($subscriber),
  138. ['count' => 1]
  139. );
  140. } else {
  141. return $this->errorResponse([
  142. APIError::NOT_FOUND => WPFunctions::get()->__('This subscriber does not exist.', 'mailpoet'),
  143. ]);
  144. }
  145. }
  146. public function trash($data = []) {
  147. $subscriber = $this->getSubscriber($data);
  148. if ($subscriber instanceof SubscriberEntity) {
  149. $this->subscribersRepository->bulkTrash([$subscriber->getId()]);
  150. $this->subscribersRepository->refresh($subscriber);
  151. return $this->successResponse(
  152. $this->subscribersResponseBuilder->build($subscriber),
  153. ['count' => 1]
  154. );
  155. } else {
  156. return $this->errorResponse([
  157. APIError::NOT_FOUND => WPFunctions::get()->__('This subscriber does not exist.', 'mailpoet'),
  158. ]);
  159. }
  160. }
  161. public function delete($data = []) {
  162. $subscriber = $this->getSubscriber($data);
  163. if ($subscriber instanceof SubscriberEntity) {
  164. $count = $this->subscribersRepository->bulkDelete([$subscriber->getId()]);
  165. return $this->successResponse(null, ['count' => $count]);
  166. } else {
  167. return $this->errorResponse([
  168. APIError::NOT_FOUND => WPFunctions::get()->__('This subscriber does not exist.', 'mailpoet'),
  169. ]);
  170. }
  171. }
  172. public function sendConfirmationEmail($data = []) {
  173. $id = (isset($data['id']) ? (int)$data['id'] : false);
  174. $subscriber = Subscriber::findOne($id);
  175. if ($subscriber instanceof Subscriber) {
  176. if ($this->confirmationEmailMailer->sendConfirmationEmail($subscriber)) {
  177. return $this->successResponse();
  178. }
  179. return $this->errorResponse();
  180. } else {
  181. return $this->errorResponse([
  182. APIError::NOT_FOUND => WPFunctions::get()->__('This subscriber does not exist.', 'mailpoet'),
  183. ]);
  184. }
  185. }
  186. public function bulkAction($data = []) {
  187. $definition = $this->listingHandler->getListingDefinition($data['listing']);
  188. $ids = $this->subscriberListingRepository->getActionableIds($definition);
  189. $count = 0;
  190. $segment = null;
  191. if (isset($data['segment_id'])) {
  192. $segment = $this->getSegment($data);
  193. if (!$segment) {
  194. return $this->errorResponse([
  195. APIError::NOT_FOUND => WPFunctions::get()->__('This segment does not exist.', 'mailpoet'),
  196. ]);
  197. }
  198. }
  199. if ($data['action'] === 'trash') {
  200. $count = $this->subscribersRepository->bulkTrash($ids);
  201. } elseif ($data['action'] === 'restore') {
  202. $count = $this->subscribersRepository->bulkRestore($ids);
  203. } elseif ($data['action'] === 'delete') {
  204. $count = $this->subscribersRepository->bulkDelete($ids);
  205. } elseif ($data['action'] === 'removeFromAllLists') {
  206. $count = $this->subscribersRepository->bulkRemoveFromAllSegments($ids);
  207. } elseif ($data['action'] === 'removeFromList' && $segment instanceof SegmentEntity) {
  208. $count = $this->subscribersRepository->bulkRemoveFromSegment($segment, $ids);
  209. } elseif ($data['action'] === 'addToList' && $segment instanceof SegmentEntity) {
  210. $count = $this->subscribersRepository->bulkAddToSegment($segment, $ids);
  211. } elseif ($data['action'] === 'moveToList' && $segment instanceof SegmentEntity) {
  212. $count = $this->subscribersRepository->bulkMoveToSegment($segment, $ids);
  213. } elseif ($data['action'] === 'unsubscribe') {
  214. $count = $this->subscribersRepository->bulkUnsubscribe($ids);
  215. } else {
  216. throw UnexpectedValueException::create()
  217. ->withErrors([APIError::BAD_REQUEST => "Invalid bulk action '{$data['action']}' provided."]);
  218. }
  219. $meta = [
  220. 'count' => $count,
  221. ];
  222. if ($segment) {
  223. $meta['segment'] = $segment->getName();
  224. }
  225. return $this->successResponse(null, $meta);
  226. }
  227. /**
  228. * @param array $data
  229. * @return SubscriberEntity|null
  230. */
  231. private function getSubscriber($data) {
  232. return isset($data['id'])
  233. ? $this->subscribersRepository->findOneById((int)$data['id'])
  234. : null;
  235. }
  236. private function getSegment(array $data): ?SegmentEntity {
  237. return isset($data['segment_id'])
  238. ? $this->segmentsRepository->findOneById((int)$data['segment_id'])
  239. : null;
  240. }
  241. private function getErrorMessage(ValidationException $exception): string {
  242. $exceptionMessage = $exception->getMessage();
  243. if (strpos($exceptionMessage, 'This value should not be blank.') !== false) {
  244. return WPFunctions::get()->__('Please enter your email address', 'mailpoet');
  245. } elseif (strpos($exceptionMessage, 'This value is not a valid email address.') !== false) {
  246. return WPFunctions::get()->__('Your email address is invalid!', 'mailpoet');
  247. }
  248. return WPFunctions::get()->__('Unexpected error.', 'mailpoet');
  249. }
  250. }