Add ability to duplicate dynamic segments
MAILPOET-4635
This commit is contained in:
committed by
Aschepikov
parent
c0ffcbac9b
commit
e636537580
@ -4,6 +4,7 @@ import ReactStringReplace from 'react-string-replace';
|
||||
|
||||
import { MailPoet } from 'mailpoet';
|
||||
import { Listing } from 'listing/listing.jsx';
|
||||
import { escapeHTML } from '@wordpress/escape-html';
|
||||
|
||||
const columns = [
|
||||
{
|
||||
@ -89,6 +90,36 @@ const itemActions = [
|
||||
),
|
||||
display: (item) => !item.is_plugin_missing,
|
||||
},
|
||||
{
|
||||
name: 'duplicate_segment',
|
||||
className: 'mailpoet-hide-on-mobile',
|
||||
label: MailPoet.I18n.t('duplicate'),
|
||||
onClick: (item, refresh) =>
|
||||
MailPoet.Ajax.post({
|
||||
api_version: window.mailpoet_api_version,
|
||||
endpoint: 'dynamic_segments',
|
||||
action: 'duplicate',
|
||||
data: {
|
||||
id: item.id,
|
||||
},
|
||||
})
|
||||
.done((response) => {
|
||||
MailPoet.Notice.success(
|
||||
MailPoet.I18n.t('segmentDuplicated').replace(
|
||||
'%1$s',
|
||||
escapeHTML(response.data.name),
|
||||
{ scroll: true },
|
||||
),
|
||||
);
|
||||
refresh();
|
||||
})
|
||||
.fail((response) => {
|
||||
MailPoet.Notice.error(
|
||||
response.errors.map((error) => error.message),
|
||||
{ scroll: true },
|
||||
);
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: 'edit_disabled',
|
||||
className: 'mailpoet-hide-on-mobile mailpoet-disabled',
|
||||
|
@ -19,6 +19,7 @@ use MailPoet\Segments\DynamicSegments\SegmentSaveController;
|
||||
use MailPoet\Segments\SegmentsRepository;
|
||||
use MailPoet\Segments\SegmentSubscribersRepository;
|
||||
use MailPoet\UnexpectedValueException;
|
||||
use Throwable;
|
||||
|
||||
class DynamicSegments extends APIEndpoint {
|
||||
|
||||
@ -124,6 +125,29 @@ class DynamicSegments extends APIEndpoint {
|
||||
}
|
||||
}
|
||||
|
||||
public function duplicate($data = []) {
|
||||
$segment = $this->getSegment($data);
|
||||
|
||||
if ($segment instanceof SegmentEntity) {
|
||||
try {
|
||||
$duplicate = $this->saveController->duplicate($segment);
|
||||
} catch (Throwable $e) {
|
||||
return $this->errorResponse([
|
||||
// translators: %s is the error message
|
||||
Error::UNKNOWN => sprintf(__('Duplicating of segment failed: %s', 'mailpoet'), $e->getMessage()),
|
||||
], [], Response::STATUS_UNKNOWN);
|
||||
}
|
||||
return $this->successResponse(
|
||||
$this->segmentsResponseBuilder->build($duplicate),
|
||||
['count' => 1]
|
||||
);
|
||||
} else {
|
||||
return $this->errorResponse([
|
||||
Error::NOT_FOUND => __('This segment does not exist.', 'mailpoet'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
private function getErrorString(InvalidFilterException $e) {
|
||||
switch ($e->getCode()) {
|
||||
case InvalidFilterException::MISSING_TYPE:
|
||||
|
@ -38,6 +38,11 @@ class DynamicSegmentFilterEntity {
|
||||
$this->filterData = $filterData;
|
||||
}
|
||||
|
||||
public function __clone() {
|
||||
$this->id = null;
|
||||
$this->segment = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return SegmentEntity|null
|
||||
*/
|
||||
|
@ -87,6 +87,7 @@ class SegmentEntity {
|
||||
public function __clone() {
|
||||
// reset ID
|
||||
$this->id = null;
|
||||
$this->dynamicFilters = new ArrayCollection();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -6,6 +6,7 @@ use MailPoet\ConflictException;
|
||||
use MailPoet\Entities\SegmentEntity;
|
||||
use MailPoet\NotFoundException;
|
||||
use MailPoet\Segments\SegmentsRepository;
|
||||
use MailPoetVendor\Doctrine\ORM\EntityManager;
|
||||
use MailPoetVendor\Doctrine\ORM\ORMException;
|
||||
|
||||
class SegmentSaveController {
|
||||
@ -15,12 +16,17 @@ class SegmentSaveController {
|
||||
/** @var FilterDataMapper */
|
||||
private $filterDataMapper;
|
||||
|
||||
/** @var EntityManager */
|
||||
private $entityManager;
|
||||
|
||||
public function __construct(
|
||||
SegmentsRepository $segmentsRepository,
|
||||
FilterDataMapper $filterDataMapper
|
||||
FilterDataMapper $filterDataMapper,
|
||||
EntityManager $entityManager
|
||||
) {
|
||||
$this->segmentsRepository = $segmentsRepository;
|
||||
$this->filterDataMapper = $filterDataMapper;
|
||||
$this->entityManager = $entityManager;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -37,4 +43,23 @@ class SegmentSaveController {
|
||||
|
||||
return $this->segmentsRepository->createOrUpdate($name, $description, SegmentEntity::TYPE_DYNAMIC, $filtersData, $id);
|
||||
}
|
||||
|
||||
public function duplicate(SegmentEntity $segmentEntity): SegmentEntity {
|
||||
$duplicate = clone $segmentEntity;
|
||||
// translators: %s is the name of the segment
|
||||
$duplicate->setName(sprintf(__('Copy of %s', 'mailpoet'), $segmentEntity->getName()));
|
||||
$this->segmentsRepository->verifyNameIsUnique($duplicate->getName(), $duplicate->getId());
|
||||
$this->entityManager->wrapInTransaction(function(EntityManager $entityManager) use ($duplicate, $segmentEntity) {
|
||||
foreach ($segmentEntity->getDynamicFilters() as $dynamicFilter) {
|
||||
$duplicateFilter = clone $dynamicFilter;
|
||||
$duplicate->addDynamicFilter($duplicateFilter);
|
||||
$duplicateFilter->setSegment($duplicate);
|
||||
$entityManager->persist($duplicateFilter);
|
||||
}
|
||||
$entityManager->persist($duplicate);
|
||||
$entityManager->flush();
|
||||
});
|
||||
|
||||
return $duplicate;
|
||||
}
|
||||
}
|
||||
|
@ -112,6 +112,39 @@ class SegmentSaveControllerTest extends \MailPoetTest {
|
||||
$this->saveController->save($segmentData);
|
||||
}
|
||||
|
||||
public function testItCanDuplicateExistingSegment(): void {
|
||||
$segment = $this->createSegment('original');
|
||||
$this->addDynamicFilter($segment, ['administrator']);
|
||||
$this->addDynamicFilter($segment, ['editor']);
|
||||
|
||||
$duplicate = $this->saveController->duplicate($segment);
|
||||
expect($duplicate->getId())->notEquals($segment->getId());
|
||||
$filters = $duplicate->getDynamicFilters();
|
||||
expect($filters)->count(2);
|
||||
|
||||
$originalFilter1 = $segment->getDynamicFilters()->get(0);
|
||||
$duplicateFilter1 = $duplicate->getDynamicFilters()->get(0);
|
||||
|
||||
$this->assertInstanceOf(DynamicSegmentFilterEntity::class, $originalFilter1);
|
||||
$this->assertInstanceOf(DynamicSegmentFilterEntity::class, $duplicateFilter1);
|
||||
|
||||
expect($originalFilter1->getId())->notEquals($duplicateFilter1->getId());
|
||||
expect($duplicateFilter1->getFilterData()->getAction())->equals(UserRole::TYPE);
|
||||
expect($duplicateFilter1->getFilterData()->getParam('wordpressRole'))->equals(['administrator']);
|
||||
expect($duplicateFilter1->getFilterData()->getParam('connect'))->equals(DynamicSegmentFilterData::CONNECT_TYPE_AND);
|
||||
|
||||
$originalFilter2 = $segment->getDynamicFilters()->get(1);
|
||||
$duplicateFilter2 = $duplicate->getDynamicFilters()->get(1);
|
||||
|
||||
$this->assertInstanceOf(DynamicSegmentFilterEntity::class, $originalFilter2);
|
||||
$this->assertInstanceOf(DynamicSegmentFilterEntity::class, $duplicateFilter2);
|
||||
|
||||
expect($originalFilter2->getId())->notEquals($duplicateFilter2->getId());
|
||||
expect($duplicateFilter2->getFilterData()->getAction())->equals(UserRole::TYPE);
|
||||
expect($duplicateFilter2->getFilterData()->getParam('wordpressRole'))->equals(['editor']);
|
||||
expect($duplicateFilter2->getFilterData()->getParam('connect'))->equals(DynamicSegmentFilterData::CONNECT_TYPE_AND);
|
||||
}
|
||||
|
||||
private function createSegment(string $name): SegmentEntity {
|
||||
$segment = new SegmentEntity($name, SegmentEntity::TYPE_DYNAMIC, 'description');
|
||||
$this->entityManager->persist($segment);
|
||||
|
@ -64,6 +64,7 @@
|
||||
'multipleSegmentsRestored': __('%1$d lists have been restored from the Trash.'),
|
||||
'duplicate': __('Duplicate'),
|
||||
'listDuplicated': __('List "%1$s" has been duplicated.'),
|
||||
'segmentDuplicated': __('Segment "%1$s" has been duplicated.'),
|
||||
'update': __('Update'),
|
||||
'forceSync': __('Force Sync'),
|
||||
'readMore': __('Read More'),
|
||||
|
Reference in New Issue
Block a user