- 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:
MrCasual
2015-11-15 22:11:49 -05:00
parent db91590159
commit e3de3a123a
7 changed files with 5175 additions and 5035 deletions

View File

@@ -6,6 +6,7 @@ if(!defined('ABSPATH')) exit;
class Env { class Env {
public static $version; public static $version;
public static $plugin_name; public static $plugin_name;
public static $plugin_url;
public static $file; public static $file;
public static $path; public static $path;
public static $views_path; public static $views_path;
@@ -29,9 +30,10 @@ class Env {
public static function init($file, $version) { public static function init($file, $version) {
global $wpdb; global $wpdb;
self::$version = $version; self::$version = $version;
self::$plugin_name = 'mailpoet';
self::$file = $file; self::$file = $file;
self::$path = dirname(self::$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::$views_path = self::$path . '/views';
self::$assets_path = self::$path . '/assets'; self::$assets_path = self::$path . '/assets';
self::$assets_url = plugins_url('/assets', $file); self::$assets_url = plugins_url('/assets', $file);

View File

@@ -135,20 +135,41 @@ class Subscriber extends Model {
MP_SUBSCRIBER_CUSTOM_FIELD_TABLE . '.value END), NULL) as "' . $customField['name'].'"'); MP_SUBSCRIBER_CUSTOM_FIELD_TABLE . '.value END), NULL) as "' . $customField['name'].'"');
} }
$orm = $orm $orm = $orm
->left_outer_join( ->leftOuterJoin(
MP_SUBSCRIBER_CUSTOM_FIELD_TABLE, MP_SUBSCRIBER_CUSTOM_FIELD_TABLE,
array(MP_SUBSCRIBERS_TABLE.'.id', '=', array(MP_SUBSCRIBERS_TABLE.'.id', '=',
MP_SUBSCRIBER_CUSTOM_FIELD_TABLE.'.subscriber_id')) MP_SUBSCRIBER_CUSTOM_FIELD_TABLE.'.subscriber_id'))
->left_outer_join( ->leftOuterJoin(
MP_CUSTOM_FIELDS_TABLE, MP_CUSTOM_FIELDS_TABLE,
array(MP_CUSTOM_FIELDS_TABLE.'.id','=', array(MP_CUSTOM_FIELDS_TABLE.'.id','=',
MP_SUBSCRIBER_CUSTOM_FIELD_TABLE.'.custom_field_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; return $orm;
} }
function customFields() { function customFields() {
return $this->has_many_through( return $this->hasManyThrough(
__NAMESPACE__.'\CustomField', __NAMESPACE__.'\CustomField',
__NAMESPACE__.'\SubscriberCustomField', __NAMESPACE__.'\SubscriberCustomField',
'subscriber_id', 'subscriber_id',

View File

@@ -2,11 +2,14 @@
namespace MailPoet\Subscribers\ImportExport\Export; namespace MailPoet\Subscribers\ImportExport\Export;
use MailPoet\Config\Env; use MailPoet\Config\Env;
use MailPoet\Subscribers\ImportExport\BootStrapMenu; use MailPoet\Models\CustomField;
use MailPoet\Models\Segment; use MailPoet\Models\Segment;
use MailPoet\Models\Subscriber; use MailPoet\Models\Subscriber;
use MailPoet\Models\SubscriberSegment; use MailPoet\Models\SubscriberSegment;
use MailPoet\Subscribers\ImportExport\BootStrapMenu;
use MailPoet\Util\Helpers;
use MailPoet\Util\XLSXWriter; use MailPoet\Util\XLSXWriter;
use Symfony\Component\Console\Helper\Helper;
class Export { class Export {
public function __construct($data) { public function __construct($data) {
@@ -18,23 +21,22 @@ class Export {
$this->subscriberFields = $data['subscriberFields']; $this->subscriberFields = $data['subscriberFields'];
$this->profilerStart = microtime(true); $this->profilerStart = microtime(true);
$this->exportFile = sprintf( $this->exportFile = sprintf(
Env::$temp_path . '/mailpoet_export_%s.%s', Env::$temp_path . '/MailPoet_export_%s.%s',
substr(md5(time()), 0, 4), substr(md5(time()), 0, 4),
$this->exportFormatOption $this->exportFormatOption
); );
$this->exportFileURL = sprintf( $this->exportFileURL = sprintf(
'%s/%s/%s/%s', '%s/%s/%s',
plugins_url(), Env::$plugin_url,
Env::$plugin_name,
Env::$temp_name, Env::$temp_name,
basename($this->exportFile) basename($this->exportFile)
); );
} }
function process() { function process() {
$subscribers = SubscriberSegment:: $subscribers = Subscriber::
left_outer_join( left_outer_join(
Subscriber::$_table, SubscriberSegment::$_table,
array( array(
Subscriber::$_table . '.id', Subscriber::$_table . '.id',
'=', '=',
@@ -47,14 +49,33 @@ class Export {
'=', '=',
SubscriberSegment::$_table . '.segment_id' SubscriberSegment::$_table . '.segment_id'
)) ))
->select(Segment::$_table . '.name', 'segment_name')
->orderByAsc('segment_name') ->orderByAsc('segment_name')
->filter('filterWithCustomFields') ->filter('filterWithCustomFieldsForExport');
->whereIn(SubscriberSegment::$_table . '.segment_id', $this->segments); 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->groupBySegmentOption) $subscribers = $subscribers->groupBy(Subscriber::$_table . '.id');
if($this->exportConfirmedOption) $subscribers = $subscribers->where(Subscriber::$_table . '.status', 'confirmed'); if($this->exportConfirmedOption) $subscribers = $subscribers->where(Subscriber::$_table . '.status', 'confirmed');
$subscribers = $subscribers->findArray(); $subscribers = $subscribers->findArray();
$formattedSubscriberFields = $this->formatSubscriberFields($this->subscriberFields); $subscriberCustomFields = Helpers::arrayColumn(
CustomField::findArray(),
'name',
'id'
);
$formattedSubscriberFields = $this->formatSubscriberFields($this->subscriberFields, $subscriberCustomFields);
try { try {
if($this->exportFormatOption === 'csv') { if($this->exportFormatOption === 'csv') {
$CSVFile = fopen($this->exportFile, 'w'); $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 // 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)); fwrite($CSVFile, chr(0xEF) . chr(0xBB) . chr(0xBF));
if($this->groupBySegmentOption) $formattedSubscriberFields[] = __('List'); 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) { foreach ($subscribers as $subscriber) {
$row = array_map(function ($field) use ($subscriber) { $row = array_map(function ($field) use ($subscriber, $subscriberCustomFields) {
return $subscriber[$field]; return (isset($subscriberCustomFields[$field])) ? $subscriberCustomFields[$field] : $subscriber[$field];
}, $this->subscriberFields); }, $this->subscriberFields);
if($this->groupBySegmentOption) $row[] = $subscriber['segment_name']; if($this->groupBySegmentOption) {
fwrite($CSVFile, implode(",", array_map($formatCSV, $row)) . "\n"); $row[] = $subscriber['segment_name'];
}
fwrite($CSVFile, implode(',', array_map($formatCSV, $row)) . "\n");
} }
fclose($CSVFile); fclose($CSVFile);
} else { } else {
$writer = new XLSXWriter(); $writer = new XLSXWriter();
$writer->setAuthor('MailPoet (www.mailpoet.com)'); $writer->setAuthor('MailPoet (www.mailpoet.com)');
$headerRow = array($formattedSubscriberFields); $headerRow = array($formattedSubscriberFields);
$segment = null; $lastSegment = false;
$rows = array(); $rows = array();
foreach ($subscribers as $subscriber) { foreach ($subscribers as $subscriber) {
if($segment && $segment != $subscriber['segment_name'] && $this->groupBySegmentOption) { if($lastSegment && $lastSegment !== $subscriber['segment_name'] && $this->groupBySegmentOption) {
$writer->writeSheet(array_merge($headerRow, $rows), ucwords($segment)); $writer->writeSheet(array_merge($headerRow, $rows), ucwords($lastSegment));
$rows = array(); $rows = array();
} }
// detect RTL language and set Excel to properly display the sheet // 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; $writer->rtl = true;
} }
$rows[] = array_map(function ($field) use ($subscriber) { $rows[] = array_map(function ($field) use ($subscriber, $subscriberCustomFields) {
return $subscriber[$field]; return (isset($subscriberCustomFields[$field])) ? $subscriber[$subscriberCustomFields[$field]] : $subscriber[$field];
}, $this->subscriberFields); }, $this->subscriberFields);
$segment = $subscriber['segment_name']; $lastSegment = $subscriber['segment_name'];
} }
$writer->writeSheet(array_merge($headerRow, $rows), 'MailPoet'); $writer->writeSheet(array_merge($headerRow, $rows), 'MailPoet');
$writer->writeToFile($this->exportFile); $writer->writeToFile($this->exportFile);
@@ -107,17 +134,19 @@ class Export {
'data' => array( 'data' => array(
'totalExported' => count($subscribers), 'totalExported' => count($subscribers),
'exportFileURL' => $this->exportFileURL 'exportFileURL' => $this->exportFileURL
) ),
'profiler' => $this->timeExecution()
); );
} }
function formatSubscriberFields($subscriberFields) { function formatSubscriberFields($subscriberFields, $subscriberCustomFields) {
$bootStrapMenu = new BootStrapMenu(); $bootStrapMenu = new BootStrapMenu();
$translatedFields = $bootStrapMenu->getSubscriberFields(); $translatedFields = $bootStrapMenu->getSubscriberFields();
return array_map(function ($field) use ($translatedFields) { return array_map(function ($field) use ($translatedFields, $subscriberCustomFields) {
return (isset($translatedFields[$field])) ? $field = (isset($translatedFields[$field])) ?
ucfirst($translatedFields[$field]) : ucfirst($translatedFields[$field]) :
ucfirst($field); ucfirst($field);
return (isset($subscriberCustomFields[$field])) ? $subscriberCustomFields[$field] : $field;
}, $subscriberFields); }, $subscriberFields);
} }

View File

@@ -75,7 +75,7 @@ class Import {
'updated' => count($updatedSubscribers), 'updated' => count($updatedSubscribers),
'segments' => $segments->getSegments() 'segments' => $segments->getSegments()
), ),
'profile' => $this->timeExecution() 'profiler' => $this->timeExecution()
); );
} }

View File

@@ -1,12 +1,84 @@
<?php <?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 { class ExportCest {
function __construct() { 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() { 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() { function itCanProcess() {
@@ -19,5 +91,7 @@ class ExportCest {
->deleteMany(); ->deleteMany();
ORM::forTable(SubscriberSegment::$_table) ORM::forTable(SubscriberSegment::$_table)
->deleteMany(); ->deleteMany();
ORM::forTable(Segment::$_table)
->deleteMany();
} }
} }

View File

@@ -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