- Corrects exported subscriber count
- Properly exports subscribers not in any list - Adds test for export class constructor method Resolves #221
This commit is contained in:
@@ -6,6 +6,7 @@ if(!defined('ABSPATH')) exit;
|
||||
class Env {
|
||||
public static $version;
|
||||
public static $plugin_name;
|
||||
public static $plugin_url;
|
||||
public static $file;
|
||||
public static $path;
|
||||
public static $views_path;
|
||||
@@ -29,9 +30,10 @@ class Env {
|
||||
public static function init($file, $version) {
|
||||
global $wpdb;
|
||||
self::$version = $version;
|
||||
self::$plugin_name = 'mailpoet';
|
||||
self::$file = $file;
|
||||
self::$path = dirname(self::$file);
|
||||
self::$plugin_name = 'mailpoet';
|
||||
self::$plugin_url = plugins_url() . '/' . basename(Env::$path);
|
||||
self::$views_path = self::$path . '/views';
|
||||
self::$assets_path = self::$path . '/assets';
|
||||
self::$assets_url = plugins_url('/assets', $file);
|
||||
|
@@ -135,20 +135,41 @@ class Subscriber extends Model {
|
||||
MP_SUBSCRIBER_CUSTOM_FIELD_TABLE . '.value END), NULL) as "' . $customField['name'].'"');
|
||||
}
|
||||
$orm = $orm
|
||||
->left_outer_join(
|
||||
->leftOuterJoin(
|
||||
MP_SUBSCRIBER_CUSTOM_FIELD_TABLE,
|
||||
array(MP_SUBSCRIBERS_TABLE.'.id', '=',
|
||||
MP_SUBSCRIBER_CUSTOM_FIELD_TABLE.'.subscriber_id'))
|
||||
->left_outer_join(
|
||||
->leftOuterJoin(
|
||||
MP_CUSTOM_FIELDS_TABLE,
|
||||
array(MP_CUSTOM_FIELDS_TABLE.'.id','=',
|
||||
MP_SUBSCRIBER_CUSTOM_FIELD_TABLE.'.custom_field_id'))
|
||||
->group_by(MP_SUBSCRIBERS_TABLE.'.id');
|
||||
->groupBy(MP_SUBSCRIBERS_TABLE.'.id');
|
||||
return $orm;
|
||||
}
|
||||
|
||||
static function filterWithCustomFieldsForExport($orm) {
|
||||
$orm = $orm->select(MP_SUBSCRIBERS_TABLE.'.*');
|
||||
$customFields = CustomField::findArray();
|
||||
foreach ($customFields as $customField) {
|
||||
$orm = $orm->selectExpr(
|
||||
'CASE WHEN ' .
|
||||
MP_CUSTOM_FIELDS_TABLE . '.id=' . $customField['id'] . ' THEN ' .
|
||||
MP_SUBSCRIBER_CUSTOM_FIELD_TABLE . '.value END as "' . $customField['name'].'"');
|
||||
}
|
||||
$orm = $orm
|
||||
->leftOuterJoin(
|
||||
MP_SUBSCRIBER_CUSTOM_FIELD_TABLE,
|
||||
array(MP_SUBSCRIBERS_TABLE.'.id', '=',
|
||||
MP_SUBSCRIBER_CUSTOM_FIELD_TABLE.'.subscriber_id'))
|
||||
->leftOuterJoin(
|
||||
MP_CUSTOM_FIELDS_TABLE,
|
||||
array(MP_CUSTOM_FIELDS_TABLE.'.id','=',
|
||||
MP_SUBSCRIBER_CUSTOM_FIELD_TABLE.'.custom_field_id'));
|
||||
return $orm;
|
||||
}
|
||||
|
||||
function customFields() {
|
||||
return $this->has_many_through(
|
||||
return $this->hasManyThrough(
|
||||
__NAMESPACE__.'\CustomField',
|
||||
__NAMESPACE__.'\SubscriberCustomField',
|
||||
'subscriber_id',
|
||||
|
@@ -2,11 +2,14 @@
|
||||
namespace MailPoet\Subscribers\ImportExport\Export;
|
||||
|
||||
use MailPoet\Config\Env;
|
||||
use MailPoet\Subscribers\ImportExport\BootStrapMenu;
|
||||
use MailPoet\Models\CustomField;
|
||||
use MailPoet\Models\Segment;
|
||||
use MailPoet\Models\Subscriber;
|
||||
use MailPoet\Models\SubscriberSegment;
|
||||
use MailPoet\Subscribers\ImportExport\BootStrapMenu;
|
||||
use MailPoet\Util\Helpers;
|
||||
use MailPoet\Util\XLSXWriter;
|
||||
use Symfony\Component\Console\Helper\Helper;
|
||||
|
||||
class Export {
|
||||
public function __construct($data) {
|
||||
@@ -18,23 +21,22 @@ class Export {
|
||||
$this->subscriberFields = $data['subscriberFields'];
|
||||
$this->profilerStart = microtime(true);
|
||||
$this->exportFile = sprintf(
|
||||
Env::$temp_path . '/mailpoet_export_%s.%s',
|
||||
Env::$temp_path . '/MailPoet_export_%s.%s',
|
||||
substr(md5(time()), 0, 4),
|
||||
$this->exportFormatOption
|
||||
);
|
||||
$this->exportFileURL = sprintf(
|
||||
'%s/%s/%s/%s',
|
||||
plugins_url(),
|
||||
Env::$plugin_name,
|
||||
'%s/%s/%s',
|
||||
Env::$plugin_url,
|
||||
Env::$temp_name,
|
||||
basename($this->exportFile)
|
||||
);
|
||||
}
|
||||
|
||||
function process() {
|
||||
$subscribers = SubscriberSegment::
|
||||
$subscribers = Subscriber::
|
||||
left_outer_join(
|
||||
Subscriber::$_table,
|
||||
SubscriberSegment::$_table,
|
||||
array(
|
||||
Subscriber::$_table . '.id',
|
||||
'=',
|
||||
@@ -47,14 +49,33 @@ class Export {
|
||||
'=',
|
||||
SubscriberSegment::$_table . '.segment_id'
|
||||
))
|
||||
->select(Segment::$_table . '.name', 'segment_name')
|
||||
->orderByAsc('segment_name')
|
||||
->filter('filterWithCustomFields')
|
||||
->whereIn(SubscriberSegment::$_table . '.segment_id', $this->segments);
|
||||
->filter('filterWithCustomFieldsForExport');
|
||||
if($this->subscribersWithoutSegment !== false) {
|
||||
$subscribers = $subscribers
|
||||
->selectExpr('CASE WHEN ' . Segment::$_table . '.name IS NOT NULL ' .
|
||||
'THEN ' . Segment::$_table . '.name ' .
|
||||
'ELSE "' . __('Not In List') . '" END as segment_name'
|
||||
)
|
||||
->whereRaw(
|
||||
SubscriberSegment::$_table . '.segment_id IN (' . rtrim(str_repeat('?,', count($this->segments)), ',') . ') OR ' .
|
||||
SubscriberSegment::$_table . '.segment_id IS NULL ',
|
||||
$this->segments
|
||||
);
|
||||
} else {
|
||||
$subscribers = $subscribers
|
||||
->select(Segment::$_table . '.name', 'segment_name')
|
||||
->whereIn(SubscriberSegment::$_table . '.segment_id', $this->segments);
|
||||
}
|
||||
if(!$this->groupBySegmentOption) $subscribers = $subscribers->groupBy(Subscriber::$_table . '.id');
|
||||
if($this->exportConfirmedOption) $subscribers = $subscribers->where(Subscriber::$_table . '.status', 'confirmed');
|
||||
$subscribers = $subscribers->findArray();
|
||||
$formattedSubscriberFields = $this->formatSubscriberFields($this->subscriberFields);
|
||||
$subscriberCustomFields = Helpers::arrayColumn(
|
||||
CustomField::findArray(),
|
||||
'name',
|
||||
'id'
|
||||
);
|
||||
$formattedSubscriberFields = $this->formatSubscriberFields($this->subscriberFields, $subscriberCustomFields);
|
||||
try {
|
||||
if($this->exportFormatOption === 'csv') {
|
||||
$CSVFile = fopen($this->exportFile, 'w');
|
||||
@@ -64,34 +85,40 @@ class Export {
|
||||
// add UTF-8 BOM (3 bytes, hex EF BB BF) at the start of the file for Excel to automatically recognize the encoding
|
||||
fwrite($CSVFile, chr(0xEF) . chr(0xBB) . chr(0xBF));
|
||||
if($this->groupBySegmentOption) $formattedSubscriberFields[] = __('List');
|
||||
fwrite($CSVFile, implode(",", array_map($formatCSV, $formattedSubscriberFields)) . "\n");
|
||||
fwrite($CSVFile, implode(',', array_map($formatCSV, $formattedSubscriberFields)) . "\n");
|
||||
foreach ($subscribers as $subscriber) {
|
||||
$row = array_map(function ($field) use ($subscriber) {
|
||||
return $subscriber[$field];
|
||||
$row = array_map(function ($field) use ($subscriber, $subscriberCustomFields) {
|
||||
return (isset($subscriberCustomFields[$field])) ? $subscriberCustomFields[$field] : $subscriber[$field];
|
||||
}, $this->subscriberFields);
|
||||
if($this->groupBySegmentOption) $row[] = $subscriber['segment_name'];
|
||||
fwrite($CSVFile, implode(",", array_map($formatCSV, $row)) . "\n");
|
||||
if($this->groupBySegmentOption) {
|
||||
$row[] = $subscriber['segment_name'];
|
||||
}
|
||||
fwrite($CSVFile, implode(',', array_map($formatCSV, $row)) . "\n");
|
||||
}
|
||||
fclose($CSVFile);
|
||||
} else {
|
||||
$writer = new XLSXWriter();
|
||||
$writer->setAuthor('MailPoet (www.mailpoet.com)');
|
||||
$headerRow = array($formattedSubscriberFields);
|
||||
$segment = null;
|
||||
$lastSegment = false;
|
||||
$rows = array();
|
||||
foreach ($subscribers as $subscriber) {
|
||||
if($segment && $segment != $subscriber['segment_name'] && $this->groupBySegmentOption) {
|
||||
$writer->writeSheet(array_merge($headerRow, $rows), ucwords($segment));
|
||||
if($lastSegment && $lastSegment !== $subscriber['segment_name'] && $this->groupBySegmentOption) {
|
||||
$writer->writeSheet(array_merge($headerRow, $rows), ucwords($lastSegment));
|
||||
$rows = array();
|
||||
}
|
||||
// detect RTL language and set Excel to properly display the sheet
|
||||
if(!$writer->rtl && preg_grep('/\p{Arabic}|\p{Hebrew}/u', $subscriber)) {
|
||||
$arabicRegex = '/\p{Arabic}|\p{Hebrew}/u';
|
||||
if(!$writer->rtl && (
|
||||
preg_grep($arabicRegex, $subscriber) ||
|
||||
preg_grep($arabicRegex, $formattedSubscriberFields))
|
||||
) {
|
||||
$writer->rtl = true;
|
||||
}
|
||||
$rows[] = array_map(function ($field) use ($subscriber) {
|
||||
return $subscriber[$field];
|
||||
$rows[] = array_map(function ($field) use ($subscriber, $subscriberCustomFields) {
|
||||
return (isset($subscriberCustomFields[$field])) ? $subscriber[$subscriberCustomFields[$field]] : $subscriber[$field];
|
||||
}, $this->subscriberFields);
|
||||
$segment = $subscriber['segment_name'];
|
||||
$lastSegment = $subscriber['segment_name'];
|
||||
}
|
||||
$writer->writeSheet(array_merge($headerRow, $rows), 'MailPoet');
|
||||
$writer->writeToFile($this->exportFile);
|
||||
@@ -107,17 +134,19 @@ class Export {
|
||||
'data' => array(
|
||||
'totalExported' => count($subscribers),
|
||||
'exportFileURL' => $this->exportFileURL
|
||||
)
|
||||
),
|
||||
'profiler' => $this->timeExecution()
|
||||
);
|
||||
}
|
||||
|
||||
function formatSubscriberFields($subscriberFields) {
|
||||
function formatSubscriberFields($subscriberFields, $subscriberCustomFields) {
|
||||
$bootStrapMenu = new BootStrapMenu();
|
||||
$translatedFields = $bootStrapMenu->getSubscriberFields();
|
||||
return array_map(function ($field) use ($translatedFields) {
|
||||
return (isset($translatedFields[$field])) ?
|
||||
return array_map(function ($field) use ($translatedFields, $subscriberCustomFields) {
|
||||
$field = (isset($translatedFields[$field])) ?
|
||||
ucfirst($translatedFields[$field]) :
|
||||
ucfirst($field);
|
||||
return (isset($subscriberCustomFields[$field])) ? $subscriberCustomFields[$field] : $field;
|
||||
}, $subscriberFields);
|
||||
}
|
||||
|
||||
|
@@ -75,7 +75,7 @@ class Import {
|
||||
'updated' => count($updatedSubscribers),
|
||||
'segments' => $segments->getSegments()
|
||||
),
|
||||
'profile' => $this->timeExecution()
|
||||
'profiler' => $this->timeExecution()
|
||||
);
|
||||
}
|
||||
|
||||
|
@@ -1,12 +1,84 @@
|
||||
<?php
|
||||
|
||||
use MailPoet\Subscribers\ImportExport\Export;
|
||||
use MailPoet\Config\Env;
|
||||
use MailPoet\Models\Segment;
|
||||
use MailPoet\Models\Subscriber;
|
||||
use MailPoet\Models\SubscriberCustomField;
|
||||
use MailPoet\Models\SubscriberSegment;
|
||||
use MailPoet\Subscribers\ImportExport\Export\Export;
|
||||
|
||||
class ExportCest {
|
||||
function __construct() {
|
||||
$this->JSONdata = json_decode(file_get_contents(dirname(__FILE__) . '/ExportTestData.json'), true);
|
||||
$this->subscribersData = array(
|
||||
'first_name' => array(
|
||||
'Adam',
|
||||
'Mary',
|
||||
'John',
|
||||
'Paul'
|
||||
),
|
||||
'last_name' => array(
|
||||
'Smith',
|
||||
'Jane',
|
||||
'Kookoo',
|
||||
'Newman'
|
||||
),
|
||||
'email' => array(
|
||||
'adam@smith.com',
|
||||
'mary@jane.com',
|
||||
'john@kookoo.com',
|
||||
'paul@newman.com'
|
||||
),
|
||||
1 => array(
|
||||
'Brazil'
|
||||
)
|
||||
);
|
||||
$this->segments = array(
|
||||
array(
|
||||
'name' => 'Newspapers'
|
||||
),
|
||||
array(
|
||||
'name' => 'Journals'
|
||||
)
|
||||
);
|
||||
$this->export = new Export($this->JSONdata);
|
||||
}
|
||||
|
||||
function itCanConstruct() {
|
||||
expect($this->export->exportConfirmedOption)
|
||||
->equals(false);
|
||||
expect($this->export->exportFormatOption)
|
||||
->equals('csv');
|
||||
expect($this->export->groupBySegmentOption)
|
||||
->equals(true);
|
||||
expect($this->export->segments)
|
||||
->equals(array(0, 1));
|
||||
expect($this->export->subscribersWithoutSegment)
|
||||
->equals(0);
|
||||
expect($this->export->subscriberFields)
|
||||
->equals(
|
||||
array(
|
||||
'email',
|
||||
'first_name',
|
||||
'1'
|
||||
)
|
||||
);
|
||||
expect(
|
||||
preg_match(
|
||||
'|'.
|
||||
Env::$temp_path.'/MailPoet_export_[a-f0-9]{4}.'.
|
||||
$this->export->exportFormatOption .
|
||||
'|', $this->export->exportFile)
|
||||
)->equals(1);
|
||||
expect(
|
||||
preg_match(
|
||||
'|'.
|
||||
Env::$plugin_url . '/' .
|
||||
Env::$temp_name . '/' .
|
||||
basename($this->export->exportFile) .
|
||||
'|'
|
||||
, $this->export->exportFileURL)
|
||||
)->equals(1);
|
||||
}
|
||||
|
||||
function itCanProcess() {
|
||||
@@ -19,5 +91,7 @@ class ExportCest {
|
||||
->deleteMany();
|
||||
ORM::forTable(SubscriberSegment::$_table)
|
||||
->deleteMany();
|
||||
ORM::forTable(Segment::$_table)
|
||||
->deleteMany();
|
||||
}
|
||||
}
|
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"exportConfirmedOption": false,
|
||||
"exportFormatOption": "csv",
|
||||
"groupBySegmentOption": true,
|
||||
"segments": [
|
||||
"0",
|
||||
"1"
|
||||
],
|
||||
"subscriberFields": [
|
||||
"email",
|
||||
"first_name",
|
||||
"1"
|
||||
]
|
||||
}
|
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user