Move plugin files to a subfolder

[MAILPOET-3988]
This commit is contained in:
Jan Jakes
2022-01-13 14:46:43 +01:00
committed by Veljko V
parent d2016701ee
commit 9f790efbf0
3200 changed files with 0 additions and 0 deletions

View File

@@ -0,0 +1,27 @@
<?php
namespace MailPoet\API\JSON\v1;
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());
}
}

View File

@@ -0,0 +1,96 @@
<?php
namespace MailPoet\API\JSON\v1;
use MailPoet\API\JSON\Endpoint as APIEndpoint;
use MailPoet\Config\AccessControl;
use MailPoet\WP\Functions as WPFunctions;
use MailPoet\WP\Posts as WPPosts;
class AutomatedLatestContent extends APIEndpoint {
/** @var \MailPoet\Newsletter\AutomatedLatestContent */
public $ALC;
private $wp;
public $permissions = [
'global' => AccessControl::PERMISSION_MANAGE_EMAILS,
];
public function __construct(
\MailPoet\Newsletter\AutomatedLatestContent $alc,
WPFunctions $wp
) {
$this->ALC = $alc;
$this->wp = $wp;
}
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 = $this->wp->applyFilters('mailpoet_search_terms_args', $args);
$terms = WPPosts::getTerms($args);
return $this->successResponse(array_values($terms));
}
public function getPosts($data = []) {
return $this->successResponse(
$this->ALC->getPosts($data)
);
}
public function getTransformedPosts($data = []) {
$posts = $this->ALC->getPosts($data);
return $this->successResponse(
$this->ALC->transformPosts($data, $posts)
);
}
public function getBulkTransformedPosts($data = []) {
$usedPosts = [];
$renderedPosts = [];
foreach ($data['blocks'] as $block) {
$posts = $this->ALC->getPosts($block, $usedPosts);
$renderedPosts[] = $this->ALC->transformPosts($block, $posts);
foreach ($posts as $post) {
$usedPosts[] = $post->ID;
}
}
return $this->successResponse($renderedPosts);
}
}

View File

@@ -0,0 +1,87 @@
<?php
namespace MailPoet\API\JSON\v1;
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 => WPFunctions::get()->__('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 => WPFunctions::get()->__('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 => WPFunctions::get()->__('Improperly formatted request.', 'mailpoet'),
]
);
}
$automaticEmail = $this->automaticEmails->getAutomaticEmailBySlug($emailSlug);
$event = $this->automaticEmails->getAutomaticEmailEventBySlug($emailSlug, $eventSlug);
if (!$event) {
return $this->errorResponse(
[
APIError::BAD_REQUEST => WPFunctions::get()->__('Automatic email event does not exist.', 'mailpoet'),
]
);
}
$eventShortcodes = (!empty($event['shortcodes']) && is_array($event['shortcodes'])) ?
[
$automaticEmail['title'] => $event['shortcodes'],
] :
null;
return $this->successResponse($eventShortcodes);
}
}

View File

@@ -0,0 +1,74 @@
<?php
namespace MailPoet\API\JSON\v1;
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\WP\Functions as WPFunctions;
class CustomFields extends APIEndpoint {
public $permissions = [
'global' => AccessControl::PERMISSION_MANAGE_FORMS,
];
/** @var CustomFieldsRepository */
private $customFieldsRepository;
/** @var CustomFieldsResponseBuilder */
private $customFieldsResponseBuilder;
public function __construct(
CustomFieldsRepository $customFieldsRepository,
CustomFieldsResponseBuilder $customFieldsResponseBuilder
) {
$this->customFieldsRepository = $customFieldsRepository;
$this->customFieldsResponseBuilder = $customFieldsResponseBuilder;
}
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 => WPFunctions::get()->__('This custom field does not exist.', 'mailpoet'),
]);
}
}
public function save($data = []) {
try {
$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 => WPFunctions::get()->__('This custom field does not exist.', 'mailpoet'),
]);
}
}

View File

