diff --git a/lib/API/JSON/v1/Newsletters.php b/lib/API/JSON/v1/Newsletters.php index c4d9b004b2..5871594cec 100644 --- a/lib/API/JSON/v1/Newsletters.php +++ b/lib/API/JSON/v1/Newsletters.php @@ -14,7 +14,6 @@ use MailPoet\Entities\NewsletterOptionFieldEntity; use MailPoet\Entities\SendingQueueEntity; use MailPoet\InvalidStateException; use MailPoet\Listing; -use MailPoet\Models\Newsletter; use MailPoet\Newsletter\Listing\NewsletterListingRepository; use MailPoet\Newsletter\NewsletterSaveController; use MailPoet\Newsletter\NewslettersRepository; @@ -255,27 +254,15 @@ class Newsletters extends APIEndpoint { } public function duplicate($data = []) { - $id = (isset($data['id']) ? (int)$data['id'] : false); - $newsletter = Newsletter::findOne($id); + $newsletter = $this->getNewsletter($data); - if ($newsletter instanceof Newsletter) { - $data = [ - 'subject' => sprintf(__('Copy of %s', 'mailpoet'), $newsletter->subject), - ]; - $duplicate = $newsletter->duplicate($data); - $errors = $duplicate->getErrors(); - - if (!empty($errors)) { - return $this->errorResponse($errors); - } else { - $this->wp->doAction('mailpoet_api_newsletters_duplicate_after', $newsletter, $duplicate); - $duplicate = Newsletter::findOne($duplicate->id); - if(!$duplicate instanceof Newsletter) return $this->errorResponse(); - return $this->successResponse( - $duplicate->asArray(), - ['count' => 1] - ); - } + 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'), diff --git a/lib/Entities/NewsletterEntity.php b/lib/Entities/NewsletterEntity.php index 2ada2dd14f..ea646bcb04 100644 --- a/lib/Entities/NewsletterEntity.php +++ b/lib/Entities/NewsletterEntity.php @@ -2,6 +2,7 @@ namespace MailPoet\Entities; +use DateTimeImmutable; use DateTimeInterface; use MailPoet\Doctrine\EntityTraits\AutoincrementedIdTrait; use MailPoet\Doctrine\EntityTraits\CreatedAtTrait; @@ -157,6 +158,15 @@ class NewsletterEntity { $this->queues = new ArrayCollection(); } + public function __clone() { + // reset ID + $this->id = null; + // reset timestamps + $this->setCreatedAt(new DateTimeImmutable()); + $this->setUpdatedAt(new DateTimeImmutable()); + $this->setDeletedAt(null); + } + /** * @return string|null */ diff --git a/lib/Newsletter/NewsletterSaveController.php b/lib/Newsletter/NewsletterSaveController.php index 87abea81f2..8dd702c66a 100644 --- a/lib/Newsletter/NewsletterSaveController.php +++ b/lib/Newsletter/NewsletterSaveController.php @@ -5,6 +5,7 @@ namespace MailPoet\Newsletter; use MailPoet\Cron\Workers\SendingQueue\Tasks\Newsletter as NewsletterQueueTask; use MailPoet\Entities\NewsletterEntity; use MailPoet\Entities\NewsletterOptionEntity; +use MailPoet\Entities\NewsletterOptionFieldEntity; use MailPoet\Entities\NewsletterSegmentEntity; use MailPoet\Entities\ScheduledTaskEntity; use MailPoet\Entities\SegmentEntity; @@ -135,6 +136,56 @@ class NewsletterSaveController { return $newsletter; } + public function duplicate(NewsletterEntity $newsletter): NewsletterEntity { + $duplicate = clone $newsletter; + + $duplicate->setSubject(sprintf(__('Copy of %s', 'mailpoet'), $newsletter->getSubject())); + // generate new unsubscribe token + $duplicate->setUnsubscribeToken($this->security->generateUnsubscribeTokenByEntity($duplicate)); + // reset status + $duplicate->setStatus(NewsletterEntity::STATUS_DRAFT); + // reset hash + $duplicate->setHash(null); + // reset sent at date + $duplicate->setSentAt(null); + + $this->newslettersRepository->persist($duplicate); + $this->newslettersRepository->flush(); + + // create relationships between duplicate and segments + foreach ($newsletter->getNewsletterSegments() as $newsletterSegment) { + $segment = $newsletterSegment->getSegment(); + if (!$segment) { + continue; + } + $duplicateSegment = new NewsletterSegmentEntity($duplicate, $segment); + $duplicate->getNewsletterSegments()->add($duplicateSegment); + $this->newsletterSegmentRepository->persist($duplicateSegment); + } + + // duplicate options + $ignoredOptions = [ + NewsletterOptionFieldEntity::NAME_IS_SCHEDULED, + NewsletterOptionFieldEntity::NAME_SCHEDULED_AT, + ]; + foreach ($newsletter->getOptions() as $newsletterOption) { + $optionField = $newsletterOption->getOptionField(); + if (!$optionField) { + continue; + } + if (in_array($optionField->getName(), $ignoredOptions, true)) { + continue; + } + $duplicateOption = new NewsletterOptionEntity($duplicate, $optionField); + $duplicateOption->setValue($newsletterOption->getValue()); + $duplicate->getOptions()->add($duplicateOption); + $this->newsletterOptionsRepository->persist($duplicateOption); + } + $this->newslettersRepository->flush(); + + return $duplicate; + } + private function getNewsletter(array $data): NewsletterEntity { if (!isset($data['id'])) { throw new UnexpectedValueException(); diff --git a/tests/integration/API/JSON/v1/NewslettersTest.php b/tests/integration/API/JSON/v1/NewslettersTest.php index 3b771a9d02..c0a800858d 100644 --- a/tests/integration/API/JSON/v1/NewslettersTest.php +++ b/tests/integration/API/JSON/v1/NewslettersTest.php @@ -11,6 +11,7 @@ use MailPoet\API\JSON\ResponseBuilders\NewslettersResponseBuilder; use MailPoet\API\JSON\v1\Newsletters; use MailPoet\Cron\CronHelper; use MailPoet\DI\ContainerWrapper; +use MailPoet\Entities\NewsletterEntity; use MailPoet\Listing\Handler; use MailPoet\Models\Newsletter; use MailPoet\Models\NewsletterOption; @@ -379,7 +380,7 @@ class NewslettersTest extends \MailPoetTest { $hookName = 'mailpoet_api_newsletters_duplicate_after'; expect(WPHooksHelper::isActionDone($hookName))->true(); - expect(WPHooksHelper::getActionDone($hookName)[0] instanceof Newsletter)->true(); + expect(WPHooksHelper::getActionDone($hookName)[0] instanceof NewsletterEntity)->true(); $response = $this->endpoint->duplicate(['id' => $this->postNotification->id]); expect($response->status)->equals(APIResponse::STATUS_OK);