Remove old dynamic segments code
[MAILPOET-3484]
This commit is contained in:
@@ -130,9 +130,6 @@ class ContainerConfigurator implements IContainerConfigurator {
|
||||
->setFactory([new Reference(\MailPoet\Doctrine\Validator\ValidatorFactory::class), 'createValidator']);
|
||||
$container->autowire(\MailPoet\PostEditorBlocks\PostEditorBlock::class);
|
||||
$container->autowire(\MailPoet\PostEditorBlocks\SubscriptionFormBlock::class);
|
||||
// Dynamic segments
|
||||
$container->autowire(\MailPoet\DynamicSegments\Mappers\DBMapper::class);
|
||||
$container->autowire(\MailPoet\DynamicSegments\Persistence\Loading\SingleSegmentLoader::class)->setPublic(true);
|
||||
// Cron
|
||||
$container->autowire(\MailPoet\Cron\CronHelper::class)->setPublic(true);
|
||||
$container->autowire(\MailPoet\Cron\CronTrigger::class)->setPublic(true);
|
||||
|
@@ -1,7 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace MailPoet\DynamicSegments\Exceptions;
|
||||
|
||||
class ErrorSavingException extends \Exception {
|
||||
|
||||
}
|
@@ -1,15 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace MailPoet\DynamicSegments\Exceptions;
|
||||
|
||||
class InvalidSegmentTypeException extends \Exception {
|
||||
|
||||
const MISSING_TYPE = 1;
|
||||
const INVALID_TYPE = 2;
|
||||
const MISSING_ROLE = 3;
|
||||
const MISSING_ACTION = 4;
|
||||
const MISSING_NEWSLETTER_ID = 5;
|
||||
const MISSING_CATEGORY_ID = 6;
|
||||
const MISSING_PRODUCT_ID = 7;
|
||||
|
||||
};
|
@@ -1,110 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace MailPoet\DynamicSegments\Filters;
|
||||
|
||||
use MailPoet\Models\StatisticsClicks;
|
||||
use MailPoet\Models\StatisticsNewsletters;
|
||||
use MailPoet\Models\StatisticsOpens;
|
||||
use MailPoet\Models\Subscriber;
|
||||
use MailPoetVendor\Idiorm\ORM;
|
||||
|
||||
class EmailAction implements Filter {
|
||||
|
||||
const SEGMENT_TYPE = 'email';
|
||||
|
||||
const ACTION_OPENED = 'opened';
|
||||
const ACTION_NOT_OPENED = 'notOpened';
|
||||
const ACTION_CLICKED = 'clicked';
|
||||
const ACTION_NOT_CLICKED = 'notClicked';
|
||||
|
||||
private static $allowedActions = [
|
||||
EmailAction::ACTION_OPENED,
|
||||
EmailAction::ACTION_NOT_OPENED,
|
||||
EmailAction::ACTION_CLICKED,
|
||||
EmailAction::ACTION_NOT_CLICKED,
|
||||
];
|
||||
|
||||
/** @var int */
|
||||
private $newsletterId;
|
||||
|
||||
/** @var int */
|
||||
private $linkId;
|
||||
|
||||
/** @var string */
|
||||
private $action;
|
||||
|
||||
/** @var string|null */
|
||||
private $connect;
|
||||
|
||||
/**
|
||||
* @param int $newsletterId
|
||||
* @param int $linkId
|
||||
* @param string $action
|
||||
* @param string|null $connect
|
||||
*/
|
||||
public function __construct($action, $newsletterId, $linkId = null, $connect = null) {
|
||||
$this->newsletterId = (int)$newsletterId;
|
||||
if ($linkId) {
|
||||
$this->linkId = (int)$linkId;
|
||||
}
|
||||
$this->setAction($action);
|
||||
$this->connect = $connect;
|
||||
}
|
||||
|
||||
private function setAction($action) {
|
||||
if (!in_array($action, EmailAction::$allowedActions)) {
|
||||
throw new \InvalidArgumentException("Unknown action " . $action);
|
||||
}
|
||||
$this->action = $action;
|
||||
}
|
||||
|
||||
public function toSql(ORM $orm) {
|
||||
if (($this->action === EmailAction::ACTION_CLICKED) || ($this->action === EmailAction::ACTION_NOT_CLICKED)) {
|
||||
$table = StatisticsClicks::$_table;
|
||||
} else {
|
||||
$table = StatisticsOpens::$_table;
|
||||
}
|
||||
|
||||
if (($this->action === EmailAction::ACTION_NOT_CLICKED) || ($this->action === EmailAction::ACTION_NOT_OPENED)) {
|
||||
$orm->rawJoin(
|
||||
'INNER JOIN ' . StatisticsNewsletters::$_table,
|
||||
'statssent.subscriber_id = ' . Subscriber::$_table . '.id AND statssent.newsletter_id = ' . $this->newsletterId,
|
||||
'statssent'
|
||||
);
|
||||
$orm->rawJoin(
|
||||
'LEFT JOIN ' . $table,
|
||||
$this->createNotStatsJoin(),
|
||||
'stats'
|
||||
);
|
||||
$orm->whereNull('stats.id');
|
||||
} else {
|
||||
$orm->rawJoin(
|
||||
'INNER JOIN ' . $table,
|
||||
'stats.subscriber_id = ' . Subscriber::$_table . '.id AND stats.newsletter_id = ' . $this->newsletterId,
|
||||
'stats'
|
||||
);
|
||||
}
|
||||
if (($this->action === EmailAction::ACTION_CLICKED) && ($this->linkId)) {
|
||||
$orm->where('stats.link_id', $this->linkId);
|
||||
}
|
||||
return $orm;
|
||||
}
|
||||
|
||||
private function createNotStatsJoin() {
|
||||
$clause = 'statssent.subscriber_id = stats.subscriber_id AND stats.newsletter_id = ' . $this->newsletterId;
|
||||
if (($this->action === EmailAction::ACTION_NOT_CLICKED) && ($this->linkId)) {
|
||||
$clause .= ' AND stats.link_id = ' . $this->linkId;
|
||||
}
|
||||
return $clause;
|
||||
}
|
||||
|
||||
public function toArray() {
|
||||
return [
|
||||
'action' => $this->action,
|
||||
'newsletter_id' => $this->newsletterId,
|
||||
'link_id' => $this->linkId,
|
||||
'connect' => $this->connect,
|
||||
'segmentType' => EmailAction::SEGMENT_TYPE,
|
||||
];
|
||||
}
|
||||
}
|
@@ -1,11 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace MailPoet\DynamicSegments\Filters;
|
||||
|
||||
use MailPoetVendor\Idiorm\ORM;
|
||||
|
||||
interface Filter {
|
||||
public function toSql(ORM $orm);
|
||||
|
||||
public function toArray();
|
||||
}
|
@@ -1,46 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace MailPoet\DynamicSegments\Filters;
|
||||
|
||||
use MailPoetVendor\Idiorm\ORM;
|
||||
|
||||
class UserRole implements Filter {
|
||||
|
||||
const SEGMENT_TYPE = 'userRole';
|
||||
|
||||
/** @var string */
|
||||
private $role;
|
||||
|
||||
/** @var string|null */
|
||||
private $connect;
|
||||
|
||||
/**
|
||||
* @param string $role
|
||||
* @param string|null $connect
|
||||
*/
|
||||
public function __construct($role, $connect = null) {
|
||||
$this->role = $role;
|
||||
$this->connect = $connect;
|
||||
}
|
||||
|
||||
public function toSql(ORM $orm) {
|
||||
global $wpdb;
|
||||
$orm->join($wpdb->users, ['wpusers.id', '=', MP_SUBSCRIBERS_TABLE . '.wp_user_id'], 'wpusers')
|
||||
->join($wpdb->usermeta, ['wpusers.ID', '=', 'wpusermeta.user_id'], 'wpusermeta')
|
||||
->whereEqual('wpusermeta.meta_key', $wpdb->prefix . 'capabilities')
|
||||
->whereLike('wpusermeta.meta_value', '%"' . $this->role . '"%');
|
||||
return $orm;
|
||||
}
|
||||
|
||||
public function toArray() {
|
||||
return [
|
||||
'wordpressRole' => $this->role,
|
||||
'connect' => $this->connect,
|
||||
'segmentType' => UserRole::SEGMENT_TYPE,
|
||||
];
|
||||
}
|
||||
|
||||
public function getRole() {
|
||||
return $this->role;
|
||||
}
|
||||
}
|
@@ -1,80 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace MailPoet\DynamicSegments\Filters;
|
||||
|
||||
use MailPoet\Models\Subscriber;
|
||||
use MailPoet\WP\Functions as WPFunctions;
|
||||
use MailPoetVendor\Idiorm\ORM;
|
||||
|
||||
class WooCommerceCategory implements Filter {
|
||||
|
||||
const SEGMENT_TYPE = 'woocommerce';
|
||||
|
||||
const ACTION_CATEGORY = 'purchasedCategory';
|
||||
|
||||
/** @var int */
|
||||
private $categoryId;
|
||||
|
||||
/** @var string|null */
|
||||
private $connect;
|
||||
|
||||
/**
|
||||
* @param int $categoryId
|
||||
* @param string|null $connect
|
||||
*/
|
||||
public function __construct($categoryId, $connect = null) {
|
||||
$this->categoryId = (int)$categoryId;
|
||||
$this->connect = $connect;
|
||||
}
|
||||
|
||||
public function toSql(ORM $orm) {
|
||||
global $wpdb;
|
||||
$orm->distinct();
|
||||
$orm->rawJoin(
|
||||
'INNER JOIN ' . $wpdb->postmeta,
|
||||
"postmeta.meta_key = '_customer_user' AND " . Subscriber::$_table . '.wp_user_id=postmeta.meta_value',
|
||||
'postmeta'
|
||||
);
|
||||
$orm->join($wpdb->prefix . 'woocommerce_order_items', ['postmeta.post_id', '=', 'items.order_id'], 'items');
|
||||
$orm->rawJoin(
|
||||
'INNER JOIN ' . $wpdb->prefix . 'woocommerce_order_itemmeta',
|
||||
"itemmeta.order_item_id = items.order_item_id AND itemmeta.meta_key = '_product_id'",
|
||||
'itemmeta'
|
||||
);
|
||||
$orm->join($wpdb->term_relationships, ['itemmeta.meta_value', '=', 'term_relationships.object_id'], 'term_relationships'); // phpcs:ignore Squiz.NamingConventions.ValidVariableName.NotCamelCaps
|
||||
$orm->rawJoin(
|
||||
'INNER JOIN ' . $wpdb->term_taxonomy, // phpcs:ignore Squiz.NamingConventions.ValidVariableName.NotCamelCaps
|
||||
'
|
||||
term_taxonomy.term_taxonomy_id=term_relationships.term_taxonomy_id
|
||||
AND
|
||||
term_taxonomy.term_id IN (' . join(',', $this->getAllCategoryIds()) . ')',
|
||||
'term_taxonomy'
|
||||
);
|
||||
$orm->where('status', Subscriber::STATUS_SUBSCRIBED);
|
||||
$orm->whereRaw(
|
||||
'postmeta.post_id NOT IN (
|
||||
SELECT id FROM ' . $wpdb->posts . ' as p WHERE p.post_status IN ("wc-cancelled", "wc-failed")
|
||||
)'
|
||||
);
|
||||
return $orm;
|
||||
}
|
||||
|
||||
private function getAllCategoryIds() {
|
||||
$subcategories = WPFunctions::get()->getTerms('product_cat', ['child_of' => $this->categoryId]);
|
||||
if (!is_array($subcategories)) return [];
|
||||
$ids = array_map(function($category) {
|
||||
return $category->term_id; // phpcs:ignore Squiz.NamingConventions.ValidVariableName.NotCamelCaps
|
||||
}, $subcategories);
|
||||
$ids[] = $this->categoryId;
|
||||
return $ids;
|
||||
}
|
||||
|
||||
public function toArray() {
|
||||
return [
|
||||
'action' => WooCommerceCategory::ACTION_CATEGORY,
|
||||
'category_id' => $this->categoryId,
|
||||
'connect' => $this->connect,
|
||||
'segmentType' => WooCommerceCategory::SEGMENT_TYPE,
|
||||
];
|
||||
}
|
||||
}
|
@@ -1,62 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace MailPoet\DynamicSegments\Filters;
|
||||
|
||||
use MailPoet\Models\Subscriber;
|
||||
use MailPoetVendor\Idiorm\ORM;
|
||||
|
||||
class WooCommerceProduct implements Filter {
|
||||
|
||||
const SEGMENT_TYPE = 'woocommerce';
|
||||
|
||||
const ACTION_PRODUCT = 'purchasedProduct';
|
||||
|
||||
/** @var int */
|
||||
private $productId;
|
||||
|
||||
/** @var string|null */
|
||||
private $connect;
|
||||
|
||||
/**
|
||||
* @param int $productId
|
||||
* @param string|null $connect
|
||||
*/
|
||||
public function __construct($productId, $connect = null) {
|
||||
$this->productId = (int)$productId;
|
||||
$this->connect = $connect;
|
||||
}
|
||||
|
||||
public function toSql(ORM $orm) {
|
||||
global $wpdb;
|
||||
$orm->distinct();
|
||||
$orm->rawJoin(
|
||||
'INNER JOIN ' . $wpdb->postmeta,
|
||||
"postmeta.meta_key = '_customer_user' AND " . Subscriber::$_table . '.wp_user_id=postmeta.meta_value',
|
||||
'postmeta'
|
||||
);
|
||||
$orm->join($wpdb->prefix . 'woocommerce_order_items', ['postmeta.post_id', '=', 'items.order_id'], 'items');
|
||||
$orm->rawJoin(
|
||||
'INNER JOIN ' . $wpdb->prefix . 'woocommerce_order_itemmeta',
|
||||
"itemmeta.order_item_id=items.order_item_id
|
||||
AND itemmeta.meta_key='_product_id'
|
||||
AND itemmeta.meta_value=" . $this->productId,
|
||||
'itemmeta'
|
||||
);
|
||||
$orm->where('status', Subscriber::STATUS_SUBSCRIBED);
|
||||
$orm->whereRaw(
|
||||
'postmeta.post_id NOT IN (
|
||||
SELECT id FROM ' . $wpdb->posts . ' as p WHERE p.post_status IN ("wc-cancelled", "wc-failed")
|
||||
)'
|
||||
);
|
||||
return $orm;
|
||||
}
|
||||
|
||||
public function toArray() {
|
||||
return [
|
||||
'action' => WooCommerceProduct::ACTION_PRODUCT,
|
||||
'product_id' => $this->productId,
|
||||
'connect' => $this->connect,
|
||||
'segmentType' => WooCommerceProduct::SEGMENT_TYPE,
|
||||
];
|
||||
}
|
||||
}
|
@@ -1,84 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace MailPoet\DynamicSegments\Mappers;
|
||||
|
||||
use MailPoet\DynamicSegments\Exceptions\InvalidSegmentTypeException;
|
||||
use MailPoet\DynamicSegments\Filters\EmailAction;
|
||||
use MailPoet\DynamicSegments\Filters\Filter;
|
||||
use MailPoet\DynamicSegments\Filters\UserRole;
|
||||
use MailPoet\DynamicSegments\Filters\WooCommerceCategory;
|
||||
use MailPoet\DynamicSegments\Filters\WooCommerceProduct;
|
||||
use MailPoet\Models\DynamicSegment;
|
||||
use MailPoet\Models\DynamicSegmentFilter;
|
||||
|
||||
class DBMapper {
|
||||
/**
|
||||
* @param DynamicSegment $segmentData
|
||||
* @param DynamicSegmentFilter[] $filtersData
|
||||
*
|
||||
* @return DynamicSegment
|
||||
*/
|
||||
public function mapSegment(DynamicSegment $segmentData, array $filtersData) {
|
||||
$filters = $this->getFilters($segmentData->id, $filtersData);
|
||||
$segmentData->setFilters($filters);
|
||||
return $segmentData;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param DynamicSegment[] $segmentsData
|
||||
* @param DynamicSegmentFilter[] $filtersData
|
||||
*
|
||||
* @return DynamicSegment[]
|
||||
*/
|
||||
public function mapSegments(array $segmentsData, array $filtersData) {
|
||||
$result = [];
|
||||
foreach ($segmentsData as $segmentData) {
|
||||
$result[] = $this->mapSegment($segmentData, $filtersData);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function getFilters($segmentId, $allFilters) {
|
||||
$result = [];
|
||||
foreach ($allFilters as $filter) {
|
||||
if ($filter->segmentId === $segmentId) {
|
||||
$result[] = $this->createFilter($filter->filterData);
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
* @return Filter
|
||||
* @throws InvalidSegmentTypeException
|
||||
*/
|
||||
private function createFilter(array $data) {
|
||||
switch ($this->getSegmentType($data)) {
|
||||
case 'userRole':
|
||||
if (!$data['wordpressRole']) throw new InvalidSegmentTypeException('Missing role');
|
||||
return new UserRole($data['wordpressRole'], 'and');
|
||||
case 'email':
|
||||
return new EmailAction($data['action'], $data['newsletter_id'], $data['link_id']);
|
||||
case 'woocommerce':
|
||||
if ($data['action'] === WooCommerceProduct::ACTION_PRODUCT) {
|
||||
return new WooCommerceProduct($data['product_id']);
|
||||
}
|
||||
return new WooCommerceCategory($data['category_id']);
|
||||
default:
|
||||
throw new InvalidSegmentTypeException('Invalid type');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
* @return string
|
||||
* @throws InvalidSegmentTypeException
|
||||
*/
|
||||
private function getSegmentType(array $data) {
|
||||
if (!isset($data['segmentType'])) {
|
||||
throw new InvalidSegmentTypeException('Segment type is not set');
|
||||
}
|
||||
return $data['segmentType'];
|
||||
}
|
||||
}
|
@@ -1,32 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace MailPoet\DynamicSegments\Persistence\Loading;
|
||||
|
||||
use MailPoet\DynamicSegments\Mappers\DBMapper;
|
||||
use MailPoet\Models\DynamicSegment;
|
||||
|
||||
class SingleSegmentLoader {
|
||||
|
||||
/** @var DBMapper */
|
||||
private $mapper;
|
||||
|
||||
public function __construct(DBMapper $mapper) {
|
||||
$this->mapper = $mapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|int $segmentId
|
||||
* @return DynamicSegment
|
||||
*/
|
||||
public function load($segmentId) {
|
||||
|
||||
$segment = DynamicSegment::findOne($segmentId);
|
||||
if (!$segment instanceof DynamicSegment) {
|
||||
throw new \InvalidArgumentException('Segment not found');
|
||||
}
|
||||
|
||||
$filters = $segment->dynamicSegmentFilters()->findMany();
|
||||
|
||||
return $this->mapper->mapSegment($segment, $filters);
|
||||
}
|
||||
}
|
@@ -1,44 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace MailPoet\DynamicSegments;
|
||||
|
||||
use MailPoet\DynamicSegments\Filters\Filter;
|
||||
use MailPoet\Models\DynamicSegment;
|
||||
use MailPoet\WooCommerce\Helper;
|
||||
|
||||
class RequirementsChecker {
|
||||
|
||||
/** @var Helper */
|
||||
private $woocommerceHelper;
|
||||
|
||||
public function __construct(Helper $woocommerceHelper = null) {
|
||||
if (!$woocommerceHelper) {
|
||||
$woocommerceHelper = new Helper();
|
||||
}
|
||||
$this->woocommerceHelper = $woocommerceHelper;
|
||||
}
|
||||
|
||||
public function shouldSkipSegment(DynamicSegment $segment) {
|
||||
foreach ($segment->getFilters() as $filter) {
|
||||
if ($this->shouldSkipFilter($filter)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private function shouldSkipFilter(Filter $filter) {
|
||||
if ($this->woocommerceHelper->isWooCommerceActive()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$className = get_class($filter);
|
||||
$ref = new \ReflectionClass($className);
|
||||
$constants = $ref->getConstants() ?? [];
|
||||
if (!array_key_exists('SEGMENT_TYPE', $constants)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $constants['SEGMENT_TYPE'] === 'woocommerce';
|
||||
}
|
||||
}
|
@@ -2,7 +2,6 @@
|
||||
|
||||
namespace MailPoet\Models;
|
||||
|
||||
use MailPoet\DynamicSegments\Filters\Filter;
|
||||
use MailPoet\Entities\SegmentEntity;
|
||||
use MailPoet\Models\Segment as MailPoetSegment;
|
||||
use MailPoet\WP\Functions as WPFunctions;
|
||||
@@ -19,23 +18,6 @@ class DynamicSegment extends MailPoetSegment {
|
||||
|
||||
const TYPE_DYNAMIC = SegmentEntity::TYPE_DYNAMIC;
|
||||
|
||||
/** @var Filter[] */
|
||||
private $filters = [];
|
||||
|
||||
/**
|
||||
* @return Filter[]
|
||||
*/
|
||||
public function getFilters() {
|
||||
return $this->filters;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Filter[] $filters
|
||||
*/
|
||||
public function setFilters(array $filters) {
|
||||
$this->filters = $filters;
|
||||
}
|
||||
|
||||
public function save() {
|
||||
$this->set('type', DynamicSegment::TYPE_DYNAMIC);
|
||||
return parent::save();
|
||||
|
@@ -1,34 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace MailPoet\Models;
|
||||
|
||||
use MailPoet\DynamicSegments\Mappers\DBMapper;
|
||||
use MailPoet\DynamicSegments\Persistence\Loading\SingleSegmentLoader;
|
||||
use MailPoet\DynamicSegments\RequirementsChecker;
|
||||
use MailPoet\WooCommerce\Helper as WooCommerceHelper;
|
||||
|
||||
class SubscribersInDynamicSegment extends Subscriber {
|
||||
public static function listingQuery(array $data = []) {
|
||||
$query = self::select(self::$_table . '.*');
|
||||
$singleSegmentLoader = new SingleSegmentLoader(new DBMapper());
|
||||
$dynamicSegment = $singleSegmentLoader->load($data['filter']['segment']);
|
||||
if (self::shouldSkip($dynamicSegment)) {
|
||||
return $query->whereRaw('0=1');
|
||||
}
|
||||
foreach ($dynamicSegment->getFilters() as $filter) {
|
||||
$query = $filter->toSql($query);
|
||||
}
|
||||
if (isset($data['group'])) {
|
||||
$query->filter('groupBy', $data['group']);
|
||||
}
|
||||
if (isset($data['search']) && $data['search']) {
|
||||
$query->filter('search', $data['search']);
|
||||
}
|
||||
return $query;
|
||||
}
|
||||
|
||||
private static function shouldSkip($dynamicSegment) {
|
||||
$requirementsChecker = new RequirementsChecker(new WooCommerceHelper());
|
||||
return $requirementsChecker->shouldSkipSegment($dynamicSegment);
|
||||
}
|
||||
}
|
@@ -1,53 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace MailPoet\Segments;
|
||||
|
||||
use MailPoet\DI\ContainerWrapper;
|
||||
use MailPoet\Entities\SegmentEntity;
|
||||
use MailPoet\Listing\BulkActionController;
|
||||
use MailPoet\Models\Segment;
|
||||
|
||||
class BulkAction {
|
||||
/** @var BulkActionController */
|
||||
private $actionsController;
|
||||
|
||||
/** @var array */
|
||||
private $data;
|
||||
|
||||
public function __construct(array $data) {
|
||||
$this->data = $data;
|
||||
$this->actionsController = ContainerWrapper::getInstance()->get(BulkActionController::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function apply() {
|
||||
if (!isset($this->data['listing']['filter']['segment'])) {
|
||||
throw new \InvalidArgumentException('Missing segment id');
|
||||
}
|
||||
$segment = Segment::findOne($this->data['listing']['filter']['segment']);
|
||||
if ($segment instanceof Segment) {
|
||||
$segment = $segment->asArray();
|
||||
}
|
||||
return $this->applySegment($segment);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array|bool $segment
|
||||
*
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function applySegment($segment) {
|
||||
if (is_bool($segment)
|
||||
|| in_array($segment['type'], [SegmentEntity::TYPE_DEFAULT, SegmentEntity::TYPE_WP_USERS, SegmentEntity::TYPE_WC_USERS], true)
|
||||
) {
|
||||
return $this->actionsController->apply('\MailPoet\Models\Subscriber', $this->data);
|
||||
} elseif (isset($segment['type']) && $segment['type'] === SegmentEntity::TYPE_DYNAMIC) {
|
||||
return $this->actionsController->apply('\MailPoet\Models\SubscribersInDynamicSegment', $this->data);
|
||||
}
|
||||
throw new \InvalidArgumentException('No handler found for segment');
|
||||
}
|
||||
}
|
@@ -1,111 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace MailPoet\DynamicSegments\Filters;
|
||||
|
||||
use MailPoet\Models\Newsletter;
|
||||
use MailPoet\Models\StatisticsClicks;
|
||||
use MailPoet\Models\StatisticsNewsletters;
|
||||
use MailPoet\Models\StatisticsOpens;
|
||||
use MailPoet\Models\Subscriber;
|
||||
|
||||
class EmailActionTest extends \MailPoetTest {
|
||||
public $subscriberOpenedNotClicked;
|
||||
public $subscriberNotSent;
|
||||
public $subscriberNotOpened;
|
||||
public $subscriberOpenedClicked;
|
||||
public $newsletter;
|
||||
|
||||
public function _before() {
|
||||
$this->newsletter = Newsletter::createOrUpdate([
|
||||
'subject' => 'newsletter 1',
|
||||
'status' => 'sent',
|
||||
'type' => Newsletter::TYPE_NOTIFICATION,
|
||||
]);
|
||||
$this->subscriberOpenedClicked = Subscriber::createOrUpdate([
|
||||
'email' => 'opened_clicked@example.com',
|
||||
]);
|
||||
$this->subscriberOpenedNotClicked = Subscriber::createOrUpdate([
|
||||
'email' => 'opened_not_clicked@example.com',
|
||||
]);
|
||||
$this->subscriberNotOpened = Subscriber::createOrUpdate([
|
||||
'email' => 'not_opened@example.com',
|
||||
]);
|
||||
$this->subscriberNotSent = Subscriber::createOrUpdate([
|
||||
'email' => 'not_sent@example.com',
|
||||
]);
|
||||
StatisticsNewsletters::createMultiple([
|
||||
['newsletter_id' => $this->newsletter->id, 'subscriber_id' => $this->subscriberOpenedClicked->id, 'queue_id' => 1],
|
||||
['newsletter_id' => $this->newsletter->id, 'subscriber_id' => $this->subscriberOpenedNotClicked->id, 'queue_id' => 1],
|
||||
['newsletter_id' => $this->newsletter->id, 'subscriber_id' => $this->subscriberNotOpened->id, 'queue_id' => 1],
|
||||
]);
|
||||
StatisticsOpens::getOrCreate($this->subscriberOpenedClicked->id, $this->newsletter->id, 1);
|
||||
StatisticsOpens::getOrCreate($this->subscriberOpenedNotClicked->id, $this->newsletter->id, 1);
|
||||
StatisticsClicks::createOrUpdateClickCount(1, $this->subscriberOpenedClicked->id, $this->newsletter->id, 1);
|
||||
}
|
||||
|
||||
public function testGetOpened() {
|
||||
$emailAction = new EmailAction(EmailAction::ACTION_OPENED, $this->newsletter->id);
|
||||
$sql = $emailAction->toSql(Subscriber::selectExpr('*')); // @phpstan-ignore-line
|
||||
expect($sql->count())->equals(2);
|
||||
}
|
||||
|
||||
public function testNotOpened() {
|
||||
$emailAction = new EmailAction(EmailAction::ACTION_NOT_OPENED, $this->newsletter->id);
|
||||
$sql = $emailAction->toSql(Subscriber::selectExpr('*')); // @phpstan-ignore-line
|
||||
expect($sql->count())->equals(1);
|
||||
}
|
||||
|
||||
public function testGetClickedWithoutLink() {
|
||||
$emailAction = new EmailAction(EmailAction::ACTION_CLICKED, $this->newsletter->id);
|
||||
$sql = $emailAction->toSql(Subscriber::selectExpr('*')); // @phpstan-ignore-line
|
||||
expect($sql->count())->equals(1);
|
||||
}
|
||||
|
||||
public function testGetClickedWithLink() {
|
||||
$emailAction = new EmailAction(EmailAction::ACTION_CLICKED, $this->newsletter->id, 1);
|
||||
$sql = $emailAction->toSql(Subscriber::selectExpr('*')); // @phpstan-ignore-line
|
||||
expect($sql->count())->equals(1);
|
||||
}
|
||||
|
||||
public function testGetClickedWithWrongLink() {
|
||||
$emailAction = new EmailAction(EmailAction::ACTION_CLICKED, $this->newsletter->id, 2);
|
||||
$sql = $emailAction->toSql(Subscriber::selectExpr('*')); // @phpstan-ignore-line
|
||||
expect($sql->count())->equals(0);
|
||||
}
|
||||
|
||||
public function testGetNotClickedWithLink() {
|
||||
$emailAction = new EmailAction(EmailAction::ACTION_NOT_CLICKED, $this->newsletter->id, 1);
|
||||
$sql = $emailAction->toSql(Subscriber::selectExpr('*')); // @phpstan-ignore-line
|
||||
expect($sql->count())->equals(2);
|
||||
}
|
||||
|
||||
public function testGetNotClickedWithWrongLink() {
|
||||
$emailAction = new EmailAction(EmailAction::ACTION_NOT_CLICKED, $this->newsletter->id, 2);
|
||||
$sql = $emailAction->toSql(Subscriber::selectExpr('*')); // @phpstan-ignore-line
|
||||
expect($sql->count())->equals(3);
|
||||
}
|
||||
|
||||
public function testGetNotClickedWithoutLink() {
|
||||
$emailAction = new EmailAction(EmailAction::ACTION_NOT_CLICKED, $this->newsletter->id);
|
||||
$sql = $emailAction->toSql(Subscriber::selectExpr('*')); // @phpstan-ignore-line
|
||||
expect($sql->count())->equals(2);
|
||||
}
|
||||
|
||||
public function _after() {
|
||||
$this->cleanData();
|
||||
}
|
||||
|
||||
private function cleanData() {
|
||||
// @phpstan-ignore-next-line
|
||||
StatisticsClicks::where('newsletter_id', $this->newsletter->id)->findResultSet()->delete();
|
||||
// @phpstan-ignore-next-line
|
||||
StatisticsNewsletters::where('newsletter_id', $this->newsletter->id)->findResultSet()->delete();
|
||||
// @phpstan-ignore-next-line
|
||||
StatisticsOpens::where('newsletter_id', $this->newsletter->id)->findResultSet()->delete();
|
||||
$this->newsletter->delete();
|
||||
$this->subscriberOpenedClicked->delete();
|
||||
$this->subscriberOpenedNotClicked->delete();
|
||||
$this->subscriberNotOpened->delete();
|
||||
$this->subscriberNotSent->delete();
|
||||
}
|
||||
}
|
@@ -1,37 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace MailPoet\DynamicSegments\Filters;
|
||||
|
||||
use MailPoet\Models\Subscriber;
|
||||
|
||||
class UserRoleTest extends \MailPoetTest {
|
||||
public function _before() {
|
||||
$this->cleanData();
|
||||
$this->tester->createWordPressUser('user-role-test1@example.com', 'editor');
|
||||
$this->tester->createWordPressUser('user-role-test2@example.com', 'administrator');
|
||||
$this->tester->createWordPressUser('user-role-test3@example.com', 'editor');
|
||||
}
|
||||
|
||||
public function testItConstructsQuery() {
|
||||
$userRole = new UserRole('editor', 'and');
|
||||
$sql = $userRole->toSql(Subscriber::selectExpr('*')); // @phpstan-ignore-line
|
||||
expect($sql->count())->equals(2);
|
||||
}
|
||||
|
||||
public function testItDoesntGetSubString() {
|
||||
$userRole = new UserRole('edit', 'and');
|
||||
$sql = $userRole->toSql(Subscriber::selectExpr('*')); // @phpstan-ignore-line
|
||||
expect($sql->count())->equals(0);
|
||||
}
|
||||
|
||||
public function _after() {
|
||||
$this->cleanData();
|
||||
}
|
||||
|
||||
private function cleanData() {
|
||||
$emails = ['user-role-test1@example.com', 'user-role-test2@example.com', 'user-role-test3@example.com'];
|
||||
foreach ($emails as $email) {
|
||||
$this->tester->deleteWordPressUser($email);
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,14 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace MailPoet\DynamicSegments\Filters;
|
||||
|
||||
class WooCommerceCategoryTest extends \MailPoetTest {
|
||||
public function testToArray() {
|
||||
$filter = new WooCommerceCategory(5);
|
||||
$data = $filter->toArray();
|
||||
expect($data)->notEmpty();
|
||||
expect($data['segmentType'])->same('woocommerce');
|
||||
expect($data['action'])->same('purchasedCategory');
|
||||
expect($data['category_id'])->same(5);
|
||||
}
|
||||
}
|
@@ -1,65 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace MailPoet\DynamicSegments\Persistence\Loading;
|
||||
|
||||
use MailPoet\DynamicSegments\Filters\UserRole;
|
||||
use MailPoet\DynamicSegments\Mappers\DBMapper;
|
||||
use MailPoet\Models\DynamicSegment;
|
||||
use MailPoet\Models\DynamicSegmentFilter;
|
||||
use MailPoetVendor\Idiorm\ORM;
|
||||
|
||||
class SingleSegmentLoaderTest extends \MailPoetTest {
|
||||
|
||||
private $segment;
|
||||
|
||||
/** @var SingleSegmentLoader */
|
||||
private $loader;
|
||||
|
||||
public function _before() {
|
||||
$this->loader = new SingleSegmentLoader(new DBMapper());
|
||||
ORM::raw_execute('TRUNCATE ' . DynamicSegment::$_table);
|
||||
ORM::raw_execute('TRUNCATE ' . DynamicSegmentFilter::$_table);
|
||||
$this->segment = DynamicSegment::createOrUpdate([
|
||||
'name' => 'segment 1',
|
||||
'description' => 'description',
|
||||
]);
|
||||
$filter = new UserRole('Administrator', 'and');
|
||||
$filterData = DynamicSegmentFilter::create();
|
||||
$filterData->hydrate([
|
||||
'segment_id' => $this->segment->id,
|
||||
'filter_data' => $filter->toArray(),
|
||||
]);
|
||||
$filterData->save();
|
||||
}
|
||||
|
||||
public function testItLoadsSegments() {
|
||||
$data = $this->loader->load($this->segment->id);
|
||||
expect($data)->isInstanceOf('\MailPoet\Models\DynamicSegment');
|
||||
}
|
||||
|
||||
public function testItThrowsForUnknownSegment() {
|
||||
$this->expectException('InvalidArgumentException');
|
||||
$this->loader->load($this->segment->id + 11564564);
|
||||
}
|
||||
|
||||
public function testItPopulatesCommonData() {
|
||||
$data = $this->loader->load($this->segment->id);
|
||||
expect($data->name)->equals('segment 1');
|
||||
expect($data->description)->equals('description');
|
||||
}
|
||||
|
||||
public function testItPopulatesFilters() {
|
||||
$data = $this->loader->load($this->segment->id);
|
||||
$filters0 = $data->getFilters();
|
||||
expect($filters0)->count(1);
|
||||
/** @var UserRole $filter0 */
|
||||
$filter0 = $filters0[0];
|
||||
expect($filter0)->isInstanceOf('\MailPoet\DynamicSegments\Filters\UserRole');
|
||||
expect($filter0->getRole())->equals('Administrator');
|
||||
}
|
||||
|
||||
public function _after() {
|
||||
ORM::raw_execute('TRUNCATE ' . DynamicSegment::$_table);
|
||||
ORM::raw_execute('TRUNCATE ' . DynamicSegmentFilter::$_table);
|
||||
}
|
||||
}
|
@@ -1,64 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace MailPoet\Models;
|
||||
|
||||
use MailPoet\DynamicSegments\Filters\UserRole;
|
||||
use MailPoetVendor\Idiorm\ORM;
|
||||
|
||||
class SubscribersInDynamicSegmentTest extends \MailPoetTest {
|
||||
public $dynamicSegment;
|
||||
|
||||
public function _before() {
|
||||
$this->cleanData();
|
||||
$this->dynamicSegment = DynamicSegment::createOrUpdate([
|
||||
'name' => 'name',
|
||||
'description' => 'desc',
|
||||
]);
|
||||
$filter = new UserRole('editor', 'and');
|
||||
$dataFilter = DynamicSegmentFilter::create();
|
||||
$dataFilter->segmentId = $this->dynamicSegment->id;
|
||||
$dataFilter->filterData = $filter->toArray();
|
||||
$dataFilter->save();
|
||||
$this->tester->createWordPressUser('user-role-test1@example.com', 'editor');
|
||||
$this->tester->createWordPressUser('user-role-test2@example.com', 'administrator');
|
||||
$this->tester->createWordPressUser('user-role-test3@example.com', 'editor');
|
||||
}
|
||||
|
||||
public function testListingQuery() {
|
||||
$listingData = [
|
||||
'filter' => ['segment' => $this->dynamicSegment->id],
|
||||
'group' => 'all',
|
||||
'search' => '',
|
||||
];
|
||||
$query = SubscribersInDynamicSegment::listingQuery($listingData);
|
||||
$data = $query->orderByAsc('email')->findMany();
|
||||
expect($data)->count(2);
|
||||
expect($data[0]->email)->equals('user-role-test1@example.com');
|
||||
expect($data[1]->email)->equals('user-role-test3@example.com');
|
||||
}
|
||||
|
||||
public function testListingQueryWithSearch() {
|
||||
$listingData = [
|
||||
'filter' => ['segment' => $this->dynamicSegment->id],
|
||||
'group' => 'all',
|
||||
'search' => 'user-role-test1',
|
||||
];
|
||||
$query = SubscribersInDynamicSegment::listingQuery($listingData);
|
||||
$data = $query->findMany();
|
||||
expect($data)->count(1);
|
||||
expect($data[0]->email)->equals('user-role-test1@example.com');
|
||||
}
|
||||
|
||||
public function _after() {
|
||||
$this->cleanData();
|
||||
}
|
||||
|
||||
private function cleanData() {
|
||||
ORM::raw_execute('TRUNCATE ' . DynamicSegment::$_table);
|
||||
ORM::raw_execute('TRUNCATE ' . DynamicSegmentFilter::$_table);
|
||||
$emails = ['user-role-test1@example.com', 'user-role-test2@example.com', 'user-role-test3@example.com'];
|
||||
foreach ($emails as $email) {
|
||||
$this->tester->deleteWordPressUser($email);
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,88 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace MailPoet\Segments;
|
||||
|
||||
use MailPoet\Models\Segment;
|
||||
use MailPoet\Models\Subscriber;
|
||||
use MailPoet\Models\SubscriberSegment;
|
||||
use MailPoetVendor\Idiorm\ORM;
|
||||
|
||||
require_once('SubscribersBulkActionHandlerMock.php');
|
||||
|
||||
class BulkActionTest extends \MailPoetTest {
|
||||
public $subscriber2;
|
||||
public $subscriber1;
|
||||
public $segment2;
|
||||
public $segment1;
|
||||
|
||||
public function _before() {
|
||||
parent::_before();
|
||||
$this->cleanData();
|
||||
$this->segment1 = Segment::createOrUpdate(['name' => 'Segment 1', 'type' => 'default']);
|
||||
$this->segment2 = Segment::createOrUpdate(['name' => 'Segment 3', 'type' => 'not default']);
|
||||
$this->subscriber1 = Subscriber::createOrUpdate([
|
||||
'email' => 'john@mailpoet.com',
|
||||
'first_name' => 'John',
|
||||
'last_name' => 'Doe',
|
||||
'status' => Subscriber::STATUS_SUBSCRIBED,
|
||||
'segments' => [
|
||||
$this->segment1->id,
|
||||
],
|
||||
]);
|
||||
$this->subscriber2 = Subscriber::createOrUpdate([
|
||||
'email' => 'jake@mailpoet.com',
|
||||
'first_name' => 'Jake',
|
||||
'last_name' => 'Doe',
|
||||
'status' => Subscriber::STATUS_SUBSCRIBED,
|
||||
'segments' => [
|
||||
$this->segment2->id,
|
||||
],
|
||||
]);
|
||||
SubscriberSegment::resubscribeToAllSegments($this->subscriber1);
|
||||
SubscriberSegment::resubscribeToAllSegments($this->subscriber2);
|
||||
}
|
||||
|
||||
public function _after() {
|
||||
$this->cleanData();
|
||||
}
|
||||
|
||||
private function cleanData() {
|
||||
ORM::raw_execute('TRUNCATE ' . Segment::$_table);
|
||||
ORM::raw_execute('TRUNCATE ' . SubscriberSegment::$_table);
|
||||
ORM::raw_execute('TRUNCATE ' . Subscriber::$_table);
|
||||
}
|
||||
|
||||
public function testBulkActionWithoutSegment() {
|
||||
$handler = new BulkAction([]);
|
||||
$this->expectException('InvalidArgumentException');
|
||||
$handler->apply();
|
||||
}
|
||||
|
||||
public function testBulkActionForDefaultSegment() {
|
||||
$handler = new BulkAction([
|
||||
'listing' => ['filter' => ['segment' => $this->segment1->id]],
|
||||
'action' => 'trash',
|
||||
]);
|
||||
$result = $handler->apply();
|
||||
expect($result['count'])->equals(1);
|
||||
}
|
||||
|
||||
public function testBulkActionForUnknownSegment() {
|
||||
$handler = new BulkAction([
|
||||
'listing' => ['filter' => ['segment' => 'this-segment-doesnt-exist']],
|
||||
'action' => 'trash',
|
||||
]);
|
||||
$result = $handler->apply();
|
||||
expect($result)->notEmpty();
|
||||
}
|
||||
|
||||
public function testForUnknownSegmentTypeWithoutHandler() {
|
||||
$handler = new BulkAction([
|
||||
'listing' => ['filter' => ['segment' => $this->segment2->id]],
|
||||
'action' => 'trash',
|
||||
]);
|
||||
$this->expectException('InvalidArgumentException');
|
||||
remove_all_filters('mailpoet_subscribers_in_segment_apply_bulk_action_handlers');
|
||||
$handler->apply();
|
||||
}
|
||||
}
|
@@ -1,9 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace MailPoet\Test\Segments;
|
||||
|
||||
class SubscribersBulkActionHandlerMock {
|
||||
public function apply() {
|
||||
|
||||
}
|
||||
}
|
@@ -1,9 +1,6 @@
|
||||
<?php
|
||||
|
||||
use Codeception\Util\Fixtures;
|
||||
use MailPoet\DynamicSegments\Filters\Filter;
|
||||
use MailPoet\Models\Subscriber;
|
||||
use MailPoetVendor\Idiorm\ORM;
|
||||
|
||||
$newsletterBodyText =
|
||||
|
||||
@@ -168,23 +165,3 @@ Fixtures::add(
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
/**
|
||||
* Simple class mocking dynamic segment filter.
|
||||
*/
|
||||
// phpcs:ignore PSR1.Classes.ClassDeclaration, Squiz.Classes.ClassFileName
|
||||
class DynamicSegmentFilter implements Filter {
|
||||
protected $ids;
|
||||
|
||||
public function __construct($ids) {
|
||||
$this->ids = $ids;
|
||||
}
|
||||
|
||||
public function toSql(ORM $orm) {
|
||||
return $orm->whereIn(Subscriber::$_table . '.id', $this->ids);
|
||||
}
|
||||
|
||||
public function toArray() {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
@@ -1,49 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace MailPoet\DynamicSegments;
|
||||
|
||||
use MailPoet\DynamicSegments\Filters\EmailAction;
|
||||
use MailPoet\DynamicSegments\Filters\WooCommerceCategory;
|
||||
use MailPoet\Models\DynamicSegment;
|
||||
use MailPoet\WooCommerce\Helper;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
|
||||
class RequirementsCheckerTest extends \MailPoetUnitTest {
|
||||
|
||||
/** @var Helper|MockObject */
|
||||
private $woocommerceHelper;
|
||||
|
||||
/** @var RequirementsChecker */
|
||||
private $requirementsChecker;
|
||||
|
||||
public function _before() {
|
||||
parent::_before();
|
||||
if (!defined('MP_SEGMENTS_TABLE')) define('MP_SEGMENTS_TABLE', '');
|
||||
$this->woocommerceHelper = $this
|
||||
->getMockBuilder(Helper::class)
|
||||
->setMethods(['isWooCommerceActive'])
|
||||
->getMock();
|
||||
$this->requirementsChecker = new RequirementsChecker($this->woocommerceHelper);
|
||||
}
|
||||
|
||||
public function testShouldntBlockSegmentIfWooCommerceIsActive() {
|
||||
$this->woocommerceHelper->method('isWooCommerceActive')->willReturn(true);
|
||||
$segment = DynamicSegment::create();
|
||||
$segment->setFilters([new WooCommerceCategory(1)]);
|
||||
expect($this->requirementsChecker->shouldSkipSegment($segment))->false();
|
||||
}
|
||||
|
||||
public function testShouldBlockWooCommerceSegmentIfWooCommerceIsInactive() {
|
||||
$this->woocommerceHelper->method('isWooCommerceActive')->willReturn(false);
|
||||
$segment = DynamicSegment::create();
|
||||
$segment->setFilters([new WooCommerceCategory(1)]);
|
||||
expect($this->requirementsChecker->shouldSkipSegment($segment))->true();
|
||||
}
|
||||
|
||||
public function testShouldntBlockNonWooCommerceSegmentIfWooCommerceIsInactive() {
|
||||
$this->woocommerceHelper->method('isWooCommerceActive')->willReturn(false);
|
||||
$segment = DynamicSegment::create();
|
||||
$segment->setFilters([new EmailAction(EmailAction::ACTION_OPENED, 2)]);
|
||||
expect($this->requirementsChecker->shouldSkipSegment($segment))->false();
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user