Files
piratepoet/lib/API/MP/v1/API.php
Rodrigo Primo 6c05b3eaf0 Pass SubscriberEntity to sendConfirmationEmail() and sendConfirmationEmailOnce()
This commit changes the methods sendConfirmationEmail() and
sendConfirmationEmailOnce() to accept as the first parameter an instance
of SubscriberEntity instead of the old model Subscriber. It also updates
all the places where those two methods are called.

[MAILPOET-3815]
2021-12-13 16:03:07 +01:00

359 lines
15 KiB
PHP

<?php
namespace MailPoet\API\MP\v1;
use MailPoet\Entities\SubscriberEntity;
use MailPoet\Models\Segment;
use MailPoet\Models\Subscriber;
use MailPoet\Models\SubscriberSegment;
use MailPoet\Newsletter\Scheduler\WelcomeScheduler;
use MailPoet\Settings\SettingsController;
use MailPoet\Subscribers\ConfirmationEmailMailer;
use MailPoet\Subscribers\NewSubscriberNotificationMailer;
use MailPoet\Subscribers\RequiredCustomFieldValidator;
use MailPoet\Subscribers\Source;
use MailPoet\Subscribers\SubscribersRepository;
use MailPoet\Tasks\Sending;
use MailPoet\Util\Helpers;
use MailPoet\WP\Functions as WPFunctions;
class API {
/** @var NewSubscriberNotificationMailer */
private $newSubscriberNotificationMailer;
/** @var ConfirmationEmailMailer */
private $confirmationEmailMailer;
/** @var RequiredCustomFieldValidator */
private $requiredCustomFieldValidator;
/** @var WelcomeScheduler */
private $welcomeScheduler;
/** @var SettingsController */
private $settings;
/** @var CustomFields */
private $customFields;
/** @var SubscribersRepository */
private $subscribersRepository;
public function __construct(
NewSubscriberNotificationMailer $newSubscriberNotificationMailer,
ConfirmationEmailMailer $confirmationEmailMailer,
RequiredCustomFieldValidator $requiredCustomFieldValidator,
WelcomeScheduler $welcomeScheduler,
CustomFields $customFields,
SettingsController $settings,
SubscribersRepository $subscribersRepository
) {
$this->newSubscriberNotificationMailer = $newSubscriberNotificationMailer;
$this->confirmationEmailMailer = $confirmationEmailMailer;
$this->requiredCustomFieldValidator = $requiredCustomFieldValidator;
$this->welcomeScheduler = $welcomeScheduler;
$this->settings = $settings;
$this->customFields = $customFields;
$this->subscribersRepository = $subscribersRepository;
}
public function getSubscriberFields() {
return $this->customFields->getSubscriberFields();
}
public function addSubscriberField(array $data = []) {
try {
return $this->customFields->addSubscriberField($data);
} catch (\InvalidArgumentException $e) {
throw new APIException($e->getMessage(), $e->getCode(), $e);
}
}
public function subscribeToList($subscriberId, $listId, $options = []) {
return $this->subscribeToLists($subscriberId, [$listId], $options);
}
public function subscribeToLists($subscriberId, array $listIds, $options = []) {
$scheduleWelcomeEmail = (isset($options['schedule_welcome_email']) && $options['schedule_welcome_email'] === false) ? false : true;
$sendConfirmationEmail = (isset($options['send_confirmation_email']) && $options['send_confirmation_email'] === false) ? false : true;
$skipSubscriberNotification = (isset($options['skip_subscriber_notification']) && $options['skip_subscriber_notification'] === true) ? true : false;
$signupConfirmationEnabled = (bool)$this->settings->get('signup_confirmation.enabled');
if (empty($listIds)) {
throw new APIException(__('At least one segment ID is required.', 'mailpoet'), APIException::SEGMENT_REQUIRED);
}
// throw exception when subscriber does not exist
$subscriber = Subscriber::findOne($subscriberId);
if (!$subscriber) {
throw new APIException(__('This subscriber does not exist.', 'mailpoet'), APIException::SUBSCRIBER_NOT_EXISTS);
}
// throw exception when none of the segments exist
$foundSegments = Segment::whereIn('id', $listIds)->findMany();
if (!$foundSegments) {
$exception = WPFunctions::get()->_n('This list does not exist.', 'These lists do not exist.', count($listIds), 'mailpoet');
throw new APIException($exception, APIException::LIST_NOT_EXISTS);
}
// throw exception when trying to subscribe to WP Users or WooCommerce Customers segments
$foundSegmentsIds = [];
foreach ($foundSegments as $foundSegment) {
if ($foundSegment->type === Segment::TYPE_WP_USERS) {
throw new APIException(__(sprintf("Can't subscribe to a WordPress Users list with ID %d.", $foundSegment->id), 'mailpoet'), APIException::SUBSCRIBING_TO_WP_LIST_NOT_ALLOWED);
}
if ($foundSegment->type === Segment::TYPE_WC_USERS) {
throw new APIException(__(sprintf("Can't subscribe to a WooCommerce Customers list with ID %d.", $foundSegment->id), 'mailpoet'), APIException::SUBSCRIBING_TO_WC_LIST_NOT_ALLOWED);
}
if ($foundSegment->type !== Segment::TYPE_DEFAULT) {
throw new APIException(__(sprintf("Can't subscribe to a list with ID %d.", $foundSegment->id), 'mailpoet'), APIException::SUBSCRIBING_TO_LIST_NOT_ALLOWED);
}
$foundSegmentsIds[] = $foundSegment->id;
}
// throw an exception when one or more segments do not exist
if (count($foundSegmentsIds) !== count($listIds)) {
$missingIds = array_values(array_diff($listIds, $foundSegmentsIds));
$exception = sprintf(
WPFunctions::get()->_n('List with ID %s does not exist.', 'Lists with IDs %s do not exist.', count($missingIds), 'mailpoet'),
implode(', ', $missingIds)
);
throw new APIException(sprintf($exception, implode(', ', $missingIds)), APIException::LIST_NOT_EXISTS);
}
SubscriberSegment::subscribeToSegments($subscriber, $foundSegmentsIds);
// set status depending on signup confirmation setting
if ($subscriber->status !== Subscriber::STATUS_SUBSCRIBED) {
if ($signupConfirmationEnabled === true) {
$subscriber->set('status', Subscriber::STATUS_UNCONFIRMED);
} else {
$subscriber->set('status', Subscriber::STATUS_SUBSCRIBED);
}
$subscriber->save();
if ($subscriber->getErrors() !== false) {
throw new APIException(
__(sprintf('Failed to save a status of a subscriber : %s', strtolower(implode(', ', $subscriber->getErrors()))), 'mailpoet'),
APIException::FAILED_TO_SAVE_SUBSCRIBER
);
}
}
// schedule welcome email
if ($scheduleWelcomeEmail && $subscriber->status === Subscriber::STATUS_SUBSCRIBED) {
$this->_scheduleWelcomeNotification($subscriber, $foundSegmentsIds);
}
// send confirmation email
if ($sendConfirmationEmail) {
$result = $this->_sendConfirmationEmail($subscriber);
if (!$result && $subscriber->getErrors()) {
throw new APIException(
__(sprintf('Subscriber added to lists, but confirmation email failed to send: %s', strtolower(implode(', ', $subscriber->getErrors()))), 'mailpoet'),
APIException::CONFIRMATION_FAILED_TO_SEND);
}
}
if (!$skipSubscriberNotification && ($subscriber->status === Subscriber::STATUS_SUBSCRIBED)) {
$this->sendSubscriberNotification($subscriber, $foundSegmentsIds);
}
return $subscriber->withCustomFields()->withSubscriptions()->asArray();
}
public function unsubscribeFromList($subscriberId, $listId) {
return $this->unsubscribeFromLists($subscriberId, [$listId]);
}
public function unsubscribeFromLists($subscriberId, array $listIds) {
if (empty($listIds)) {
throw new APIException(__('At least one segment ID is required.', 'mailpoet'), APIException::SEGMENT_REQUIRED);
}
// throw exception when subscriber does not exist
$subscriber = Subscriber::findOne($subscriberId);
if (!$subscriber) {
throw new APIException(__('This subscriber does not exist.', 'mailpoet'), APIException::SUBSCRIBER_NOT_EXISTS);
}
// throw exception when none of the segments exist
$foundSegments = Segment::whereIn('id', $listIds)->findMany();
if (!$foundSegments) {
$exception = WPFunctions::get()->_n('This list does not exist.', 'These lists do not exist.', count($listIds), 'mailpoet');
throw new APIException($exception, APIException::LIST_NOT_EXISTS);
}
// throw exception when trying to subscribe to WP Users or WooCommerce Customers segments
$foundSegmentsIds = [];
foreach ($foundSegments as $segment) {
if ($segment->type === Segment::TYPE_WP_USERS) {
throw new APIException(__(sprintf("Can't unsubscribe from a WordPress Users list with ID %d.", $segment->id), 'mailpoet'), APIException::SUBSCRIBING_TO_WP_LIST_NOT_ALLOWED);
}
if ($segment->type === Segment::TYPE_WC_USERS) {
throw new APIException(__(sprintf("Can't unsubscribe from a WooCommerce Customers list with ID %d.", $segment->id), 'mailpoet'), APIException::SUBSCRIBING_TO_WC_LIST_NOT_ALLOWED);
}
$foundSegmentsIds[] = $segment->id;
}
// throw an exception when one or more segments do not exist
if (count($foundSegmentsIds) !== count($listIds)) {
$missingIds = array_values(array_diff($listIds, $foundSegmentsIds));
$exception = sprintf(
WPFunctions::get()->_n('List with ID %s does not exist.', 'Lists with IDs %s do not exist.', count($missingIds), 'mailpoet'),
implode(', ', $missingIds)
);
throw new APIException($exception, APIException::LIST_NOT_EXISTS);
}
SubscriberSegment::unsubscribeFromSegments($subscriber, $foundSegmentsIds);
return $subscriber->withCustomFields()->withSubscriptions()->asArray();
}
public function getLists() {
return Segment::where('type', Segment::TYPE_DEFAULT)
->findArray();
}
public function addSubscriber(array $subscriber, $listIds = [], $options = []) {
$sendConfirmationEmail = (isset($options['send_confirmation_email']) && $options['send_confirmation_email'] === false) ? false : true;
$scheduleWelcomeEmail = (isset($options['schedule_welcome_email']) && $options['schedule_welcome_email'] === false) ? false : true;
$skipSubscriberNotification = (isset($options['skip_subscriber_notification']) && $options['skip_subscriber_notification'] === true) ? true : false;
// throw exception when subscriber email is missing
if (empty($subscriber['email'])) {
throw new APIException(
__('Subscriber email address is required.', 'mailpoet'),
APIException::EMAIL_ADDRESS_REQUIRED
);
}
// throw exception when subscriber already exists
if (Subscriber::findOne($subscriber['email'])) {
throw new APIException(
__('This subscriber already exists.', 'mailpoet'),
APIException::SUBSCRIBER_EXISTS
);
}
if (empty($subscriber['subscribed_ip'])) {
$subscriber['subscribed_ip'] = Helpers::getIP();
}
// separate data into default and custom fields
[$defaultFields, $customFields] = Subscriber::extractCustomFieldsFromFromObject($subscriber);
// filter out all incoming data that we don't want to change, like status ...
$defaultFields = array_intersect_key($defaultFields, array_flip(['email', 'first_name', 'last_name', 'subscribed_ip']));
// if some required default fields are missing, set their values
$defaultFields = Subscriber::setRequiredFieldsDefaultValues($defaultFields);
$this->requiredCustomFieldValidator->validate($customFields);
// add subscriber
$newSubscriber = Subscriber::create();
$newSubscriber->hydrate($defaultFields);
$newSubscriber = Source::setSource($newSubscriber, Source::API);
$newSubscriber->save();
if ($newSubscriber->getErrors() !== false) {
throw new APIException(
__(sprintf('Failed to add subscriber: %s', strtolower(implode(', ', $newSubscriber->getErrors()))), 'mailpoet'),
APIException::FAILED_TO_SAVE_SUBSCRIBER
);
}
if (!empty($customFields)) {
$newSubscriber->saveCustomFields($customFields);
}
// reload subscriber to get the saved status/created|updated|delete dates/other fields
$newSubscriber = Subscriber::findOne($newSubscriber->id);
// subscribe to segments and optionally: 1) send confirmation email, 2) schedule welcome email(s)
if (!empty($listIds)) {
$this->subscribeToLists($newSubscriber->id, $listIds, [
'send_confirmation_email' => $sendConfirmationEmail,
'schedule_welcome_email' => $scheduleWelcomeEmail,
'skip_subscriber_notification' => $skipSubscriberNotification,
]);
}
return $newSubscriber->withCustomFields()->withSubscriptions()->asArray();
}
public function addList(array $list) {
// throw exception when list name is missing
if (empty($list['name'])) {
throw new APIException(
__('List name is required.', 'mailpoet'),
APIException::LIST_NAME_REQUIRED
);
}
// throw exception when list already exists
if (Segment::where('name', $list['name'])->findOne()) {
throw new APIException(
__('This list already exists.', 'mailpoet'),
APIException::LIST_EXISTS
);
}
// filter out all incoming data that we don't want to change, like type,
$list = array_intersect_key($list, array_flip(['name', 'description']));
// add list
$newList = Segment::create();
$newList->hydrate($list);
$newList->save();
if ($newList->getErrors() !== false) {
throw new APIException(
__(sprintf('Failed to add list: %s', strtolower(implode(', ', $newList->getErrors()))), 'mailpoet'),
APIException::FAILED_TO_SAVE_LIST
);
}
// reload list to get the saved created|updated|delete dates/other fields
$newList = Segment::findOne($newList->id);
if (!$newList instanceof Segment) {
throw new APIException(__('Failed to add list', 'mailpoet'), APIException::FAILED_TO_SAVE_LIST);
}
return $newList->asArray();
}
public function getSubscriber($subscriberEmail) {
$subscriber = Subscriber::findOne($subscriberEmail);
// throw exception when subscriber does not exist
if (!$subscriber) {
throw new APIException(__('This subscriber does not exist.', 'mailpoet'), APIException::SUBSCRIBER_NOT_EXISTS);
}
return $subscriber->withCustomFields()->withSubscriptions()->asArray();
}
protected function _sendConfirmationEmail(Subscriber $subscriber) {
$subscriberEntity = $this->subscribersRepository->findOneById($subscriber->id);
if ($subscriberEntity instanceof SubscriberEntity) {
return $this->confirmationEmailMailer->sendConfirmationEmailOnce($subscriberEntity);
}
}
protected function _scheduleWelcomeNotification(Subscriber $subscriber, array $segments) {
$result = $this->welcomeScheduler->scheduleSubscriberWelcomeNotification($subscriber->id, $segments);
if (is_array($result)) {
foreach ($result as $queue) {
if ($queue instanceof Sending && $queue->getErrors()) {
throw new APIException(
__(sprintf('Subscriber added, but welcome email failed to send: %s', strtolower(implode(', ', $queue->getErrors()))), 'mailpoet'),
APIException::WELCOME_FAILED_TO_SEND
);
}
}
}
return $result;
}
private function sendSubscriberNotification(Subscriber $subscriber, array $segmentIds) {
$this->newSubscriberNotificationMailer->send($subscriber, Segment::whereIn('id', $segmentIds)->findMany());
}
}