init
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
<?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
|
||||
|
||||
namespace MailPoet\API\JSON\v1;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use MailPoet\Analytics\Reporter;
|
||||
use MailPoet\API\JSON\Endpoint as APIEndpoint;
|
||||
use MailPoet\Config\AccessControl;
|
||||
|
||||
class Analytics extends APIEndpoint {
|
||||
|
||||
/** @var Reporter */
|
||||
private $reporter;
|
||||
|
||||
public $permissions = [
|
||||
'global' => AccessControl::NO_ACCESS_RESTRICTION,
|
||||
];
|
||||
|
||||
public function __construct(
|
||||
Reporter $reporter
|
||||
) {
|
||||
$this->reporter = $reporter;
|
||||
}
|
||||
|
||||
public function getTrackingData() {
|
||||
return $this->successResponse($this->reporter->getTrackingData());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
<?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
|
||||
|
||||
namespace MailPoet\API\JSON\v1;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use MailPoet\API\JSON\Endpoint as APIEndpoint;
|
||||
use MailPoet\API\JSON\SuccessResponse;
|
||||
use MailPoet\Config\AccessControl;
|
||||
use MailPoet\Newsletter\AutomatedLatestContent as ALC;
|
||||
use MailPoet\Newsletter\BlockPostQuery;
|
||||
use MailPoet\Util\APIPermissionHelper;
|
||||
use MailPoet\WP\Functions as WPFunctions;
|
||||
use MailPoet\WP\Posts as WPPosts;
|
||||
|
||||
class AutomatedLatestContent extends APIEndpoint {
|
||||
/** @var ALC */
|
||||
public $ALC;
|
||||
|
||||
/*** @var WPFunctions */
|
||||
private $wp;
|
||||
|
||||
/*** @var APIPermissionHelper */
|
||||
private $permissionHelper;
|
||||
|
||||
public $permissions = [
|
||||
'global' => AccessControl::PERMISSION_MANAGE_EMAILS,
|
||||
];
|
||||
|
||||
public function __construct(
|
||||
ALC $alc,
|
||||
APIPermissionHelper $permissionHelper,
|
||||
WPFunctions $wp
|
||||
) {
|
||||
$this->ALC = $alc;
|
||||
$this->wp = $wp;
|
||||
$this->permissionHelper = $permissionHelper;
|
||||
}
|
||||
|
||||
public function getPostTypes() {
|
||||
$postTypes = array_map(function($postType) {
|
||||
return [
|
||||
'name' => $postType->name,
|
||||
'label' => $postType->label,
|
||||
];
|
||||
}, WPPosts::getTypes([], 'objects'));
|
||||
return $this->successResponse(
|
||||
array_filter($postTypes)
|
||||
);
|
||||
}
|
||||
|
||||
public function getTaxonomies($data = []) {
|
||||
$postType = (isset($data['postType'])) ? $data['postType'] : 'post';
|
||||
$allTaxonomies = WPFunctions::get()->getObjectTaxonomies($postType, 'objects');
|
||||
$taxonomiesWithLabel = array_filter($allTaxonomies, function($taxonomy) {
|
||||
return $taxonomy->label;
|
||||
});
|
||||
return $this->successResponse($taxonomiesWithLabel);
|
||||
}
|
||||
|
||||
public function getTerms($data = []) {
|
||||
$taxonomies = (isset($data['taxonomies'])) ? $data['taxonomies'] : [];
|
||||
$search = (isset($data['search'])) ? $data['search'] : '';
|
||||
$limit = (isset($data['limit'])) ? (int)$data['limit'] : 100;
|
||||
$page = (isset($data['page'])) ? (int)$data['page'] : 1;
|
||||
$args = [
|
||||
'taxonomy' => $taxonomies,
|
||||
'hide_empty' => false,
|
||||
'search' => $search,
|
||||
'number' => $limit,
|
||||
'offset' => $limit * ($page - 1),
|
||||
'orderby' => 'name',
|
||||
'order' => 'ASC',
|
||||
];
|
||||
|
||||
$args = (array)$this->wp->applyFilters('mailpoet_search_terms_args', $args);
|
||||
$terms = WPFunctions::get()->getTerms($args);
|
||||
|
||||
return $this->successResponse(array_values($terms));
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches posts for Posts static block
|
||||
*/
|
||||
public function getPosts(array $data = []): SuccessResponse {
|
||||
return $this->successResponse(
|
||||
$this->getPermittedPosts($this->ALC->getPosts(new BlockPostQuery(['args' => $data, 'dynamic' => false])))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches products for Abandoned Cart Content dynamic block
|
||||
*/
|
||||
public function getTransformedPosts(array $data = []): SuccessResponse {
|
||||
$posts = $this->getPermittedPosts($this->ALC->getPosts(new BlockPostQuery([
|
||||
'args' => $data,
|
||||
// If the request is for Posts or Products block then we are fetching data for a static block
|
||||
'dynamic' => !(isset($data['type']) && in_array($data['type'], ["posts", "products"])),
|
||||
])));
|
||||
return $this->successResponse(
|
||||
$this->ALC->transformPosts($data, $posts)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches different post types for ALC dynamic block
|
||||
*/
|
||||
public function getBulkTransformedPosts(array $data = []): SuccessResponse {
|
||||
$usedPosts = [];
|
||||
$renderedPosts = [];
|
||||
|
||||
foreach ($data['blocks'] as $block) {
|
||||
$query = new BlockPostQuery(['args' => $block, 'postsToExclude' => $usedPosts]);
|
||||
$posts = $this->getPermittedPosts($this->ALC->getPosts($query));
|
||||
$renderedPosts[] = $this->ALC->transformPosts($block, $posts);
|
||||
|
||||
foreach ($posts as $post) {
|
||||
$usedPosts[] = $post->ID;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->successResponse($renderedPosts);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \WP_Post[] $posts
|
||||
* @return \WP_Post[]
|
||||
*/
|
||||
private function getPermittedPosts($posts) {
|
||||
return array_filter($posts, function ($post) {
|
||||
return $this->permissionHelper->checkReadPermission($post);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
<?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
|
||||
|
||||
namespace MailPoet\API\JSON\v1;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use MailPoet\API\JSON\Endpoint as APIEndpoint;
|
||||
use MailPoet\API\JSON\Error as APIError;
|
||||
use MailPoet\AutomaticEmails\AutomaticEmails as AutomaticEmailsController;
|
||||
use MailPoet\Config\AccessControl;
|
||||
use MailPoet\WP\Functions as WPFunctions;
|
||||
|
||||
class AutomaticEmails extends APIEndpoint {
|
||||
public $permissions = [
|
||||
'global' => AccessControl::PERMISSION_MANAGE_SEGMENTS,
|
||||
];
|
||||
|
||||
/** @var AutomaticEmailsController */
|
||||
private $automaticEmails;
|
||||
|
||||
/** @var WPFunctions */
|
||||
private $wp;
|
||||
|
||||
public function __construct(
|
||||
AutomaticEmailsController $automaticEmails,
|
||||
WPFunctions $wp
|
||||
) {
|
||||
$this->automaticEmails = $automaticEmails;
|
||||
$this->wp = $wp;
|
||||
}
|
||||
|
||||
public function getEventOptions($data) {
|
||||
$query = (!empty($data['query'])) ? $data['query'] : null;
|
||||
$filter = (!empty($data['filter'])) ? $data['filter'] : null;
|
||||
$emailSlug = (!empty($data['email_slug'])) ? $data['email_slug'] : null;
|
||||
$eventSlug = (!empty($data['event_slug'])) ? $data['event_slug'] : null;
|
||||
|
||||
if (!$query || !$filter || !$emailSlug || !$eventSlug) {
|
||||
return $this->errorResponse(
|
||||
[
|
||||
APIError::BAD_REQUEST => __('Improperly formatted request.', 'mailpoet'),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
$event = $this->automaticEmails->getAutomaticEmailEventBySlug($emailSlug, $eventSlug);
|
||||
$eventFilter = (!empty($event['options']['remoteQueryFilter'])) ? $event['options']['remoteQueryFilter'] : null;
|
||||
|
||||
return ($eventFilter === $filter && WPFunctions::get()->hasFilter($eventFilter)) ?
|
||||
$this->successResponse($this->wp->applyFilters($eventFilter, $query)) :
|
||||
$this->errorResponse(
|
||||
[
|
||||
APIError::BAD_REQUEST => __('Automatic email event filter does not exist.', 'mailpoet'),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function getEventShortcodes($data) {
|
||||
$emailSlug = (!empty($data['email_slug'])) ? $data['email_slug'] : null;
|
||||
$eventSlug = (!empty($data['event_slug'])) ? $data['event_slug'] : null;
|
||||
|
||||
if (!$emailSlug || !$eventSlug) {
|
||||
return $this->errorResponse(
|
||||
[
|
||||
APIError::BAD_REQUEST => __('Improperly formatted request.', 'mailpoet'),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
$automaticEmail = $this->automaticEmails->getAutomaticEmailBySlug($emailSlug);
|
||||
$event = $this->automaticEmails->getAutomaticEmailEventBySlug($emailSlug, $eventSlug);
|
||||
|
||||
if (!$event) {
|
||||
return $this->errorResponse(
|
||||
[
|
||||
APIError::BAD_REQUEST => __('Automatic email event does not exist.', 'mailpoet'),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
$eventShortcodes = (!empty($event['shortcodes']) && is_array($event['shortcodes'])) ?
|
||||
[
|
||||
$automaticEmail['title'] => $event['shortcodes'],
|
||||
] :
|
||||
null;
|
||||
|
||||
return $this->successResponse($eventShortcodes);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php declare(strict_types = 1);
|
||||
|
||||
namespace MailPoet\API\JSON\v1;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use MailPoet\API\JSON\Endpoint as APIEndpoint;
|
||||
use MailPoet\Captcha\CaptchaSession;
|
||||
use MailPoet\Captcha\CaptchaUrlFactory;
|
||||
use MailPoet\Config\AccessControl;
|
||||
|
||||
class Captcha extends APIEndpoint {
|
||||
private CaptchaSession $captchaSession;
|
||||
private CaptchaUrlFactory $urlFactory;
|
||||
|
||||
public $permissions = [
|
||||
'global' => AccessControl::NO_ACCESS_RESTRICTION,
|
||||
];
|
||||
|
||||
public function __construct(
|
||||
CaptchaSession $captchaSession,
|
||||
CaptchaUrlFactory $urlFactory
|
||||
) {
|
||||
$this->captchaSession = $captchaSession;
|
||||
$this->urlFactory = $urlFactory;
|
||||
}
|
||||
|
||||
public function render(array $data = []) {
|
||||
$sessionId = $this->captchaSession->generateSessionId();
|
||||
$data = array_merge($data, ['captcha_session_id' => $sessionId]);
|
||||
$captchaUrl = $this->urlFactory->getCaptchaUrl($data);
|
||||
|
||||
return $this->redirectResponse($captchaUrl);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
<?php declare(strict_types = 1);
|
||||
|
||||
namespace MailPoet\API\JSON\v1;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use MailPoet\API\JSON\Endpoint as APIEndpoint;
|
||||
use MailPoet\API\JSON\SuccessResponse;
|
||||
use MailPoet\Config\AccessControl;
|
||||
use MailPoet\WooCommerce\Helper;
|
||||
use MailPoet\WP\Functions as WPFunctions;
|
||||
|
||||
class Coupons extends APIEndpoint {
|
||||
public const DEFAULT_PAGE_SIZE = 100;
|
||||
|
||||
/** @var Helper */
|
||||
public $helper;
|
||||
|
||||
/*** @var WPFunctions */
|
||||
private $wp;
|
||||
|
||||
public $permissions = [
|
||||
'global' => AccessControl::PERMISSION_MANAGE_EMAILS,
|
||||
];
|
||||
|
||||
public function __construct(
|
||||
WPFunctions $wp,
|
||||
Helper $helper
|
||||
) {
|
||||
$this->wp = $wp;
|
||||
$this->helper = $helper;
|
||||
}
|
||||
|
||||
public function getCoupons(array $data = []): SuccessResponse {
|
||||
$pageSize = $data['page_size'] ?? self::DEFAULT_PAGE_SIZE;
|
||||
$pageNumber = $data['page_number'] ?? 1;
|
||||
$discountType = $data['discount_type'] ?? null;
|
||||
$search = $data['search'] ?? null;
|
||||
$includeCouponIds = $data['include_coupon_ids'] ?? [];
|
||||
return $this->successResponse(
|
||||
$this->formatCoupons($this->helper->getCouponList(
|
||||
(int)$pageSize,
|
||||
(int)$pageNumber,
|
||||
$discountType,
|
||||
$search,
|
||||
$includeCouponIds
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $couponPosts
|
||||
* @return array
|
||||
*/
|
||||
private function formatCoupons(array $couponPosts): array {
|
||||
return array_map(function (\WP_Post $post): array {
|
||||
$discountType = $this->wp->getPostMeta($post->ID, 'discount_type', true);
|
||||
return [
|
||||
'id' => $post->ID,
|
||||
'text' => $post->post_title, // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps
|
||||
'excerpt' => $post->post_excerpt, // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps
|
||||
'discountType' => $discountType,
|
||||
];
|
||||
}, $couponPosts);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
<?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
|
||||
|
||||
namespace MailPoet\API\JSON\v1;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use MailPoet\API\JSON\Endpoint as APIEndpoint;
|
||||
use MailPoet\API\JSON\Error as APIError;
|
||||
use MailPoet\API\JSON\Response;
|
||||
use MailPoet\API\JSON\ResponseBuilders\CustomFieldsResponseBuilder;
|
||||
use MailPoet\Config\AccessControl;
|
||||
use MailPoet\CustomFields\CustomFieldsRepository;
|
||||
use MailPoet\Entities\CustomFieldEntity;
|
||||
use MailPoet\Form\ApiDataSanitizer;
|
||||
|
||||
class CustomFields extends APIEndpoint {
|
||||
public $permissions = [
|
||||
'global' => AccessControl::PERMISSION_MANAGE_FORMS,
|
||||
];
|
||||
|
||||
/** @var CustomFieldsRepository */
|
||||
private $customFieldsRepository;
|
||||
|
||||
/** @var CustomFieldsResponseBuilder */
|
||||
private $customFieldsResponseBuilder;
|
||||
|
||||
/** @var ApiDataSanitizer */
|
||||
private $dataSanitizer;
|
||||
|
||||
public function __construct(
|
||||
CustomFieldsRepository $customFieldsRepository,
|
||||
CustomFieldsResponseBuilder $customFieldsResponseBuilder,
|
||||
ApiDataSanitizer $dataSanitizer
|
||||
) {
|
||||
$this->customFieldsRepository = $customFieldsRepository;
|
||||
$this->customFieldsResponseBuilder = $customFieldsResponseBuilder;
|
||||
$this->dataSanitizer = $dataSanitizer;
|
||||
}
|
||||
|
||||
public function getAll() {
|
||||
$collection = $this->customFieldsRepository->findBy([], ['createdAt' => 'asc']);
|
||||
return $this->successResponse($this->customFieldsResponseBuilder->buildBatch($collection));
|
||||
}
|
||||
|
||||
public function delete($data = []) {
|
||||
$id = (isset($data['id']) ? (int)$data['id'] : null);
|
||||
$customField = $this->customFieldsRepository->findOneById($id);
|
||||
if ($customField instanceof CustomFieldEntity) {
|
||||
$this->customFieldsRepository->remove($customField);
|
||||
$this->customFieldsRepository->flush();
|
||||
|
||||
return $this->successResponse($this->customFieldsResponseBuilder->build($customField));
|
||||
} else {
|
||||
return $this->errorResponse([
|
||||
APIError::NOT_FOUND => __('This custom field does not exist.', 'mailpoet'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
public function save($data = []) {
|
||||
try {
|
||||
$data = $this->dataSanitizer->sanitizeBlock($data);
|
||||
$customField = $this->customFieldsRepository->createOrUpdate($data);
|
||||
$customField = $this->customFieldsRepository->findOneById($customField->getId());
|
||||
if(!$customField instanceof CustomFieldEntity) return $this->errorResponse();
|
||||
return $this->successResponse($this->customFieldsResponseBuilder->build($customField));
|
||||
} catch (\Exception $e) {
|
||||
return $this->errorResponse($errors = [], $meta = [], $status = Response::STATUS_BAD_REQUEST);
|
||||
}
|
||||
}
|
||||
|
||||
public function get($data = []) {
|
||||
$id = (isset($data['id']) ? (int)$data['id'] : null);
|
||||
$customField = $this->customFieldsRepository->findOneById($id);
|
||||
if ($customField instanceof CustomFieldEntity) {
|
||||
return $this->successResponse($this->customFieldsResponseBuilder->build($customField));
|
||||
}
|
||||
return $this->errorResponse([
|
||||
APIError::NOT_FOUND => __('This custom field does not exist.', 'mailpoet'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,317 @@
|
||||
<?php declare(strict_types = 1);
|
||||
|
||||
namespace MailPoet\API\JSON\v1;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use MailPoet\API\JSON\Endpoint as APIEndpoint;
|
||||
use MailPoet\API\JSON\Error;
|
||||
use MailPoet\API\JSON\Response;
|
||||
use MailPoet\API\JSON\ResponseBuilders\DynamicSegmentsResponseBuilder;
|
||||
use MailPoet\Config\AccessControl;
|
||||
use MailPoet\ConflictException;
|
||||
use MailPoet\Doctrine\Validator\ValidationException;
|
||||
use MailPoet\Entities\SegmentEntity;
|
||||
use MailPoet\Listing\Handler;
|
||||
use MailPoet\Newsletter\Segment\NewsletterSegmentRepository;
|
||||
use MailPoet\Segments\DynamicSegments\DynamicSegmentsListingRepository;
|
||||
use MailPoet\Segments\DynamicSegments\Exceptions\InvalidFilterException;
|
||||
use MailPoet\Segments\DynamicSegments\FilterDataMapper;
|
||||
use MailPoet\Segments\DynamicSegments\SegmentSaveController;
|
||||
use MailPoet\Segments\SegmentsRepository;
|
||||
use MailPoet\Segments\SegmentSubscribersRepository;
|
||||
use MailPoet\UnexpectedValueException;
|
||||
use Throwable;
|
||||
|
||||
class DynamicSegments extends APIEndpoint {
|
||||
|
||||
public $permissions = [
|
||||
'global' => AccessControl::PERMISSION_MANAGE_SEGMENTS,
|
||||
];
|
||||
|
||||
/** @var Handler */
|
||||
private $listingHandler;
|
||||
|
||||
/** @var DynamicSegmentsListingRepository */
|
||||
private $dynamicSegmentsListingRepository;
|
||||
|
||||
/** @var SegmentsRepository */
|
||||
private $segmentsRepository;
|
||||
|
||||
/** @var DynamicSegmentsResponseBuilder */
|
||||
private $segmentsResponseBuilder;
|
||||
|
||||
/** @var SegmentSaveController */
|
||||
private $saveController;
|
||||
|
||||
/** @var SegmentSubscribersRepository */
|
||||
private $segmentSubscribersRepository;
|
||||
|
||||
/** @var FilterDataMapper */
|
||||
private $filterDataMapper;
|
||||
|
||||
/** @var NewsletterSegmentRepository */
|
||||
private $newsletterSegmentRepository;
|
||||
|
||||
public function __construct(
|
||||
Handler $handler,
|
||||
DynamicSegmentsListingRepository $dynamicSegmentsListingRepository,
|
||||
DynamicSegmentsResponseBuilder $segmentsResponseBuilder,
|
||||
SegmentsRepository $segmentsRepository,
|
||||
SegmentSubscribersRepository $segmentSubscribersRepository,
|
||||
FilterDataMapper $filterDataMapper,
|
||||
SegmentSaveController $saveController,
|
||||
NewsletterSegmentRepository $newsletterSegmentRepository
|
||||
) {
|
||||
$this->listingHandler = $handler;
|
||||
$this->dynamicSegmentsListingRepository = $dynamicSegmentsListingRepository;
|
||||
$this->segmentsResponseBuilder = $segmentsResponseBuilder;
|
||||
$this->segmentsRepository = $segmentsRepository;
|
||||
$this->saveController = $saveController;
|
||||
$this->segmentSubscribersRepository = $segmentSubscribersRepository;
|
||||
$this->filterDataMapper = $filterDataMapper;
|
||||
$this->newsletterSegmentRepository = $newsletterSegmentRepository;
|
||||
}
|
||||
|
||||
public function get($data = []) {
|
||||
if (isset($data['id'])) {
|
||||
$id = (int)$data['id'];
|
||||
} else {
|
||||
return $this->errorResponse([
|
||||
Error::BAD_REQUEST => __('Missing mandatory argument `id`.', 'mailpoet'),
|
||||
]);
|
||||
}
|
||||
|
||||
$segment = $this->segmentsRepository->findOneById($id);
|
||||
if (!$segment instanceof SegmentEntity) {
|
||||
return $this->errorResponse([
|
||||
Error::NOT_FOUND => __('This segment does not exist.', 'mailpoet'),
|
||||
]);
|
||||
}
|
||||
|
||||
return $this->successResponse($this->segmentsResponseBuilder->build($segment));
|
||||
}
|
||||
|
||||
public function getCount($data = []) {
|
||||
try {
|
||||
$filterData = $this->filterDataMapper->map($data);
|
||||
$count = $this->segmentSubscribersRepository->getDynamicSubscribersCount($filterData);
|
||||
return $this->successResponse([
|
||||
'count' => $count,
|
||||
]);
|
||||
} catch (InvalidFilterException $e) {
|
||||
return $this->errorResponse([
|
||||
Error::BAD_REQUEST => $this->getErrorString($e),
|
||||
], [], Response::STATUS_BAD_REQUEST);
|
||||
}
|
||||
}
|
||||
|
||||
public function save($data) {
|
||||
try {
|
||||
$data['name'] = isset($data['name']) ? sanitize_text_field($data['name']) : '';
|
||||
$data['description'] = isset($data['description']) ? sanitize_textarea_field($data['description']) : '';
|
||||
$segment = $this->saveController->save($data);
|
||||
return $this->successResponse($this->segmentsResponseBuilder->build($segment));
|
||||
} catch (InvalidFilterException $e) {
|
||||
return $this->errorResponse([
|
||||
Error::BAD_REQUEST => $this->getErrorString($e),
|
||||
], [], Response::STATUS_BAD_REQUEST);
|
||||
} catch (ConflictException $e) {
|
||||
return $this->badRequest([
|
||||
Error::BAD_REQUEST => __('Another record already exists. Please specify a different "name".', 'mailpoet'),
|
||||
]);
|
||||
} catch (ValidationException $exception) {
|
||||
return $this->badRequest([
|
||||
Error::BAD_REQUEST => __('Please specify a name.', 'mailpoet'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
public function duplicate($data = []) {
|
||||
$segment = $this->getSegment($data);
|
||||
|
||||
if ($segment instanceof SegmentEntity) {
|
||||
try {
|
||||
$duplicate = $this->saveController->duplicate($segment);
|
||||
} catch (Throwable $e) {
|
||||
return $this->errorResponse([
|
||||
// translators: %s is the error message
|
||||
Error::UNKNOWN => sprintf(__('Duplicating of segment failed: %s', 'mailpoet'), $e->getMessage()),
|
||||
], [], Response::STATUS_UNKNOWN);
|
||||
}
|
||||
return $this->successResponse(
|
||||
$this->segmentsResponseBuilder->build($duplicate),
|
||||
['count' => 1]
|
||||
);
|
||||
} else {
|
||||
return $this->errorResponse([
|
||||
Error::NOT_FOUND => __('This segment does not exist.', 'mailpoet'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
private function getErrorString(InvalidFilterException $e) {
|
||||
switch ($e->getCode()) {
|
||||
case InvalidFilterException::MISSING_TYPE:
|
||||
return __('The segment type is missing.', 'mailpoet');
|
||||
case InvalidFilterException::INVALID_TYPE:
|
||||
return __('The segment type is unknown.', 'mailpoet');
|
||||
case InvalidFilterException::MISSING_ROLE:
|
||||
return __('Please select a user role.', 'mailpoet');
|
||||
case InvalidFilterException::MISSING_ACTION:
|
||||
case InvalidFilterException::INVALID_EMAIL_ACTION:
|
||||
return __('Please select an email action.', 'mailpoet');
|
||||
case InvalidFilterException::MISSING_NEWSLETTER_ID:
|
||||
return __('Please select an email.', 'mailpoet');
|
||||
case InvalidFilterException::MISSING_PRODUCT_ID:
|
||||
return __('Please select a product.', 'mailpoet');
|
||||
case InvalidFilterException::MISSING_COUNTRY:
|
||||
return __('Please select a country.', 'mailpoet');
|
||||
case InvalidFilterException::MISSING_CATEGORY_ID:
|
||||
return __('Please select a category.', 'mailpoet');
|
||||
case InvalidFilterException::MISSING_VALUE:
|
||||
return __('Please fill all required values.', 'mailpoet');
|
||||
case InvalidFilterException::MISSING_NUMBER_OF_ORDERS_FIELDS:
|
||||
return __('Please select a type for the comparison, a number of orders and a number of days.', 'mailpoet');
|
||||
case InvalidFilterException::MISSING_TOTAL_SPENT_FIELDS:
|
||||
case InvalidFilterException::MISSING_SINGLE_ORDER_VALUE_FIELDS:
|
||||
case InvalidFilterException::MISSING_AVERAGE_SPENT_FIELDS:
|
||||
return __('Please select a type for the comparison, an amount and a number of days.', 'mailpoet');
|
||||
case InvalidFilterException::MISSING_FILTER:
|
||||
return __('Please add at least one condition for filtering.', 'mailpoet');
|
||||
case InvalidFilterException::MISSING_OPERATOR:
|
||||
return __('Please select a type for the comparison.', 'mailpoet');
|
||||
default:
|
||||
return __('An error occurred while saving data.', 'mailpoet');
|
||||
}
|
||||
}
|
||||
|
||||
public function trash($data = []) {
|
||||
if (!isset($data['id'])) {
|
||||
return $this->errorResponse([
|
||||
Error::BAD_REQUEST => __('Missing mandatory argument `id`.', 'mailpoet'),
|
||||
]);
|
||||
}
|
||||
|
||||
$segment = $this->getSegment($data);
|
||||
if ($segment === null) {
|
||||
return $this->errorResponse([
|
||||
Error::NOT_FOUND => __('This segment does not exist.', 'mailpoet'),
|
||||
]);
|
||||
}
|
||||
|
||||
$activelyUsedErrors = $this->getErrorMessagesForSegmentsUsedInActiveNewsletters([$segment->getId()]);
|
||||
if (count($activelyUsedErrors) > 0) {
|
||||
return $this->badRequest($activelyUsedErrors);
|
||||
}
|
||||
|
||||
$this->segmentsRepository->bulkTrash([$segment->getId()], SegmentEntity::TYPE_DYNAMIC);
|
||||
return $this->successResponse(
|
||||
$this->segmentsResponseBuilder->build($segment),
|
||||
['count' => 1]
|
||||
);
|
||||
}
|
||||
|
||||
public function getErrorMessagesForSegmentsUsedInActiveNewsletters(array $segmentIds): array {
|
||||
$errors = [];
|
||||
$activelyUsedNewslettersSubjects = $this->newsletterSegmentRepository->getSubjectsOfActivelyUsedEmailsForSegments($segmentIds);
|
||||
foreach ($segmentIds as $segmentId) {
|
||||
if (isset($activelyUsedNewslettersSubjects[$segmentId])) {
|
||||
$segment = $this->getSegment(['id' => $segmentId]);
|
||||
if ($segment) {
|
||||
$errors[] = sprintf(
|
||||
// translators: %1$s is the name of the segment, %2$s is a comma-seperated list of emails for which the segment is used.
|
||||
_x('Segment \'%1$s\' cannot be deleted because it’s used for \'%2$s\' email', 'Alert shown when trying to delete segment, which is assigned to any automatic emails.', 'mailpoet'),
|
||||
$segment->getName(),
|
||||
join("', '", $activelyUsedNewslettersSubjects[$segmentId])
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $errors;
|
||||
}
|
||||
|
||||
public function restore($data = []) {
|
||||
if (!isset($data['id'])) {
|
||||
return $this->errorResponse([
|
||||
Error::BAD_REQUEST => __('Missing mandatory argument `id`.', 'mailpoet'),
|
||||
]);
|
||||
}
|
||||
|
||||
$segment = $this->getSegment($data);
|
||||
if ($segment === null) {
|
||||
return $this->errorResponse([
|
||||
Error::NOT_FOUND => __('This segment does not exist.', 'mailpoet'),
|
||||
]);
|
||||
}
|
||||
|
||||
$this->segmentsRepository->bulkRestore([$segment->getId()], SegmentEntity::TYPE_DYNAMIC);
|
||||
return $this->successResponse(
|
||||
$this->segmentsResponseBuilder->build($segment),
|
||||
['count' => 1]
|
||||
);
|
||||
}
|
||||
|
||||
public function delete($data = []) {
|
||||
if (!isset($data['id'])) {
|
||||
return $this->errorResponse([
|
||||
Error::BAD_REQUEST => __('Missing mandatory argument `id`.', 'mailpoet'),
|
||||
]);
|
||||
}
|
||||
|
||||
$segment = $this->getSegment($data);
|
||||
if ($segment === null) {
|
||||
return $this->errorResponse([
|
||||
Error::NOT_FOUND => __('This segment does not exist.', 'mailpoet'),
|
||||
]);
|
||||
}
|
||||
|
||||
$this->segmentsRepository->bulkDelete([$segment->getId()], SegmentEntity::TYPE_DYNAMIC);
|
||||
return $this->successResponse(null, ['count' => 1]);
|
||||
}
|
||||
|
||||
public function listing($data = []) {
|
||||
$data['params'] = $data['params'] ?? ['segments']; // Dummy param to apply constraints properly
|
||||
$definition = $this->listingHandler->getListingDefinition($data);
|
||||
$items = $this->dynamicSegmentsListingRepository->getData($definition);
|
||||
$count = $this->dynamicSegmentsListingRepository->getCount($definition);
|
||||
$filters = $this->dynamicSegmentsListingRepository->getFilters($definition);
|
||||
$groups = $this->dynamicSegmentsListingRepository->getGroups($definition);
|
||||
$segments = $this->segmentsResponseBuilder->buildForListing($items);
|
||||
|
||||
return $this->successResponse($segments, [
|
||||
'count' => $count,
|
||||
'filters' => $filters,
|
||||
'groups' => $groups,
|
||||
]);
|
||||
}
|
||||
|
||||
public function bulkAction($data = []) {
|
||||
$definition = $this->listingHandler->getListingDefinition($data['listing']);
|
||||
$ids = $this->dynamicSegmentsListingRepository->getActionableIds($definition);
|
||||
$meta = [];
|
||||
if ($data['action'] === 'trash') {
|
||||
$errors = $this->getErrorMessagesForSegmentsUsedInActiveNewsletters($ids);
|
||||
if (count($errors) > 0) {
|
||||
$meta['errors'] = $errors;
|
||||
}
|
||||
$meta['count'] = $this->segmentsRepository->bulkTrash($ids, SegmentEntity::TYPE_DYNAMIC);
|
||||
} elseif ($data['action'] === 'restore') {
|
||||
$meta['count'] = $this->segmentsRepository->bulkRestore($ids, SegmentEntity::TYPE_DYNAMIC);
|
||||
} elseif ($data['action'] === 'delete') {
|
||||
$meta['count'] = $this->segmentsRepository->bulkDelete($ids, SegmentEntity::TYPE_DYNAMIC);
|
||||
} else {
|
||||
throw UnexpectedValueException::create()
|
||||
->withErrors([Error::BAD_REQUEST => "Invalid bulk action '{$data['action']}' provided."]);
|
||||
}
|
||||
return $this->successResponse(null, $meta);
|
||||
}
|
||||
|
||||
private function getSegment(array $data): ?SegmentEntity {
|
||||
return isset($data['id'])
|
||||
? $this->segmentsRepository->findOneById((int)$data['id'])
|
||||
: null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
<?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
|
||||
|
||||
namespace MailPoet\API\JSON\v1;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use MailPoet\API\JSON\Endpoint as APIEndpoint;
|
||||
use MailPoet\API\JSON\Error as APIError;
|
||||
use MailPoet\Config\AccessControl;
|
||||
use MailPoet\Features\FeatureFlagsController;
|
||||
use MailPoet\Features\FeaturesController;
|
||||
|
||||
class FeatureFlags extends APIEndpoint {
|
||||
|
||||
public $permissions = [
|
||||
'global' => AccessControl::PERMISSION_MANAGE_FEATURES,
|
||||
];
|
||||
|
||||
/** @var FeaturesController */
|
||||
private $featuresController;
|
||||
|
||||
/** @var FeatureFlagsController */
|
||||
private $featureFlagsController;
|
||||
|
||||
public function __construct(
|
||||
FeaturesController $featuresController,
|
||||
FeatureFlagsController $featureFlags
|
||||
) {
|
||||
$this->featuresController = $featuresController;
|
||||
$this->featureFlagsController = $featureFlags;
|
||||
}
|
||||
|
||||
public function getAll() {
|
||||
$featureFlags = $this->featureFlagsController->getAll();
|
||||
return $this->successResponse($featureFlags);
|
||||
}
|
||||
|
||||
public function set(array $flags) {
|
||||
foreach ($flags as $name => $value) {
|
||||
if (!$this->featuresController->exists($name)) {
|
||||
return $this->badRequest([
|
||||
APIError::BAD_REQUEST => "Feature '$name' does not exist'",
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($flags as $name => $value) {
|
||||
$this->featureFlagsController->set($name, (bool)$value);
|
||||
}
|
||||
return $this->successResponse([]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,361 @@
|
||||
<?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
|
||||
|
||||
namespace MailPoet\API\JSON\v1;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use Exception;
|
||||
use MailPoet\API\JSON\Endpoint as APIEndpoint;
|
||||
use MailPoet\API\JSON\Error;
|
||||
use MailPoet\API\JSON\Error as APIError;
|
||||
use MailPoet\API\JSON\Response;
|
||||
use MailPoet\API\JSON\ResponseBuilders\FormsResponseBuilder;
|
||||
use MailPoet\API\JSON\SuccessResponse;
|
||||
use MailPoet\Config\AccessControl;
|
||||
use MailPoet\Entities\FormEntity;
|
||||
use MailPoet\Form\ApiDataSanitizer;
|
||||
use MailPoet\Form\DisplayFormInWPContent;
|
||||
use MailPoet\Form\FormSaveController;
|
||||
use MailPoet\Form\FormsRepository;
|
||||
use MailPoet\Form\Listing\FormListingRepository;
|
||||
use MailPoet\Form\PreviewPage;
|
||||
use MailPoet\Form\Templates\TemplateRepository;
|
||||
use MailPoet\Listing;
|
||||
use MailPoet\Settings\UserFlagsController;
|
||||
use MailPoet\Tags\TagRepository;
|
||||
use MailPoet\UnexpectedValueException;
|
||||
use MailPoet\WP\Emoji;
|
||||
use MailPoet\WP\Functions as WPFunctions;
|
||||
|
||||
class Forms extends APIEndpoint {
|
||||
public $permissions = [
|
||||
'global' => AccessControl::PERMISSION_MANAGE_FORMS,
|
||||
];
|
||||
|
||||
/** @var Listing\Handler */
|
||||
private $listingHandler;
|
||||
|
||||
/** @var UserFlagsController */
|
||||
private $userFlags;
|
||||
|
||||
/** @var FormsResponseBuilder */
|
||||
private $formsResponseBuilder;
|
||||
|
||||
/** @var WPFunctions */
|
||||
private $wp;
|
||||
|
||||
/** @var FormsRepository */
|
||||
private $formsRepository;
|
||||
|
||||
/** @var TemplateRepository */
|
||||
private $templateRepository;
|
||||
|
||||
/** @var FormListingRepository */
|
||||
private $formListingRepository;
|
||||
|
||||
/** @var Emoji */
|
||||
private $emoji;
|
||||
|
||||
/** @var ApiDataSanitizer */
|
||||
private $dataSanitizer;
|
||||
|
||||
/** @var TagRepository */
|
||||
private $tagRepository;
|
||||
|
||||
/** @var FormSaveController */
|
||||
private $formSaveController;
|
||||
|
||||
public function __construct(
|
||||
Listing\Handler $listingHandler,
|
||||
UserFlagsController $userFlags,
|
||||
FormsRepository $formsRepository,
|
||||
TemplateRepository $templateRepository,
|
||||
FormListingRepository $formListingRepository,
|
||||
FormsResponseBuilder $formsResponseBuilder,
|
||||
WPFunctions $wp,
|
||||
Emoji $emoji,
|
||||
ApiDataSanitizer $dataSanitizer,
|
||||
TagRepository $tagRepository,
|
||||
FormSaveController $formSaveController
|
||||
) {
|
||||
$this->listingHandler = $listingHandler;
|
||||
$this->userFlags = $userFlags;
|
||||
$this->wp = $wp;
|
||||
$this->formsRepository = $formsRepository;
|
||||
$this->templateRepository = $templateRepository;
|
||||
$this->formListingRepository = $formListingRepository;
|
||||
$this->formsResponseBuilder = $formsResponseBuilder;
|
||||
$this->emoji = $emoji;
|
||||
$this->dataSanitizer = $dataSanitizer;
|
||||
$this->tagRepository = $tagRepository;
|
||||
$this->formSaveController = $formSaveController;
|
||||
}
|
||||
|
||||
public function get($data = []) {
|
||||
$id = (isset($data['id']) ? (int)$data['id'] : false);
|
||||
$form = $this->formsRepository->findOneById($id);
|
||||
if ($form instanceof FormEntity) {
|
||||
return $this->successResponse($this->formsResponseBuilder->build($form));
|
||||
}
|
||||
return $this->errorResponse([
|
||||
APIError::NOT_FOUND => __('This form does not exist.', 'mailpoet'),
|
||||
]);
|
||||
}
|
||||
|
||||
public function setStatus($data = []) {
|
||||
$status = (isset($data['status']) ? $data['status'] : null);
|
||||
|
||||
if (!$status) {
|
||||
return $this->badRequest([
|
||||
APIError::BAD_REQUEST => __('You need to specify a status.', 'mailpoet'),
|
||||
]);
|
||||
}
|
||||
|
||||
$id = (isset($data['id'])) ? (int)$data['id'] : false;
|
||||
$form = $this->formsRepository->findOneById($id);
|
||||
|
||||
if (!$form instanceof FormEntity) {
|
||||
return $this->errorResponse([
|
||||
APIError::NOT_FOUND => __('This form does not exist.', 'mailpoet'),
|
||||
]);
|
||||
}
|
||||
|
||||
if (!in_array($status, [FormEntity::STATUS_ENABLED, FormEntity::STATUS_DISABLED])) {
|
||||
return $this->badRequest([
|
||||
APIError::BAD_REQUEST =>
|
||||
sprintf(
|
||||
// translators: %1$s is a comma-seperated list of allowed values, %2$s the status the user specified.
|
||||
__('Invalid status. Allowed values are (%1$s), you specified %2$s', 'mailpoet'),
|
||||
join(', ', [FormEntity::STATUS_ENABLED, FormEntity::STATUS_DISABLED]),
|
||||
$status
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
$form->setStatus($status);
|
||||
$this->formsRepository->flush();
|
||||
|
||||
if ($status === FormEntity::STATUS_ENABLED) {
|
||||
$this->wp->deleteTransient(DisplayFormInWPContent::NO_FORM_TRANSIENT_KEY);
|
||||
}
|
||||
|
||||
$form = $this->formsRepository->findOneById($id);
|
||||
if (!$form instanceof FormEntity) return $this->errorResponse();
|
||||
return $this->successResponse(
|
||||
$form->toArray()
|
||||
);
|
||||
}
|
||||
|
||||
public function listing($data = []) {
|
||||
$data['sort_order'] = $data['sort_order'] ?? 'desc';
|
||||
$data['sort_by'] = $data['sort_by'] ?? 'updatedAt';
|
||||
|
||||
$definition = $this->listingHandler->getListingDefinition($data);
|
||||
$items = $this->formListingRepository->getData($definition);
|
||||
$count = $this->formListingRepository->getCount($definition);
|
||||
$filters = $this->formListingRepository->getFilters($definition);
|
||||
$groups = $this->formListingRepository->getGroups($definition);
|
||||
|
||||
return $this->successResponse($this->formsResponseBuilder->buildForListing($items), [
|
||||
'count' => $count,
|
||||
'filters' => $filters,
|
||||
'groups' => $groups,
|
||||
]);
|
||||
}
|
||||
|
||||
public function previewEditor($data = []) {
|
||||
// We want to allow preview for unsaved forms
|
||||
$formId = $data['id'] ?? 0;
|
||||
$this->wp->setTransient(PreviewPage::PREVIEW_DATA_TRANSIENT_PREFIX . $formId, $data, PreviewPage::PREVIEW_DATA_EXPIRATION);
|
||||
return $this->successResponse();
|
||||
}
|
||||
|
||||
public function saveEditor($data = []) {
|
||||
$formId = (isset($data['id']) ? (int)$data['id'] : 0);
|
||||
$initialForm = $this->getFormTemplateData(TemplateRepository::INITIAL_FORM_TEMPLATE);
|
||||
$name = ($data['name'] ?? __('New form', 'mailpoet'));
|
||||
$body = ($data['body'] ?? $initialForm['body']);
|
||||
$body = $this->dataSanitizer->sanitizeBody($body);
|
||||
$settings = ($data['settings'] ?? $initialForm['settings']);
|
||||
$styles = ($data['styles'] ?? $initialForm['styles']);
|
||||
$status = ($data['status'] ?? FormEntity::STATUS_ENABLED);
|
||||
|
||||
// check if the form is used as a widget
|
||||
$isWidget = false;
|
||||
$widgets = $this->wp->getOption('widget_mailpoet_form');
|
||||
if (!empty($widgets)) {
|
||||
foreach ($widgets as $widget) {
|
||||
if (isset($widget['form']) && (int)$widget['form'] === $formId) {
|
||||
$isWidget = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Reset no form cache
|
||||
$this->wp->deleteTransient(DisplayFormInWPContent::NO_FORM_TRANSIENT_KEY);
|
||||
|
||||
// check if the user gets to pick his own lists
|
||||
// or if it's selected by the admin
|
||||
$formEntity = new FormEntity($name);
|
||||
$formEntity->setBody($body);
|
||||
$listSelection = $formEntity->getSegmentBlocksSegmentIds();
|
||||
|
||||
// check list selection
|
||||
if (count($listSelection)) {
|
||||
$settings['segments_selected_by'] = 'user';
|
||||
$settings['segments'] = $listSelection;
|
||||
} else {
|
||||
$settings['segments_selected_by'] = 'admin';
|
||||
}
|
||||
|
||||
// check tags and create them if they don't exist
|
||||
if (isset($settings['tags'])) {
|
||||
$this->createTagsIfDoNotExist($settings['tags']);
|
||||
}
|
||||
|
||||
// Check Custom HTML block permissions
|
||||
$customHtmlBlocks = $formEntity->getBlocksByTypes([FormEntity::HTML_BLOCK_TYPE]);
|
||||
if (count($customHtmlBlocks) && !$this->wp->currentUserCan('administrator')) {
|
||||
return $this->errorResponse([
|
||||
Error::FORBIDDEN => __('Only administrator can edit forms containing Custom HTML block.', 'mailpoet'),
|
||||
], [], Response::STATUS_FORBIDDEN);
|
||||
}
|
||||
|
||||
if ($body !== null) {
|
||||
$body = $this->emoji->sanitizeEmojisInFormBody($body);
|
||||
}
|
||||
|
||||
$form = $this->getForm($data);
|
||||
|
||||
if (!$form instanceof FormEntity) {
|
||||
$form = new FormEntity($name);
|
||||
}
|
||||
$form->setName($name);
|
||||
$form->setBody($body);
|
||||
$form->setSettings($settings);
|
||||
$form->setStyles($styles);
|
||||
$form->setStatus($status);
|
||||
$this->formsRepository->persist($form);
|
||||
|
||||
try {
|
||||
$this->formsRepository->flush();
|
||||
} catch (\Exception $e) {
|
||||
return $this->badRequest();
|
||||
}
|
||||
|
||||
if (isset($data['editor_version']) && $data['editor_version'] === "2") {
|
||||
$this->userFlags->set('display_new_form_editor_nps_survey', true);
|
||||
}
|
||||
|
||||
$form = $this->getForm(['id' => $form->getId()]);
|
||||
if(!$form instanceof FormEntity) return $this->errorResponse();
|
||||
return $this->successResponse(
|
||||
$this->formsResponseBuilder->build($form),
|
||||
['is_widget' => $isWidget]
|
||||
);
|
||||
}
|
||||
|
||||
public function restore($data = []) {
|
||||
$form = $this->getForm($data);
|
||||
|
||||
if ($form instanceof FormEntity) {
|
||||
$this->formsRepository->restore($form);
|
||||
return $this->successResponse(
|
||||
$form->toArray(),
|
||||
['count' => 1]
|
||||
);
|
||||
} else {
|
||||
return $this->errorResponse([
|
||||
APIError::NOT_FOUND => __('This form does not exist.', 'mailpoet'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
public function trash($data = []) {
|
||||
$form = $this->getForm($data);
|
||||
|
||||
if ($form instanceof FormEntity) {
|
||||
$this->formsRepository->trash($form);
|
||||
return $this->successResponse(
|
||||
$form->toArray(),
|
||||
['count' => 1]
|
||||
);
|
||||
} else {
|
||||
return $this->errorResponse([
|
||||
APIError::NOT_FOUND => __('This form does not exist.', 'mailpoet'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
public function delete($data = []) {
|
||||
$form = $this->getForm($data);
|
||||
|
||||
if ($form instanceof FormEntity) {
|
||||
$this->formsRepository->delete($form);
|
||||
|
||||
return $this->successResponse(null, ['count' => 1]);
|
||||
} else {
|
||||
return $this->errorResponse([
|
||||
APIError::NOT_FOUND => __('This form does not exist.', 'mailpoet'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
public function duplicate($data = []) {
|
||||
$form = $this->getForm($data);
|
||||
|
||||
if ($form instanceof FormEntity) {
|
||||
try {
|
||||
$duplicate = $this->formSaveController->duplicate($form);
|
||||
} catch (Exception $e) {
|
||||
return $this->errorResponse([
|
||||
APIError::UNKNOWN => __('Duplicating form failed.', 'mailpoet'),
|
||||
], [], Response::STATUS_UNKNOWN);
|
||||
}
|
||||
return $this->successResponse(
|
||||
$this->formsResponseBuilder->build($duplicate),
|
||||
['count' => 1]
|
||||
);
|
||||
} else {
|
||||
return $this->errorResponse([
|
||||
APIError::NOT_FOUND => __('This form does not exist.', 'mailpoet'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
public function bulkAction($data = []): SuccessResponse {
|
||||
$definition = $this->listingHandler->getListingDefinition($data['listing']);
|
||||
$ids = $this->formListingRepository->getActionableIds($definition);
|
||||
if ($data['action'] === 'trash') {
|
||||
$this->formsRepository->bulkTrash($ids);
|
||||
} elseif ($data['action'] === 'restore') {
|
||||
$this->formsRepository->bulkRestore($ids);
|
||||
} elseif ($data['action'] === 'delete') {
|
||||
$this->formsRepository->bulkDelete($ids);
|
||||
} else {
|
||||
throw UnexpectedValueException::create()
|
||||
->withErrors([APIError::BAD_REQUEST => "Invalid bulk action '{$data['action']}' provided."]);
|
||||
}
|
||||
return $this->successResponse(null, ['count' => count($ids)]);
|
||||
}
|
||||
|
||||
private function getForm(array $data): ?FormEntity {
|
||||
return isset($data['id'])
|
||||
? $this->formsRepository->findOneById((int)$data['id'])
|
||||
: null;
|
||||
}
|
||||
|
||||
private function getFormTemplateData(string $templateId): array {
|
||||
$formTemplate = $this->templateRepository->getFormTemplate($templateId);
|
||||
$form = $formTemplate->toFormEntity();
|
||||
return $form->toArray();
|
||||
}
|
||||
|
||||
private function createTagsIfDoNotExist(array $tagNames): void {
|
||||
foreach ($tagNames as $tagName) {
|
||||
$this->tagRepository->createOrUpdate(['name' => $tagName]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
<?php declare(strict_types = 1);
|
||||
|
||||
namespace MailPoet\API\JSON\v1;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use MailPoet\API\JSON\Endpoint as APIEndpoint;
|
||||
use MailPoet\API\JSON\Error as APIError;
|
||||
use MailPoet\API\JSON\Response;
|
||||
use MailPoet\Config\AccessControl;
|
||||
use MailPoet\Entities\ScheduledTaskEntity;
|
||||
use MailPoet\Newsletter\Sending\ScheduledTasksRepository;
|
||||
use MailPoet\Util\DataInconsistency\DataInconsistencyController;
|
||||
|
||||
class Help extends APIEndpoint {
|
||||
|
||||
public $permissions = [
|
||||
'global' => AccessControl::PERMISSION_MANAGE_HELP,
|
||||
];
|
||||
|
||||
private ScheduledTasksRepository $scheduledTasksRepository;
|
||||
private DataInconsistencyController $dataInconsistencyController;
|
||||
|
||||
public function __construct(
|
||||
ScheduledTasksRepository $scheduledTasksRepository,
|
||||
DataInconsistencyController $dataInconsistencyController
|
||||
) {
|
||||
$this->scheduledTasksRepository = $scheduledTasksRepository;
|
||||
$this->dataInconsistencyController = $dataInconsistencyController;
|
||||
}
|
||||
|
||||
public function cancelTask($data): Response {
|
||||
try {
|
||||
$this->validateTaskId($data);
|
||||
|
||||
$task = $this->scheduledTasksRepository->findOneById($data['id']);
|
||||
if (!$task instanceof ScheduledTaskEntity) {
|
||||
return $this->errorResponse([
|
||||
APIError::NOT_FOUND => __('Task not found.', 'mailpoet'),
|
||||
]);
|
||||
}
|
||||
|
||||
$this->scheduledTasksRepository->cancelTask($task);
|
||||
return $this->successResponse();
|
||||
} catch (\Exception $e) {
|
||||
return $this->badRequest([ApiError::BAD_REQUEST => $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
public function rescheduleTask($data): Response {
|
||||
try {
|
||||
$this->validateTaskId($data);
|
||||
|
||||
$task = $this->scheduledTasksRepository->findOneById($data['id']);
|
||||
if (!$task instanceof ScheduledTaskEntity) {
|
||||
return $this->errorResponse([
|
||||
APIError::NOT_FOUND => __('Task not found.', 'mailpoet'),
|
||||
]);
|
||||
}
|
||||
|
||||
$this->scheduledTasksRepository->rescheduleTask($task);
|
||||
return $this->successResponse();
|
||||
} catch (\Exception $e) {
|
||||
return $this->badRequest([ApiError::BAD_REQUEST => $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
public function getInconsistentDataStatus(): Response {
|
||||
return $this->successResponse($this->dataInconsistencyController->getInconsistentDataStatus());
|
||||
}
|
||||
|
||||
public function fixInconsistentData($data): Response {
|
||||
try {
|
||||
$this->dataInconsistencyController->fixInconsistentData($data['inconsistency'] ?? '');
|
||||
} catch (\Exception $e) {
|
||||
return $this->badRequest([ApiError::BAD_REQUEST => $e->getMessage()]);
|
||||
}
|
||||
return $this->successResponse($this->dataInconsistencyController->getInconsistentDataStatus());
|
||||
}
|
||||
|
||||
private function validateTaskId($data): void {
|
||||
$isValid = isset($data['id']) && is_numeric($data['id']);
|
||||
if (!$isValid) {
|
||||
throw new \Exception(__('Invalid or missing parameter `id`.', 'mailpoet'));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
<?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
|
||||
|
||||
namespace MailPoet\API\JSON\v1;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use MailPoet\API\JSON\Endpoint as APIEndpoint;
|
||||
use MailPoet\API\JSON\Error as APIError;
|
||||
use MailPoet\API\JSON\ResponseBuilders\SegmentsResponseBuilder;
|
||||
use MailPoet\Config\AccessControl;
|
||||
use MailPoet\ConflictException;
|
||||
use MailPoet\Cron\CronWorkerScheduler;
|
||||
use MailPoet\Cron\Workers\WooCommerceSync;
|
||||
use MailPoet\CustomFields\CustomFieldsRepository;
|
||||
use MailPoet\Doctrine\Validator\ValidationException;
|
||||
use MailPoet\Newsletter\Options\NewsletterOptionsRepository;
|
||||
use MailPoet\Segments\SegmentSaveController;
|
||||
use MailPoet\Segments\SegmentsRepository;
|
||||
use MailPoet\Segments\WP;
|
||||
use MailPoet\Services\Validator;
|
||||
use MailPoet\Subscribers\ImportExport\Export\Export;
|
||||
use MailPoet\Subscribers\ImportExport\Import\Import;
|
||||
use MailPoet\Subscribers\ImportExport\Import\MailChimp;
|
||||
use MailPoet\Subscribers\ImportExport\ImportExportRepository;
|
||||
use MailPoet\Subscribers\SubscribersRepository;
|
||||
use MailPoet\Tags\TagRepository;
|
||||
|
||||
class ImportExport extends APIEndpoint {
|
||||
|
||||
/** @var WP */
|
||||
private $wpSegment;
|
||||
|
||||
/** @var CustomFieldsRepository */
|
||||
private $customFieldsRepository;
|
||||
|
||||
/** @var ImportExportRepository */
|
||||
private $importExportRepository;
|
||||
|
||||
/** @var NewsletterOptionsRepository */
|
||||
private $newsletterOptionsRepository;
|
||||
|
||||
/** @var SegmentsRepository */
|
||||
private $segmentsRepository;
|
||||
|
||||
/** @var SubscribersRepository */
|
||||
private $subscriberRepository;
|
||||
|
||||
/** @var SegmentSaveController */
|
||||
private $segmentSavecontroller;
|
||||
|
||||
/** @var SegmentsResponseBuilder */
|
||||
private $segmentsResponseBuilder;
|
||||
|
||||
/** @var TagRepository */
|
||||
private $tagRepository;
|
||||
|
||||
/** @var Validator */
|
||||
private $validator;
|
||||
|
||||
/** @var CronWorkerScheduler */
|
||||
private $cronWorkerScheduler;
|
||||
|
||||
public $permissions = [
|
||||
'global' => AccessControl::PERMISSION_MANAGE_SUBSCRIBERS,
|
||||
];
|
||||
|
||||
public function __construct(
|
||||
WP $wpSegment,
|
||||
CustomFieldsRepository $customFieldsRepository,
|
||||
ImportExportRepository $importExportRepository,
|
||||
NewsletterOptionsRepository $newsletterOptionsRepository,
|
||||
SegmentsRepository $segmentsRepository,
|
||||
SegmentSaveController $segmentSavecontroller,
|
||||
SegmentsResponseBuilder $segmentsResponseBuilder,
|
||||
CronWorkerScheduler $cronWorkerScheduler,
|
||||
SubscribersRepository $subscribersRepository,
|
||||
TagRepository $tagRepository,
|
||||
Validator $validator
|
||||
) {
|
||||
$this->wpSegment = $wpSegment;
|
||||
$this->customFieldsRepository = $customFieldsRepository;
|
||||
$this->importExportRepository = $importExportRepository;
|
||||
$this->newsletterOptionsRepository = $newsletterOptionsRepository;
|
||||
$this->segmentsRepository = $segmentsRepository;
|
||||
$this->subscriberRepository = $subscribersRepository;
|
||||
$this->segmentSavecontroller = $segmentSavecontroller;
|
||||
$this->cronWorkerScheduler = $cronWorkerScheduler;
|
||||
$this->segmentsResponseBuilder = $segmentsResponseBuilder;
|
||||
$this->tagRepository = $tagRepository;
|
||||
$this->validator = $validator;
|
||||
}
|
||||
|
||||
public function getMailChimpLists($data) {
|
||||
try {
|
||||
$mailChimp = new MailChimp($data['api_key']);
|
||||
$lists = $mailChimp->getLists();
|
||||
return $this->successResponse($lists);
|
||||
} catch (\Exception $e) {
|
||||
return $this->errorResponse([
|
||||
$e->getCode() => $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
public function getMailChimpSubscribers($data) {
|
||||
try {
|
||||
$mailChimp = new MailChimp($data['api_key']);
|
||||
$subscribers = $mailChimp->getSubscribers($data['lists']);
|
||||
return $this->successResponse($subscribers);
|
||||
} catch (\Exception $e) {
|
||||
return $this->errorResponse([
|
||||
$e->getCode() => $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
public function addSegment($data) {
|
||||
try {
|
||||
$data['name'] = isset($data['name']) ? sanitize_text_field($data['name']) : '';
|
||||
$data['description'] = isset($data['description']) ? sanitize_textarea_field($data['description']) : '';
|
||||
$segment = $this->segmentSavecontroller->save($data);
|
||||
$response = $this->segmentsResponseBuilder->build($segment);
|
||||
return $this->successResponse($response);
|
||||
} catch (ValidationException $exception) {
|
||||
return $this->badRequest([
|
||||
APIError::BAD_REQUEST => __('Please specify a name.', 'mailpoet'),
|
||||
]);
|
||||
} catch (ConflictException $exception) {
|
||||
return $this->badRequest([
|
||||
APIError::BAD_REQUEST => __('Another record already exists. Please specify a different "name".', 'mailpoet'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
public function processImport($data) {
|
||||
try {
|
||||
$import = new Import(
|
||||
$this->wpSegment,
|
||||
$this->customFieldsRepository,
|
||||
$this->importExportRepository,
|
||||
$this->newsletterOptionsRepository,
|
||||
$this->subscriberRepository,
|
||||
$this->tagRepository,
|
||||
$this->validator,
|
||||
json_decode($data, true)
|
||||
);
|
||||
$process = $import->process();
|
||||
return $this->successResponse($process);
|
||||
} catch (\Exception $e) {
|
||||
return $this->errorResponse([
|
||||
$e->getCode() => $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
public function processExport($data) {
|
||||
try {
|
||||
$export = new Export(
|
||||
$this->customFieldsRepository,
|
||||
$this->importExportRepository,
|
||||
$this->segmentsRepository,
|
||||
json_decode($data, true)
|
||||
);
|
||||
$process = $export->process();
|
||||
return $this->successResponse($process);
|
||||
} catch (\Exception $e) {
|
||||
return $this->errorResponse([
|
||||
$e->getCode() => $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
public function setupWooCommerceInitialImport() {
|
||||
try {
|
||||
$this->cronWorkerScheduler->scheduleImmediatelyIfNotRunning(WooCommerceSync::TASK_TYPE);
|
||||
return $this->successResponse();
|
||||
} catch (\Exception $e) {
|
||||
return $this->errorResponse([
|
||||
$e->getCode() => $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
<?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
|
||||
|
||||
namespace MailPoet\API\JSON\v1;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use MailPoet\API\JSON\Endpoint as APIEndpoint;
|
||||
use MailPoet\API\JSON\Error as APIError;
|
||||
use MailPoet\Config\AccessControl;
|
||||
use MailPoet\Mailer\MailerFactory;
|
||||
use MailPoet\Mailer\MailerLog;
|
||||
use MailPoet\Mailer\MetaInfo;
|
||||
use MailPoet\Services\AuthorizedEmailsController;
|
||||
use MailPoet\Services\AuthorizedSenderDomainController;
|
||||
use MailPoet\Settings\SettingsController;
|
||||
|
||||
class Mailer extends APIEndpoint {
|
||||
|
||||
/** @var AuthorizedEmailsController */
|
||||
private $authorizedEmailsController;
|
||||
|
||||
/** @var SettingsController */
|
||||
private $settings;
|
||||
|
||||
/** @var MetaInfo */
|
||||
private $mailerMetaInfo;
|
||||
|
||||
/** @var MailerFactory */
|
||||
private $mailerFactory;
|
||||
|
||||
/** @var AuthorizedSenderDomainController */
|
||||
private $senderDomainController;
|
||||
|
||||
public $permissions = [
|
||||
'global' => AccessControl::PERMISSION_MANAGE_EMAILS,
|
||||
];
|
||||
|
||||
public function __construct(
|
||||
AuthorizedEmailsController $authorizedEmailsController,
|
||||
SettingsController $settings,
|
||||
MailerFactory $mailerFactory,
|
||||
MetaInfo $mailerMetaInfo,
|
||||
AuthorizedSenderDomainController $senderDomainController
|
||||
) {
|
||||
$this->authorizedEmailsController = $authorizedEmailsController;
|
||||
$this->settings = $settings;
|
||||
$this->mailerFactory = $mailerFactory;
|
||||
$this->mailerMetaInfo = $mailerMetaInfo;
|
||||
$this->senderDomainController = $senderDomainController;
|
||||
}
|
||||
|
||||
public function send($data = []) {
|
||||
try {
|
||||
$mailer = $this->mailerFactory->buildMailer(
|
||||
$data['mailer'] ?? null,
|
||||
$data['sender'] ?? null,
|
||||
$data['reply_to'] ?? null
|
||||
);
|
||||
// report this as 'sending_test' in metadata since this endpoint is only used to test sending methods for now
|
||||
$extraParams = [
|
||||
'meta' => $this->mailerMetaInfo->getSendingTestMetaInfo(),
|
||||
];
|
||||
$result = $mailer->send($data['newsletter'], $data['subscriber'], $extraParams);
|
||||
} catch (\Exception $e) {
|
||||
return $this->errorResponse([
|
||||
$e->getCode() => $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
|
||||
if ($result['response'] === false) {
|
||||
$error = sprintf(
|
||||
// translators: %s is the error message.
|
||||
__('The email could not be sent: %s', 'mailpoet'),
|
||||
$result['error']->getMessage()
|
||||
);
|
||||
return $this->errorResponse([APIError::BAD_REQUEST => $error]);
|
||||
} else {
|
||||
return $this->successResponse(null);
|
||||
}
|
||||
}
|
||||
|
||||
public function resumeSending() {
|
||||
if ($this->settings->get(AuthorizedEmailsController::AUTHORIZED_EMAIL_ADDRESSES_ERROR_SETTING)) {
|
||||
$this->authorizedEmailsController->checkAuthorizedEmailAddresses();
|
||||
}
|
||||
MailerLog::resumeSending();
|
||||
return $this->successResponse(null);
|
||||
}
|
||||
|
||||
public function getAuthorizedEmailAddresses() {
|
||||
$authorizedEmails = $this->authorizedEmailsController->getAuthorizedEmailAddresses();
|
||||
return $this->successResponse($authorizedEmails);
|
||||
}
|
||||
|
||||
public function getVerifiedSenderDomains() {
|
||||
$verifiedDomains = $this->senderDomainController->getVerifiedSenderDomains();
|
||||
return $this->successResponse($verifiedDomains);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
|
||||
|
||||
namespace MailPoet\API\JSON\v1;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use MailPoet\API\JSON\Endpoint as APIEndpoint;
|
||||
use MailPoet\Config\AccessControl;
|
||||
use MailPoet\Cron\Workers\StatsNotifications\NewsletterLinkRepository;
|
||||
|
||||
class NewsletterLinks extends APIEndpoint {
|
||||
|
||||
public $permissions = [
|
||||
'global' => AccessControl::PERMISSION_MANAGE_SEGMENTS,
|
||||
];
|
||||
|
||||
/** @var NewsletterLinkRepository */
|
||||
private $newsletterLinkRepository;
|
||||
|
||||
public function __construct(
|
||||
NewsletterLinkRepository $newsletterLinkRepository
|
||||
) {
|
||||
$this->newsletterLinkRepository = $newsletterLinkRepository;
|
||||
}
|
||||
|
||||
public function get($data = []) {
|
||||
$links = $this->newsletterLinkRepository->findBy(['newsletter' => $data['newsletterId']]);
|
||||
$response = [];
|
||||
foreach ($links as $link) {
|
||||
$response[] = [
|
||||
'id' => $link->getId(),
|
||||
'url' => $link->getUrl(),
|
||||
];
|
||||
}
|
||||
return $this->successResponse($response);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
<?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
|
||||
|
||||
namespace MailPoet\API\JSON\v1;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use MailPoet\API\JSON\Endpoint as APIEndpoint;
|
||||
use MailPoet\API\JSON\Error as APIError;
|
||||
use MailPoet\API\JSON\ResponseBuilders\NewsletterTemplatesResponseBuilder;
|
||||
use MailPoet\Config\AccessControl;
|
||||
use MailPoet\Newsletter\ApiDataSanitizer;
|
||||
use MailPoet\Newsletter\NewsletterCoupon;
|
||||
use MailPoet\Newsletter\NewslettersRepository;
|
||||
use MailPoet\NewsletterTemplates\NewsletterTemplatesRepository;
|
||||
use MailPoet\NewsletterTemplates\ThumbnailSaver;
|
||||
|
||||
class NewsletterTemplates extends APIEndpoint {
|
||||
public $permissions = [
|
||||
'global' => AccessControl::PERMISSION_MANAGE_EMAILS,
|
||||
];
|
||||
|
||||
protected static $getMethods = [
|
||||
'getAll',
|
||||
];
|
||||
|
||||
/** @var NewsletterTemplatesRepository */
|
||||
private $newsletterTemplatesRepository;
|
||||
|
||||
/** @var NewsletterTemplatesResponseBuilder */
|
||||
private $newsletterTemplatesResponseBuilder;
|
||||
|
||||
/** @var ThumbnailSaver */
|
||||
private $thumbnailImageSaver;
|
||||
|
||||
/** @var ApiDataSanitizer */
|
||||
private $apiDataSanitizer;
|
||||
|
||||
/** @var NewslettersRepository */
|
||||
private $newsletterRepository;
|
||||
|
||||
/*** @var NewsletterCoupon */
|
||||
private $newsletterCoupon;
|
||||
|
||||
public function __construct(
|
||||
NewsletterTemplatesRepository $newsletterTemplatesRepository,
|
||||
NewsletterTemplatesResponseBuilder $newsletterTemplatesResponseBuilder,
|
||||
ThumbnailSaver $thumbnailImageSaver,
|
||||
ApiDataSanitizer $apiDataSanitizer,
|
||||
NewslettersRepository $newsletterRepository,
|
||||
NewsletterCoupon $newsletterCoupon
|
||||
) {
|
||||
$this->newsletterTemplatesRepository = $newsletterTemplatesRepository;
|
||||
$this->newsletterTemplatesResponseBuilder = $newsletterTemplatesResponseBuilder;
|
||||
$this->thumbnailImageSaver = $thumbnailImageSaver;
|
||||
$this->apiDataSanitizer = $apiDataSanitizer;
|
||||
$this->newsletterRepository = $newsletterRepository;
|
||||
$this->newsletterCoupon = $newsletterCoupon;
|
||||
}
|
||||
|
||||
public function get($data = []) {
|
||||
$template = isset($data['id'])
|
||||
? $this->newsletterTemplatesRepository->findOneById((int)$data['id'])
|
||||
: null;
|
||||
|
||||
if (!$template) {
|
||||
return $this->errorResponse([
|
||||
APIError::NOT_FOUND => __('This template does not exist.', 'mailpoet'),
|
||||
]);
|
||||
}
|
||||
|
||||
$data = $this->newsletterTemplatesResponseBuilder->build($template);
|
||||
return $this->successResponse($data);
|
||||
}
|
||||
|
||||
public function getAll() {
|
||||
$templates = $this->newsletterTemplatesRepository->findAllForListing();
|
||||
$data = $this->newsletterTemplatesResponseBuilder->buildForListing($templates);
|
||||
return $this->successResponse($data);
|
||||
}
|
||||
|
||||
public function save($data = []) {
|
||||
ignore_user_abort(true);
|
||||
// Do not save templates for emails created via Gutenberg editor
|
||||
$newsletterId = isset($data['newsletter_id']) ? (int)$data['newsletter_id'] : null;
|
||||
if ($newsletterId) {
|
||||
$newsletter = $this->newsletterRepository->findOneById($newsletterId);
|
||||
if ($newsletter && $newsletter->getWpPostId() !== null) {
|
||||
return $this->successResponse($data);
|
||||
}
|
||||
}
|
||||
if (!empty($data['body'])) {
|
||||
$body = $this->apiDataSanitizer->sanitizeBody(json_decode($data['body'], true));
|
||||
$body = $this->newsletterCoupon->cleanupBodySensitiveData($body);
|
||||
$data['body'] = json_encode($body);
|
||||
}
|
||||
try {
|
||||
$template = $this->newsletterTemplatesRepository->createOrUpdate($data);
|
||||
$template = $this->thumbnailImageSaver->ensureTemplateThumbnailFile($template);
|
||||
if (!empty($data['categories']) && $data['categories'] === NewsletterTemplatesRepository::RECENTLY_SENT_CATEGORIES) {
|
||||
$this->newsletterTemplatesRepository->cleanRecentlySent();
|
||||
}
|
||||
$data = $this->newsletterTemplatesResponseBuilder->build($template);
|
||||
return $this->successResponse($data);
|
||||
} catch (\Throwable $e) {
|
||||
return $this->errorResponse();
|
||||
}
|
||||
}
|
||||
|
||||
public function delete($data = []) {
|
||||
$template = isset($data['id'])
|
||||
? $this->newsletterTemplatesRepository->findOneById((int)$data['id'])
|
||||
: null;
|
||||
|
||||
if (!$template) {
|
||||
return $this->errorResponse([
|
||||
APIError::NOT_FOUND => __('This template does not exist.', 'mailpoet'),
|
||||
]);
|
||||
}
|
||||
|
||||
$this->newsletterTemplatesRepository->remove($template);
|
||||
$this->newsletterTemplatesRepository->flush();
|
||||
return $this->successResponse(null, ['count' => 1]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,438 @@
|
||||
<?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
|
||||
|
||||
namespace MailPoet\API\JSON\v1;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use MailPoet\API\JSON\Endpoint as APIEndpoint;
|
||||
use MailPoet\API\JSON\Error as APIError;
|
||||
use MailPoet\API\JSON\Response;
|
||||
use MailPoet\API\JSON\ResponseBuilders\NewslettersResponseBuilder;
|
||||
use MailPoet\Config\AccessControl;
|
||||
use MailPoet\Cron\CronHelper;
|
||||
use MailPoet\Doctrine\Validator\ValidationException;
|
||||
use MailPoet\Entities\NewsletterEntity;
|
||||
use MailPoet\Entities\NewsletterOptionFieldEntity;
|
||||
use MailPoet\Entities\ScheduledTaskEntity;
|
||||
use MailPoet\Entities\SendingQueueEntity;
|
||||
use MailPoet\InvalidStateException;
|
||||
use MailPoet\Listing;
|
||||
use MailPoet\Newsletter\Listing\NewsletterListingRepository;
|
||||
use MailPoet\Newsletter\NewsletterDeleteController;
|
||||
use MailPoet\Newsletter\NewsletterSaveController;
|
||||
use MailPoet\Newsletter\NewslettersRepository;
|
||||
use MailPoet\Newsletter\NewsletterValidator;
|
||||
use MailPoet\Newsletter\Preview\SendPreviewController;
|
||||
use MailPoet\Newsletter\Preview\SendPreviewException;
|
||||
use MailPoet\Newsletter\Scheduler\PostNotificationScheduler;
|
||||
use MailPoet\Newsletter\Scheduler\Scheduler;
|
||||
use MailPoet\Newsletter\Url as NewsletterUrl;
|
||||
use MailPoet\Services\AuthorizedEmailsController;
|
||||
use MailPoet\Settings\SettingsController;
|
||||
use MailPoet\UnexpectedValueException;
|
||||
use MailPoet\Util\License\Features\Subscribers as SubscribersFeature;
|
||||
use MailPoet\WP\Emoji;
|
||||
use MailPoet\WP\Functions as WPFunctions;
|
||||
use MailPoetVendor\Carbon\Carbon;
|
||||
|
||||
class Newsletters extends APIEndpoint {
|
||||
|
||||
/** @var Listing\Handler */
|
||||
private $listingHandler;
|
||||
|
||||
/** @var WPFunctions */
|
||||
private $wp;
|
||||
|
||||
/** @var SettingsController */
|
||||
private $settings;
|
||||
|
||||
/** @var CronHelper */
|
||||
private $cronHelper;
|
||||
|
||||
public $permissions = [
|
||||
'global' => AccessControl::PERMISSION_MANAGE_EMAILS,
|
||||
];
|
||||
|
||||
/** @var NewslettersRepository */
|
||||
private $newslettersRepository;
|
||||
|
||||
/** @var NewsletterListingRepository */
|
||||
private $newsletterListingRepository;
|
||||
|
||||
/** @var NewslettersResponseBuilder */
|
||||
private $newslettersResponseBuilder;
|
||||
|
||||
/** @var PostNotificationScheduler */
|
||||
private $postNotificationScheduler;
|
||||
|
||||
/** @var Emoji */
|
||||
private $emoji;
|
||||
|
||||
/** @var SubscribersFeature */
|
||||
private $subscribersFeature;
|
||||
|
||||
/** @var SendPreviewController */
|
||||
private $sendPreviewController;
|
||||
|
||||
/** @var NewsletterSaveController */
|
||||
private $newsletterSaveController;
|
||||
|
||||
private NewsletterDeleteController $newsletterDeleteController;
|
||||
|
||||
/** @var NewsletterUrl */
|
||||
private $newsletterUrl;
|
||||
|
||||
/** @var NewsletterValidator */
|
||||
private $newsletterValidator;
|
||||
|
||||
/** @var Scheduler */
|
||||
private $scheduler;
|
||||
|
||||
/** @var AuthorizedEmailsController */
|
||||
private $authorizedEmailsController;
|
||||
|
||||
public function __construct(
|
||||
Listing\Handler $listingHandler,
|
||||
WPFunctions $wp,
|
||||
SettingsController $settings,
|
||||
CronHelper $cronHelper,
|
||||
NewslettersRepository $newslettersRepository,
|
||||
NewsletterListingRepository $newsletterListingRepository,
|
||||
NewslettersResponseBuilder $newslettersResponseBuilder,
|
||||
PostNotificationScheduler $postNotificationScheduler,
|
||||
SubscribersFeature $subscribersFeature,
|
||||
Emoji $emoji,
|
||||
SendPreviewController $sendPreviewController,
|
||||
NewsletterSaveController $newsletterSaveController,
|
||||
NewsletterDeleteController $newsletterDeleteController,
|
||||
NewsletterUrl $newsletterUrl,
|
||||
Scheduler $scheduler,
|
||||
NewsletterValidator $newsletterValidator,
|
||||
AuthorizedEmailsController $authorizedEmailsController
|
||||
) {
|
||||
$this->listingHandler = $listingHandler;
|
||||
$this->wp = $wp;
|
||||
$this->settings = $settings;
|
||||
$this->cronHelper = $cronHelper;
|
||||
$this->newslettersRepository = $newslettersRepository;
|
||||
$this->newsletterListingRepository = $newsletterListingRepository;
|
||||
$this->newslettersResponseBuilder = $newslettersResponseBuilder;
|
||||
$this->postNotificationScheduler = $postNotificationScheduler;
|
||||
$this->subscribersFeature = $subscribersFeature;
|
||||
$this->emoji = $emoji;
|
||||
$this->sendPreviewController = $sendPreviewController;
|
||||
$this->newsletterSaveController = $newsletterSaveController;
|
||||
$this->newsletterDeleteController = $newsletterDeleteController;
|
||||
$this->newsletterUrl = $newsletterUrl;
|
||||
$this->scheduler = $scheduler;
|
||||
$this->newsletterValidator = $newsletterValidator;
|
||||
$this->authorizedEmailsController = $authorizedEmailsController;
|
||||
}
|
||||
|
||||
public function get($data = []) {
|
||||
$newsletter = $this->getNewsletter($data);
|
||||
if (!$newsletter) {
|
||||
return $this->errorResponse([
|
||||
APIError::NOT_FOUND => __('This email does not exist.', 'mailpoet'),
|
||||
]);
|
||||
}
|
||||
|
||||
$response = $this->newslettersResponseBuilder->build($newsletter, [
|
||||
NewslettersResponseBuilder::RELATION_SEGMENTS,
|
||||
NewslettersResponseBuilder::RELATION_OPTIONS,
|
||||
NewslettersResponseBuilder::RELATION_QUEUE,
|
||||
]);
|
||||
$response = $this->wp->applyFilters('mailpoet_api_newsletters_get_after', $response);
|
||||
return $this->successResponse($response, ['preview_url' => $this->getViewInBrowserUrl($newsletter)]);
|
||||
}
|
||||
|
||||
public function getWithStats($data = []) {
|
||||
$newsletter = $this->getNewsletter($data);
|
||||
if (!$newsletter) {
|
||||
return $this->errorResponse([
|
||||
APIError::NOT_FOUND => __('This email does not exist.', 'mailpoet'),
|
||||
]);
|
||||
}
|
||||
|
||||
$response = $this->newslettersResponseBuilder->build($newsletter, [
|
||||
NewslettersResponseBuilder::RELATION_SEGMENTS,
|
||||
NewslettersResponseBuilder::RELATION_OPTIONS,
|
||||
NewslettersResponseBuilder::RELATION_QUEUE,
|
||||
NewslettersResponseBuilder::RELATION_TOTAL_SENT,
|
||||
NewslettersResponseBuilder::RELATION_STATISTICS,
|
||||
]);
|
||||
$response = $this->wp->applyFilters('mailpoet_api_newsletters_get_after', $response);
|
||||
$response['preview_url'] = $this->getViewInBrowserUrl($newsletter);
|
||||
return $this->successResponse($response);
|
||||
}
|
||||
|
||||
public function save($data = []) {
|
||||
$data = $this->wp->applyFilters('mailpoet_api_newsletters_save_before', $data);
|
||||
$newsletter = $this->newsletterSaveController->save($data);
|
||||
$response = $this->newslettersResponseBuilder->build($newsletter, [
|
||||
NewslettersResponseBuilder::RELATION_SEGMENTS,
|
||||
]);
|
||||
$previewUrl = $this->getViewInBrowserUrl($newsletter);
|
||||
$response = $this->wp->applyFilters('mailpoet_api_newsletters_save_after', $response);
|
||||
return $this->successResponse($response, ['preview_url' => $previewUrl]);
|
||||
}
|
||||
|
||||
public function setStatus($data = []) {
|
||||
$status = (isset($data['status']) ? $data['status'] : null);
|
||||
|
||||
if (!$status) {
|
||||
return $this->badRequest([
|
||||
APIError::BAD_REQUEST => __('You need to specify a status.', 'mailpoet'),
|
||||
]);
|
||||
}
|
||||
|
||||
if ($status === NewsletterEntity::STATUS_ACTIVE && $this->subscribersFeature->check()) {
|
||||
return $this->errorResponse([
|
||||
APIError::FORBIDDEN => __('Subscribers limit reached.', 'mailpoet'),
|
||||
], [], Response::STATUS_FORBIDDEN);
|
||||
}
|
||||
|
||||
$newsletter = $this->getNewsletter($data);
|
||||
if ($newsletter === null) {
|
||||
return $this->errorResponse([
|
||||
APIError::NOT_FOUND => __('This email does not exist.', 'mailpoet'),
|
||||
]);
|
||||
}
|
||||
|
||||
if ($status === NewsletterEntity::STATUS_ACTIVE && !$this->authorizedEmailsController->isSenderAddressValid($newsletter)) {
|
||||
return $this->errorResponse([
|
||||
APIError::FORBIDDEN => __('The sender address is not an authorized sender domain.', 'mailpoet'),
|
||||
], [], Response::STATUS_FORBIDDEN);
|
||||
}
|
||||
|
||||
if ($status === NewsletterEntity::STATUS_ACTIVE) {
|
||||
$validationError = $this->newsletterValidator->validate($newsletter);
|
||||
if ($validationError !== null) {
|
||||
return $this->errorResponse([APIError::FORBIDDEN => $validationError], [], Response::STATUS_FORBIDDEN);
|
||||
}
|
||||
}
|
||||
|
||||
$this->newslettersRepository->prefetchOptions([$newsletter]);
|
||||
$newsletter->setStatus($status);
|
||||
|
||||
// if there are paused tasks unpause them
|
||||
if ($newsletter->getStatus() === NewsletterEntity::STATUS_ACTIVE) {
|
||||
$queues = $newsletter->getUnfinishedQueues();
|
||||
foreach ($queues as $queue) {
|
||||
$task = $queue->getTask();
|
||||
if ($task && $task->getStatus() === ScheduledTaskEntity::STATUS_PAUSED) {
|
||||
$task->setStatus(ScheduledTaskEntity::STATUS_SCHEDULED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if there are past due notifications, reschedule them for the next send date
|
||||
if ($newsletter->getType() === NewsletterEntity::TYPE_NOTIFICATION && $status === NewsletterEntity::STATUS_ACTIVE) {
|
||||
$scheduleOption = $newsletter->getOption(NewsletterOptionFieldEntity::NAME_SCHEDULE);
|
||||
if ($scheduleOption === null) {
|
||||
return $this->errorResponse([
|
||||
APIError::BAD_REQUEST => __('This email has incorrect state.', 'mailpoet'),
|
||||
]);
|
||||
}
|
||||
$nextRunDate = $this->scheduler->getNextRunDate($scheduleOption->getValue());
|
||||
$queues = $newsletter->getQueues();
|
||||
foreach ($queues as $queue) {
|
||||
$task = $queue->getTask();
|
||||
if (
|
||||
$task &&
|
||||
$task->getScheduledAt() <= Carbon::now()->millisecond(0) &&
|
||||
$task->getStatus() === SendingQueueEntity::STATUS_SCHEDULED
|
||||
) {
|
||||
$nextRunDate = $nextRunDate ? Carbon::createFromFormat('Y-m-d H:i:s', $nextRunDate) : null;
|
||||
if ($nextRunDate === false) {
|
||||
throw InvalidStateException::create()->withMessage('Invalid next run date generated');
|
||||
}
|
||||
$task->setScheduledAt($nextRunDate);
|
||||
}
|
||||
}
|
||||
$this->postNotificationScheduler->createPostNotificationSendingTask($newsletter);
|
||||
}
|
||||
|
||||
$this->newslettersRepository->flush();
|
||||
|
||||
return $this->successResponse(
|
||||
$this->newslettersResponseBuilder->build($newsletter)
|
||||
);
|
||||
}
|
||||
|
||||
public function restore($data = []) {
|
||||
$newsletter = $this->getNewsletter($data);
|
||||
if ($newsletter instanceof NewsletterEntity) {
|
||||
$this->newslettersRepository->bulkRestore([$newsletter->getId()]);
|
||||
$this->newslettersRepository->refresh($newsletter);
|
||||
return $this->successResponse(
|
||||
$this->newslettersResponseBuilder->build($newsletter),
|
||||
['count' => 1]
|
||||
);
|
||||
} else {
|
||||
return $this->errorResponse([
|
||||
APIError::NOT_FOUND => __('This email does not exist.', 'mailpoet'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
public function trash($data = []) {
|
||||
$newsletter = $this->getNewsletter($data);
|
||||
if ($newsletter instanceof NewsletterEntity) {
|
||||
$this->newslettersRepository->bulkTrash([$newsletter->getId()]);
|
||||
$this->newslettersRepository->refresh($newsletter);
|
||||
return $this->successResponse(
|
||||
$this->newslettersResponseBuilder->build($newsletter),
|
||||
['count' => 1]
|
||||
);
|
||||
} else {
|
||||
return $this->errorResponse([
|
||||
APIError::NOT_FOUND => __('This email does not exist.', 'mailpoet'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
public function delete($data = []) {
|
||||
$newsletter = $this->getNewsletter($data);
|
||||
if ($newsletter instanceof NewsletterEntity) {
|
||||
$this->wp->doAction('mailpoet_api_newsletters_delete_before', [$newsletter->getId()]);
|
||||
$this->newsletterDeleteController->bulkDelete([(int)$newsletter->getId()]);
|
||||
$this->wp->doAction('mailpoet_api_newsletters_delete_after', [$newsletter->getId()]);
|
||||
return $this->successResponse(null, ['count' => 1]);
|
||||
} else {
|
||||
return $this->errorResponse([
|
||||
APIError::NOT_FOUND => __('This email does not exist.', 'mailpoet'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
public function duplicate($data = []) {
|
||||
$newsletter = $this->getNewsletter($data);
|
||||
|
||||
if ($newsletter instanceof NewsletterEntity) {
|
||||
$duplicate = $this->newsletterSaveController->duplicate($newsletter);
|
||||
$this->wp->doAction('mailpoet_api_newsletters_duplicate_after', $newsletter, $duplicate);
|
||||
return $this->successResponse(
|
||||
$this->newslettersResponseBuilder->build($duplicate),
|
||||
['count' => 1]
|
||||
);
|
||||
} else {
|
||||
return $this->errorResponse([
|
||||
APIError::NOT_FOUND => __('This email does not exist.', 'mailpoet'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
public function showPreview($data = []) {
|
||||
if (empty($data['body'])) {
|
||||
return $this->badRequest([
|
||||
APIError::BAD_REQUEST => __('Newsletter data is missing.', 'mailpoet'),
|
||||
]);
|
||||
}
|
||||
|
||||
$newsletter = $this->getNewsletter($data);
|
||||
if (!$newsletter) {
|
||||
return $this->errorResponse([
|
||||
APIError::NOT_FOUND => __('This email does not exist.', 'mailpoet'),
|
||||
]);
|
||||
}
|
||||
|
||||
$newslettersTableName = $this->newslettersRepository->getTableName();
|
||||
$newsletter->setBody(
|
||||
json_decode($this->emoji->encodeForUTF8Column($newslettersTableName, 'body', $data['body']), true)
|
||||
);
|
||||
$this->newslettersRepository->flush();
|
||||
|
||||
$response = $this->newslettersResponseBuilder->build($newsletter);
|
||||
return $this->successResponse($response, ['preview_url' => $this->getViewInBrowserUrl($newsletter)]);
|
||||
}
|
||||
|
||||
public function sendPreview($data = []) {
|
||||
if (empty($data['subscriber'])) {
|
||||
return $this->badRequest([
|
||||
APIError::BAD_REQUEST => __('Please specify receiver information.', 'mailpoet'),
|
||||
]);
|
||||
}
|
||||
|
||||
$newsletter = $this->getNewsletter($data);
|
||||
if (!$newsletter) {
|
||||
return $this->errorResponse([
|
||||
APIError::NOT_FOUND => __('This email does not exist.', 'mailpoet'),
|
||||
]);
|
||||
}
|
||||
|
||||
try {
|
||||
$this->sendPreviewController->sendPreview($newsletter, $data['subscriber']);
|
||||
} catch (SendPreviewException $e) {
|
||||
return $this->errorResponse([APIError::BAD_REQUEST => $e->getMessage()]);
|
||||
} catch (\Throwable $e) {
|
||||
return $this->errorResponse([$e->getCode() => $e->getMessage()]);
|
||||
}
|
||||
return $this->successResponse($this->newslettersResponseBuilder->build($newsletter));
|
||||
}
|
||||
|
||||
public function listing($data = []) {
|
||||
$definition = $this->listingHandler->getListingDefinition($data);
|
||||
$items = $this->newsletterListingRepository->getData($definition);
|
||||
$count = $this->newsletterListingRepository->getCount($definition);
|
||||
$filters = $this->newsletterListingRepository->getFilters($definition);
|
||||
$groups = $this->newsletterListingRepository->getGroups($definition);
|
||||
|
||||
$data = [];
|
||||
foreach ($this->newslettersResponseBuilder->buildForListing($items) as $newsletterData) {
|
||||
$data[] = $this->wp->applyFilters('mailpoet_api_newsletters_listing_item', $newsletterData);
|
||||
}
|
||||
|
||||
return $this->successResponse($data, [
|
||||
'count' => $count,
|
||||
'filters' => $filters,
|
||||
'groups' => $groups,
|
||||
'mta_log' => $this->settings->get('mta_log'),
|
||||
'mta_method' => $this->settings->get('mta.method'),
|
||||
'cron_accessible' => $this->cronHelper->isDaemonAccessible(),
|
||||
'current_time' => $this->wp->currentTime('mysql'),
|
||||
]);
|
||||
}
|
||||
|
||||
public function bulkAction($data = []) {
|
||||
$definition = $this->listingHandler->getListingDefinition($data['listing']);
|
||||
$ids = $this->newsletterListingRepository->getActionableIds($definition);
|
||||
if ($data['action'] === 'trash') {
|
||||
$this->newslettersRepository->bulkTrash($ids);
|
||||
} elseif ($data['action'] === 'restore') {
|
||||
$this->newslettersRepository->bulkRestore($ids);
|
||||
} elseif ($data['action'] === 'delete') {
|
||||
$this->wp->doAction('mailpoet_api_newsletters_delete_before', $ids);
|
||||
$this->newsletterDeleteController->bulkDelete($ids);
|
||||
$this->wp->doAction('mailpoet_api_newsletters_delete_after', $ids);
|
||||
} else {
|
||||
throw UnexpectedValueException::create()
|
||||
->withErrors([APIError::BAD_REQUEST => "Invalid bulk action '{$data['action']}' provided."]);
|
||||
}
|
||||
return $this->successResponse(null, ['count' => count($ids)]);
|
||||
}
|
||||
|
||||
public function create($data = []) {
|
||||
try {
|
||||
$newsletter = $this->newsletterSaveController->save($data);
|
||||
} catch (ValidationException $exception) {
|
||||
return $this->badRequest(['Please specify a type.']);
|
||||
}
|
||||
$response = $this->newslettersResponseBuilder->build($newsletter);
|
||||
return $this->successResponse($response);
|
||||
}
|
||||
|
||||
/** @return NewsletterEntity|null */
|
||||
private function getNewsletter(array $data) {
|
||||
return isset($data['id'])
|
||||
? $this->newslettersRepository->findOneById((int)$data['id'])
|
||||
: null;
|
||||
}
|
||||
|
||||
private function getViewInBrowserUrl(NewsletterEntity $newsletter): string {
|
||||
$url = $this->newsletterUrl->getViewInBrowserUrl($newsletter);
|
||||
// strip protocol to avoid mix content error
|
||||
return preg_replace('/^https?:/i', '', $url);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
<?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
|
||||
|
||||
namespace MailPoet\API\JSON\v1;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use MailPoet\API\JSON\Endpoint as APIEndpoint;
|
||||
use MailPoet\API\JSON\Error as APIError;
|
||||
use MailPoet\Config\AccessControl;
|
||||
use MailPoet\Config\ServicesChecker;
|
||||
use MailPoet\WP\Functions as WPFunctions;
|
||||
use MailPoet\WPCOM\DotcomHelperFunctions;
|
||||
use WP_Error;
|
||||
|
||||
class Premium extends APIEndpoint {
|
||||
const PREMIUM_PLUGIN_SLUG = 'mailpoet-premium';
|
||||
const PREMIUM_PLUGIN_PATH = 'mailpoet-premium/mailpoet-premium.php';
|
||||
// This is the path to the managed plugin on Dotcom platform. It is relative to WP_PLUGIN_DIR.
|
||||
const DOTCOM_SYMLINK_PATH = '../../../../wordpress/plugins/mailpoet-premium/latest';
|
||||
|
||||
public $permissions = [
|
||||
'global' => AccessControl::PERMISSION_MANAGE_SETTINGS,
|
||||
];
|
||||
|
||||
/** @var ServicesChecker */
|
||||
private $servicesChecker;
|
||||
|
||||
/** @var WPFunctions */
|
||||
private $wp;
|
||||
|
||||
/** @var DotcomHelperFunctions */
|
||||
private $dotcomHelperFunctions;
|
||||
|
||||
public function __construct(
|
||||
ServicesChecker $servicesChecker,
|
||||
WPFunctions $wp,
|
||||
DotcomHelperFunctions $dotcomHelperFunctions
|
||||
) {
|
||||
$this->servicesChecker = $servicesChecker;
|
||||
$this->wp = $wp;
|
||||
$this->dotcomHelperFunctions = $dotcomHelperFunctions;
|
||||
}
|
||||
|
||||
public function installPlugin() {
|
||||
$premiumKeyValid = $this->servicesChecker->isPremiumKeyValid(false);
|
||||
if (!$premiumKeyValid) {
|
||||
return $this->error(__('Premium key is not valid.', 'mailpoet'));
|
||||
}
|
||||
|
||||
$pluginInfo = $this->wp->pluginsApi('plugin_information', [
|
||||
'slug' => self::PREMIUM_PLUGIN_SLUG,
|
||||
]);
|
||||
|
||||
if (!$pluginInfo || $pluginInfo instanceof WP_Error) {
|
||||
return $this->error(__('Error when installing MailPoet Premium plugin.', 'mailpoet'));
|
||||
}
|
||||
|
||||
$pluginInfo = (array)$pluginInfo;
|
||||
|
||||
// If we are in Dotcom platform, we try to symlink the plugin instead of downloading it
|
||||
try {
|
||||
if ($this->dotcomHelperFunctions->isDotcom()) {
|
||||
$result = symlink(self::DOTCOM_SYMLINK_PATH, WP_PLUGIN_DIR . '/' . self::PREMIUM_PLUGIN_SLUG);
|
||||
if ($result === true) {
|
||||
return $this->successResponse();
|
||||
}
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
// Do nothing and continue with a regular installation
|
||||
}
|
||||
|
||||
$result = $this->wp->installPlugin($pluginInfo['download_link']);
|
||||
if ($result !== true) {
|
||||
return $this->error(__('Error when installing MailPoet Premium plugin.', 'mailpoet'));
|
||||
}
|
||||
return $this->successResponse();
|
||||
}
|
||||
|
||||
public function activatePlugin() {
|
||||
$premiumKeyValid = $this->servicesChecker->isPremiumKeyValid(false);
|
||||
if (!$premiumKeyValid) {
|
||||
return $this->error(__('Premium key is not valid.', 'mailpoet'));
|
||||
}
|
||||
|
||||
$result = $this->wp->activatePlugin(self::PREMIUM_PLUGIN_PATH);
|
||||
if ($result !== null) {
|
||||
return $this->error(__('Error when activating MailPoet Premium plugin.', 'mailpoet'));
|
||||
}
|
||||
return $this->successResponse();
|
||||
}
|
||||
|
||||
private function error($message) {
|
||||
return $this->badRequest([
|
||||
APIError::BAD_REQUEST => $message,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php declare(strict_types = 1);
|
||||
|
||||
namespace MailPoet\API\JSON\v1;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use MailPoet\API\JSON\Response;
|
||||
|
||||
class RedirectResponse extends Response {
|
||||
|
||||
public function __construct($location) { // phpcs:ignore
|
||||
parent::__construct(self::REDIRECT, [], $location);
|
||||
}
|
||||
|
||||
public function getData() {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,337 @@
|
||||
<?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
|
||||
|
||||
namespace MailPoet\API\JSON\v1;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use Exception;
|
||||
use MailPoet\API\JSON\Endpoint as APIEndpoint;
|
||||
use MailPoet\API\JSON\Error as APIError;
|
||||
use MailPoet\API\JSON\Response;
|
||||
use MailPoet\API\JSON\ResponseBuilders\SegmentsResponseBuilder;
|
||||
use MailPoet\Config\AccessControl;
|
||||
use MailPoet\ConflictException;
|
||||
use MailPoet\Cron\CronWorkerScheduler;
|
||||
use MailPoet\Cron\Workers\WooCommerceSync;
|
||||
use MailPoet\Doctrine\Validator\ValidationException;
|
||||
use MailPoet\Entities\SegmentEntity;
|
||||
use MailPoet\Entities\SubscriberEntity;
|
||||
use MailPoet\Form\FormsRepository;
|
||||
use MailPoet\Listing;
|
||||
use MailPoet\Newsletter\Segment\NewsletterSegmentRepository;
|
||||
use MailPoet\Segments\SegmentListingRepository;
|
||||
use MailPoet\Segments\SegmentSaveController;
|
||||
use MailPoet\Segments\SegmentsRepository;
|
||||
use MailPoet\Segments\SegmentSubscribersRepository;
|
||||
use MailPoet\Segments\WooCommerce;
|
||||
use MailPoet\Segments\WP;
|
||||
use MailPoet\Subscribers\SubscribersRepository;
|
||||
use MailPoet\UnexpectedValueException;
|
||||
|
||||
class Segments extends APIEndpoint {
|
||||
public $permissions = [
|
||||
'global' => AccessControl::PERMISSION_MANAGE_SEGMENTS,
|
||||
];
|
||||
|
||||
/** @var Listing\Handler */
|
||||
private $listingHandler;
|
||||
|
||||
/** @var SegmentsRepository */
|
||||
private $segmentsRepository;
|
||||
|
||||
/** @var SegmentsResponseBuilder */
|
||||
private $segmentsResponseBuilder;
|
||||
|
||||
/** @var SegmentSaveController */
|
||||
private $segmentSavecontroller;
|
||||
|
||||
/** @var SubscribersRepository */
|
||||
private $subscribersRepository;
|
||||
|
||||
/** @var WooCommerce */
|
||||
private $wooCommerceSync;
|
||||
|
||||
/** @var WP */
|
||||
private $wpSegment;
|
||||
|
||||
/** @var SegmentListingRepository */
|
||||
private $segmentListingRepository;
|
||||
|
||||
/** @var NewsletterSegmentRepository */
|
||||
private $newsletterSegmentRepository;
|
||||
|
||||
/** @var CronWorkerScheduler */
|
||||
private $cronWorkerScheduler;
|
||||
|
||||
/** @var FormsRepository */
|
||||
private $formsRepository;
|
||||
|
||||
/** @var SegmentSubscribersRepository */
|
||||
private $segmentSubscribersRepository;
|
||||
|
||||
public function __construct(
|
||||
Listing\Handler $listingHandler,
|
||||
SegmentsRepository $segmentsRepository,
|
||||
SegmentListingRepository $segmentListingRepository,
|
||||
SegmentsResponseBuilder $segmentsResponseBuilder,
|
||||
SegmentSaveController $segmentSavecontroller,
|
||||
SegmentSubscribersRepository $segmentSubscribersRepository,
|
||||
SubscribersRepository $subscribersRepository,
|
||||
WooCommerce $wooCommerce,
|
||||
WP $wpSegment,
|
||||
NewsletterSegmentRepository $newsletterSegmentRepository,
|
||||
CronWorkerScheduler $cronWorkerScheduler,
|
||||
FormsRepository $formsRepository
|
||||
) {
|
||||
$this->listingHandler = $listingHandler;
|
||||
$this->wooCommerceSync = $wooCommerce;
|
||||
$this->segmentsRepository = $segmentsRepository;
|
||||
$this->segmentsResponseBuilder = $segmentsResponseBuilder;
|
||||
$this->segmentSavecontroller = $segmentSavecontroller;
|
||||
$this->subscribersRepository = $subscribersRepository;
|
||||
$this->wpSegment = $wpSegment;
|
||||
$this->segmentListingRepository = $segmentListingRepository;
|
||||
$this->newsletterSegmentRepository = $newsletterSegmentRepository;
|
||||
$this->cronWorkerScheduler = $cronWorkerScheduler;
|
||||
$this->formsRepository = $formsRepository;
|
||||
$this->segmentSubscribersRepository = $segmentSubscribersRepository;
|
||||
}
|
||||
|
||||
public function get($data = []) {
|
||||
$id = (isset($data['id']) ? (int)$data['id'] : false);
|
||||
$segment = $this->segmentsRepository->findOneById($id);
|
||||
if ($segment instanceof SegmentEntity) {
|
||||
return $this->successResponse($this->segmentsResponseBuilder->build($segment));
|
||||
} else {
|
||||
return $this->errorResponse([
|
||||
APIError::NOT_FOUND => __('This list does not exist.', 'mailpoet'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
public function listing($data = []) {
|
||||
$data['params'] = $data['params'] ?? ['lists']; // Dummy param to apply constraints properly
|
||||
$definition = $this->listingHandler->getListingDefinition($data);
|
||||
$items = $this->segmentListingRepository->getData($definition);
|
||||
$count = $this->segmentListingRepository->getCount($definition);
|
||||
$filters = $this->segmentListingRepository->getFilters($definition);
|
||||
$groups = $this->segmentListingRepository->getGroups($definition);
|
||||
$segments = $this->segmentsResponseBuilder->buildForListing($items);
|
||||
|
||||
return $this->successResponse($segments, [
|
||||
'count' => $count,
|
||||
'filters' => $filters,
|
||||
'groups' => $groups,
|
||||
]);
|
||||
}
|
||||
|
||||
public function save($data = []) {
|
||||
try {
|
||||
$data['name'] = isset($data['name']) ? sanitize_text_field($data['name']) : '';
|
||||
$data['description'] = isset($data['description']) ? sanitize_textarea_field($data['description']) : '';
|
||||
$segment = $this->segmentSavecontroller->save($data);
|
||||
} catch (ValidationException $exception) {
|
||||
return $this->badRequest([
|
||||
APIError::BAD_REQUEST => __('Please specify a name.', 'mailpoet'),
|
||||
]);
|
||||
} catch (ConflictException $exception) {
|
||||
return $this->badRequest([
|
||||
APIError::BAD_REQUEST => __('Another record already exists. Please specify a different "name".', 'mailpoet'),
|
||||
]);
|
||||
}
|
||||
$response = $this->segmentsResponseBuilder->build($segment);
|
||||
return $this->successResponse($response);
|
||||
}
|
||||
|
||||
public function restore($data = []) {
|
||||
$segment = $this->getSegment($data);
|
||||
if ($segment instanceof SegmentEntity) {
|
||||
if (!$this->isTrashOrRestoreAllowed($segment)) {
|
||||
return $this->errorResponse([
|
||||
APIError::FORBIDDEN => __('This list cannot be moved to trash.', 'mailpoet'),
|
||||
]);
|
||||
}
|
||||
// When the segment is of type WP_USERS we want to restore all its subscribers
|
||||
if ($segment->getType() === SegmentEntity::TYPE_WP_USERS) {
|
||||
$subscribers = $this->subscribersRepository->findBySegment((int)$segment->getId());
|
||||
$subscriberIds = array_map(function (SubscriberEntity $subscriberEntity): int {
|
||||
return (int)$subscriberEntity->getId();
|
||||
}, $subscribers);
|
||||
$this->subscribersRepository->bulkRestore($subscriberIds);
|
||||
}
|
||||
|
||||
$this->segmentsRepository->bulkRestore([$segment->getId()], $segment->getType());
|
||||
$this->segmentsRepository->refresh($segment);
|
||||
return $this->successResponse(
|
||||
$this->segmentsResponseBuilder->build($segment),
|
||||
['count' => 1]
|
||||
);
|
||||
} else {
|
||||
return $this->errorResponse([
|
||||
APIError::NOT_FOUND => __('This list does not exist.', 'mailpoet'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
public function trash($data = []) {
|
||||
$segment = $this->getSegment($data);
|
||||
if (!$segment instanceof SegmentEntity) {
|
||||
|
||||
return $this->errorResponse([
|
||||
APIError::NOT_FOUND => __('This list does not exist.', 'mailpoet'),
|
||||
]);
|
||||
}
|
||||
|
||||
if (!$this->isTrashOrRestoreAllowed($segment)) {
|
||||
return $this->errorResponse([
|
||||
APIError::FORBIDDEN => __('This list cannot be moved to trash.', 'mailpoet'),
|
||||
]);
|
||||
}
|
||||
|
||||
$activelyUsedNewslettersSubjects = $this->newsletterSegmentRepository->getSubjectsOfActivelyUsedEmailsForSegments([$segment->getId()]);
|
||||
if (isset($activelyUsedNewslettersSubjects[$segment->getId()])) {
|
||||
return $this->badRequest([
|
||||
APIError::BAD_REQUEST => str_replace(
|
||||
'%1$s',
|
||||
"'" . join("', '", $activelyUsedNewslettersSubjects[$segment->getId()]) . "'",
|
||||
// translators: %1$s is a comma-seperated list of emails for which the segment is used.
|
||||
_x('List cannot be deleted because it’s used for %1$s email', 'Alert shown when trying to delete segment, which is assigned to any automatic emails.', 'mailpoet')
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
$activelyUsedFormNames = $this->formsRepository->getNamesOfFormsForSegments();
|
||||
if (isset($activelyUsedFormNames[$segment->getId()])) {
|
||||
return $this->badRequest([
|
||||
APIError::BAD_REQUEST => str_replace(
|
||||
'%1$s',
|
||||
"'" . join("', '", $activelyUsedFormNames[$segment->getId()]) . "'",
|
||||
// translators: %1$s is a comma-seperated list of forms for which the segment is used.
|
||||
_nx(
|
||||
'List cannot be deleted because it’s used for %1$s form',
|
||||
'List cannot be deleted because it’s used for %1$s forms',
|
||||
count($activelyUsedFormNames[$segment->getId()]),
|
||||
'Alert shown when trying to delete segment, when it is assigned to a form.',
|
||||
'mailpoet'
|
||||
)
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
// When the segment is of type WP_USERS we want to trash all subscribers who aren't subscribed in another list
|
||||
if ($segment->getType() === SegmentEntity::TYPE_WP_USERS) {
|
||||
$subscribers = $this->subscribersRepository->findExclusiveSubscribersBySegment((int)$segment->getId());
|
||||
$subscriberIds = array_map(function (SubscriberEntity $subscriberEntity): int {
|
||||
return (int)$subscriberEntity->getId();
|
||||
}, $subscribers);
|
||||
$this->subscribersRepository->bulkTrash($subscriberIds);
|
||||
}
|
||||
|
||||
$this->segmentsRepository->doTrash([$segment->getId()], $segment->getType());
|
||||
$this->segmentsRepository->refresh($segment);
|
||||
return $this->successResponse(
|
||||
$this->segmentsResponseBuilder->build($segment),
|
||||
['count' => 1]
|
||||
);
|
||||
}
|
||||
|
||||
public function delete($data = []) {
|
||||
$segment = $this->getSegment($data);
|
||||
if ($segment instanceof SegmentEntity) {
|
||||
$this->segmentsRepository->bulkDelete([$segment->getId()]);
|
||||
return $this->successResponse(null, ['count' => 1]);
|
||||
} else {
|
||||
return $this->errorResponse([
|
||||
APIError::NOT_FOUND => __('This list does not exist.', 'mailpoet'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
public function duplicate($data = []) {
|
||||
$segment = $this->getSegment($data);
|
||||
|
||||
if ($segment instanceof SegmentEntity) {
|
||||
try {
|
||||
$duplicate = $this->segmentSavecontroller->duplicate($segment);
|
||||
} catch (Exception $e) {
|
||||
return $this->errorResponse([
|
||||
APIError::UNKNOWN => __('Duplicating of segment failed.', 'mailpoet'),
|
||||
], [], Response::STATUS_UNKNOWN);
|
||||
}
|
||||
return $this->successResponse(
|
||||
$this->segmentsResponseBuilder->build($duplicate),
|
||||
['count' => 1]
|
||||
);
|
||||
} else {
|
||||
return $this->errorResponse([
|
||||
APIError::NOT_FOUND => __('This list does not exist.', 'mailpoet'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
public function synchronize($data) {
|
||||
try {
|
||||
if ($data['type'] === SegmentEntity::TYPE_WC_USERS) {
|
||||
$this->cronWorkerScheduler->scheduleImmediatelyIfNotRunning(WooCommerceSync::TASK_TYPE);
|
||||
} else {
|
||||
$this->wpSegment->synchronizeUsers();
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
return $this->errorResponse([
|
||||
$e->getCode() => $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
|
||||
return $this->successResponse(null);
|
||||
}
|
||||
|
||||
public function bulkAction($data = []) {
|
||||
$definition = $this->listingHandler->getListingDefinition($data['listing']);
|
||||
$ids = $this->segmentListingRepository->getActionableIds($definition);
|
||||
$count = 0;
|
||||
if ($data['action'] === 'trash') {
|
||||
$count = $this->segmentsRepository->bulkTrash($ids);
|
||||
} elseif ($data['action'] === 'restore') {
|
||||
$count = $this->segmentsRepository->bulkRestore($ids);
|
||||
} elseif ($data['action'] === 'delete') {
|
||||
$count = $this->segmentsRepository->bulkDelete($ids);
|
||||
} else {
|
||||
throw UnexpectedValueException::create()
|
||||
->withErrors([APIError::BAD_REQUEST => "Invalid bulk action '{$data['action']}' provided."]);
|
||||
}
|
||||
return $this->successResponse(null, ['count' => $count]);
|
||||
}
|
||||
|
||||
public function subscriberCount($data = []) {
|
||||
$segmentIds = $data['segmentIds'] ?? [];
|
||||
if (empty($segmentIds)) {
|
||||
return $this->errorResponse([
|
||||
APIError::BAD_REQUEST => __('No segment IDs provided.', 'mailpoet'),
|
||||
]);
|
||||
}
|
||||
$filterSegmentId = $data['filterSegmentId'] ?? null;
|
||||
$status = $data['status'] ?? SubscriberEntity::STATUS_SUBSCRIBED;
|
||||
$response['count'] = $this->segmentSubscribersRepository->getSubscribersCountBySegmentIds($segmentIds, $status, $filterSegmentId);
|
||||
|
||||
return $this->successResponse($response);
|
||||
}
|
||||
|
||||
private function isTrashOrRestoreAllowed(SegmentEntity $segment): bool {
|
||||
$allowedSegmentTypes = [
|
||||
SegmentEntity::TYPE_DEFAULT,
|
||||
SegmentEntity::TYPE_WP_USERS,
|
||||
];
|
||||
if (in_array($segment->getType(), $allowedSegmentTypes, true)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function getSegment(array $data): ?SegmentEntity {
|
||||
return isset($data['id'])
|
||||
? $this->segmentsRepository->findOneById((int)$data['id'])
|
||||
: null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,286 @@
|
||||
<?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
|
||||
|
||||
namespace MailPoet\API\JSON\v1;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use MailPoet\API\JSON\Endpoint as APIEndpoint;
|
||||
use MailPoet\API\JSON\Error as APIError;
|
||||
use MailPoet\API\JSON\Response;
|
||||
use MailPoet\API\JSON\ResponseBuilders\SendingQueuesResponseBuilder;
|
||||
use MailPoet\Config\AccessControl;
|
||||
use MailPoet\Cron\ActionScheduler\Actions\DaemonTrigger;
|
||||
use MailPoet\Cron\CronHelper;
|
||||
use MailPoet\Cron\CronTrigger;
|
||||
use MailPoet\Cron\Triggers\WordPress;
|
||||
use MailPoet\Cron\Workers\SendingQueue\SendingQueue as SendingQueueWorker;
|
||||
use MailPoet\Entities\NewsletterEntity;
|
||||
use MailPoet\Entities\ScheduledTaskEntity;
|
||||
use MailPoet\Entities\SendingQueueEntity;
|
||||
use MailPoet\Mailer\MailerFactory;
|
||||
use MailPoet\Newsletter\NewslettersRepository;
|
||||
use MailPoet\Newsletter\NewsletterValidator;
|
||||
use MailPoet\Newsletter\Sending\ScheduledTasksRepository;
|
||||
use MailPoet\Newsletter\Sending\SendingQueuesRepository;
|
||||
use MailPoet\Segments\SubscribersFinder;
|
||||
use MailPoet\Settings\SettingsController;
|
||||
use MailPoet\Util\License\Features\Subscribers as SubscribersFeature;
|
||||
use MailPoetVendor\Carbon\Carbon;
|
||||
|
||||
class SendingQueue extends APIEndpoint {
|
||||
public $permissions = [
|
||||
'global' => AccessControl::PERMISSION_MANAGE_EMAILS,
|
||||
];
|
||||
|
||||
/** @var SubscribersFeature */
|
||||
private $subscribersFeature;
|
||||
|
||||
/** @var SubscribersFinder */
|
||||
private $subscribersFinder;
|
||||
|
||||
/** @var NewslettersRepository */
|
||||
private $newsletterRepository;
|
||||
|
||||
/** @var SendingQueuesRepository */
|
||||
private $sendingQueuesRepository;
|
||||
|
||||
/** @var ScheduledTasksRepository */
|
||||
private $scheduledTasksRepository;
|
||||
|
||||
/** @var MailerFactory */
|
||||
private $mailerFactory;
|
||||
|
||||
/** @var NewsletterValidator */
|
||||
private $newsletterValidator;
|
||||
|
||||
/** @var SettingsController */
|
||||
private $settings;
|
||||
|
||||
/** @var DaemonTrigger */
|
||||
private $actionSchedulerDaemonTriggerAction;
|
||||
|
||||
/** @var SendingQueuesResponseBuilder */
|
||||
private $sendingQueuesResponseBuilder;
|
||||
|
||||
/** @var CronHelper */
|
||||
private $cronHelper;
|
||||
|
||||
public function __construct(
|
||||
SubscribersFeature $subscribersFeature,
|
||||
NewslettersRepository $newsletterRepository,
|
||||
SendingQueuesRepository $sendingQueuesRepository,
|
||||
SubscribersFinder $subscribersFinder,
|
||||
ScheduledTasksRepository $scheduledTasksRepository,
|
||||
MailerFactory $mailerFactory,
|
||||
SettingsController $settings,
|
||||
DaemonTrigger $actionSchedulerDaemonTriggerAction,
|
||||
NewsletterValidator $newsletterValidator,
|
||||
SendingQueuesResponseBuilder $sendingQueuesResponseBuilder,
|
||||
CronHelper $cronHelper
|
||||
) {
|
||||
$this->subscribersFeature = $subscribersFeature;
|
||||
$this->subscribersFinder = $subscribersFinder;
|
||||
$this->newsletterRepository = $newsletterRepository;
|
||||
$this->sendingQueuesRepository = $sendingQueuesRepository;
|
||||
$this->scheduledTasksRepository = $scheduledTasksRepository;
|
||||
$this->mailerFactory = $mailerFactory;
|
||||
$this->settings = $settings;
|
||||
$this->actionSchedulerDaemonTriggerAction = $actionSchedulerDaemonTriggerAction;
|
||||
$this->newsletterValidator = $newsletterValidator;
|
||||
$this->sendingQueuesResponseBuilder = $sendingQueuesResponseBuilder;
|
||||
$this->cronHelper = $cronHelper;
|
||||
}
|
||||
|
||||
public function add($data = []) {
|
||||
if ($this->subscribersFeature->check()) {
|
||||
return $this->errorResponse([
|
||||
APIError::FORBIDDEN => __('Subscribers limit reached.', 'mailpoet'),
|
||||
], [], Response::STATUS_FORBIDDEN);
|
||||
}
|
||||
$newsletterId = (isset($data['newsletter_id'])
|
||||
? (int)$data['newsletter_id']
|
||||
: false
|
||||
);
|
||||
|
||||
// check that the newsletter exists
|
||||
$newsletter = $this->newsletterRepository->findOneById($newsletterId);
|
||||
$this->newsletterRepository->prefetchOptions([$newsletter]);
|
||||
|
||||
if (!$newsletter instanceof NewsletterEntity) {
|
||||
return $this->errorResponse([
|
||||
APIError::NOT_FOUND => __('This newsletter does not exist.', 'mailpoet'),
|
||||
]);
|
||||
}
|
||||
|
||||
$validationError = $this->newsletterValidator->validate($newsletter);
|
||||
if ($validationError) {
|
||||
return $this->errorResponse([
|
||||
APIError::BAD_REQUEST => $validationError,
|
||||
]);
|
||||
}
|
||||
|
||||
try {
|
||||
// check that the sending method has been configured properly by verifying that default mailer can be build
|
||||
$this->mailerFactory->getDefaultMailer();
|
||||
|
||||
$sendingQueue = $this->sendingQueuesRepository->findOneByNewsletterAndTaskStatus($newsletter, null);
|
||||
|
||||
if ($sendingQueue instanceof SendingQueueEntity) {
|
||||
return $this->errorResponse([
|
||||
APIError::NOT_FOUND => __('This newsletter is already being sent.', 'mailpoet'),
|
||||
]);
|
||||
}
|
||||
|
||||
$sendingQueue = $this->sendingQueuesRepository->findOneByNewsletterAndTaskStatus($newsletter, ScheduledTaskEntity::STATUS_SCHEDULED);
|
||||
|
||||
if (is_null($sendingQueue)) {
|
||||
$scheduledTask = new ScheduledTaskEntity();
|
||||
$scheduledTask->setType(SendingQueueWorker::TASK_TYPE);
|
||||
$sendingQueue = new SendingQueueEntity();
|
||||
$sendingQueue->setNewsletter($newsletter);
|
||||
$sendingQueue->setTask($scheduledTask);
|
||||
|
||||
$this->sendingQueuesRepository->persist($sendingQueue);
|
||||
$this->newsletterRepository->refresh($newsletter);
|
||||
} else {
|
||||
$scheduledTask = $sendingQueue->getTask();
|
||||
}
|
||||
|
||||
if (!$scheduledTask instanceof ScheduledTaskEntity) {
|
||||
return $this->errorResponse([
|
||||
APIError::NOT_FOUND => __('Unable to find scheduled task associated with this newsletter.', 'mailpoet'),
|
||||
]);
|
||||
}
|
||||
|
||||
$scheduledTask->setPriority(ScheduledTaskEntity::PRIORITY_MEDIUM);
|
||||
$this->scheduledTasksRepository->persist($scheduledTask);
|
||||
$this->scheduledTasksRepository->flush();
|
||||
|
||||
WordPress::resetRunInterval();
|
||||
if ((bool)$newsletter->getOptionValue('isScheduled')) {
|
||||
// set newsletter status
|
||||
$newsletter->setStatus(NewsletterEntity::STATUS_SCHEDULED);
|
||||
|
||||
// set scheduled task status
|
||||
$scheduledTask->setStatus(ScheduledTaskEntity::STATUS_SCHEDULED);
|
||||
$scheduledTask->setScheduledAt(new Carbon($newsletter->getOptionValue('scheduledAt')));
|
||||
} else {
|
||||
$segments = $newsletter->getSegmentIds();
|
||||
|
||||
$this->scheduledTasksRepository->refresh($scheduledTask);
|
||||
$this->subscribersFinder->addSubscribersToTaskFromSegments($scheduledTask, $segments, $newsletter->getFilterSegmentId());
|
||||
$subscribersCount = $scheduledTask->getSubscribers()->count();
|
||||
|
||||
if (!$subscribersCount) {
|
||||
return $this->errorResponse([
|
||||
APIError::UNKNOWN => __('There are no subscribers in that list!', 'mailpoet'),
|
||||
]);
|
||||
}
|
||||
|
||||
$this->sendingQueuesRepository->updateCounts($sendingQueue);
|
||||
$scheduledTask->setStatus(null);
|
||||
$scheduledTask->setScheduledAt(null);
|
||||
|
||||
// set newsletter status
|
||||
$newsletter->setStatus(NewsletterEntity::STATUS_SENDING);
|
||||
}
|
||||
$this->scheduledTasksRepository->persist($scheduledTask);
|
||||
$this->newsletterRepository->flush();
|
||||
|
||||
$this->triggerSending($newsletter);
|
||||
return $this->successResponse(
|
||||
($newsletter->getLatestQueue() instanceof SendingQueueEntity) ? $this->sendingQueuesResponseBuilder->build($newsletter->getLatestQueue()) : null
|
||||
);
|
||||
} catch (\Exception $e) {
|
||||
return $this->errorResponse([
|
||||
$e->getCode() => $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
public function pause($data = []) {
|
||||
$newsletterId = (isset($data['newsletter_id'])
|
||||
? (int)$data['newsletter_id']
|
||||
: false
|
||||
);
|
||||
$newsletter = $this->newsletterRepository->findOneById($newsletterId);
|
||||
|
||||
if ($newsletter instanceof NewsletterEntity) {
|
||||
$queue = $newsletter->getLastUpdatedQueue();
|
||||
|
||||
if (!$queue instanceof SendingQueueEntity) {
|
||||
return $this->errorResponse([
|
||||
APIError::UNKNOWN => __('This newsletter has not been sent yet.', 'mailpoet'),
|
||||
]);
|
||||
} else {
|
||||
$this->sendingQueuesRepository->pause($queue);
|
||||
return $this->successResponse();
|
||||
}
|
||||
} else {
|
||||
return $this->errorResponse([
|
||||
APIError::NOT_FOUND => __('This newsletter does not exist.', 'mailpoet'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
public function resume($data = []) {
|
||||
if ($this->subscribersFeature->check()) {
|
||||
return $this->errorResponse([
|
||||
APIError::FORBIDDEN => __('Subscribers limit reached.', 'mailpoet'),
|
||||
], [], Response::STATUS_FORBIDDEN);
|
||||
}
|
||||
$newsletterId = (isset($data['newsletter_id'])
|
||||
? (int)$data['newsletter_id']
|
||||
: false
|
||||
);
|
||||
$newsletter = $this->newsletterRepository->findOneById($newsletterId);
|
||||
|
||||
if ($newsletter instanceof NewsletterEntity) {
|
||||
$queue = $newsletter->getLastUpdatedQueue();
|
||||
|
||||
if (!$queue instanceof SendingQueueEntity) {
|
||||
return $this->errorResponse([
|
||||
APIError::UNKNOWN => __('This newsletter has not been sent yet.', 'mailpoet'),
|
||||
]);
|
||||
} else {
|
||||
$this->sendingQueuesRepository->resume($queue);
|
||||
$this->triggerSending($newsletter);
|
||||
return $this->successResponse();
|
||||
}
|
||||
} else {
|
||||
return $this->errorResponse([
|
||||
APIError::NOT_FOUND => __('This newsletter does not exist.', 'mailpoet'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
public function pingCron() {
|
||||
try {
|
||||
$cronPingResponse = $this->cronHelper->pingDaemon();
|
||||
} catch (\Exception $e) {
|
||||
return $this->errorResponse([
|
||||
APIError::UNKNOWN => $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
if (!$this->cronHelper->validatePingResponse($cronPingResponse)) {
|
||||
return $this->errorResponse([
|
||||
APIError::UNKNOWN => $cronPingResponse,
|
||||
]);
|
||||
}
|
||||
return $this->successResponse();
|
||||
}
|
||||
|
||||
/**
|
||||
* In case the newsletter was switched to sending trigger the background job immediately.
|
||||
* This is done so that user immediately sees that email is sending and doesn't have to wait on WP Cron to start it.
|
||||
*/
|
||||
private function triggerSending(NewsletterEntity $newsletter): void {
|
||||
if (
|
||||
$newsletter->getStatus() === NewsletterEntity::STATUS_SENDING
|
||||
&& $this->settings->get('cron_trigger.method') === CronTrigger::METHOD_ACTION_SCHEDULER
|
||||
) {
|
||||
$this->actionSchedulerDaemonTriggerAction->process();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
<?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
|
||||
|
||||
namespace MailPoet\API\JSON\v1;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use MailPoet\API\JSON\Endpoint as APIEndpoint;
|
||||
use MailPoet\API\JSON\Error as APIError;
|
||||
use MailPoet\API\JSON\Response;
|
||||
use MailPoet\API\JSON\ResponseBuilders\ScheduledTaskSubscriberResponseBuilder;
|
||||
use MailPoet\Config\AccessControl;
|
||||
use MailPoet\Cron\CronHelper;
|
||||
use MailPoet\Entities\NewsletterEntity;
|
||||
use MailPoet\Listing;
|
||||
use MailPoet\Newsletter\Sending\ScheduledTaskSubscribersListingRepository;
|
||||
use MailPoet\Newsletter\Sending\ScheduledTaskSubscribersRepository;
|
||||
use MailPoet\Newsletter\Sending\SendingQueuesRepository;
|
||||
use MailPoet\Settings\SettingsController;
|
||||
use MailPoet\WP\Functions as WPFunctions;
|
||||
|
||||
class SendingTaskSubscribers extends APIEndpoint {
|
||||
public $permissions = [
|
||||
'global' => AccessControl::PERMISSION_MANAGE_EMAILS,
|
||||
];
|
||||
|
||||
/** @var Listing\Handler */
|
||||
private $listingHandler;
|
||||
|
||||
/** @var SettingsController */
|
||||
private $settings;
|
||||
|
||||
/** @var CronHelper */
|
||||
private $cronHelper;
|
||||
|
||||
/** @var WPFunctions */
|
||||
private $wp;
|
||||
|
||||
/** @var SendingQueuesRepository */
|
||||
private $sendingQueuesRepository;
|
||||
|
||||
/** @var ScheduledTaskSubscribersRepository */
|
||||
private $scheduledTaskSubscribersRepository;
|
||||
|
||||
/** @var ScheduledTaskSubscribersListingRepository */
|
||||
private $taskSubscribersListingRepository;
|
||||
|
||||
/** @var ScheduledTaskSubscriberResponseBuilder */
|
||||
private $scheduledTaskSubscriberResponseBuilder;
|
||||
|
||||
public function __construct(
|
||||
Listing\Handler $listingHandler,
|
||||
SettingsController $settings,
|
||||
CronHelper $cronHelper,
|
||||
SendingQueuesRepository $sendingQueuesRepository,
|
||||
ScheduledTaskSubscribersListingRepository $taskSubscribersListingRepository,
|
||||
ScheduledTaskSubscriberResponseBuilder $scheduledTaskSubscriberResponseBuilder,
|
||||
ScheduledTaskSubscribersRepository $scheduledTaskSubscribersRepository,
|
||||
WPFunctions $wp
|
||||
) {
|
||||
$this->listingHandler = $listingHandler;
|
||||
$this->settings = $settings;
|
||||
$this->cronHelper = $cronHelper;
|
||||
$this->sendingQueuesRepository = $sendingQueuesRepository;
|
||||
$this->taskSubscribersListingRepository = $taskSubscribersListingRepository;
|
||||
$this->scheduledTaskSubscriberResponseBuilder = $scheduledTaskSubscriberResponseBuilder;
|
||||
$this->scheduledTaskSubscribersRepository = $scheduledTaskSubscribersRepository;
|
||||
$this->wp = $wp;
|
||||
}
|
||||
|
||||
public function listing($data = []) {
|
||||
$newsletterId = !empty($data['params']['id']) ? (int)$data['params']['id'] : false;
|
||||
if (empty($newsletterId)) {
|
||||
return $this->errorResponse([
|
||||
APIError::NOT_FOUND => __('Newsletter not found!', 'mailpoet'),
|
||||
]);
|
||||
}
|
||||
$tasksIds = $this->sendingQueuesRepository->getTaskIdsByNewsletterId($newsletterId);
|
||||
|
||||
if (empty($tasksIds)) {
|
||||
return $this->errorResponse([
|
||||
APIError::NOT_FOUND => __('This email has not been sent yet.', 'mailpoet'),
|
||||
]);
|
||||
}
|
||||
$data['params']['task_ids'] = $tasksIds;
|
||||
$definition = $this->listingHandler->getListingDefinition($data);
|
||||
$items = $this->taskSubscribersListingRepository->getData($definition);
|
||||
$groups = $this->taskSubscribersListingRepository->getGroups($definition);
|
||||
$filters = $this->taskSubscribersListingRepository->getFilters($definition);
|
||||
$count = $this->taskSubscribersListingRepository->getCount($definition);
|
||||
|
||||
return $this->successResponse($this->scheduledTaskSubscriberResponseBuilder->buildForListing($items), [
|
||||
'count' => $count,
|
||||
'filters' => $filters,
|
||||
'groups' => $groups,
|
||||
'mta_log' => $this->settings->get('mta_log'),
|
||||
'mta_method' => $this->settings->get('mta.method'),
|
||||
'cron_accessible' => $this->cronHelper->isDaemonAccessible(),
|
||||
'current_time' => $this->wp->currentTime('mysql'),
|
||||
]);
|
||||
}
|
||||
|
||||
public function resend($data = []) {
|
||||
$taskId = !empty($data['taskId']) ? (int)$data['taskId'] : 0;
|
||||
$subscriberId = !empty($data['subscriberId']) ? (int)$data['subscriberId'] : 0;
|
||||
|
||||
$taskSubscriber = $this->scheduledTaskSubscribersRepository->findOneBy([
|
||||
'task' => $taskId,
|
||||
'subscriber' => $subscriberId,
|
||||
'failed' => 1,
|
||||
]);
|
||||
|
||||
$sendingQueue = $this->sendingQueuesRepository->findOneBy(['task' => $taskId]);
|
||||
|
||||
if (
|
||||
!$taskSubscriber
|
||||
|| !$taskSubscriber->getTask()
|
||||
|| !$sendingQueue
|
||||
) {
|
||||
return $this->errorResponse([
|
||||
APIError::NOT_FOUND => __('Failed sending task not found!', 'mailpoet'),
|
||||
]);
|
||||
}
|
||||
|
||||
$newsletter = $sendingQueue->getNewsletter();
|
||||
if (!$newsletter) {
|
||||
return $this->errorResponse([
|
||||
APIError::NOT_FOUND => __('Newsletter not found!', 'mailpoet'),
|
||||
]);
|
||||
}
|
||||
|
||||
if ($newsletter->canBeSetActive() && $newsletter->getStatus() !== NewsletterEntity::STATUS_ACTIVE) {
|
||||
return $this->errorResponse([
|
||||
// translators: This error occurs when resending a failed email message to a recipient and the associated email definition (e.g., a welcome email, an automation email) is inactive.
|
||||
APIError::BAD_REQUEST => __('Failed to resend! The email is not active. Please activate it first.', 'mailpoet'),
|
||||
], [], Response::STATUS_BAD_REQUEST);
|
||||
}
|
||||
|
||||
$taskSubscriber->resetToUnprocessed();
|
||||
$taskSubscriber->getTask()->setStatus(null);
|
||||
if (!$newsletter->canBeSetActive()) {
|
||||
$newsletter->setStatus(NewsletterEntity::STATUS_SENDING);
|
||||
}
|
||||
// Each repository flushes all changes
|
||||
$this->scheduledTaskSubscribersRepository->flush();
|
||||
return $this->successResponse([]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,358 @@
|
||||
<?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
|
||||
|
||||
namespace MailPoet\API\JSON\v1;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use MailPoet\Analytics\Analytics as AnalyticsHelper;
|
||||
use MailPoet\API\JSON\Endpoint as APIEndpoint;
|
||||
use MailPoet\API\JSON\Error as APIError;
|
||||
use MailPoet\API\JSON\Response;
|
||||
use MailPoet\Config\AccessControl;
|
||||
use MailPoet\Config\Installer;
|
||||
use MailPoet\Config\ServicesChecker;
|
||||
use MailPoet\Cron\Workers\KeyCheck\PremiumKeyCheck;
|
||||
use MailPoet\Cron\Workers\KeyCheck\SendingServiceKeyCheck;
|
||||
use MailPoet\Mailer\MailerLog;
|
||||
use MailPoet\Services\AuthorizedEmailsController;
|
||||
use MailPoet\Services\AuthorizedSenderDomainController;
|
||||
use MailPoet\Services\Bridge;
|
||||
use MailPoet\Services\CongratulatoryMssEmailController;
|
||||
use MailPoet\Services\SubscribersCountReporter;
|
||||
use MailPoet\Settings\SettingsController;
|
||||
use MailPoet\Util\Helpers;
|
||||
use MailPoet\WP\DateTime;
|
||||
use MailPoet\WP\Functions as WPFunctions;
|
||||
|
||||
class Services extends APIEndpoint {
|
||||
/** @var Bridge */
|
||||
private $bridge;
|
||||
|
||||
/** @var SettingsController */
|
||||
private $settings;
|
||||
|
||||
/** @var AnalyticsHelper */
|
||||
private $analytics;
|
||||
|
||||
/** @var DateTime */
|
||||
public $dateTime;
|
||||
|
||||
/** @var SendingServiceKeyCheck */
|
||||
private $mssWorker;
|
||||
|
||||
/** @var PremiumKeyCheck */
|
||||
private $premiumWorker;
|
||||
|
||||
/** @var ServicesChecker */
|
||||
private $servicesChecker;
|
||||
|
||||
/** @var CongratulatoryMssEmailController */
|
||||
private $congratulatoryMssEmailController;
|
||||
|
||||
/** @var WPFunctions */
|
||||
private $wp;
|
||||
|
||||
/** @var AuthorizedSenderDomainController */
|
||||
private $senderDomainController;
|
||||
|
||||
/** @var AuthorizedEmailsController */
|
||||
private $authorizedEmailsController;
|
||||
|
||||
/** @var SubscribersCountReporter */
|
||||
private $subscribersCountReporter;
|
||||
|
||||
public $permissions = [
|
||||
'global' => AccessControl::PERMISSION_MANAGE_SETTINGS,
|
||||
'methods' => ['pingBridge' => AccessControl::PERMISSION_ACCESS_PLUGIN_ADMIN],
|
||||
];
|
||||
|
||||
public function __construct(
|
||||
Bridge $bridge,
|
||||
SettingsController $settings,
|
||||
AnalyticsHelper $analytics,
|
||||
SendingServiceKeyCheck $mssWorker,
|
||||
PremiumKeyCheck $premiumWorker,
|
||||
ServicesChecker $servicesChecker,
|
||||
SubscribersCountReporter $subscribersCountReporter,
|
||||
CongratulatoryMssEmailController $congratulatoryMssEmailController,
|
||||
WPFunctions $wp,
|
||||
AuthorizedSenderDomainController $senderDomainController,
|
||||
AuthorizedEmailsController $authorizedEmailsController
|
||||
) {
|
||||
$this->bridge = $bridge;
|
||||
$this->settings = $settings;
|
||||
$this->analytics = $analytics;
|
||||
$this->mssWorker = $mssWorker;
|
||||
$this->premiumWorker = $premiumWorker;
|
||||
$this->dateTime = new DateTime();
|
||||
$this->servicesChecker = $servicesChecker;
|
||||
$this->subscribersCountReporter = $subscribersCountReporter;
|
||||
$this->congratulatoryMssEmailController = $congratulatoryMssEmailController;
|
||||
$this->wp = $wp;
|
||||
$this->senderDomainController = $senderDomainController;
|
||||
$this->authorizedEmailsController = $authorizedEmailsController;
|
||||
}
|
||||
|
||||
public function checkMSSKey($data = []) {
|
||||
$key = isset($data['key']) ? trim($data['key']) : null;
|
||||
|
||||
if (!$key) {
|
||||
return $this->badRequest([
|
||||
APIError::BAD_REQUEST => __('Please specify a key.', 'mailpoet'),
|
||||
]);
|
||||
}
|
||||
|
||||
$wasPendingApproval = $this->servicesChecker->isMailPoetAPIKeyPendingApproval();
|
||||
|
||||
try {
|
||||
$result = $this->bridge->checkMSSKey($key);
|
||||
$this->bridge->storeMSSKeyAndState($key, $result);
|
||||
} catch (\Exception $e) {
|
||||
return $this->errorResponse([
|
||||
$e->getCode() => $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
|
||||
// pause sending when key is pending approval, resume when not pending anymore
|
||||
$isPendingApproval = $this->servicesChecker->isMailPoetAPIKeyPendingApproval();
|
||||
if (!$wasPendingApproval && $isPendingApproval) {
|
||||
MailerLog::pauseSending(MailerLog::getMailerLog());
|
||||
} elseif ($wasPendingApproval && !$isPendingApproval) {
|
||||
MailerLog::resumeSending();
|
||||
}
|
||||
|
||||
$state = !empty($result['state']) ? $result['state'] : null;
|
||||
|
||||
$successMessage = null;
|
||||
if ($state == Bridge::KEY_VALID) {
|
||||
$successMessage = __('Your MailPoet Sending Service key has been successfully validated', 'mailpoet');
|
||||
} else if ($state == Bridge::KEY_VALID_UNDERPRIVILEGED) {
|
||||
$successMessage = __('Your Premium key has been successfully validated, but is not valid for MailPoet Sending Service', 'mailpoet');
|
||||
} elseif ($state == Bridge::KEY_EXPIRING) {
|
||||
$successMessage = sprintf(
|
||||
// translators: %s is the expiration date.
|
||||
__('Your MailPoet Sending Service key expires on %s!', 'mailpoet'),
|
||||
$this->dateTime->formatDate(strtotime($result['data']['expire_at']))
|
||||
);
|
||||
}
|
||||
|
||||
if (!empty($result['data']['public_id'])) {
|
||||
$this->analytics->setPublicId($result['data']['public_id']);
|
||||
}
|
||||
|
||||
if ($successMessage) {
|
||||
return $this->successResponse(['message' => $successMessage, 'state' => $state, 'result' => $result]);
|
||||
}
|
||||
|
||||
switch ($state) {
|
||||
case Bridge::KEY_INVALID:
|
||||
$error = __('Your key is not valid for the MailPoet Sending Service', 'mailpoet');
|
||||
break;
|
||||
case Bridge::KEY_ALREADY_USED:
|
||||
$error = __('Your MailPoet Sending Service key is already <a>used on another site</a>', 'mailpoet'); // we will use createInterpolateElement to replace <a> element
|
||||
break;
|
||||
default:
|
||||
$code = !empty($result['code']) ? $result['code'] : Bridge::CHECK_ERROR_UNKNOWN;
|
||||
// translators: %s is the error message.
|
||||
$errorMessage = __('Error validating MailPoet Sending Service key, please try again later (%s).', 'mailpoet');
|
||||
// If site runs on localhost
|
||||
if (1 === preg_match("/^(http|https)\:\/\/(localhost|127\.0\.0\.1)/", $this->wp->siteUrl())) {
|
||||
$errorMessage .= ' ' . __("Note that it doesn't work on localhost.", 'mailpoet');
|
||||
}
|
||||
$error = sprintf(
|
||||
$errorMessage,
|
||||
$this->getErrorDescriptionByCode($code)
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
return $this->errorResponse([APIError::BAD_REQUEST => $error]);
|
||||
}
|
||||
|
||||
public function checkPremiumKey($data = []) {
|
||||
$key = isset($data['key']) ? trim($data['key']) : null;
|
||||
|
||||
if (!$key) {
|
||||
return $this->badRequest([
|
||||
APIError::BAD_REQUEST => __('Please specify a key.', 'mailpoet'),
|
||||
]);
|
||||
}
|
||||
|
||||
try {
|
||||
$result = $this->bridge->checkPremiumKey($key);
|
||||
$this->bridge->storePremiumKeyAndState($key, $result);
|
||||
} catch (\Exception $e) {
|
||||
return $this->errorResponse([
|
||||
$e->getCode() => $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
|
||||
$state = !empty($result['state']) ? $result['state'] : null;
|
||||
|
||||
$successMessage = null;
|
||||
if ($state == Bridge::KEY_VALID) {
|
||||
$successMessage = __('Your Premium key has been successfully validated', 'mailpoet');
|
||||
} else if ($state == Bridge::KEY_VALID_UNDERPRIVILEGED) {
|
||||
$successMessage = __('Your Premium key has been successfully validated, but is not valid for MailPoet Sending Service', 'mailpoet');
|
||||
} elseif ($state == Bridge::KEY_EXPIRING) {
|
||||
$successMessage = sprintf(
|
||||
// translators: %s is the expiration date.
|
||||
__('Your Premium key expires on %s', 'mailpoet'),
|
||||
$this->dateTime->formatDate(strtotime($result['data']['expire_at']))
|
||||
);
|
||||
}
|
||||
|
||||
if (!empty($result['data']['public_id'])) {
|
||||
$this->analytics->setPublicId($result['data']['public_id']);
|
||||
}
|
||||
|
||||
if ($successMessage) {
|
||||
return $this->successResponse(
|
||||
['message' => $successMessage, 'state' => $state, 'result' => $result],
|
||||
Installer::getPremiumStatus()
|
||||
);
|
||||
}
|
||||
|
||||
switch ($state) {
|
||||
case Bridge::KEY_INVALID:
|
||||
$error = __('Your key is not valid for MailPoet Premium', 'mailpoet');
|
||||
break;
|
||||
case Bridge::KEY_ALREADY_USED:
|
||||
$error = __('Your Premium key is already <a>used on another site</a>', 'mailpoet'); // we will use createInterpolateElement to replace <a> element
|
||||
break;
|
||||
default:
|
||||
$code = !empty($result['code']) ? $result['code'] : Bridge::CHECK_ERROR_UNKNOWN;
|
||||
$error = sprintf(
|
||||
// translators: %s is the error message.
|
||||
__('Error validating Premium key, please try again later (%s)', 'mailpoet'),
|
||||
$this->getErrorDescriptionByCode($code)
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
return $this->errorResponse(
|
||||
[APIError::BAD_REQUEST => $error],
|
||||
['code' => $result['code'] ?? null]
|
||||
);
|
||||
}
|
||||
|
||||
public function recheckKeys() {
|
||||
// Report subscribers count before rechecking keys so that shop can lift access restrictions in case
|
||||
// user deleted subscribers and no longer exceeds the limit.
|
||||
$key = $this->servicesChecker->getValidAccountKey();
|
||||
if ($key) {
|
||||
$this->subscribersCountReporter->report($key);
|
||||
}
|
||||
$this->mssWorker->init();
|
||||
$mssCheck = $this->mssWorker->checkKey();
|
||||
$this->premiumWorker->init();
|
||||
$premiumCheck = $this->premiumWorker->checkKey();
|
||||
// continue sending when it is paused and states are valid
|
||||
$mailerLog = MailerLog::getMailerLog();
|
||||
if (
|
||||
(isset($mailerLog['status']) && $mailerLog['status'] === MailerLog::STATUS_PAUSED)
|
||||
&& (isset($mssCheck['state']) && $mssCheck['state'] === Bridge::KEY_VALID)
|
||||
&& (isset($premiumCheck['state']) && $premiumCheck['state'] === Bridge::PREMIUM_KEY_VALID)
|
||||
) {
|
||||
MailerLog::resumeSending();
|
||||
}
|
||||
return $this->successResponse();
|
||||
}
|
||||
|
||||
public function sendCongratulatoryMssEmail() {
|
||||
if (!Bridge::isMPSendingServiceEnabled()) {
|
||||
return $this->createBadRequest(__('MailPoet Sending Service is not active.', 'mailpoet'));
|
||||
}
|
||||
|
||||
$fromEmail = $this->settings->get('sender.address');
|
||||
if (!$fromEmail) {
|
||||
return $this->createBadRequest(__('Sender email address is not set.', 'mailpoet'));
|
||||
}
|
||||
|
||||
$verifiedDomains = $this->senderDomainController->getVerifiedSenderDomainsIgnoringCache();
|
||||
|
||||
$emailDomain = Helpers::extractEmailDomain($fromEmail);
|
||||
|
||||
if (!$this->isItemInArray($emailDomain, $verifiedDomains)) {
|
||||
$authorizedEmails = $this->authorizedEmailsController->getAuthorizedEmailAddresses();
|
||||
|
||||
if (!$authorizedEmails) {
|
||||
return $this->createBadRequest(__('No FROM email addresses are authorized.', 'mailpoet'));
|
||||
}
|
||||
|
||||
if (!$this->isItemInArray($fromEmail, $authorizedEmails)) {
|
||||
// translators: %s is the email address, which is not authorized.
|
||||
return $this->createBadRequest(sprintf(__("Sender email address '%s' is not authorized.", 'mailpoet'), $fromEmail));
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
// congratulatory email is sent to the current FROM address (authorized at this point)
|
||||
$this->congratulatoryMssEmailController->sendCongratulatoryEmail($fromEmail);
|
||||
} catch (\Throwable $e) {
|
||||
return $this->errorResponse([
|
||||
APIError::UNKNOWN => __('Sending of congratulatory email failed.', 'mailpoet'),
|
||||
], [], Response::STATUS_UNKNOWN);
|
||||
}
|
||||
return $this->successResponse([
|
||||
'email_address' => $fromEmail,
|
||||
]);
|
||||
}
|
||||
|
||||
public function pingBridge() {
|
||||
$response = $this->bridge->pingBridge();
|
||||
if ($this->wp->isWpError($response)) {
|
||||
/** @var \WP_Error $response */
|
||||
$errorDesc = $this->getErrorDescriptionByCode(Bridge::CHECK_ERROR_UNKNOWN);
|
||||
return $this->errorResponse([
|
||||
APIError::UNKNOWN => "{$errorDesc}: {$response->get_error_message()}",
|
||||
]);
|
||||
}
|
||||
|
||||
if (!$this->bridge->validateBridgePingResponse($response)) {
|
||||
$code = $this->wp->wpRemoteRetrieveResponseCode($response) ?: Bridge::CHECK_ERROR_UNKNOWN;
|
||||
return $this->errorResponse([
|
||||
APIError::UNKNOWN => $this->getErrorDescriptionByCode($code),
|
||||
]);
|
||||
}
|
||||
return $this->successResponse();
|
||||
}
|
||||
|
||||
public function refreshMSSKeyStatus() {
|
||||
$key = $this->settings->get('mta.mailpoet_api_key');
|
||||
return $this->checkMSSKey(['key' => $key]);
|
||||
}
|
||||
|
||||
public function refreshPremiumKeyStatus() {
|
||||
$key = $this->settings->get('premium.premium_key');
|
||||
return $this->checkPremiumKey(['key' => $key]);
|
||||
}
|
||||
|
||||
private function isItemInArray($item, $array): bool {
|
||||
return in_array($item, $array, true);
|
||||
}
|
||||
|
||||
private function getErrorDescriptionByCode($code) {
|
||||
switch ($code) {
|
||||
case Bridge::CHECK_ERROR_UNAVAILABLE:
|
||||
$text = __('Service unavailable', 'mailpoet');
|
||||
break;
|
||||
case Bridge::CHECK_ERROR_UNKNOWN:
|
||||
$text = __('Contact your hosting support to check the connection between your host and https://bridge.mailpoet.com', 'mailpoet');
|
||||
break;
|
||||
default:
|
||||
// translators: %s is the code.
|
||||
$text = sprintf(_x('code: %s', 'Error code (inside parentheses)', 'mailpoet'), $code);
|
||||
break;
|
||||
}
|
||||
|
||||
return $text;
|
||||
}
|
||||
|
||||
private function createBadRequest(string $message) {
|
||||
return $this->badRequest([
|
||||
APIError::BAD_REQUEST => $message,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,499 @@
|
||||
<?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
|
||||
|
||||
namespace MailPoet\API\JSON\v1;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use MailPoet\API\JSON\Endpoint as APIEndpoint;
|
||||
use MailPoet\API\JSON\Error as APIError;
|
||||
use MailPoet\API\JSON\ErrorResponse;
|
||||
use MailPoet\API\JSON\Response;
|
||||
use MailPoet\API\JSON\SuccessResponse;
|
||||
use MailPoet\Config\AccessControl;
|
||||
use MailPoet\Config\ServicesChecker;
|
||||
use MailPoet\Cron\Workers\SubscribersEngagementScore;
|
||||
use MailPoet\Entities\NewsletterEntity;
|
||||
use MailPoet\Entities\ScheduledTaskEntity;
|
||||
use MailPoet\Form\FormMessageController;
|
||||
use MailPoet\Mailer\Mailer;
|
||||
use MailPoet\Mailer\MailerLog;
|
||||
use MailPoet\Newsletter\NewslettersRepository;
|
||||
use MailPoet\Newsletter\Sending\ScheduledTasksRepository;
|
||||
use MailPoet\Segments\SegmentsRepository;
|
||||
use MailPoet\Services\AuthorizedEmailsController;
|
||||
use MailPoet\Services\AuthorizedSenderDomainController;
|
||||
use MailPoet\Services\Bridge;
|
||||
use MailPoet\Settings\SettingsChangeHandler;
|
||||
use MailPoet\Settings\SettingsController;
|
||||
use MailPoet\Settings\TrackingConfig;
|
||||
use MailPoet\Statistics\StatisticsOpensRepository;
|
||||
use MailPoet\Subscribers\ConfirmationEmailCustomizer;
|
||||
use MailPoet\Subscribers\SubscribersCountsController;
|
||||
use MailPoet\Util\Notices\DisabledMailFunctionNotice;
|
||||
use MailPoet\WooCommerce\TransactionalEmails;
|
||||
use MailPoetVendor\Carbon\Carbon;
|
||||
use MailPoetVendor\Doctrine\ORM\EntityManager;
|
||||
|
||||
class Settings extends APIEndpoint {
|
||||
|
||||
/** @var SettingsController */
|
||||
private $settings;
|
||||
|
||||
/** @var Bridge */
|
||||
private $bridge;
|
||||
|
||||
/** @var AuthorizedEmailsController */
|
||||
private $authorizedEmailsController;
|
||||
|
||||
/** @var AuthorizedSenderDomainController */
|
||||
private $senderDomainController;
|
||||
|
||||
/** @var TransactionalEmails */
|
||||
private $wcTransactionalEmails;
|
||||
|
||||
/** @var ServicesChecker */
|
||||
private $servicesChecker;
|
||||
|
||||
/** @var EntityManager */
|
||||
private $entityManager;
|
||||
|
||||
/** @var StatisticsOpensRepository */
|
||||
private $statisticsOpensRepository;
|
||||
|
||||
/** @var ScheduledTasksRepository */
|
||||
private $scheduledTasksRepository;
|
||||
|
||||
/** @var FormMessageController */
|
||||
private $messageController;
|
||||
|
||||
/** @var SegmentsRepository */
|
||||
private $segmentsRepository;
|
||||
|
||||
/** @var SubscribersCountsController */
|
||||
private $subscribersCountsController;
|
||||
|
||||
public $permissions = [
|
||||
'global' => AccessControl::PERMISSION_MANAGE_SETTINGS,
|
||||
];
|
||||
/** @var NewslettersRepository */
|
||||
private $newsletterRepository;
|
||||
|
||||
/** @var TrackingConfig */
|
||||
private $trackingConfig;
|
||||
|
||||
/** @var SettingsChangeHandler */
|
||||
private $settingsChangeHandler;
|
||||
|
||||
/** @var ConfirmationEmailCustomizer */
|
||||
private $confirmationEmailCustomizer;
|
||||
|
||||
public function __construct(
|
||||
SettingsController $settings,
|
||||
Bridge $bridge,
|
||||
AuthorizedEmailsController $authorizedEmailsController,
|
||||
AuthorizedSenderDomainController $senderDomainController,
|
||||
TransactionalEmails $wcTransactionalEmails,
|
||||
EntityManager $entityManager,
|
||||
NewslettersRepository $newslettersRepository,
|
||||
StatisticsOpensRepository $statisticsOpensRepository,
|
||||
ScheduledTasksRepository $scheduledTasksRepository,
|
||||
FormMessageController $messageController,
|
||||
ServicesChecker $servicesChecker,
|
||||
SegmentsRepository $segmentsRepository,
|
||||
SettingsChangeHandler $settingsChangeHandler,
|
||||
SubscribersCountsController $subscribersCountsController,
|
||||
TrackingConfig $trackingConfig,
|
||||
ConfirmationEmailCustomizer $confirmationEmailCustomizer
|
||||
) {
|
||||
$this->settings = $settings;
|
||||
$this->bridge = $bridge;
|
||||
$this->authorizedEmailsController = $authorizedEmailsController;
|
||||
$this->senderDomainController = $senderDomainController;
|
||||
$this->wcTransactionalEmails = $wcTransactionalEmails;
|
||||
$this->servicesChecker = $servicesChecker;
|
||||
$this->entityManager = $entityManager;
|
||||
$this->newsletterRepository = $newslettersRepository;
|
||||
$this->statisticsOpensRepository = $statisticsOpensRepository;
|
||||
$this->scheduledTasksRepository = $scheduledTasksRepository;
|
||||
$this->messageController = $messageController;
|
||||
$this->segmentsRepository = $segmentsRepository;
|
||||
$this->settingsChangeHandler = $settingsChangeHandler;
|
||||
$this->subscribersCountsController = $subscribersCountsController;
|
||||
$this->trackingConfig = $trackingConfig;
|
||||
$this->confirmationEmailCustomizer = $confirmationEmailCustomizer;
|
||||
}
|
||||
|
||||
public function get() {
|
||||
return $this->successResponse($this->settings->getAll());
|
||||
}
|
||||
|
||||
public function set($settings = []) {
|
||||
if (empty($settings)) {
|
||||
return $this->badRequest(
|
||||
[
|
||||
APIError::BAD_REQUEST =>
|
||||
__('You have not specified any settings to be saved.', 'mailpoet'),
|
||||
]
|
||||
);
|
||||
} else {
|
||||
$oldSettings = $this->settings->getAll();
|
||||
$meta = [];
|
||||
$signupConfirmation = $this->settings->get('signup_confirmation.enabled');
|
||||
foreach ($settings as $name => $value) {
|
||||
$this->settings->set($name, $value);
|
||||
}
|
||||
|
||||
$this->onSettingsChange($oldSettings, $this->settings->getAll());
|
||||
|
||||
// when pending approval, leave this to cron / Key Activation tab logic
|
||||
if (!$this->servicesChecker->isMailPoetAPIKeyPendingApproval()) {
|
||||
$this->settingsChangeHandler->updateApiKeyState($settings);
|
||||
}
|
||||
|
||||
$meta = $this->authorizedEmailsController->onSettingsSave($settings);
|
||||
if ($signupConfirmation !== $this->settings->get('signup_confirmation.enabled')) {
|
||||
$this->messageController->updateSuccessMessages();
|
||||
}
|
||||
|
||||
// Tracking and re-engagement Emails
|
||||
$meta['showNotice'] = false;
|
||||
if ($oldSettings['tracking'] !== $this->settings->get('tracking')) {
|
||||
try {
|
||||
$meta = $this->updateReEngagementEmailStatus($this->settings->get('tracking'));
|
||||
} catch (\Exception $e) {
|
||||
return $this->badRequest([
|
||||
APIError::UNKNOWN => $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->successResponse($this->settings->getAll(), $meta);
|
||||
}
|
||||
}
|
||||
|
||||
public function delete(string $settingName): Response {
|
||||
if (empty($settingName)) {
|
||||
return $this->badRequest(
|
||||
[
|
||||
APIError::BAD_REQUEST =>
|
||||
__('You have not specified any setting to be deleted.', 'mailpoet'),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
$setting = $this->settings->get($settingName);
|
||||
|
||||
if (is_null($setting)) {
|
||||
return $this->badRequest(
|
||||
[
|
||||
APIError::BAD_REQUEST =>
|
||||
__('Setting doesn\'t exist.', 'mailpoet'),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
$this->settings->delete($settingName);
|
||||
|
||||
return $this->successResponse();
|
||||
}
|
||||
|
||||
public function recalculateSubscribersScore() {
|
||||
$this->statisticsOpensRepository->resetSubscribersScoreCalculation();
|
||||
$this->statisticsOpensRepository->resetSegmentsScoreCalculation();
|
||||
$task = $this->scheduledTasksRepository->findOneBy([
|
||||
'type' => SubscribersEngagementScore::TASK_TYPE,
|
||||
'status' => ScheduledTaskEntity::STATUS_SCHEDULED,
|
||||
]);
|
||||
if (!$task) {
|
||||
$task = new ScheduledTaskEntity();
|
||||
$task->setType(SubscribersEngagementScore::TASK_TYPE);
|
||||
$task->setStatus(ScheduledTaskEntity::STATUS_SCHEDULED);
|
||||
}
|
||||
$task->setScheduledAt(Carbon::now()->millisecond(0));
|
||||
$this->entityManager->persist($task);
|
||||
$this->entityManager->flush();
|
||||
return $this->successResponse();
|
||||
}
|
||||
|
||||
public function setAuthorizedFromAddress($data = []) {
|
||||
$address = $data['address'] ?? null;
|
||||
if (!$address) {
|
||||
return $this->badRequest([
|
||||
APIError::BAD_REQUEST => __('No email address specified.', 'mailpoet'),
|
||||
]);
|
||||
}
|
||||
$address = trim($address);
|
||||
|
||||
try {
|
||||
$this->authorizedEmailsController->setFromEmailAddress($address);
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
return $this->badRequest([
|
||||
APIError::UNAUTHORIZED => __('Can’t use this email yet! Please authorize it first.', 'mailpoet'),
|
||||
]);
|
||||
}
|
||||
|
||||
if (!$this->servicesChecker->isMailPoetAPIKeyPendingApproval()) {
|
||||
MailerLog::resumeSending();
|
||||
}
|
||||
return $this->successResponse();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create POST request to Bridge endpoint to add email to user email authorization list
|
||||
*/
|
||||
public function authorizeSenderEmailAddress($data = []) {
|
||||
$emailAddress = $data['email'] ?? null;
|
||||
|
||||
if (!$emailAddress) {
|
||||
return $this->badRequest([
|
||||
APIError::BAD_REQUEST => __('No email address specified.', 'mailpoet'),
|
||||
]);
|
||||
}
|
||||
|
||||
$emailAddress = trim($emailAddress);
|
||||
|
||||
try {
|
||||
$response = $this->authorizedEmailsController->createAuthorizedEmailAddress($emailAddress);
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
if (
|
||||
$e->getMessage() === AuthorizedEmailsController::AUTHORIZED_EMAIL_ERROR_ALREADY_AUTHORIZED ||
|
||||
$e->getMessage() === AuthorizedEmailsController::AUTHORIZED_EMAIL_ERROR_PENDING_CONFIRMATION
|
||||
) {
|
||||
// return true if the email is already authorized or pending confirmation
|
||||
$response = ['status' => true];
|
||||
} else {
|
||||
return $this->badRequest([
|
||||
APIError::BAD_REQUEST => $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->successResponse($response);
|
||||
}
|
||||
|
||||
public function confirmSenderEmailAddressIsAuthorized($data = []) {
|
||||
$emailAddress = $data['email'] ?? null;
|
||||
|
||||
if (!$emailAddress) {
|
||||
return $this->badRequest([
|
||||
APIError::BAD_REQUEST => __('No email address specified.', 'mailpoet'),
|
||||
]);
|
||||
}
|
||||
|
||||
$emailAddress = trim($emailAddress);
|
||||
|
||||
$response = ['isAuthorized' => $this->authorizedEmailsController->isEmailAddressAuthorized($emailAddress)];
|
||||
|
||||
return $this->successResponse($response);
|
||||
}
|
||||
|
||||
public function getAuthorizedSenderDomains($data = []) {
|
||||
$domain = $data['domain'] ?? null;
|
||||
|
||||
if (!$domain) {
|
||||
return $this->badRequest([
|
||||
APIError::BAD_REQUEST => __('No sender domain specified.', 'mailpoet'),
|
||||
]);
|
||||
}
|
||||
|
||||
$domain = strtolower(trim($domain));
|
||||
|
||||
$records = $this->bridge->getAuthorizedSenderDomains($domain);
|
||||
return $this->successResponse($records);
|
||||
}
|
||||
|
||||
public function createAuthorizedSenderDomain($data = []) {
|
||||
$domain = $data['domain'] ?? null;
|
||||
|
||||
if (!$domain) {
|
||||
return $this->badRequest([
|
||||
APIError::BAD_REQUEST => __('No sender domain specified.', 'mailpoet'),
|
||||
]);
|
||||
}
|
||||
|
||||
$domain = strtolower(trim($domain));
|
||||
|
||||
try {
|
||||
$response = $this->senderDomainController->createAuthorizedSenderDomain($domain);
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
if (
|
||||
$e->getMessage() === AuthorizedSenderDomainController::AUTHORIZED_SENDER_DOMAIN_ERROR_ALREADY_CREATED
|
||||
) {
|
||||
// domain already created
|
||||
$response = $this->senderDomainController->getDomainRecords($domain);
|
||||
} else {
|
||||
return $this->badRequest([
|
||||
APIError::BAD_REQUEST => $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->successResponse($response);
|
||||
}
|
||||
|
||||
public function verifyAuthorizedSenderDomain($data = []) {
|
||||
$domain = $data['domain'] ?? null;
|
||||
|
||||
if (!$domain) {
|
||||
return $this->badRequest([
|
||||
APIError::BAD_REQUEST => __('No sender domain specified.', 'mailpoet'),
|
||||
]);
|
||||
}
|
||||
|
||||
$domain = strtolower(trim($domain));
|
||||
|
||||
try {
|
||||
$response = $this->senderDomainController->verifyAuthorizedSenderDomain($domain);
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
if (
|
||||
$e->getMessage() === AuthorizedSenderDomainController::AUTHORIZED_SENDER_DOMAIN_ERROR_ALREADY_VERIFIED
|
||||
) {
|
||||
// domain already verified, we have to wrap this in the format returned by the api
|
||||
$response = ['ok' => true, 'dns' => $this->senderDomainController->getDomainRecords($domain)];
|
||||
} else {
|
||||
return $this->badRequest([
|
||||
APIError::BAD_REQUEST => $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$response['ok']) {
|
||||
// sender domain verification error. probably an improper setup
|
||||
return $this->badRequest([
|
||||
APIError::BAD_REQUEST => $response['message'] ?? __('Sender domain verification failed.', 'mailpoet'),
|
||||
], $response);
|
||||
}
|
||||
|
||||
return $this->successResponse($response);
|
||||
}
|
||||
|
||||
private function onSettingsChange($oldSettings, $newSettings) {
|
||||
// Recalculate inactive subscribers
|
||||
$oldInactivationInterval = $oldSettings['deactivate_subscriber_after_inactive_days'];
|
||||
$newInactivationInterval = $newSettings['deactivate_subscriber_after_inactive_days'];
|
||||
if ($oldInactivationInterval !== $newInactivationInterval) {
|
||||
$this->settingsChangeHandler->onInactiveSubscribersIntervalChange();
|
||||
}
|
||||
|
||||
$oldSendingMethod = $oldSettings['mta_group'];
|
||||
$newSendingMethod = $newSettings['mta_group'];
|
||||
if (($oldSendingMethod !== $newSendingMethod) && ($newSendingMethod === 'mailpoet')) {
|
||||
$this->settingsChangeHandler->onMSSActivate($newSettings);
|
||||
}
|
||||
|
||||
if (($oldSendingMethod !== $newSendingMethod)) {
|
||||
$sendingMethodSet = $newSettings['mta']['method'] ?? null;
|
||||
if ($sendingMethodSet === 'PHPMail') {
|
||||
// check for valid mail function
|
||||
$this->settings->set(DisabledMailFunctionNotice::QUEUE_DISABLED_MAIL_FUNCTION_CHECK, true);
|
||||
} else {
|
||||
// when the user switch to a new sending method
|
||||
// do not display the DisabledMailFunctionNotice
|
||||
$this->settings->set(DisabledMailFunctionNotice::QUEUE_DISABLED_MAIL_FUNCTION_CHECK, false);
|
||||
$this->settings->set(DisabledMailFunctionNotice::DISABLED_MAIL_FUNCTION_CHECK, false); // do not display notice
|
||||
}
|
||||
}
|
||||
|
||||
// Sync WooCommerce Customers list
|
||||
$oldSubscribeOldWoocommerceCustomers = isset($oldSettings['mailpoet_subscribe_old_woocommerce_customers']['enabled'])
|
||||
? $oldSettings['mailpoet_subscribe_old_woocommerce_customers']['enabled']
|
||||
: '0';
|
||||
$newSubscribeOldWoocommerceCustomers = isset($newSettings['mailpoet_subscribe_old_woocommerce_customers']['enabled'])
|
||||
? $newSettings['mailpoet_subscribe_old_woocommerce_customers']['enabled']
|
||||
: '0';
|
||||
if ($oldSubscribeOldWoocommerceCustomers !== $newSubscribeOldWoocommerceCustomers) {
|
||||
$this->settingsChangeHandler->onSubscribeOldWoocommerceCustomersChange();
|
||||
}
|
||||
|
||||
if (!empty($newSettings['woocommerce']['use_mailpoet_editor'])) {
|
||||
$this->wcTransactionalEmails->init();
|
||||
}
|
||||
|
||||
if (!empty($newSettings['signup_confirmation']['use_mailpoet_editor'])) {
|
||||
$this->confirmationEmailCustomizer->init();
|
||||
}
|
||||
}
|
||||
|
||||
public function recalculateSubscribersCountsCache() {
|
||||
$segments = $this->segmentsRepository->findAll();
|
||||
foreach ($segments as $segment) {
|
||||
$this->subscribersCountsController->recalculateSegmentStatisticsCache($segment);
|
||||
}
|
||||
$this->subscribersCountsController->recalculateSubscribersWithoutSegmentStatisticsCache();
|
||||
// remove redundancies from cache
|
||||
$this->subscribersCountsController->removeRedundancyFromStatisticsCache();
|
||||
return $this->successResponse();
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function updateReEngagementEmailStatus($newTracking): array {
|
||||
if (!empty($newTracking['level']) && $this->trackingConfig->isEmailTrackingEnabled($newTracking['level'])) {
|
||||
return $this->reactivateReEngagementEmails();
|
||||
}
|
||||
try {
|
||||
return $this->deactivateReEngagementEmails();
|
||||
} catch (\Exception $e) {
|
||||
throw new \Exception(
|
||||
sprintf(
|
||||
// translators: %s is the error message.
|
||||
__('Unable to deactivate re-engagement emails: %s', 'mailpoet'),
|
||||
$e->getMessage()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function deactivateReEngagementEmails(): array {
|
||||
$reEngagementEmails = $this->newsletterRepository->findActiveByTypes(([NewsletterEntity::TYPE_RE_ENGAGEMENT]));
|
||||
if (!$reEngagementEmails) {
|
||||
return [
|
||||
'showNotice' => false,
|
||||
'action' => 'deactivate',
|
||||
];
|
||||
}
|
||||
|
||||
foreach ($reEngagementEmails as $reEngagementEmail) {
|
||||
$reEngagementEmail->setStatus(NewsletterEntity::STATUS_DRAFT);
|
||||
$this->entityManager->persist($reEngagementEmail);
|
||||
$this->entityManager->flush();
|
||||
}
|
||||
return [
|
||||
'showNotice' => true,
|
||||
'action' => 'deactivate',
|
||||
];
|
||||
}
|
||||
|
||||
public function reactivateReEngagementEmails(): array {
|
||||
$draftReEngagementEmails = $this->newsletterRepository->findDraftByTypes(([NewsletterEntity::TYPE_RE_ENGAGEMENT]));
|
||||
return [
|
||||
'showNotice' => !!$draftReEngagementEmails,
|
||||
'action' => 'reactivate',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the settings to set up MSS with the given key and calls the set method.
|
||||
*
|
||||
* @param string $apiKey
|
||||
* @return ErrorResponse|SuccessResponse
|
||||
*/
|
||||
public function setKeyAndSetupMss(string $apiKey) {
|
||||
$new_settings = [
|
||||
'mta_group' => 'mailpoet',
|
||||
'mta' => [
|
||||
'method' => Mailer::METHOD_MAILPOET,
|
||||
'mailpoet_api_key' => $apiKey,
|
||||
],
|
||||
'signup_confirmation' => [
|
||||
'enabled' => '1',
|
||||
],
|
||||
'premium.premium_key' => $apiKey,
|
||||
];
|
||||
return $this->set($new_settings);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
|
||||
|
||||
namespace MailPoet\API\JSON\v1;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use MailPoet\API\JSON\Endpoint as APIEndpoint;
|
||||
use MailPoet\Config\AccessControl;
|
||||
use MailPoet\Config\Activator;
|
||||
use MailPoet\Settings\SettingsController;
|
||||
use MailPoet\WP\Functions as WPFunctions;
|
||||
|
||||
class Setup extends APIEndpoint {
|
||||
public $permissions = [
|
||||
'global' => AccessControl::PERMISSION_MANAGE_SETTINGS,
|
||||
];
|
||||
|
||||
/** @var WPFunctions */
|
||||
private $wp;
|
||||
|
||||
/** @var Activator */
|
||||
private $activator;
|
||||
|
||||
/** @var SettingsController */
|
||||
private $settings;
|
||||
|
||||
public function __construct(
|
||||
WPFunctions $wp,
|
||||
Activator $activator,
|
||||
SettingsController $settings
|
||||
) {
|
||||
$this->wp = $wp;
|
||||
$this->activator = $activator;
|
||||
$this->settings = $settings;
|
||||
}
|
||||
|
||||
public function reset() {
|
||||
try {
|
||||
$this->activator->deactivate();
|
||||
$this->settings->resetCache();
|
||||
$this->activator->activate();
|
||||
$this->wp->doAction('mailpoet_setup_reset');
|
||||
return $this->successResponse();
|
||||
} catch (\Exception $e) {
|
||||
return $this->errorResponse([
|
||||
$e->getCode() => $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
<?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
|
||||
|
||||
namespace MailPoet\API\JSON\v1;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use MailPoet\API\JSON\Endpoint as APIEndpoint;
|
||||
use MailPoet\API\JSON\Error as APIError;
|
||||
use MailPoet\Config\AccessControl;
|
||||
use MailPoet\Entities\SubscriberEntity;
|
||||
use MailPoet\Subscribers\Statistics\SubscriberStatistics;
|
||||
use MailPoet\Subscribers\Statistics\SubscriberStatisticsRepository;
|
||||
use MailPoet\Subscribers\SubscribersRepository;
|
||||
use MailPoet\WooCommerce\Helper;
|
||||
use MailPoetVendor\Carbon\Carbon;
|
||||
|
||||
class SubscriberStats extends APIEndpoint {
|
||||
public $permissions = [
|
||||
'global' => AccessControl::PERMISSION_MANAGE_SUBSCRIBERS,
|
||||
];
|
||||
|
||||
/** @var SubscribersRepository */
|
||||
private $subscribersRepository;
|
||||
|
||||
/** @var SubscriberStatisticsRepository */
|
||||
private $subscribersStatisticsRepository;
|
||||
|
||||
/** @var Helper */
|
||||
private $wooCommerceHelper;
|
||||
|
||||
public function __construct(
|
||||
SubscribersRepository $subscribersRepository,
|
||||
SubscriberStatisticsRepository $subscribersStatisticsRepository,
|
||||
Helper $wooCommerceHelper
|
||||
) {
|
||||
$this->subscribersRepository = $subscribersRepository;
|
||||
$this->subscribersStatisticsRepository = $subscribersStatisticsRepository;
|
||||
$this->wooCommerceHelper = $wooCommerceHelper;
|
||||
}
|
||||
|
||||
public function get($data) {
|
||||
$subscriber = isset($data['subscriber_id'])
|
||||
? $this->subscribersRepository->findOneById((int)$data['subscriber_id'])
|
||||
: null;
|
||||
if (!$subscriber instanceof SubscriberEntity) {
|
||||
return $this->errorResponse([
|
||||
APIError::NOT_FOUND => __('This subscriber does not exist.', 'mailpoet'),
|
||||
]);
|
||||
}
|
||||
$response = [
|
||||
'email' => $subscriber->getEmail(),
|
||||
'engagement_score' => $subscriber->getEngagementScore(),
|
||||
'is_woo_active' => $this->wooCommerceHelper->isWooCommerceActive(),
|
||||
];
|
||||
|
||||
$statsMapper = function(SubscriberStatistics $statistics, string $timeframe) {
|
||||
return [
|
||||
'timeframe' => $timeframe,
|
||||
'total_sent' => $statistics->getTotalSentCount(),
|
||||
'open' => $statistics->getOpenCount(),
|
||||
'machine_open' => $statistics->getMachineOpenCount(),
|
||||
'click' => $statistics->getClickCount(),
|
||||
'woocommerce' => $statistics->getWooCommerceRevenue() ? $statistics->getWooCommerceRevenue()->asArray() : null,
|
||||
];
|
||||
};
|
||||
|
||||
$lifetimeStats = $this->subscribersStatisticsRepository->getStatistics($subscriber);
|
||||
$oneYearStats = $this->subscribersStatisticsRepository->getStatistics($subscriber, Carbon::now()->subYear());
|
||||
$thirtyDaysStats = $this->subscribersStatisticsRepository->getStatistics($subscriber, Carbon::now()->subDays(30));
|
||||
|
||||
$response['periodic_stats'] = [
|
||||
// translators: table header meaning 30 days
|
||||
$statsMapper($thirtyDaysStats, __('30(d)', 'mailpoet')),
|
||||
// translators: table header meaning 12 months
|
||||
$statsMapper($oneYearStats, __('12(m)', 'mailpoet')),
|
||||
$statsMapper($lifetimeStats, __('Lifetime', 'mailpoet')),
|
||||
];
|
||||
|
||||
$dateFormat = 'Y-m-d H:i:s';
|
||||
$lastEngagement = $subscriber->getLastEngagementAt();
|
||||
if ($lastEngagement instanceof \DateTimeInterface) {
|
||||
$response['last_engagement'] = $lastEngagement->format($dateFormat);
|
||||
}
|
||||
$lastClick = $subscriber->getLastClickAt();
|
||||
if ($lastClick instanceof \DateTimeInterface) {
|
||||
$response['last_click'] = $lastClick->format($dateFormat);
|
||||
}
|
||||
$lastOpen = $subscriber->getLastOpenAt();
|
||||
if ($lastOpen instanceof \DateTimeInterface) {
|
||||
$response['last_open'] = $lastOpen->format($dateFormat);
|
||||
}
|
||||
$lastPageView = $subscriber->getLastPageViewAt();
|
||||
if ($lastPageView instanceof \DateTimeInterface) {
|
||||
$response['last_page_view'] = $lastPageView->format($dateFormat);
|
||||
}
|
||||
$lastPurchase = $subscriber->getLastPurchaseAt();
|
||||
if ($lastPurchase instanceof \DateTimeInterface) {
|
||||
$response['last_purchase'] = $lastPurchase->format($dateFormat);
|
||||
}
|
||||
$lastSending = $subscriber->getLastSendingAt();
|
||||
if ($lastSending instanceof \DateTimeInterface) {
|
||||
$response['last_sending'] = $lastSending->format($dateFormat);
|
||||
}
|
||||
return $this->successResponse($response);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,350 @@
|
||||
<?php declare(strict_types = 1);
|
||||
|
||||
namespace MailPoet\API\JSON\v1;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use MailPoet\API\JSON\Endpoint as APIEndpoint;
|
||||
use MailPoet\API\JSON\Error as APIError;
|
||||
use MailPoet\API\JSON\ErrorResponse;
|
||||
use MailPoet\API\JSON\Response;
|
||||
use MailPoet\API\JSON\ResponseBuilders\SubscribersResponseBuilder;
|
||||
use MailPoet\API\JSON\SuccessResponse;
|
||||
use MailPoet\Config\AccessControl;
|
||||
use MailPoet\ConflictException;
|
||||
use MailPoet\Doctrine\Validator\ValidationException;
|
||||
use MailPoet\Entities\SegmentEntity;
|
||||
use MailPoet\Entities\SubscriberEntity;
|
||||
use MailPoet\Entities\TagEntity;
|
||||
use MailPoet\Exception;
|
||||
use MailPoet\Listing;
|
||||
use MailPoet\Segments\SegmentsRepository;
|
||||
use MailPoet\Settings\SettingsController;
|
||||
use MailPoet\Subscribers\ConfirmationEmailMailer;
|
||||
use MailPoet\Subscribers\SubscriberListingRepository;
|
||||
use MailPoet\Subscribers\SubscriberSaveController;
|
||||
use MailPoet\Subscribers\SubscribersRepository;
|
||||
use MailPoet\Subscribers\SubscriberSubscribeController;
|
||||
use MailPoet\Tags\TagRepository;
|
||||
use MailPoet\UnexpectedValueException;
|
||||
use MailPoet\Util\Helpers;
|
||||
|
||||
class Subscribers extends APIEndpoint {
|
||||
const SUBSCRIPTION_LIMIT_COOLDOWN = 60;
|
||||
|
||||
public $permissions = [
|
||||
'global' => AccessControl::PERMISSION_MANAGE_SUBSCRIBERS,
|
||||
'methods' => ['subscribe' => AccessControl::NO_ACCESS_RESTRICTION],
|
||||
];
|
||||
|
||||
/** @var Listing\Handler */
|
||||
private $listingHandler;
|
||||
|
||||
/** @var ConfirmationEmailMailer; */
|
||||
private $confirmationEmailMailer;
|
||||
|
||||
/** @var SubscribersRepository */
|
||||
private $subscribersRepository;
|
||||
|
||||
/** @var SubscribersResponseBuilder */
|
||||
private $subscribersResponseBuilder;
|
||||
|
||||
/** @var SubscriberListingRepository */
|
||||
private $subscriberListingRepository;
|
||||
|
||||
/** @var SegmentsRepository */
|
||||
private $segmentsRepository;
|
||||
|
||||
/** @var TagRepository */
|
||||
private $tagRepository;
|
||||
|
||||
/** @var SubscriberSaveController */
|
||||
private $saveController;
|
||||
|
||||
/** @var SubscriberSubscribeController */
|
||||
private $subscribeController;
|
||||
|
||||
/** @var SettingsController */
|
||||
private $settings;
|
||||
|
||||
public function __construct(
|
||||
Listing\Handler $listingHandler,
|
||||
ConfirmationEmailMailer $confirmationEmailMailer,
|
||||
SubscribersRepository $subscribersRepository,
|
||||
SubscribersResponseBuilder $subscribersResponseBuilder,
|
||||
SubscriberListingRepository $subscriberListingRepository,
|
||||
SegmentsRepository $segmentsRepository,
|
||||
TagRepository $tagRepository,
|
||||
SubscriberSaveController $saveController,
|
||||
SubscriberSubscribeController $subscribeController,
|
||||
SettingsController $settings
|
||||
) {
|
||||
$this->listingHandler = $listingHandler;
|
||||
$this->confirmationEmailMailer = $confirmationEmailMailer;
|
||||
$this->subscribersRepository = $subscribersRepository;
|
||||
$this->subscribersResponseBuilder = $subscribersResponseBuilder;
|
||||
$this->subscriberListingRepository = $subscriberListingRepository;
|
||||
$this->segmentsRepository = $segmentsRepository;
|
||||
$this->tagRepository = $tagRepository;
|
||||
$this->saveController = $saveController;
|
||||
$this->subscribeController = $subscribeController;
|
||||
$this->settings = $settings;
|
||||
}
|
||||
|
||||
public function get($data = []) {
|
||||
$subscriber = $this->getSubscriber($data);
|
||||
if (!$subscriber instanceof SubscriberEntity) {
|
||||
return $this->errorResponse([
|
||||
APIError::NOT_FOUND => __('This subscriber does not exist.', 'mailpoet'),
|
||||
]);
|
||||
}
|
||||
$result = $this->subscribersResponseBuilder->build($subscriber);
|
||||
return $this->successResponse($result);
|
||||
}
|
||||
|
||||
public function listing($data = []) {
|
||||
$definition = $this->listingHandler->getListingDefinition($data);
|
||||
$items = $this->subscriberListingRepository->getData($definition);
|
||||
$count = $this->subscriberListingRepository->getCount($definition);
|
||||
$filters = $this->subscriberListingRepository->getFilters($definition);
|
||||
$groups = $this->subscriberListingRepository->getGroups($definition);
|
||||
$subscribers = $this->subscribersResponseBuilder->buildForListing($items);
|
||||
if ($data['filter']['segment'] ?? false) {
|
||||
foreach ($subscribers as $key => $subscriber) {
|
||||
$subscribers[$key] = $this->preferUnsubscribedStatusFromSegment($subscriber, $data['filter']['segment']);
|
||||
}
|
||||
}
|
||||
return $this->successResponse($subscribers, [
|
||||
'count' => $count,
|
||||
'filters' => $filters,
|
||||
'groups' => $groups,
|
||||
]);
|
||||
}
|
||||
|
||||
private function preferUnsubscribedStatusFromSegment(array $subscriber, $segmentId) {
|
||||
$segmentStatus = $this->findSegmentStatus($subscriber, $segmentId);
|
||||
|
||||
if ($segmentStatus === SubscriberEntity::STATUS_UNSUBSCRIBED) {
|
||||
$subscriber['status'] = $segmentStatus;
|
||||
}
|
||||
return $subscriber;
|
||||
}
|
||||
|
||||
private function findSegmentStatus(array $subscriber, $segmentId) {
|
||||
foreach ($subscriber['subscriptions'] as $segment) {
|
||||
if ($segment['segment_id'] === $segmentId) {
|
||||
return $segment['status'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function subscribe($data = []) {
|
||||
try {
|
||||
$meta = $this->subscribeController->subscribe($data);
|
||||
} catch (Exception $exception) {
|
||||
return $this->badRequest([$exception->getMessage()]);
|
||||
}
|
||||
|
||||
if (!empty($meta['error'])) {
|
||||
$errorMessage = $meta['error'];
|
||||
unset($meta['error']);
|
||||
return $this->badRequest([APIError::BAD_REQUEST => $errorMessage], $meta);
|
||||
}
|
||||
|
||||
return $this->successResponse(
|
||||
[],
|
||||
$meta
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
* @return ErrorResponse|SuccessResponse
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function save(array $data = []) {
|
||||
try {
|
||||
$subscriber = $this->saveController->save($data);
|
||||
} catch (ValidationException $validationException) {
|
||||
return $this->badRequest([$this->getErrorMessage($validationException)]);
|
||||
} catch (ConflictException $conflictException) {
|
||||
return $this->badRequest([
|
||||
APIError::BAD_REQUEST => $conflictException->getMessage(),
|
||||
]);
|
||||
};
|
||||
|
||||
return $this->successResponse(
|
||||
$this->subscribersResponseBuilder->build($subscriber)
|
||||
);
|
||||
}
|
||||
|
||||
public function restore($data = []) {
|
||||
$subscriber = $this->getSubscriber($data);
|
||||
if ($subscriber instanceof SubscriberEntity) {
|
||||
$this->subscribersRepository->bulkRestore([$subscriber->getId()]);
|
||||
$this->subscribersRepository->refresh($subscriber);
|
||||
return $this->successResponse(
|
||||
$this->subscribersResponseBuilder->build($subscriber),
|
||||
['count' => 1]
|
||||
);
|
||||
} else {
|
||||
return $this->errorResponse([
|
||||
APIError::NOT_FOUND => __('This subscriber does not exist.', 'mailpoet'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
public function trash($data = []) {
|
||||
$subscriber = $this->getSubscriber($data);
|
||||
if ($subscriber instanceof SubscriberEntity) {
|
||||
$this->subscribersRepository->bulkTrash([$subscriber->getId()]);
|
||||
$this->subscribersRepository->refresh($subscriber);
|
||||
return $this->successResponse(
|
||||
$this->subscribersResponseBuilder->build($subscriber),
|
||||
['count' => 1]
|
||||
);
|
||||
} else {
|
||||
return $this->errorResponse([
|
||||
APIError::NOT_FOUND => __('This subscriber does not exist.', 'mailpoet'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
public function delete($data = []) {
|
||||
$subscriber = $this->getSubscriber($data);
|
||||
if ($subscriber instanceof SubscriberEntity) {
|
||||
$count = $this->subscribersRepository->bulkDelete([$subscriber->getId()]);
|
||||
return $this->successResponse(null, ['count' => $count]);
|
||||
} else {
|
||||
return $this->errorResponse([
|
||||
APIError::NOT_FOUND => __('This subscriber does not exist.', 'mailpoet'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
public function sendConfirmationEmail($data = []) {
|
||||
if (!(bool)$this->settings->get('signup_confirmation.enabled', true)) {
|
||||
$errorMessage = __('Sign-up confirmation is disabled in your [link]MailPoet settings[/link]. Please enable it to resend confirmation emails or update your subscriber’s status manually.', 'mailpoet');
|
||||
$errorMessage = Helpers::replaceLinkTags($errorMessage, 'admin.php?page=mailpoet-settings#/signup');
|
||||
return $this->errorResponse([APIError::BAD_REQUEST => $errorMessage], [], Response::STATUS_BAD_REQUEST);
|
||||
}
|
||||
|
||||
$id = (isset($data['id']) ? (int)$data['id'] : false);
|
||||
$subscriber = $this->subscribersRepository->findOneById($id);
|
||||
if ($subscriber instanceof SubscriberEntity) {
|
||||
try {
|
||||
if ($this->confirmationEmailMailer->sendConfirmationEmail($subscriber)) {
|
||||
return $this->successResponse();
|
||||
} else {
|
||||
return $this->errorResponse([
|
||||
APIError::UNKNOWN => __('There was a problem with your sending method. Please check if your sending method is properly configured.', 'mailpoet'),
|
||||
]);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
return $this->errorResponse([
|
||||
APIError::UNKNOWN => __('There was a problem with your sending method. Please check if your sending method is properly configured.', 'mailpoet'),
|
||||
]);
|
||||
}
|
||||
} else {
|
||||
return $this->errorResponse([
|
||||
APIError::NOT_FOUND => __('This subscriber does not exist.', 'mailpoet'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
public function bulkAction($data = []) {
|
||||
$definition = $this->listingHandler->getListingDefinition($data['listing']);
|
||||
$ids = $this->subscriberListingRepository->getActionableIds($definition);
|
||||
|
||||
$count = 0;
|
||||
$segment = null;
|
||||
if (isset($data['segment_id'])) {
|
||||
$segment = $this->getSegment($data);
|
||||
if (!$segment) {
|
||||
return $this->errorResponse([
|
||||
APIError::NOT_FOUND => __('This segment does not exist.', 'mailpoet'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
$tag = null;
|
||||
if (isset($data['tag_id'])) {
|
||||
$tag = $this->getTag($data);
|
||||
if (!$tag) {
|
||||
return $this->errorResponse([
|
||||
APIError::NOT_FOUND => __('This tag does not exist.', 'mailpoet'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
if ($data['action'] === 'trash') {
|
||||
$count = $this->subscribersRepository->bulkTrash($ids);
|
||||
} elseif ($data['action'] === 'restore') {
|
||||
$count = $this->subscribersRepository->bulkRestore($ids);
|
||||
} elseif ($data['action'] === 'delete') {
|
||||
$count = $this->subscribersRepository->bulkDelete($ids);
|
||||
} elseif ($data['action'] === 'removeFromAllLists') {
|
||||
$count = $this->subscribersRepository->bulkRemoveFromAllSegments($ids);
|
||||
} elseif ($data['action'] === 'removeFromList' && $segment instanceof SegmentEntity) {
|
||||
$count = $this->subscribersRepository->bulkRemoveFromSegment($segment, $ids);
|
||||
} elseif ($data['action'] === 'addToList' && $segment instanceof SegmentEntity) {
|
||||
$count = $this->subscribersRepository->bulkAddToSegment($segment, $ids);
|
||||
} elseif ($data['action'] === 'moveToList' && $segment instanceof SegmentEntity) {
|
||||
$count = $this->subscribersRepository->bulkMoveToSegment($segment, $ids);
|
||||
} elseif ($data['action'] === 'unsubscribe') {
|
||||
$count = $this->subscribersRepository->bulkUnsubscribe($ids);
|
||||
} elseif ($data['action'] === 'addTag' && $tag instanceof TagEntity) {
|
||||
$count = $this->subscribersRepository->bulkAddTag($tag, $ids);
|
||||
} elseif ($data['action'] === 'removeTag' && $tag instanceof TagEntity) {
|
||||
$count = $this->subscribersRepository->bulkRemoveTag($tag, $ids);
|
||||
} else {
|
||||
throw UnexpectedValueException::create()
|
||||
->withErrors([APIError::BAD_REQUEST => "Invalid bulk action '{$data['action']}' provided."]);
|
||||
}
|
||||
$meta = [
|
||||
'count' => $count,
|
||||
];
|
||||
|
||||
if ($segment) {
|
||||
$meta['segment'] = $segment->getName();
|
||||
}
|
||||
if ($tag) {
|
||||
$meta['tag'] = $tag->getName();
|
||||
}
|
||||
return $this->successResponse(null, $meta);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
* @return SubscriberEntity|null
|
||||
*/
|
||||
private function getSubscriber($data) {
|
||||
return isset($data['id'])
|
||||
? $this->subscribersRepository->findOneById((int)$data['id'])
|
||||
: null;
|
||||
}
|
||||
|
||||
private function getSegment(array $data): ?SegmentEntity {
|
||||
return isset($data['segment_id'])
|
||||
? $this->segmentsRepository->findOneById((int)$data['segment_id'])
|
||||
: null;
|
||||
}
|
||||
|
||||
private function getTag(array $data): ?TagEntity {
|
||||
return isset($data['tag_id'])
|
||||
? $this->tagRepository->findOneById((int)$data['tag_id'])
|
||||
: null;
|
||||
}
|
||||
|
||||
private function getErrorMessage(ValidationException $exception): string {
|
||||
$exceptionMessage = $exception->getMessage();
|
||||
if (strpos($exceptionMessage, 'This value should not be blank.') !== false) {
|
||||
return __('Please enter your email address', 'mailpoet');
|
||||
} elseif (strpos($exceptionMessage, 'This value is not a valid email address.') !== false) {
|
||||
return __('Your email address is invalid!', 'mailpoet');
|
||||
}
|
||||
|
||||
return __('Unexpected error.', 'mailpoet');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php declare(strict_types = 1);
|
||||
|
||||
namespace MailPoet\API\JSON\v1;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use MailPoet\API\JSON\Endpoint as APIEndpoint;
|
||||
use MailPoet\API\JSON\Error as APIError;
|
||||
use MailPoet\Entities\TagEntity;
|
||||
use MailPoet\Tags\TagRepository;
|
||||
|
||||
class Tags extends APIEndpoint {
|
||||
|
||||
private $repository;
|
||||
|
||||
public function __construct(
|
||||
TagRepository $repository
|
||||
) {
|
||||
$this->repository = $repository;
|
||||
}
|
||||
|
||||
public function create($data = []) {
|
||||
if (!isset($data['name'])) {
|
||||
return $this->badRequest([
|
||||
APIError::BAD_REQUEST => __('A tag needs to have a name.', 'mailpoet'),
|
||||
]);
|
||||
}
|
||||
|
||||
$data['name'] = sanitize_text_field(wp_unslash($data['name']));
|
||||
$data['description'] = isset($data['description']) ? sanitize_text_field(wp_unslash($data['description'])) : '';
|
||||
|
||||
return $this->successResponse(
|
||||
$this->mapTagEntity($this->repository->createOrUpdate($data))
|
||||
);
|
||||
}
|
||||
|
||||
public function listing() {
|
||||
return $this->successResponse(
|
||||
array_map(
|
||||
[$this, 'mapTagEntity'],
|
||||
$this->repository->findAll()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private function mapTagEntity(TagEntity $tag): array {
|
||||
return [
|
||||
'id' => $tag->getId(),
|
||||
'name' => $tag->getName(),
|
||||
'description' => $tag->getDescription(),
|
||||
'created_at' => $tag->getCreatedAt(),
|
||||
'updated_at' => $tag->getUpdatedAt(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
|
||||
|
||||
namespace MailPoet\API\JSON\v1;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use MailPoet\API\JSON\Endpoint as APIEndpoint;
|
||||
use MailPoet\API\JSON\Error as APIError;
|
||||
use MailPoet\Config\AccessControl;
|
||||
use MailPoet\Settings\UserFlagsController;
|
||||
|
||||
class UserFlags extends APIEndpoint {
|
||||
|
||||
/** @var UserFlagsController */
|
||||
private $userFlags;
|
||||
|
||||
public $permissions = [
|
||||
'global' => AccessControl::ALL_ROLES_ACCESS,
|
||||
];
|
||||
|
||||
public function __construct(
|
||||
UserFlagsController $userFlags
|
||||
) {
|
||||
$this->userFlags = $userFlags;
|
||||
}
|
||||
|
||||
public function set(array $flags = []) {
|
||||
if (empty($flags)) {
|
||||
return $this->badRequest(
|
||||
[
|
||||
APIError::BAD_REQUEST =>
|
||||
__('You have not specified any user flags to be saved.', 'mailpoet'),
|
||||
]
|
||||
);
|
||||
} else {
|
||||
foreach ($flags as $name => $value) {
|
||||
$this->userFlags->set($name, $value);
|
||||
}
|
||||
return $this->successResponse([]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
|
||||
|
||||
namespace MailPoet\API\JSON\v1;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use MailPoet\API\JSON\Endpoint as APIEndpoint;
|
||||
use MailPoet\Config\AccessControl;
|
||||
use MailPoet\WP\Functions as WPFunctions;
|
||||
|
||||
class WoocommerceSettings extends APIEndpoint {
|
||||
public $permissions = [
|
||||
'global' => AccessControl::PERMISSION_MANAGE_EMAILS,
|
||||
];
|
||||
|
||||
private $allowedSettings = [
|
||||
'woocommerce_email_base_color',
|
||||
];
|
||||
|
||||
/** @var WPFunctions */
|
||||
private $wp;
|
||||
|
||||
public function __construct(
|
||||
WPFunctions $wp
|
||||
) {
|
||||
$this->wp = $wp;
|
||||
}
|
||||
|
||||
public function set($data = []) {
|
||||
foreach ($data as $option => $value) {
|
||||
if (in_array($option, $this->allowedSettings)) {
|
||||
$this->wp->updateOption($option, $value);
|
||||
}
|
||||
}
|
||||
return $this->successResponse([]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<?php
|
||||
Reference in New Issue
Block a user