@@ -0,0 +1,268 @@
<?php declare(strict_types = 1);
namespace MailPoet\API\JSON\v1;
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 MailPoet\WP\Functions as WPFunctions;
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 => WPFunctions::get()->__('Missing mandatory argument `id`.', 'mailpoet'),
]);
}
$segment = $this->segmentsRepository->findOneById($id);
if (!$segment instanceof SegmentEntity) {
return $this->errorResponse([
Error::NOT_FOUND => WPFunctions::get()->__('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 {
$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'),
]);
}
}
private function getErrorString(InvalidFilterException $e) {
switch ($e->getCode()) {
case InvalidFilterException::MISSING_TYPE:
return WPFunctions::get()->__('The segment type is missing.', 'mailpoet');
case InvalidFilterException::INVALID_TYPE:
return WPFunctions::get()->__('The segment type is unknown.', 'mailpoet');
case InvalidFilterException::MISSING_ROLE:
return WPFunctions::get()->__('Please select a user role.', 'mailpoet');
case InvalidFilterException::MISSING_ACTION:
case InvalidFilterException::INVALID_EMAIL_ACTION:
return WPFunctions::get()->__('Please select an email action.', 'mailpoet');
case InvalidFilterException::MISSING_NEWSLETTER_ID:
return WPFunctions::get()->__('Please select an email.', 'mailpoet');
case InvalidFilterException::MISSING_PRODUCT_ID:
return WPFunctions::get()->__('Please select a product.', 'mailpoet');
case InvalidFilterException::MISSING_COUNTRY:
return WPFunctions::get()->__('Please select a country.', 'mailpoet');
case InvalidFilterException::MISSING_CATEGORY_ID:
return WPFunctions::get()->__('Please select a category.', 'mailpoet');
case InvalidFilterException::MISSING_VALUE:
return WPFunctions::get()->__('Please fill all required values.', 'mailpoet');
case InvalidFilterException::MISSING_NUMBER_OF_ORDERS_FIELDS:
return WPFunctions::get()->__('Please select a type for the comparison, a number of orders and a number of days.', 'mailpoet');
case InvalidFilterException::MISSING_TOTAL_SPENT_FIELDS:
return WPFunctions::get()->__('Please select a type for the comparison, an amount and a number of days.', 'mailpoet');
case InvalidFilterException::MISSING_FILTER:
return WPFunctions::get()->__('Please add at least one condition for filtering.', 'mailpoet');
case InvalidFilterException::MISSING_OPERATOR:
return WPFunctions::get()->__('Please select a type for the comparison.', 'mailpoet');
default:
return WPFunctions::get()->__('An error occurred while saving data.', 'mailpoet');
}
}
public function trash($data = []) {
if (!isset($data['id'])) {
return $this->errorResponse([
Error::BAD_REQUEST => WPFunctions::get()->__('Missing mandatory argument `id`.', 'mailpoet'),
]);
}
$segment = $this->getSegment($data);
if ($segment === null) {
return $this->errorResponse([
Error::NOT_FOUND => WPFunctions::get()->__('This segment does not exist.', 'mailpoet'),
]);
}
$activelyUsedNewslettersSubjects = $this->newsletterSegmentRepository->getSubjectsOfActivelyUsedEmailsForSegments([$segment->getId()]);
if (isset($activelyUsedNewslettersSubjects[$segment->getId()])) {
return $this->badRequest([
Error::BAD_REQUEST => str_replace(
'%1$s',
"'" . join("', '", $activelyUsedNewslettersSubjects[$segment->getId()] ) . "'",
_x('Segment cannot be deleted because its used for %1$s email', 'Alert shown when trying to delete segment, which is assigned to any automatic emails.', 'mailpoet')
),
]);
}
$this->segmentsRepository->bulkTrash([$segment->getId()], SegmentEntity::TYPE_DYNAMIC);
return $this->successResponse(
$this->segmentsResponseBuilder->build($segment),
['count' => 1]
);
}
public function restore($data = []) {
if (!isset($data['id'])) {
return $this->errorResponse([
Error::BAD_REQUEST => WPFunctions::get()->__('Missing mandatory argument `id`.', 'mailpoet'),
]);
}
$segment = $this->getSegment($data);
if ($segment === null) {
return $this->errorResponse([
Error::NOT_FOUND => WPFunctions::get()->__('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 => WPFunctions::get()->__('Missing mandatory argument `id`.', 'mailpoet'),
]);
}
$segment = $this->getSegment($data);
if ($segment === null) {
return $this->errorResponse([
Error::NOT_FOUND => WPFunctions::get()->__('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);
if ($data['action'] === 'trash') {
$count = $this->segmentsRepository->bulkTrash($ids, SegmentEntity::TYPE_DYNAMIC);
} elseif ($data['action'] === 'restore') {
$count = $this->segmentsRepository->bulkRestore($ids, SegmentEntity::TYPE_DYNAMIC);
} elseif ($data['action'] === 'delete') {
$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, ['count' => $count]);
}
private function getSegment(array $data): ?SegmentEntity {
return isset($data['id'])
? $this->segmentsRepository->findOneById((int)$data['id'])
: null;
}
}

View File

@@ -0,0 +1,50 @@
<?php
namespace MailPoet\API\JSON\v1;
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([]);
}
}

View File

@@ -0,0 +1,340 @@
<?php
namespace MailPoet\API\JSON\v1;
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\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 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,
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->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(
__('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 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();
}
}

View File

@@ -0,0 +1,165 @@
<?php
namespace MailPoet\API\JSON\v1;
use InvalidArgumentException;
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\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\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;
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 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
) {
$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;
}
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 {
$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 (InvalidArgumentException $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,
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(),
]);
}
}
}

View File

@@ -0,0 +1,72 @@
<?php
namespace MailPoet\API\JSON\v1;
use MailPoet\API\JSON\Endpoint as APIEndpoint;
use MailPoet\Config\AccessControl;
class MP2Migrator extends APIEndpoint {
public $permissions = [
'global' => AccessControl::PERMISSION_MANAGE_SETTINGS,
];
/** @var \MailPoet\Config\MP2Migrator */
private $MP2Migrator;
public function __construct(
\MailPoet\Config\MP2Migrator $MP2Migrator
) {
$this->MP2Migrator = $MP2Migrator;
}
/**
* Import end point
*
* @param object $data
* @return object
*/
public function import($data) {
try {
$process = $this->MP2Migrator->import();
return $this->successResponse($process);
} catch (\Exception $e) {
return $this->errorResponse([
$e->getCode() => $e->getMessage(),
]);
}
}
/**
* Stop import end point
*
* @param object $data
* @return object
*/
public function stopImport($data) {
try {
$process = $this->MP2Migrator->stopImport();
return $this->successResponse($process);
} catch (\Exception $e) {
return $this->errorResponse([
$e->getCode() => $e->getMessage(),
]);
}
}
/**
* Skip import end point
*
* @param object $data
* @return object
*/
public function skipImport($data) {
try {
$process = $this->MP2Migrator->skipImport();
return $this->successResponse($process);
} catch (\Exception $e) {
return $this->errorResponse([
$e->getCode() => $e->getMessage(),
]);
}
}
}

View File

@@ -0,0 +1,87 @@
<?php
namespace MailPoet\API\JSON\v1;
use MailPoet\API\JSON\Endpoint as APIEndpoint;
use MailPoet\API\JSON\Error as APIError;
use MailPoet\Config\AccessControl;
use MailPoet\Mailer\MailerLog;
use MailPoet\Mailer\MetaInfo;
use MailPoet\Services\AuthorizedEmailsController;
use MailPoet\Services\Bridge;
use MailPoet\Settings\SettingsController;
use MailPoet\WP\Functions as WPFunctions;
class Mailer extends APIEndpoint {
/** @var AuthorizedEmailsController */
private $authorizedEmailsController;
/** @var Bridge */
private $bridge;
/** @var SettingsController */
private $settings;
/** @var MetaInfo */
private $mailerMetaInfo;
public $permissions = [
'global' => AccessControl::PERMISSION_MANAGE_EMAILS,
];
public function __construct(
AuthorizedEmailsController $authorizedEmailsController,
SettingsController $settings,
Bridge $bridge,
MetaInfo $mailerMetaInfo
) {
$this->authorizedEmailsController = $authorizedEmailsController;
$this->settings = $settings;
$this->bridge = $bridge;
$this->mailerMetaInfo = $mailerMetaInfo;
}
public function send($data = []) {
try {
$mailer = new \MailPoet\Mailer\Mailer();
$mailer->init(
(isset($data['mailer'])) ? $data['mailer'] : false,
(isset($data['sender'])) ? $data['sender'] : false,
(isset($data['reply_to'])) ? $data['reply_to'] : false
);
// 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(
WPFunctions::get()->__('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->bridge->getAuthorizedEmailAddresses();
return $this->successResponse($authorizedEmails);
}
}

View File

@@ -0,0 +1,35 @@
<?php
namespace MailPoet\API\JSON\v1;
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);
}
}

