Aucune description

DynamicSegments.php 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. <?php declare(strict_types = 1);
  2. namespace MailPoet\API\JSON\v1;
  3. if (!defined('ABSPATH')) exit;
  4. use InvalidArgumentException;
  5. use MailPoet\API\JSON\Endpoint as APIEndpoint;
  6. use MailPoet\API\JSON\Error;
  7. use MailPoet\API\JSON\Response;
  8. use MailPoet\API\JSON\ResponseBuilders\DynamicSegmentsResponseBuilder;
  9. use MailPoet\Config\AccessControl;
  10. use MailPoet\Doctrine\Validator\ValidationException;
  11. use MailPoet\Entities\SegmentEntity;
  12. use MailPoet\Listing\Handler;
  13. use MailPoet\Newsletter\Segment\NewsletterSegmentRepository;
  14. use MailPoet\Segments\DynamicSegments\DynamicSegmentsListingRepository;
  15. use MailPoet\Segments\DynamicSegments\Exceptions\InvalidFilterException;
  16. use MailPoet\Segments\DynamicSegments\FilterDataMapper;
  17. use MailPoet\Segments\DynamicSegments\SegmentSaveController;
  18. use MailPoet\Segments\SegmentsRepository;
  19. use MailPoet\Segments\SegmentSubscribersRepository;
  20. use MailPoet\UnexpectedValueException;
  21. use MailPoet\WP\Functions as WPFunctions;
  22. class DynamicSegments extends APIEndpoint {
  23. public $permissions = [
  24. 'global' => AccessControl::PERMISSION_MANAGE_SEGMENTS,
  25. ];
  26. /** @var Handler */
  27. private $listingHandler;
  28. /** @var DynamicSegmentsListingRepository */
  29. private $dynamicSegmentsListingRepository;
  30. /** @var SegmentsRepository */
  31. private $segmentsRepository;
  32. /** @var DynamicSegmentsResponseBuilder */
  33. private $segmentsResponseBuilder;
  34. /** @var SegmentSaveController */
  35. private $saveController;
  36. /** @var SegmentSubscribersRepository */
  37. private $segmentSubscribersRepository;
  38. /** @var FilterDataMapper */
  39. private $filterDataMapper;
  40. /** @var NewsletterSegmentRepository */
  41. private $newsletterSegmentRepository;
  42. public function __construct(
  43. Handler $handler,
  44. DynamicSegmentsListingRepository $dynamicSegmentsListingRepository,
  45. DynamicSegmentsResponseBuilder $segmentsResponseBuilder,
  46. SegmentsRepository $segmentsRepository,
  47. SegmentSubscribersRepository $segmentSubscribersRepository,
  48. FilterDataMapper $filterDataMapper,
  49. SegmentSaveController $saveController,
  50. NewsletterSegmentRepository $newsletterSegmentRepository
  51. ) {
  52. $this->listingHandler = $handler;
  53. $this->dynamicSegmentsListingRepository = $dynamicSegmentsListingRepository;
  54. $this->segmentsResponseBuilder = $segmentsResponseBuilder;
  55. $this->segmentsRepository = $segmentsRepository;
  56. $this->saveController = $saveController;
  57. $this->segmentSubscribersRepository = $segmentSubscribersRepository;
  58. $this->filterDataMapper = $filterDataMapper;
  59. $this->newsletterSegmentRepository = $newsletterSegmentRepository;
  60. }
  61. public function get($data = []) {
  62. if (isset($data['id'])) {
  63. $id = (int)$data['id'];
  64. } else {
  65. return $this->errorResponse([
  66. Error::BAD_REQUEST => WPFunctions::get()->__('Missing mandatory argument `id`.', 'mailpoet'),
  67. ]);
  68. }
  69. $segment = $this->segmentsRepository->findOneById($id);
  70. if (!$segment instanceof SegmentEntity) {
  71. return $this->errorResponse([
  72. Error::NOT_FOUND => WPFunctions::get()->__('This segment does not exist.', 'mailpoet'),
  73. ]);
  74. }
  75. return $this->successResponse($this->segmentsResponseBuilder->build($segment));
  76. }
  77. public function getCount($data = []) {
  78. try {
  79. $filterData = $this->filterDataMapper->map($data);
  80. $count = $this->segmentSubscribersRepository->getDynamicSubscribersCount($filterData);
  81. return $this->successResponse([
  82. 'count' => $count,
  83. ]);
  84. } catch (InvalidFilterException $e) {
  85. return $this->errorResponse([
  86. Error::BAD_REQUEST => $this->getErrorString($e),
  87. ], [], Response::STATUS_BAD_REQUEST);
  88. }
  89. }
  90. public function save($data) {
  91. try {
  92. $segment = $this->saveController->save($data);
  93. return $this->successResponse($this->segmentsResponseBuilder->build($segment));
  94. } catch (InvalidFilterException $e) {
  95. return $this->errorResponse([
  96. Error::BAD_REQUEST => $this->getErrorString($e),
  97. ], [], Response::STATUS_BAD_REQUEST);
  98. } catch (InvalidArgumentException $e) {
  99. return $this->badRequest([
  100. Error::BAD_REQUEST => __('Another record already exists. Please specify a different "name".', 'mailpoet'),
  101. ]);
  102. } catch (ValidationException $exception) {
  103. return $this->badRequest([
  104. Error::BAD_REQUEST => __('Please specify a name.', 'mailpoet'),
  105. ]);
  106. }
  107. }
  108. private function getErrorString(InvalidFilterException $e) {
  109. switch ($e->getCode()) {
  110. case InvalidFilterException::MISSING_TYPE:
  111. return WPFunctions::get()->__('The segment type is missing.', 'mailpoet');
  112. case InvalidFilterException::INVALID_TYPE:
  113. return WPFunctions::get()->__('The segment type is unknown.', 'mailpoet');
  114. case InvalidFilterException::MISSING_ROLE:
  115. return WPFunctions::get()->__('Please select a user role.', 'mailpoet');
  116. case InvalidFilterException::MISSING_ACTION:
  117. case InvalidFilterException::INVALID_EMAIL_ACTION:
  118. return WPFunctions::get()->__('Please select an email action.', 'mailpoet');
  119. case InvalidFilterException::MISSING_NEWSLETTER_ID:
  120. return WPFunctions::get()->__('Please select an email.', 'mailpoet');
  121. case InvalidFilterException::MISSING_PRODUCT_ID:
  122. return WPFunctions::get()->__('Please select a product.', 'mailpoet');
  123. case InvalidFilterException::MISSING_COUNTRY:
  124. return WPFunctions::get()->__('Please select a country.', 'mailpoet');
  125. case InvalidFilterException::MISSING_CATEGORY_ID:
  126. return WPFunctions::get()->__('Please select a category.', 'mailpoet');
  127. case InvalidFilterException::MISSING_VALUE:
  128. return WPFunctions::get()->__('Please fill all required values.', 'mailpoet');
  129. case InvalidFilterException::MISSING_NUMBER_OF_ORDERS_FIELDS:
  130. return WPFunctions::get()->__('Please select a type for the comparison, a number of orders and a number of days.', 'mailpoet');
  131. case InvalidFilterException::MISSING_TOTAL_SPENT_FIELDS:
  132. return WPFunctions::get()->__('Please select a type for the comparison, an amount and a number of days.', 'mailpoet');
  133. case InvalidFilterException::MISSING_FILTER:
  134. return WPFunctions::get()->__('Please add at least one condition for filtering.', 'mailpoet');
  135. default:
  136. return WPFunctions::get()->__('An error occurred while saving data.', 'mailpoet');
  137. }
  138. }
  139. public function trash($data = []) {
  140. if (!isset($data['id'])) {
  141. return $this->errorResponse([
  142. Error::BAD_REQUEST => WPFunctions::get()->__('Missing mandatory argument `id`.', 'mailpoet'),
  143. ]);
  144. }
  145. $segment = $this->getSegment($data);
  146. if ($segment === null) {
  147. return $this->errorResponse([
  148. Error::NOT_FOUND => WPFunctions::get()->__('This segment does not exist.', 'mailpoet'),
  149. ]);
  150. }
  151. $activelyUsedNewslettersSubjects = $this->newsletterSegmentRepository->getSubjectsOfActivelyUsedEmailsForSegments([$segment->getId()]);
  152. if (isset($activelyUsedNewslettersSubjects[$segment->getId()])) {
  153. return $this->badRequest([
  154. Error::BAD_REQUEST => str_replace(
  155. '%$1s',
  156. "'" . join("', '", $activelyUsedNewslettersSubjects[$segment->getId()] ) . "'",
  157. _x('Segment cannot be deleted because it’s used for %$1s email', 'Alert shown when trying to delete segment, which is assigned to any automatic emails.', 'mailpoet')
  158. ),
  159. ]);
  160. }
  161. $this->segmentsRepository->bulkTrash([$segment->getId()], SegmentEntity::TYPE_DYNAMIC);
  162. return $this->successResponse(
  163. $this->segmentsResponseBuilder->build($segment),
  164. ['count' => 1]
  165. );
  166. }
  167. public function restore($data = []) {
  168. if (!isset($data['id'])) {
  169. return $this->errorResponse([
  170. Error::BAD_REQUEST => WPFunctions::get()->__('Missing mandatory argument `id`.', 'mailpoet'),
  171. ]);
  172. }
  173. $segment = $this->getSegment($data);
  174. if ($segment === null) {
  175. return $this->errorResponse([
  176. Error::NOT_FOUND => WPFunctions::get()->__('This segment does not exist.', 'mailpoet'),
  177. ]);
  178. }
  179. $this->segmentsRepository->bulkRestore([$segment->getId()], SegmentEntity::TYPE_DYNAMIC);
  180. return $this->successResponse(
  181. $this->segmentsResponseBuilder->build($segment),
  182. ['count' => 1]
  183. );
  184. }
  185. public function delete($data = []) {
  186. if (!isset($data['id'])) {
  187. return $this->errorResponse([
  188. Error::BAD_REQUEST => WPFunctions::get()->__('Missing mandatory argument `id`.', 'mailpoet'),
  189. ]);
  190. }
  191. $segment = $this->getSegment($data);
  192. if ($segment === null) {
  193. return $this->errorResponse([
  194. Error::NOT_FOUND => WPFunctions::get()->__('This segment does not exist.', 'mailpoet'),
  195. ]);
  196. }
  197. $this->segmentsRepository->bulkDelete([$segment->getId()], SegmentEntity::TYPE_DYNAMIC);
  198. return $this->successResponse(null, ['count' => 1]);
  199. }
  200. public function listing($data = []) {
  201. $data['params'] = $data['params'] ?? ['segments']; // Dummy param to apply constraints properly
  202. $definition = $this->listingHandler->getListingDefinition($data);
  203. $items = $this->dynamicSegmentsListingRepository->getData($definition);
  204. $count = $this->dynamicSegmentsListingRepository->getCount($definition);
  205. $filters = $this->dynamicSegmentsListingRepository->getFilters($definition);
  206. $groups = $this->dynamicSegmentsListingRepository->getGroups($definition);
  207. $segments = $this->segmentsResponseBuilder->buildForListing($items);
  208. return $this->successResponse($segments, [
  209. 'count' => $count,
  210. 'filters' => $filters,
  211. 'groups' => $groups,
  212. ]);
  213. }
  214. public function bulkAction($data = []) {
  215. $definition = $this->listingHandler->getListingDefinition($data['listing']);
  216. $ids = $this->dynamicSegmentsListingRepository->getActionableIds($definition);
  217. if ($data['action'] === 'trash') {
  218. $count = $this->segmentsRepository->bulkTrash($ids, SegmentEntity::TYPE_DYNAMIC);
  219. } elseif ($data['action'] === 'restore') {
  220. $count = $this->segmentsRepository->bulkRestore($ids, SegmentEntity::TYPE_DYNAMIC);
  221. } elseif ($data['action'] === 'delete') {
  222. $count = $this->segmentsRepository->bulkDelete($ids, SegmentEntity::TYPE_DYNAMIC);
  223. } else {
  224. throw UnexpectedValueException::create()
  225. ->withErrors([Error::BAD_REQUEST => "Invalid bulk action '{$data['action']}' provided."]);
  226. }
  227. return $this->successResponse(null, ['count' => $count]);
  228. }
  229. private function getSegment(array $data): ?SegmentEntity {
  230. return isset($data['id'])
  231. ? $this->segmentsRepository->findOneById((int)$data['id'])
  232. : null;
  233. }
  234. }