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 { MailPoet } from 'mailpoet';
|
||||||
import { Listing } from 'listing/listing.jsx';
|
import { Listing } from 'listing/listing.jsx';
|
||||||
|
import { escapeHTML } from '@wordpress/escape-html';
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
@ -89,6 +90,36 @@ const itemActions = [
|
|||||||
),
|
),
|
||||||
display: (item) => !item.is_plugin_missing,
|
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',
|
name: 'edit_disabled',
|
||||||
className: 'mailpoet-hide-on-mobile mailpoet-disabled',
|
className: 'mailpoet-hide-on-mobile mailpoet-disabled',
|
||||||
|
@ -19,6 +19,7 @@ use MailPoet\Segments\DynamicSegments\SegmentSaveController;
|
|||||||
use MailPoet\Segments\SegmentsRepository;
|
use MailPoet\Segments\SegmentsRepository;
|
||||||
use MailPoet\Segments\SegmentSubscribersRepository;
|
use MailPoet\Segments\SegmentSubscribersRepository;
|
||||||
use MailPoet\UnexpectedValueException;
|
use MailPoet\UnexpectedValueException;
|
||||||
|
use Throwable;
|
||||||
|
|
||||||
class DynamicSegments extends APIEndpoint {
|
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) {
|
private function getErrorString(InvalidFilterException $e) {
|
||||||
switch ($e->getCode()) {
|
switch ($e->getCode()) {
|
||||||
case InvalidFilterException::MISSING_TYPE:
|
case InvalidFilterException::MISSING_TYPE:
|
||||||
|
@ -38,6 +38,11 @@ class DynamicSegmentFilterEntity {
|
|||||||
$this->filterData = $filterData;
|
$this->filterData = $filterData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function __clone() {
|
||||||
|
$this->id = null;
|
||||||
|
$this->segment = null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return SegmentEntity|null
|
* @return SegmentEntity|null
|
||||||
*/
|
*/
|
||||||
|
@ -87,6 +87,7 @@ class SegmentEntity {
|
|||||||
public function __clone() {
|
public function __clone() {
|
||||||
// reset ID
|
// reset ID
|
||||||
$this->id = null;
|
$this->id = null;
|
||||||
|
$this->dynamicFilters = new ArrayCollection();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -6,6 +6,7 @@ use MailPoet\ConflictException;
|
|||||||
use MailPoet\Entities\SegmentEntity;
|
use MailPoet\Entities\SegmentEntity;
|
||||||
use MailPoet\NotFoundException;
|
use MailPoet\NotFoundException;
|
||||||
use MailPoet\Segments\SegmentsRepository;
|
use MailPoet\Segments\SegmentsRepository;
|
||||||
|
use MailPoetVendor\Doctrine\ORM\EntityManager;
|
||||||
use MailPoetVendor\Doctrine\ORM\ORMException;
|
use MailPoetVendor\Doctrine\ORM\ORMException;
|
||||||
|
|
||||||
class SegmentSaveController {
|
class SegmentSaveController {
|
||||||
@ -15,12 +16,17 @@ class SegmentSaveController {
|
|||||||
/** @var FilterDataMapper */
|
/** @var FilterDataMapper */
|
||||||
private $filterDataMapper;
|
private $filterDataMapper;
|
||||||
|
|
||||||
|
/** @var EntityManager */
|
||||||
|
private $entityManager;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
SegmentsRepository $segmentsRepository,
|
SegmentsRepository $segmentsRepository,
|
||||||
FilterDataMapper $filterDataMapper
|
FilterDataMapper $filterDataMapper,
|
||||||
|
EntityManager $entityManager
|
||||||
) {
|
) {
|
||||||
$this->segmentsRepository = $segmentsRepository;
|
$this->segmentsRepository = $segmentsRepository;
|
||||||
$this->filterDataMapper = $filterDataMapper;
|
$this->filterDataMapper = $filterDataMapper;
|
||||||
|
$this->entityManager = $entityManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -37,4 +43,23 @@ class SegmentSaveController {
|
|||||||
|
|
||||||
return $this->segmentsRepository->createOrUpdate($name, $description, SegmentEntity::TYPE_DYNAMIC, $filtersData, $id);
|
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);
|
$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 {
|
private function createSegment(string $name): SegmentEntity {
|
||||||
$segment = new SegmentEntity($name, SegmentEntity::TYPE_DYNAMIC, 'description');
|
$segment = new SegmentEntity($name, SegmentEntity::TYPE_DYNAMIC, 'description');
|
||||||
$this->entityManager->persist($segment);
|
$this->entityManager->persist($segment);
|
||||||
|
@ -64,6 +64,7 @@
|
|||||||
'multipleSegmentsRestored': __('%1$d lists have been restored from the Trash.'),
|
'multipleSegmentsRestored': __('%1$d lists have been restored from the Trash.'),
|
||||||
'duplicate': __('Duplicate'),
|
'duplicate': __('Duplicate'),
|
||||||
'listDuplicated': __('List "%1$s" has been duplicated.'),
|
'listDuplicated': __('List "%1$s" has been duplicated.'),
|
||||||
|
'segmentDuplicated': __('Segment "%1$s" has been duplicated.'),
|
||||||
'update': __('Update'),
|
'update': __('Update'),
|
||||||
'forceSync': __('Force Sync'),
|
'forceSync': __('Force Sync'),
|
||||||
'readMore': __('Read More'),
|
'readMore': __('Read More'),
|
||||||
|
Reference in New Issue
Block a user