View File

@@ -0,0 +1,102 @@
<?php
namespace MailPoet\API\JSON\v1;
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\NewsletterTemplates\NewsletterTemplatesRepository;
use MailPoet\NewsletterTemplates\ThumbnailSaver;
use MailPoet\WP\Functions as WPFunctions;
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;
public function __construct(
NewsletterTemplatesRepository $newsletterTemplatesRepository,
NewsletterTemplatesResponseBuilder $newsletterTemplatesResponseBuilder,
ThumbnailSaver $thumbnailImageSaver,
ApiDataSanitizer $apiDataSanitizer
) {
$this->newsletterTemplatesRepository = $newsletterTemplatesRepository;
$this->newsletterTemplatesResponseBuilder = $newsletterTemplatesResponseBuilder;
$this->thumbnailImageSaver = $thumbnailImageSaver;
$this->apiDataSanitizer = $apiDataSanitizer;
}
public function get($data = []) {
$template = isset($data['id'])
? $this->newsletterTemplatesRepository->findOneById((int)$data['id'])
: null;
if (!$template) {
return $this->errorResponse([
APIError::NOT_FOUND => WPFunctions::get()->__('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);
if (!empty($data['body'])) {
$body = $this->apiDataSanitizer->sanitizeBody(json_decode($data['body'], true));
$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 => WPFunctions::get()->__('This template does not exist.', 'mailpoet'),
]);
}
$this->newsletterTemplatesRepository->remove($template);
$this->newsletterTemplatesRepository->flush();
return $this->successResponse(null, ['count' => 1]);
}
}

View File

@@ -0,0 +1,437 @@
<?php
namespace MailPoet\API\JSON\v1;
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\SendingQueueEntity;
use MailPoet\InvalidStateException;
use MailPoet\Listing;
use MailPoet\Newsletter\Listing\NewsletterListingRepository;
use MailPoet\Newsletter\NewsletterSaveController;
use MailPoet\Newsletter\NewslettersRepository;
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\Settings\SettingsController;
use MailPoet\Settings\TrackingConfig;
use MailPoet\UnexpectedValueException;
use MailPoet\Util\License\Features\Subscribers as SubscribersFeature;
use MailPoet\Util\Security;
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;
/** @var NewsletterUrl */
private $newsletterUrl;
/** @var TrackingConfig */
private $trackingConfig;
public function __construct(
Listing\Handler $listingHandler,
WPFunctions $wp,
SettingsController $settings,
CronHelper $cronHelper,
NewslettersRepository $newslettersRepository,
NewsletterListingRepository $newsletterListingRepository,
NewslettersResponseBuilder $newslettersResponseBuilder,
PostNotificationScheduler $postNotificationScheduler,
Emoji $emoji,
SubscribersFeature $subscribersFeature,
SendPreviewController $sendPreviewController,
NewsletterSaveController $newsletterSaveController,
NewsletterUrl $newsletterUrl,
TrackingConfig $trackingConfig
) {
$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->emoji = $emoji;
$this->subscribersFeature = $subscribersFeature;
$this->sendPreviewController = $sendPreviewController;
$this->newsletterSaveController = $newsletterSaveController;
$this->newsletterUrl = $newsletterUrl;
$this->trackingConfig = $trackingConfig;
}
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);
$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 the re-engagement email doesn't contain the re-engage link, it can't be activated
if ($newsletter->getType() === NewsletterEntity::TYPE_RE_ENGAGEMENT && $status === NewsletterEntity::STATUS_ACTIVE) {
if (strpos($newsletter->getContent(), '[link:subscription_re_engage_url]') === false) {
return $this->errorResponse([
APIError::FORBIDDEN => __(
'A re-engagement email must include a link with [link:subscription_re_engage_url] shortcode.',
'mailpoet'
),
], [], Response::STATUS_FORBIDDEN);
}
}
$tracking_enabled = $this->trackingConfig->isEmailTrackingEnabled();
if (!$tracking_enabled && $newsletter->getType() === NewsletterEntity::TYPE_RE_ENGAGEMENT && $status === NewsletterEntity::STATUS_ACTIVE) {
return $this->errorResponse([
APIError::FORBIDDEN => __(
'Re-engagement emails are disabled because open and click tracking is disabled in MailPoet → Settings → Advanced.',
'mailpoet'
),
], [], Response::STATUS_FORBIDDEN);
}
$this->newslettersRepository->prefetchOptions([$newsletter]);
$newsletter->setStatus($status);
// 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 = Scheduler::getNextRunDate($scheduleOption->getValue());
$queues = $newsletter->getQueues();
foreach ($queues as $queue) {
$task = $queue->getTask();
if (
$task &&
$task->getScheduledAt() <= Carbon::createFromTimestamp($this->wp->currentTime('timestamp')) &&
$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->newslettersRepository->bulkDelete([$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'),
]);
}
$newsletter->setBody(
json_decode($this->emoji->encodeForUTF8Column(MP_NEWSLETTERS_TABLE, '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);
$this->fixMissingHash($items); // Fix for MAILPOET-3275. Remove after May 2021
$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->newslettersRepository->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 {
$this->fixMissingHash([$newsletter]); // Fix for MAILPOET-3275. Remove after May 2021
$url = $this->newsletterUrl->getViewInBrowserUrl(
(object)[
'id' => $newsletter->getId(),
'hash' => $newsletter->getHash(),
]
);
// strip protocol to avoid mix content error
return preg_replace('/^https?:/i', '', $url);
}
/**
* Some Newsletters were created without a hash due to a bug MAILPOET-3275
* We can remove this fix after May 2021 since by then most users should have their data fixed
* @param NewsletterEntity[] $newsletters
*/
private function fixMissingHash(array $newsletters) {
foreach ($newsletters as $newsletter) {
if (!$newsletter instanceof NewsletterEntity || $newsletter->getHash() !== null) {
continue;
}
$newsletter->setHash(Security::generateHash());
$this->newslettersRepository->flush();
}
}
}

View File

@@ -0,0 +1,74 @@
<?php
namespace MailPoet\API\JSON\v1;
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 WP_Error;
class Premium extends APIEndpoint {
const PREMIUM_PLUGIN_SLUG = 'mailpoet-premium';
const PREMIUM_PLUGIN_PATH = 'mailpoet-premium/mailpoet-premium.php';
public $permissions = [
'global' => AccessControl::PERMISSION_MANAGE_SETTINGS,
];
/** @var ServicesChecker */
private $servicesChecker;
/** @var WPFunctions */
private $wp;
public function __construct(
ServicesChecker $servicesChecker,
WPFunctions $wp
) {
$this->servicesChecker = $servicesChecker;
$this->wp = $wp;
}
public function installPlugin() {
$premiumKeyValid = $this->servicesChecker->isPremiumKeyValid(false);
if (!$premiumKeyValid) {
return $this->error($this->wp->__('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($this->wp->__('Error when installing MailPoet Premium plugin.', 'mailpoet'));
}
$pluginInfo = (array)$pluginInfo;
$result = $this->wp->installPlugin($pluginInfo['download_link']);
if ($result !== true) {
return $this->error($this->wp->__('Error when installing MailPoet Premium plugin.', 'mailpoet'));
}
return $this->successResponse();
}
public function activatePlugin() {
$premiumKeyValid = $this->servicesChecker->isPremiumKeyValid(false);
if (!$premiumKeyValid) {
return $this->error($this->wp->__('Premium key is not valid.', 'mailpoet'));
}
$result = $this->wp->activatePlugin(self::PREMIUM_PLUGIN_PATH);
if ($result !== null) {
return $this->error($this->wp->__('Error when activating MailPoet Premium plugin.', 'mailpoet'));
}
return $this->successResponse();
}
private function error($message) {
return $this->badRequest([
APIError::BAD_REQUEST => $message,
]);
}
}

View File

@@ -0,0 +1,311 @@
<?php
namespace MailPoet\API\JSON\v1;
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\WooCommerce;
use MailPoet\Segments\WP;
use MailPoet\Subscribers\SubscribersRepository;
use MailPoet\UnexpectedValueException;
use MailPoet\WP\Functions as WPFunctions;
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;
public function __construct(
Listing\Handler $listingHandler,
SegmentsRepository $segmentsRepository,
SegmentListingRepository $segmentListingRepository,
SegmentsResponseBuilder $segmentsResponseBuilder,
SegmentSaveController $segmentSavecontroller,
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;
}
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 => WPFunctions::get()->__('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 {
$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 => WPFunctions::get()->__('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 => WPFunctions::get()->__('This list does not exist.', 'mailpoet'),
]);
}
}
public function trash($data = []) {
$segment = $this->getSegment($data);
if (!$segment instanceof SegmentEntity) {
return $this->errorResponse([
APIError::NOT_FOUND => WPFunctions::get()->__('This list does not exist.', 'mailpoet'),
]);
}
if (!$this->isTrashOrRestoreAllowed($segment)) {
return $this->errorResponse([
APIError::FORBIDDEN => WPFunctions::get()->__('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()] ) . "'",
_x('List cannot be deleted because its 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()] ) . "'",
_nx(
'List cannot be deleted because its used for %1$s form',
'List cannot be deleted because its 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 => WPFunctions::get()->__('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 => WPFunctions::get()->__('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]);
}
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;
}
}

View File

@@ -0,0 +1,236 @@
<?php
namespace MailPoet\API\JSON\v1;
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\Cron\Triggers\WordPress;
use MailPoet\Entities\NewsletterEntity;
use MailPoet\Entities\SendingQueueEntity;
use MailPoet\Models\Newsletter;
use MailPoet\Models\SendingQueue as SendingQueueModel;
use MailPoet\Newsletter\NewslettersRepository;
use MailPoet\Newsletter\Scheduler\Scheduler;
use MailPoet\Newsletter\Sending\SendingQueuesRepository;
use MailPoet\Segments\SubscribersFinder;
use MailPoet\Services\Bridge;
use MailPoet\Tasks\Sending as SendingTask;
use MailPoet\Util\License\Features\Subscribers as SubscribersFeature;
class SendingQueue extends APIEndpoint {
public $permissions = [
'global' => AccessControl::PERMISSION_MANAGE_EMAILS,
];
/** @var SubscribersFeature */
private $subscribersFeature;
/** @var SubscribersFinder */
private $subscribersFinder;
/** @var NewslettersRepository */
private $newsletterRepository;
/** @var Bridge */
private $bridge;
/** @var SendingQueuesRepository */
private $sendingQueuesRepository;
public function __construct(
SubscribersFeature $subscribersFeature,
NewslettersRepository $newsletterRepository,
SendingQueuesRepository $sendingQueuesRepository,
Bridge $bridge,
SubscribersFinder $subscribersFinder
) {
$this->subscribersFeature = $subscribersFeature;
$this->subscribersFinder = $subscribersFinder;
$this->newsletterRepository = $newsletterRepository;
$this->bridge = $bridge;
$this->sendingQueuesRepository = $sendingQueuesRepository;
}
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 = Newsletter::findOneWithOptions($newsletterId);
if (!$newsletter instanceof Newsletter) {
return $this->errorResponse([
APIError::NOT_FOUND => __('This newsletter does not exist.', 'mailpoet'),
]);
}
$newsletterEntity = $this->newsletterRepository->findOneById($newsletterId);
if (!$newsletterEntity instanceof NewsletterEntity) {
return $this->errorResponse([
APIError::NOT_FOUND => __('This newsletter does not exist.', 'mailpoet'),
]);
}
$validationError = $this->validateNewsletter($newsletterEntity);
if ($validationError) {
return $this->errorResponse([
APIError::BAD_REQUEST => $validationError,
]);
}
// check that the sending method has been configured properly
try {
$mailer = new \MailPoet\Mailer\Mailer();
$mailer->init();
} catch (\Exception $e) {
return $this->errorResponse([
$e->getCode() => $e->getMessage(),
]);
}
// add newsletter to the sending queue
$queue = SendingQueueModel::joinWithTasks()
->where('queues.newsletter_id', $newsletterEntity->getId())
->whereNull('tasks.status')
->findOne();
if (!empty($queue)) {
return $this->errorResponse([
APIError::NOT_FOUND => __('This newsletter is already being sent.', 'mailpoet'),
]);
}
$scheduledQueue = SendingQueueModel::joinWithTasks()
->where('queues.newsletter_id', $newsletterEntity->getId())
->where('tasks.status', SendingQueueModel::STATUS_SCHEDULED)
->findOne();
if ($scheduledQueue instanceof SendingQueueModel) {
$queue = SendingTask::createFromQueue($scheduledQueue);
} else {
$queue = SendingTask::create();
$queue->newsletterId = $newsletterEntity->getId();
}
WordPress::resetRunInterval();
if ((bool)$newsletterEntity->getOptionValue('isScheduled')) {
// set newsletter status
$newsletterEntity->setStatus(NewsletterEntity::STATUS_SCHEDULED);
// set queue status
$queue->status = SendingQueueModel::STATUS_SCHEDULED;
$queue->scheduledAt = Scheduler::formatDatetimeString($newsletterEntity->getOptionValue('scheduledAt'));
} else {
$segments = $newsletter->segments()->findMany();
$subscribersCount = $this->subscribersFinder->addSubscribersToTaskFromSegments($queue->task(), $segments);
if (!$subscribersCount) {
return $this->errorResponse([
APIError::UNKNOWN => __('There are no subscribers in that list!', 'mailpoet'),
]);
}
$queue->updateCount();
$queue->status = null;
$queue->scheduledAt = null;
// set newsletter status
$newsletterEntity->setStatus(Newsletter::STATUS_SENDING);
}
$queue->save();
$this->newsletterRepository->flush();
$errors = $queue->getErrors();
if (!empty($errors)) {
return $this->errorResponse($errors);
} else {
return $this->successResponse(
$newsletter->getQueue()->asArray()
);
}
}
private function validateNewsletter(NewsletterEntity $newsletterEntity): ?string {
if (
$newsletterEntity->getBody()
&& is_array($newsletterEntity->getBody())
&& $newsletterEntity->getBody()['content']
) {
$body = json_encode($newsletterEntity->getBody()['content']);
if ($body === false) {
return __('Poet, please add prose to your masterpiece before you send it to your followers.');
}
if (
$this->bridge->isMailpoetSendingServiceEnabled()
&& (strpos($body, '[link:subscription_unsubscribe_url]') === false)
&& (strpos($body, '[link:subscription_unsubscribe]') === false)
) {
return __('All emails must include an "Unsubscribe" link. Add a footer widget to your email to continue.');
}
} else {
return __('Poet, please add prose to your masterpiece before you send it to your followers.');
}
return null;
}
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);
return $this->successResponse();
}
} else {
return $this->errorResponse([
APIError::NOT_FOUND => __('This newsletter does not exist.', 'mailpoet'),
]);
}
}
}

View File

@@ -0,0 +1,113 @@
<?php
namespace MailPoet\API\JSON\v1;
use MailPoet\API\JSON\Endpoint as APIEndpoint;
use MailPoet\API\JSON\Error as APIError;
use MailPoet\Config\AccessControl;
use MailPoet\Cron\CronHelper;
use MailPoet\Listing;
use MailPoet\Models\Newsletter;
use MailPoet\Models\ScheduledTask;
use MailPoet\Models\ScheduledTaskSubscriber;
use MailPoet\Models\SendingQueue as SendingQueueModel;
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;
public function __construct(
Listing\Handler $listingHandler,
SettingsController $settings,
CronHelper $cronHelper,
WPFunctions $wp
) {
$this->listingHandler = $listingHandler;
$this->settings = $settings;
$this->cronHelper = $cronHelper;
$this->wp = $wp;
}
public function listing($data = []) {
$newsletterId = !empty($data['params']['id']) ? (int)$data['params']['id'] : false;
$tasksIds = SendingQueueModel::select('task_id')
->where('newsletter_id', $newsletterId)
->findArray();
if (empty($tasksIds)) {
return $this->errorResponse([
APIError::NOT_FOUND => __('This email has not been sent yet.', 'mailpoet'),
]);
}
$data['params']['task_ids'] = array_column($tasksIds, 'task_id');
$listingData = $this->listingHandler->get('\MailPoet\Models\ScheduledTaskSubscriber', $data);
$items = [];
foreach ($listingData['items'] as $item) {
$items[] = $item->asArray();
}
return $this->successResponse($items, [
'count' => $listingData['count'],
'filters' => $listingData['filters'],
'groups' => $listingData['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'] : false;
$subscriberId = !empty($data['subscriberId']) ? (int)$data['subscriberId'] : false;
$taskSubscriber = ScheduledTaskSubscriber::where('task_id', $taskId)
->where('subscriber_id', $subscriberId)
->findOne();
$task = ScheduledTask::findOne($taskId);
$sendingQueue = SendingQueueModel::where('task_id', $taskId)->findOne();
if (
!($task instanceof ScheduledTask)
|| !($taskSubscriber instanceof ScheduledTaskSubscriber)
|| !($sendingQueue instanceof SendingQueueModel)
|| $taskSubscriber->failed != 1
) {
return $this->errorResponse([
APIError::NOT_FOUND => __('Failed sending task not found!', 'mailpoet'),
]);
}
$newsletter = Newsletter::findOne($sendingQueue->newsletterId);
if (!($newsletter instanceof Newsletter)) {
return $this->errorResponse([
APIError::NOT_FOUND => __('Newsletter not found!', 'mailpoet'),
]);
}
$taskSubscriber->error = '';
$taskSubscriber->failed = 0;
$taskSubscriber->processed = 0;
$taskSubscriber->save();
$task->status = null;
$task->save();
$newsletter->status = Newsletter::STATUS_SENDING;
$newsletter->save();
return $this->successResponse([]);
}
}

View File

@@ -0,0 +1,269 @@
<?php
namespace MailPoet\API\JSON\v1;
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\Bridge;
use MailPoet\Services\CongratulatoryMssEmailController;
use MailPoet\Settings\SettingsController;
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;
public $permissions = [
'global' => AccessControl::PERMISSION_MANAGE_SETTINGS,
];
public function __construct(
Bridge $bridge,
SettingsController $settings,
AnalyticsHelper $analytics,
SendingServiceKeyCheck $mssWorker,
PremiumKeyCheck $premiumWorker,
ServicesChecker $servicesChecker,
CongratulatoryMssEmailController $congratulatoryMssEmailController,
WPFunctions $wp
) {
$this->bridge = $bridge;
$this->settings = $settings;
$this->analytics = $analytics;
$this->mssWorker = $mssWorker;
$this->premiumWorker = $premiumWorker;
$this->dateTime = new DateTime();
$this->servicesChecker = $servicesChecker;
$this->congratulatoryMssEmailController = $congratulatoryMssEmailController;
$this->wp = $wp;
}
public function checkMSSKey($data = []) {
$key = isset($data['key']) ? trim($data['key']) : null;
if (!$key) {
return $this->badRequest([
APIError::BAD_REQUEST => $this->wp->__('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 = $this->wp->__('Your MailPoet Sending Service key has been successfully validated', 'mailpoet');
} elseif ($state == Bridge::KEY_EXPIRING) {
$successMessage = sprintf(
$this->wp->__('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]);
}
switch ($state) {
case Bridge::KEY_INVALID:
$error = $this->wp->__('Your key is not valid for the MailPoet Sending Service', 'mailpoet');
break;
case Bridge::KEY_ALREADY_USED:
$error = $this->wp->__('Your MailPoet Sending Service key is already used on another site', 'mailpoet');
break;
default:
$code = !empty($result['code']) ? $result['code'] : Bridge::CHECK_ERROR_UNKNOWN;
$errorMessage = $this->wp->__('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 .= ' ' . $this->wp->__("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 => $this->wp->__('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 = $this->wp->__('Your Premium key has been successfully validated', 'mailpoet');
} elseif ($state == Bridge::KEY_EXPIRING) {
$successMessage = sprintf(
$this->wp->__('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],
Installer::getPremiumStatus()
);
}
switch ($state) {
case Bridge::KEY_INVALID:
$error = $this->wp->__('Your key is not valid for MailPoet Premium', 'mailpoet');
break;
case Bridge::KEY_ALREADY_USED:
$error = $this->wp->__('Your Premium key is already used on another site', 'mailpoet');
break;
default:
$code = !empty($result['code']) ? $result['code'] : Bridge::CHECK_ERROR_UNKNOWN;
$error = sprintf(
$this->wp->__('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() {
$this->mssWorker->init();
$this->mssWorker->checkKey();
$this->premiumWorker->init();
$this->premiumWorker->checkKey();
return $this->successResponse();
}
public function sendCongratulatoryMssEmail() {
if (!Bridge::isMPSendingServiceEnabled()) {
return $this->createBadRequest(__('MailPoet Sending Service is not active.', 'mailpoet'));
}
$authorizedEmails = $this->bridge->getAuthorizedEmailAddresses();
if (!$authorizedEmails) {
return $this->createBadRequest(__('No FROM email addresses are authorized.', 'mailpoet'));
}
$fromEmail = $this->settings->get('sender.address');
if (!$fromEmail) {
return $this->createBadRequest(__('Sender email address is not set.', 'mailpoet'));
}
if (!in_array($fromEmail, $authorizedEmails, true)) {
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,
]);
}
private function getErrorDescriptionByCode($code) {
switch ($code) {
case Bridge::CHECK_ERROR_UNAVAILABLE:
$text = $this->wp->__('Service unavailable', 'mailpoet');
break;
case Bridge::CHECK_ERROR_UNKNOWN:
$text = $this->wp->__('Contact your hosting support to check the connection between your host and https://bridge.mailpoet.com', 'mailpoet');
break;
default:
$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,
]);
}
}

View File

@@ -0,0 +1,337 @@
<?php
namespace MailPoet\API\JSON\v1;
use MailPoet\API\JSON\Endpoint as APIEndpoint;
use MailPoet\API\JSON\Error as APIError;
use MailPoet\Config\AccessControl;
use MailPoet\Config\ServicesChecker;
use MailPoet\Cron\Workers\InactiveSubscribers;
use MailPoet\Cron\Workers\SubscribersEngagementScore;
use MailPoet\Cron\Workers\WooCommerceSync;
use MailPoet\Entities\NewsletterEntity;
use MailPoet\Entities\ScheduledTaskEntity;
use MailPoet\Form\FormMessageController;
use MailPoet\Mailer\MailerLog;
use MailPoet\Newsletter\NewslettersRepository;
use MailPoet\Newsletter\Sending\ScheduledTasksRepository;
use MailPoet\Segments\SegmentsRepository;
use MailPoet\Services\AuthorizedEmailsController;
use MailPoet\Services\Bridge;
use MailPoet\Settings\SettingsController;
use MailPoet\Settings\TrackingConfig;
use MailPoet\Statistics\StatisticsOpensRepository;
use MailPoet\Subscribers\SubscribersCountsController;
use MailPoet\WooCommerce\TransactionalEmails;
use MailPoet\WP\Functions as WPFunctions;
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 TransactionalEmails */
private $wcTransactionalEmails;
/** @var ServicesChecker */
private $servicesChecker;
/** @var WPFunctions */
private $wp;
/** @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;
public function __construct(
SettingsController $settings,
Bridge $bridge,
AuthorizedEmailsController $authorizedEmailsController,
TransactionalEmails $wcTransactionalEmails,
WPFunctions $wp,
EntityManager $entityManager,
NewslettersRepository $newslettersRepository,
StatisticsOpensRepository $statisticsOpensRepository,
ScheduledTasksRepository $scheduledTasksRepository,
FormMessageController $messageController,
ServicesChecker $servicesChecker,
SegmentsRepository $segmentsRepository,
SubscribersCountsController $subscribersCountsController,
TrackingConfig $trackingConfig
) {
$this->settings = $settings;
$this->bridge = $bridge;
$this->authorizedEmailsController = $authorizedEmailsController;
$this->wcTransactionalEmails = $wcTransactionalEmails;
$this->servicesChecker = $servicesChecker;
$this->wp = $wp;
$this->entityManager = $entityManager;
$this->newsletterRepository = $newslettersRepository;
$this->statisticsOpensRepository = $statisticsOpensRepository;
$this->scheduledTasksRepository = $scheduledTasksRepository;
$this->messageController = $messageController;
$this->segmentsRepository = $segmentsRepository;
$this->subscribersCountsController = $subscribersCountsController;
$this->trackingConfig = $trackingConfig;
}
public function get() {
return $this->successResponse($this->settings->getAll());
}
public function set($settings = []) {
if (empty($settings)) {
return $this->badRequest(
[
APIError::BAD_REQUEST =>
WPFunctions::get()->__('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->bridge->onSettingsSave($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 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::createFromTimestamp($this->wp->currentTime('timestamp')));
$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 => WPFunctions::get()->__('No email address specified.', 'mailpoet'),
]);
}
$address = trim($address);
try {
$this->authorizedEmailsController->setFromEmailAddress($address);
} catch (\InvalidArgumentException $e) {
return $this->badRequest([
APIError::UNAUTHORIZED => WPFunctions::get()->__('Cant use this email yet! Please authorize it first.', 'mailpoet'),
]);
}
if (!$this->servicesChecker->isMailPoetAPIKeyPendingApproval()) {
MailerLog::resumeSending();
}
return $this->successResponse();
}
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->onInactiveSubscribersIntervalChange();
}
$oldSendingMethod = $oldSettings['mta_group'];
$newSendingMethod = $newSettings['mta_group'];
if (($oldSendingMethod !== $newSendingMethod) && ($newSendingMethod === 'mailpoet')) {
$this->onMSSActivate($newSettings);
}
// 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->onSubscribeOldWoocommerceCustomersChange();
}
if (!empty($newSettings['woocommerce']['use_mailpoet_editor'])) {
$this->wcTransactionalEmails->init();
}
}
private function onMSSActivate($newSettings) {
// see mailpoet/assets/js/src/wizard/create_sender_settings.jsx:freeAddress
$domain = str_replace('www.', '', $_SERVER['HTTP_HOST']);
if (
isset($newSettings['sender']['address'])
&& !empty($newSettings['reply_to']['address'])
&& ($newSettings['sender']['address'] === ('wordpress@' . $domain))
) {
$sender = [
'name' => $newSettings['reply_to']['name'] ?? '',
'address' => $newSettings['reply_to']['address'],
];
$this->settings->set('sender', $sender);
$this->settings->set('reply_to', null);
}
}
public function onSubscribeOldWoocommerceCustomersChange(): void {
$task = $this->scheduledTasksRepository->findOneBy([
'type' => WooCommerceSync::TASK_TYPE,
'status' => ScheduledTaskEntity::STATUS_SCHEDULED,
]);
if (!($task instanceof ScheduledTaskEntity)) {
$task = $this->createScheduledTask(WooCommerceSync::TASK_TYPE);
}
$datetime = Carbon::createFromTimestamp((int)WPFunctions::get()->currentTime('timestamp'));
$task->setScheduledAt($datetime->subMinute());
$this->scheduledTasksRepository->persist($task);
$this->scheduledTasksRepository->flush();
}
public function onInactiveSubscribersIntervalChange(): void {
$task = $this->scheduledTasksRepository->findOneBy([
'type' => InactiveSubscribers::TASK_TYPE,
'status' => ScheduledTaskEntity::STATUS_SCHEDULED,
]);
if (!($task instanceof ScheduledTaskEntity)) {
$task = $this->createScheduledTask(InactiveSubscribers::TASK_TYPE);
}
$datetime = Carbon::createFromTimestamp((int)WPFunctions::get()->currentTime('timestamp'));
$task->setScheduledAt($datetime->subMinute());
$this->scheduledTasksRepository->persist($task);
$this->scheduledTasksRepository->flush();
}
private function createScheduledTask(string $type): ScheduledTaskEntity {
$task = new ScheduledTaskEntity();
$task->setType($type);
$task->setStatus(ScheduledTaskEntity::STATUS_SCHEDULED);
return $task;
}
public function recalculateSubscribersCountsCache() {
$segments = $this->segmentsRepository->findAll();
foreach ($segments as $segment) {
$this->subscribersCountsController->recalculateSegmentStatisticsCache($segment);
if ($segment->isStatic()) {
$this->subscribersCountsController->recalculateSegmentGlobalStatusStatisticsCache($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(
__('Unable to deactivate re-engagement emails: ' . $e->getMessage(), 'mailpoet'));
}
}
/**
* @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',
];
}
}

View File

@@ -0,0 +1,41 @@
<?php
namespace MailPoet\API\JSON\v1;
use MailPoet\API\JSON\Endpoint as APIEndpoint;
use MailPoet\Config\AccessControl;
use MailPoet\Config\Activator;
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;
public function __construct(
WPFunctions $wp,
Activator $activator
) {
$this->wp = $wp;
$this->activator = $activator;
}
public function reset() {
try {
$this->activator->deactivate();
$this->activator->activate();
$this->wp->doAction('mailpoet_setup_reset');
return $this->successResponse();
} catch (\Exception $e) {
return $this->errorResponse([
$e->getCode() => $e->getMessage(),
]);
}
}
}

View File

@@ -0,0 +1,61 @@
<?php
namespace MailPoet\API\JSON\v1;
use MailPoet\API\JSON\Endpoint as APIEndpoint;
use MailPoet\API\JSON\Error as APIError;
use MailPoet\Config\AccessControl;
use MailPoet\Entities\SubscriberEntity;
use MailPoet\Newsletter\Statistics\WooCommerceRevenue;
use MailPoet\Subscribers\Statistics\SubscriberStatisticsRepository;
use MailPoet\Subscribers\SubscribersRepository;
use MailPoet\WP\Functions as WPFunctions;
class SubscriberStats extends APIEndpoint {
public $permissions = [
'global' => AccessControl::PERMISSION_MANAGE_SUBSCRIBERS,
];
/** @var SubscribersRepository */
private $subscribersRepository;
/** @var SubscriberStatisticsRepository */
private $subscribersStatisticsRepository;
public function __construct(
SubscribersRepository $subscribersRepository,
SubscriberStatisticsRepository $subscribersStatisticsRepository
) {
$this->subscribersRepository = $subscribersRepository;
$this->subscribersStatisticsRepository = $subscribersStatisticsRepository;
}
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 => WPFunctions::get()->__('This subscriber does not exist.', 'mailpoet'),
]);
}
$statistics = $this->subscribersStatisticsRepository->getStatistics($subscriber);
$response = [
'email' => $subscriber->getEmail(),
'total_sent' => $statistics->getTotalSentCount(),
'open' => $statistics->getOpenCount(),
'machine_open' => $statistics->getMachineOpenCount(),
'click' => $statistics->getClickCount(),
'engagement_score' => $subscriber->getEngagementScore(),
];
$lastEngagement = $subscriber->getLastEngagementAt();
if ($lastEngagement instanceof \DateTimeInterface) {
$response['last_engagement'] = $lastEngagement->format('Y-m-d H:i:s');
}
$woocommerce = $statistics->getWooCommerceRevenue();
if ($woocommerce instanceof WooCommerceRevenue) {
$response['woocommerce'] = $woocommerce->asArray();
}
return $this->successResponse($response);
}
}

View File

@@ -0,0 +1,293 @@
<?php declare(strict_types = 1);
namespace MailPoet\API\JSON\v1;
use MailPoet\API\JSON\Endpoint as APIEndpoint;
use MailPoet\API\JSON\Error as APIError;
use MailPoet\API\JSON\ResponseBuilders\SubscribersResponseBuilder;
use MailPoet\Config\AccessControl;
use MailPoet\Doctrine\Validator\ValidationException;
use MailPoet\Entities\SegmentEntity;
use MailPoet\Entities\SubscriberEntity;
use MailPoet\Exception;
use MailPoet\Listing;
use MailPoet\Models\Subscriber;
use MailPoet\Segments\SegmentsRepository;
use MailPoet\Subscribers\ConfirmationEmailMailer;
use MailPoet\Subscribers\SubscriberListingRepository;
use MailPoet\Subscribers\SubscriberSaveController;
use MailPoet\Subscribers\SubscribersRepository;
use MailPoet\Subscribers\SubscriberSubscribeController;
use MailPoet\UnexpectedValueException;
use MailPoet\WP\Functions as WPFunctions;
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 SubscriberSaveController */
private $saveController;
/** @var SubscriberSubscribeController */
private $subscribeController;
public function __construct(
Listing\Handler $listingHandler,
ConfirmationEmailMailer $confirmationEmailMailer,
SubscribersRepository $subscribersRepository,
SubscribersResponseBuilder $subscribersResponseBuilder,
SubscriberListingRepository $subscriberListingRepository,
SegmentsRepository $segmentsRepository,
SubscriberSaveController $saveController,
SubscriberSubscribeController $subscribeController
) {
$this->listingHandler = $listingHandler;
$this->confirmationEmailMailer = $confirmationEmailMailer;
$this->subscribersRepository = $subscribersRepository;
$this->subscribersResponseBuilder = $subscribersResponseBuilder;
$this->subscriberListingRepository = $subscriberListingRepository;
$this->segmentsRepository = $segmentsRepository;
$this->saveController = $saveController;
$this->subscribeController = $subscribeController;
}
public function get($data = []) {
$subscriber = $this->getSubscriber($data);
if (!$subscriber instanceof SubscriberEntity) {
return $this->errorResponse([
APIError::NOT_FOUND => WPFunctions::get()->__('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 === Subscriber::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
);
}
public function save($data = []) {
try {
$subscriber = $this->saveController->save($data);
} catch (ValidationException $validationException) {
return $this->badRequest([$this->getErrorMessage($validationException)]);
}
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 => WPFunctions::get()->__('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 => WPFunctions::get()->__('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 => WPFunctions::get()->__('This subscriber does not exist.', 'mailpoet'),
]);
}
}
public function sendConfirmationEmail($data = []) {
$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 => WPFunctions::get()->__('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 => WPFunctions::get()->__('This segment 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);
} else {
throw UnexpectedValueException::create()
->withErrors([APIError::BAD_REQUEST => "Invalid bulk action '{$data['action']}' provided."]);
}
$meta = [
'count' => $count,
];
if ($segment) {
$meta['segment'] = $segment->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 getErrorMessage(ValidationException $exception): string {
$exceptionMessage = $exception->getMessage();
if (strpos($exceptionMessage, 'This value should not be blank.') !== false) {
return WPFunctions::get()->__('Please enter your email address', 'mailpoet');
} elseif (strpos($exceptionMessage, 'This value is not a valid email address.') !== false) {
return WPFunctions::get()->__('Your email address is invalid!', 'mailpoet');
}
return WPFunctions::get()->__('Unexpected error.', 'mailpoet');
}
}

View File

@@ -0,0 +1,40 @@
<?php
namespace MailPoet\API\JSON\v1;
use MailPoet\API\JSON\Endpoint as APIEndpoint;
use MailPoet\API\JSON\Error as APIError;
use MailPoet\Config\AccessControl;
use MailPoet\Settings\UserFlagsController;
use MailPoet\WP\Functions as WPFunctions;
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 =>
WPFunctions::get()->__('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([]);
}
}
}

View File

@@ -0,0 +1,35 @@
<?php
namespace MailPoet\API\JSON\v1;
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([]);
}
}