- Finishes import migration

- Updates models
- Improves Notice.js
This commit is contained in:
MrCasual
2015-11-06 21:28:24 -05:00
parent 158d26ef86
commit 3f168d052f
16 changed files with 586 additions and 139 deletions

View File

@ -145,7 +145,7 @@ define(
mailChimpListsContainerElement.hide();
jQuery('.mailpoet_mailchimp-key-status').html('').removeClass('mailpoet_mailchimp-ok mailpoet_mailchimp-error');
mailChimpKeyVerifyButtonEelement.prop('disabled', true);
toggleNextStepButton(mailChimpProcessButtonElement, 'on');
toggleNextStepButton(mailChimpProcessButtonElement, 'off');
}
else {
mailChimpKeyVerifyButtonEelement.prop('disabled', false);
@ -158,21 +158,21 @@ define(
endpoint: 'import',
action: 'getMailChimpLists',
data: {api_key: mailChimpKeyInputElement.val()}
}).done(function (result) {
if (result.status !== 'success') {
}).done(function (request) {
if (request.result === false) {
MailPoet.Notice.hide();
MailPoet.Notice.error(result.message);
MailPoet.Notice.error(request.message);
jQuery('.mailpoet_mailchimp-key-status').removeClass().addClass('mailpoet_mailchimp-key-status mailpoet_mailchimp-error');
mailChimpListsContainerElement.hide();
toggleNextStepButton(mailChimpProcessButtonElement, 'off');
} else {
jQuery('.mailpoet_mailchimp-key-status').html('').removeClass().addClass('mailpoet_mailchimp-key-status mailpoet_mailchimp-ok');
if (!result.data) {
if (!request.data) {
jQuery('.mailpoet_mailchimp-key-status').html(MailPoetI18n.noMailChimpLists);
mailChimpListsContainerElement.hide();
toggleNextStepButton(mailChimpProcessButtonElement, 'off');
} else {
displayMailChimpLists(result.data);
displayMailChimpLists(request.data);
}
}
MailPoet.Modal.loading(false);
@ -195,17 +195,17 @@ define(
api_key: mailChimpKeyInputElement.val(),
lists: mailChimpListsContainerElement.find('select').val()
}
}).done(function (result) {
if (result.status === 'success') {
importData.step1 = result;
}).done(function (request) {
if (request.result === true) {
importData.step1 = request.data;
router.navigate('step2', {trigger: true});
}
else {
MailPoet.Notice.hide();
MailPoet.Notice.error(result.message);
MailPoet.Notice.error(request.message);
}
MailPoet.Modal.loading(false);
}).error(function (error) {
}).error(function () {
MailPoet.Modal.loading(false);
MailPoet.Notice.error(MailPoetI18n.serverError + result.statusText.toLowerCase() + '.');
});
@ -376,7 +376,7 @@ define(
return;
}
// define reusable variables
var nextStepButton = jQuery('#step_2_process'),
var nextStepButton = jQuery('#step2_process'),
subscribers = jQuery.extend(true, {}, importData.step1), // create a copy of subscribers object for further manipulation
subscribersDataTemplate = Handlebars.compile(jQuery('#subscribers_data_template').html()),
subscribersDataTemplatePartial = Handlebars.compile(jQuery('#subscribers_data_template_partial').html()),
@ -458,7 +458,7 @@ define(
MailPoet.Notice.error(MailPoetI18n.segmentSelectionRequired, {
static: true,
scroll: true,
addCustomClass: 'segmentSelection',
id: 'segmentSelection',
hideClose: true
});
}
@ -510,18 +510,18 @@ define(
description: segmentDescription
}
})
.done(function (result) {
if (result.status === 'success') {
.done(function (request) {
if (request.result === true) {
mailpoetLists.push({
'id': result.segment.id,
'name': result.segment.name
'id': request.segment.id,
'name': request.segment.name
});
var selected_values = segmentSelectElement.val();
if (selected_values === null) {
selected_values = [result.segment.id]
selected_values = [request.segment.id]
} else {
selected_values.push(result.segment.id);
selected_values.push(request.segment.id);
}
enableListSelection(mailpoetLists);
@ -532,7 +532,7 @@ define(
}
else {
MailPoet.Modal.close();
MailPoet.Notice.error(MailPoetI18n.segmentCreateError + result.message + '.');
MailPoet.Notice.error(MailPoetI18n.segmentCreateError + request.message + '.');
}
})
.error(function (error) {
@ -559,7 +559,7 @@ define(
column_id = 'ignore'; // set default column type
// if the column is not undefined and has a valid e-mail, set type as email
if (column_data % 1 !== 0 && emailRegex.test(column_data)) {
column_id = 'subscriber_email';
column_id = 's_email';
} else if (subscribers.header) {
var header_name = subscribers.header[i],
header_name_match = mailpoet_columns.map(function (el) {
@ -571,15 +571,15 @@ define(
}// set column type using header name
else if (header_name) {
if (/first|first name|given name/i.test(header_name)) {
column_id = 'subscriber_firstname';
column_id = 's_first_name';
} else if (/last|last name/i.test(header_name)) {
column_id = 'subscriber_lastname';
column_id = 's_last_name';
} else if (/status/i.test(header_name)) {
column_id = 'subscriber_state';
column_id = 's_status';
} else if (/subscribed|subscription/i.test(header_name)) {
column_id = 'subscriber_confirmed_at';
column_id = 's_confirmed_at';
} else if (/ip/i.test(header_name)) {
column_id = 'subscriber_confirmed_ip';
column_id = 's_confirmed_ip';
}
}
}
@ -681,10 +681,10 @@ define(
type: type
}
})
.done(function (result) {
if (result.status === 'success') {
.done(function (request) {
if (request.result === true) {
var new_column_data = {
'id': result.customField.id,
'id': request.customField.id,
'name': name,
'type': type,
'custom': true,
@ -759,7 +759,7 @@ define(
// filter subscribers' data to detect dates, emails, etc.
function filterSubscribers() {
jQuery('.mailpoet_invalidEmail, .mailpoet_invalidDate').remove();
jQuery('[data-id="notice_invalidEmail"], [data-id="notice_invalidDate"]').remove();
var subscribersClone = jQuery.extend(true, {}, subscribers),
preventNextStep = false,
@ -775,15 +775,15 @@ define(
var matchedColumn = jQuery.inArray(column.id, displayedColumnsIds);
// EMAIL filter: if the last value in the column doesn't have a valid email, hide the next button
if (column.id === "subscriber_email") {
if (column.id === "s_email") {
if (!emailRegex.test(subscribersClone.subscribers[0][matchedColumn])) {
preventNextStep = true;
if (!jQuery('.mailpoet_invalidEmail').length) {
if (!jQuery('[data-id="notice_invalidEmail"]').length) {
MailPoet.Notice.error(MailPoetI18n.columnContainsInvalidElement, {
static: true,
scroll: true,
hideClose: true,
addCustomClass: 'invalidEmail'
id: 'invalidEmail'
});
}
}
@ -839,7 +839,7 @@ define(
static: true,
scroll: true,
hideClose: true,
addCustomClass: 'invalidDate'
id: 'invalidDate'
});
}
}
@ -899,23 +899,62 @@ define(
segments: segmentSelectElement.val(),
updateSubscribers: (jQuery(':radio[name="subscriber_update_option"]:checked').val() === 'yes') ? true : false
})
}).done(function (result) {
if (result.status !== 'success') {
MailPoet.Notice.error(result.message);
}).done(function (request) {
MailPoet.Modal.loading(false);
if (request.result === false) {
MailPoet.Notice.error(request.error);
} else {
alert('Processed ' + result.count)
request.data.lists = [];
importData.step2 = request.data;
router.navigate('step3', {trigger: true});
}
}).error(function (error) {
MailPoet.Modal.loading(false);
MailPoet.Notice.error(MailPoetI18n.serverError + error.statusText.toLowerCase() + '.');
});
MailPoet.Modal.loading(false);
});
filterSubscribers();
enableListSelection(mailpoetLists);
});
router.on('route:step3', function () {
if (typeof (importData.step2) === 'undefined') {
router.navigate('step2', {trigger: true});
return;
}
showCurrentStep();
// display statistics
var subscribers_data_import_results_template = Handlebars.compile(jQuery('#subscribers_data_import_results_template').html()),
import_results = {
added: (importData.step2.added) ? MailPoetI18n.subscribersAdded.replace('%1$s', '<strong>' + importData.step2.added + '</strong>').replace('%2$s', '"' + importData.step2.lists.join('", "') + '"') : false,
updated: (importData.step2.updated) ? MailPoetI18n.subscribersUpdated.replace('%1$s', '<strong>' + importData.step2.updated + '</strong>').replace('%2$s', '"' + importData.step2.lists.join('", "') + '"') : false,
noaction: (!importData.step2.updated && !importData.step2.added) ? true : false
},
export_menu_item = jQuery('span.mailpoet_export');
jQuery('#subscribers_data_import_results').html(subscribers_data_import_results_template(import_results)).show();
jQuery('a.mailpoet_import_again').off().click(function () {
jQuery("#subscribers_data_import_results").hide();
router.navigate('step1', {trigger: true});
});
jQuery('a.mailpoet_view_subscribers').off().click(function () {
window.location.href = 'admin.php?page=mailpoet-subscribers';
});
// if new subscribers were added and the export menu item is hidden (it's shown only when there are subscribers), display it
if (import_results.added && export_menu_item.not(':visible')) {
export_menu_item.show();
}
// reset previous step's data so that coming back to this step is prevented
importData.step2 = undefined;
});
if (!Backbone.History.started) {
Backbone.history.start();

View File

@ -48,7 +48,7 @@ define('notice', ['mailpoet', 'jquery'], function(MailPoet, jQuery) {
message: '',
static: false,
hideClose: false,
addCustomClass: false,
id: null,
scroll: false,
timeout: 2000,
onOpen: null,
@ -62,8 +62,8 @@ define('notice', ['mailpoet', 'jquery'], function(MailPoet, jQuery) {
// clone element
this.element = jQuery('#mailpoet_notice_'+this.options.type).clone();
// add custom identifier class to the element
if (this.options.addCustomClass) this.element.addClass('mailpoet_'+this.options.addCustomClass);
// add data-id to the element
if (this.options.id) this.element.attr('data-id', 'notice_' + this.options.id);
// remove id from clone
this.element.removeAttr('id');
@ -168,12 +168,12 @@ define('notice', ['mailpoet', 'jquery'], function(MailPoet, jQuery) {
if(all !== undefined && all === true) {
jQuery('.mailpoet_notice:not([id])').trigger('close');
} else if (all !== undefined && jQuery.isArray(all)) {
for (var noticeClass in all) {
jQuery('.mailpoet_'+all[noticeClass])
for (var id in all) {
jQuery('[data-id="notice_' + all[id] + '"]')
.trigger('close');
}
} if (all !== undefined) {
jQuery('.mailpoet_'+noticeClass)
jQuery('[data-id="notice_' + all + '"]')
.trigger('close');
} else {
jQuery('.mailpoet_notice.updated:not([id]), .mailpoet_notice.error:not([id])')

View File

@ -17,6 +17,8 @@ class Env {
public static $db_prefix;
public static $db_source_name;
public static $db_host;
public static $db_socket;
public static $db_port;
public static $db_name;
public static $db_username;
public static $db_password;
@ -35,22 +37,33 @@ class Env {
self::$lib_path = self::$path . '/lib';
self::$plugin_prefix = 'mailpoet_';
self::$db_prefix = $wpdb->prefix . self::$plugin_prefix;
self::$db_source_name = self::dbSourceName();
self::$db_host = DB_HOST;
self::$db_port = 3306;
self::$db_socket = false;
if (preg_match('/(?=:\d+$)/', DB_HOST)) {
list(self::$db_host, self::$db_port) = explode(':', DB_HOST);
}
else if (preg_match('/:/', DB_HOST)) {
self::$db_socket = true;
}
self::$db_name = DB_NAME;
self::$db_username = DB_USER;
self::$db_password = DB_PASSWORD;
self::$db_charset = $wpdb->get_charset_collate();
self::$db_source_name = self::dbSourceName(self::$db_host, self::$db_socket, self::$db_port);
}
private static function dbSourceName() {
private static function dbSourceName($host, $socket,$port) {
$source_name = array(
'mysql:host=',
DB_HOST,
(!$socket) ? 'mysql:host=' : 'mysql:unix_socket=',
$host,
';',
'port=',
$port,
';',
'dbname=',
DB_NAME
);
return implode('', $source_name);
}
}
}

View File

@ -166,7 +166,8 @@ class Migrator {
'value varchar(255) NOT NULL,',
'created_at TIMESTAMP NOT NULL DEFAULT 0,',
'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
'PRIMARY KEY (id)'
'PRIMARY KEY (id),',
'UNIQUE KEY subscriber_id_custom_field_id (subscriber_id,custom_field_id)'
);
return $this->sqlify(__FUNCTION__, $attributes);
}
@ -192,7 +193,8 @@ class Migrator {
'value varchar(255) NOT NULL,',
'created_at TIMESTAMP NOT NULL DEFAULT 0,',
'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
'PRIMARY KEY (id)'
'PRIMARY KEY (id),',
'UNIQUE KEY newsletter_id_option_field_id (newsletter_id,option_field_id)'
);
return $this->sqlify(__FUNCTION__, $attributes);
}

View File

@ -5,43 +5,43 @@ use MailPoet\Models\Segment;
use MailPoet\Util\Helpers;
class BootstrapMenu {
function __construct() {
$this->subscriberFields = $this->getSubscriberFields();
$this->subscriberCustomFields = $this->getSubscriberCustomFields();
$this->segments = $this->getSegments();
}
function getSubscriberFields() {
return array(
'subscriber_email' => __("Email"),
'subscriber_firstname' => __("First name"),
'subscriber_lastname' => __("Last name"),
/* 'subscriber_confirmed_ip' => __("IP address"),
'subscriber_confirmed_at' => __("Subscription date"),*/
'subscriber_state' => __("Status")
's_email' => __('Email'),
's_first_name' => __('First name'),
's_last_name' => __('Last name'),
/* 's_confirmed_ip' => __('IP address'),
's_confirmed_at' => __('Subscription date'),*/
's_status' => __('Status')
);
}
function getSegments() {
return Segment::findArray();
}
function getSubscriberCustomFields() {
return CustomField::findArray();
}
function formatSubscriberFields() {
return array_map(function ($fieldId, $fieldName) {
return array(
'id' => $fieldId,
'name' => $fieldName,
'type' => ($fieldId === 'subscriber_confirmed_at') ? 'date' : null,
'type' => ($fieldId === 's_confirmed_at') ? 'date' : null,
'custom' => false
);
}, array_keys($this->subscriberFields), $this->subscriberFields);
}
function formatSubscriberCustomFields() {
return array_map(function ($field) {
return array(
@ -53,36 +53,36 @@ class BootstrapMenu {
);
}, $this->subscriberCustomFields);
}
function formatSubscriberFieldsSelect2() {
$select2Fields = array(
array(
'name' => __("Actions"),
'name' => __('Actions'),
'children' => array(
array(
'id' => 'ignore',
'name' => __("Ignore column..."),
'name' => __('Ignore column...'),
),
array(
'id' => 'create',
'name' => __("Create new column...")
'name' => __('Create new column...')
),
)
),
array(
'name' => __("System columns"),
'name' => __('System columns'),
'children' => $this->formatSubscriberFields()
)
);
if($this->subscriberCustomFields) {
array_push($select2Fields, array(
'name' => __("User columns"),
'name' => __('User columns'),
'children' => $this->formatSubscriberCustomFields()
));
));
}
return $select2Fields;
}
function bootstrap() {
$data['segments'] = array_map(function ($segment) {
return array(
@ -90,14 +90,14 @@ class BootstrapMenu {
'name' => $segment['name'],
);
}, $this->getSegments());
$data['subscriberFields'] = array_merge(
$this->formatSubscriberFields(),
$this->formatSubscriberCustomFields()
);
$data['subscriberFieldsSelect2'] = $this->formatSubscriberFieldsSelect2();
$data = array_map('json_encode', $data);
$data['maxPostSizeBytes'] = Helpers::getMaxPostSize('bytes');
$data['maxPostSize'] = Helpers::getMaxPostSize();

View File

@ -1,5 +1,9 @@
<?php namespace MailPoet\Import;
use MailPoet\Models\Subscriber;
use MailPoet\Models\SubscriberCustomField;
use MailPoet\Util\Helpers;
class Import {
public function __construct($data) {
$this->subscribersData = $data['subscribers'];
@ -7,36 +11,136 @@ class Import {
$this->updateSubscribers = $data['updateSubscribers'];
$this->subscriberFields = $this->getSubscriberFields();
$this->subscriberCustomFields = $this->getCustomSubscriberFields();
$this->currentTime = time();
$this->subscribersCount = count(reset($this->subscribersData));
$this->currentTime = date('Y-m-d H:i:s');
$this->profilerStart = microtime(true);
}
function process() {
// :)
return array(
'status' => 'success',
'count' => count($this->subscribersData['subscriber_email'])
$subscriberFields = $this->subscriberFields;
$subscribersData = $this->subscribersData;
$subscribersData = $this->filterSubscriberState($subscribersData);
list($subscribersData, $subscriberFields) = $this->extendSubscribersAndFields(
$subscribersData, $subscriberFields
);
if(in_array('subscriber_status', $subscriberFields)) {
$this->subscribersData['subscriber_state'] = $this->filterSubscriberState(
$this->subscribersData['subscriber_state']
list($existingSubscribers, $newSubscribers) = $this->splitSubscribers(
$subscribersData
);
$addedSubscribers = $updatedSubscribers = array();
if($newSubscribers) {
$addedSubscribers = $this->addOrUpdateSubscribers(
'create',
$newSubscribers,
$subscriberFields
);
}
if($existingSubscribers && $this->updateSubscribers) {
$updatedSubscribers = $this->addOrUpdateSubscribers(
'update',
$existingSubscribers,
$subscriberFields
);
if($addedSubscribers) {
$updatedSubscribers = array_diff_key(
$updatedSubscribers,
$addedSubscribers
);
}
}
return array(
'result' => true,
'data' => array(
'added' => count($addedSubscribers),
'updated' => count($updatedSubscribers),
),
'profile' => $this->timeExecution()
);
}
function splitSubscribers($subscribersData) {
$existingRecords = array_filter(
array_map(function ($subscriberEmails) {
return Subscriber::selectMany(array('email'))
->whereIn('email', $subscriberEmails)
->findArray();
}, array_chunk($subscribersData['s_email'], 200))
);
if(!$existingRecords) {
return array(
false,
$subscribersData
);
}
$existingRecords = Helpers::flattenArray($existingRecords);
$newRecords = array_keys(
array_diff(
$subscribersData['s_email'],
$existingRecords
)
);
if(!$newRecords) {
return array(
$subscribersData,
false
);
}
$newSubscribers =
array_filter(
array_map(function ($subscriber) use ($newRecords) {
return array_map(function ($index) use ($subscriber) {
return $subscriber[$index];
}, $newRecords);
}, $subscribersData)
);
$existingSubscribers =
array_map(function ($subscriber) use ($newRecords) {
return array_values( // reindex array
array_filter( // remove NULL entries
array_map(function ($index, $data) use ($newRecords) {
if(!in_array($index, $newRecords)) return $data;
}, array_keys($subscriber), $subscriber)
)
);
}, $subscribersData);
return array(
$existingSubscribers,
$newSubscribers
);
}
function extendSubscribersAndFields($subscribersData, $subscriberFields) {
$subscribersData['created_at'] = $this->filterSubscriberCreatedAtDate();
$subscriberFields[] = 'created_at';
return array(
$subscribersData,
$subscriberFields
);
}
function getSubscriberFields() {
return array_map(function ($field) {
if(!is_int($field)) return $field;
}, array_keys($this->subscribersData));
return array_filter(
array_map(function ($field) {
if(!is_int($field)) return $field;
}, array_keys($this->subscribersData))
);
}
function getCustomSubscriberFields() {
return array_map(function ($field) {
if(is_int($field)) return $field;
}, array_keys($this->subscribersData));
return array_filter(
array_map(function ($field) {
if(is_int($field)) return $field;
}, array_keys($this->subscribersData))
);
}
function filterSubscriberState($data) {
function filterSubscriberCreatedAtDate() {
return array_fill(0, $this->subscribersCount, $this->currentTime);
}
function filterSubscriberState($subscribersData) {
if(!in_array('s_status', $this->subscriberFields)) return;
$states = array(
'subscribed' => array(
'subscribed',
@ -52,8 +156,7 @@ class Import {
'false'
)
);
return array_map(function ($state) use ($states) {
$subscribersData['s_status'] = array_map(function ($state) use ($states) {
if(in_array(strtolower($state), $states['subscribed'])) {
return 1;
}
@ -61,7 +164,92 @@ class Import {
return -1;
}
return 1; // make "subscribed" a default state
}, $data);
}, $subscribersData['s_status']);
return $subscribersData;
}
function addOrUpdateSubscribers($action, $subscribersData, $subscriberFields) {
$subscribersCount = count(reset($subscribersData)) - 1;
$subscribers = array_map(function ($index) use ($subscribersData, $subscriberFields) {
return array_map(function ($field) use ($index, $subscribersData) {
return $subscribersData[$field][$index];
}, $subscriberFields);
}, range(0, $subscribersCount));
$subscriberFields = str_replace('s_', '', $subscriberFields);
$currentTime = ($action === 'update') ? date('Y-m-d H:i:s') : $this->currentTime;
foreach (array_chunk($subscribers, 200) as $data) {
try {
if($action == 'create') {
Subscriber::createMultiple(
$subscriberFields,
$data
);
}
if($action == 'update') {
Subscriber::updateMultiple(
$subscriberFields,
$data,
$currentTime
);
}
} catch (\PDOException $e) {
throw new \Exception($e->getMessage());
}
}
$result = Helpers::arrayColumn( // return id=>email array of results
Subscriber::selectMany(
array(
'id',
'email'
))
->where(($action === 'create') ? 'created_at' : 'updated_at', $currentTime)
->findArray(),
'email', 'id'
);
if($this->subscriberCustomFields) {
$this->addOrUpdateCustomFields(
($action === 'create') ? 'create' : 'update',
$result,
$subscribersData
);
}
return $result;
}
function addOrUpdateCustomFields($action, $dbSubscribers, $subscribersData) {
$subscribers = array_map(
function ($column) use ($dbSubscribers, $subscribersData) {
$count = range(0, count($subscribersData[$column]) - 1);
return array_map(
function ($index, $value)
use ($dbSubscribers, $subscribersData, $column) {
$subscriberId = array_search(
$subscribersData['s_email'][$index],
$dbSubscribers
);
return array(
$column,
$subscriberId,
$value
);
}, $count, $subscribersData[$column]);
}, $this->subscriberCustomFields)[0];
foreach (array_chunk($subscribers, 200) as $data) {
try {
if($action === 'create') {
SubscriberCustomField::createMultiple(
$data
);
}
if($action === 'update') {
SubscriberCustomField::updateMultiple(
$data
);
}
} catch (\PDOException $e) {
throw new \Exception($e->getMessage());
}
}
}
function timeExecution() {

View File

@ -47,7 +47,7 @@ class MailChimp {
}
return array(
'status' => 'success',
'result' => true,
'data' => $lists
);
}
@ -108,12 +108,14 @@ class MailChimp {
}
return array(
'status' => 'success',
'data' => $subscribers,
'invalid' => false,
'duplicate' => false,
'header' => $header,
'count' => count($subscribers)
'result' => true,
'data' => array(
'subscribers' => $subscribers,
'invalid' => false,
'duplicate' => false,
'header' => $header,
'count' => count($subscribers)
)
);
}
@ -128,24 +130,24 @@ class MailChimp {
private function processError($error) {
switch ($error) {
case 'API':
$message = __('Invalid API key.');
break;
case 'connection':
$message = __('Could not connect to your MailChimp account.');
break;
case 'headers':
$message = __('The selected lists do not have matching columns (headers).');
break;
case 'size':
$message = __('Information received from MailChimp is too large for processing. Please limit the number of lists.');
break;
case 'subscribers':
$message = __('Did not find any active subscribers.');
break;
case 'lists':
$message = __('Did not find any valid lists');
break;
case 'API':
$message = __('Invalid API key.');
break;
case 'connection':
$message = __('Could not connect to your MailChimp account.');
break;
case 'headers':
$message = __('The selected lists do not have matching columns (headers).');
break;
case 'size':
$message = __('Information received from MailChimp is too large for processing. Please limit the number of lists.');
break;
case 'subscribers':
$message = __('Did not find any active subscribers.');
break;
case 'lists':
$message = __('Did not find any valid lists');
break;
}
return array(
'status' => 'error',

View File

@ -38,12 +38,12 @@ class CustomField extends Model {
}
function subscribers() {
return $this->has_many_through(
return $this->hasManyThrough(
__NAMESPACE__ . '\Subscriber',
__NAMESPACE__ . '\SubscriberCustomField',
'custom_field_id',
'subscriber_id'
)->select_expr(MP_SUBSCRIBER_CUSTOM_FIELD_TABLE.'.value');
)->selectExpr(MP_SUBSCRIBER_CUSTOM_FIELD_TABLE . '.value');
}
static function createOrUpdate($data = array()) {

View File

@ -1,6 +1,7 @@
<?php
namespace MailPoet\Models;
use MailPoet\Util\Helpers;
if(!defined('ABSPATH')) exit;
class Subscriber extends Model {
@ -302,4 +303,52 @@ class Subscriber extends Model {
->whereNull('deleted_at')
->where('status', 'unconfirmed');
}
static function createMultiple($columns, $values) {
return self::rawExecute(
'INSERT IGNORE INTO `' . self::$_table . '` ' .
'(' . implode(', ', $columns) . ') ' .
'VALUES ' . rtrim(
str_repeat(
'(' . rtrim(str_repeat('?,', count($columns)), ',') . ')' . ', '
, count($values)
)
, ', '),
Helpers::flattenArray($values)
);
}
static function updateMultiple($columns, $subscribers, $currentTime) {
$ignoreColumnsOnUpdate = array(
'email',
'created_at'
);
$emailPosition = array_search('email', $columns);
$sql = function ($type) use ($columns, $subscribers, $emailPosition, $ignoreColumnsOnUpdate) {
return array_filter(
array_map(function ($columnPosition, $columnName) use ($type, $subscribers, $emailPosition, $ignoreColumnsOnUpdate) {
if(in_array($columnName, $ignoreColumnsOnUpdate)) return;
$query = array_map(
function ($subscriber) use ($type, $columnPosition, $emailPosition) {
return ($type === 'values') ?
array(
$subscriber[$emailPosition],
$subscriber[$columnPosition]
) :
'WHEN email = ? THEN ?';
}, $subscribers);
return ($type === 'values') ?
Helpers::flattenArray($query) :
$columnName . '= (CASE ' . implode(' ', $query) . ' END)';
}, array_keys($columns), $columns)
);
};
return self::rawExecute(
'UPDATE `' . self::$_table . '` ' .
'SET ' . implode(', ', $sql('statement')) . ', ' .
'updated_at = "' . $currentTime . '" ' .
'WHERE email IN (' . rtrim(str_repeat('?,', count($subscribers)), ',') . ')',
array_merge(Helpers::flattenArray($sql('values')), Helpers::arrayColumn($subscribers, $emailPosition))
);
}
}

View File

@ -1,6 +1,8 @@
<?php
namespace MailPoet\Models;
use MailPoet\Util\Helpers;
if(!defined('ABSPATH')) exit;
class SubscriberCustomField extends Model {
@ -9,4 +11,38 @@ class SubscriberCustomField extends Model {
function __construct() {
parent::__construct();
}
static function createMultiple($values) {
return self::rawExecute(
'INSERT IGNORE INTO `' . self::$_table . '` ' .
'(custom_field_id, subscriber_id, value) ' .
'VALUES ' . rtrim(
str_repeat(
'(?, ?, ?)' . ', '
, count($values)
), ', '
),
Helpers::flattenArray($values)
);
}
static function updateMultiple($subscribers) {
self::createMultiple($subscribers);
self::rawExecute(
'UPDATE `' . self::$_table . '` ' .
'SET value = ' .
'(CASE ' .
str_repeat(
'WHEN custom_field_id = ? AND subscriber_id = ? THEN ? ',
count($subscribers)
) .
'END) ' .
'WHERE subscriber_id IN (' .
implode(', ', Helpers::arrayColumn($subscribers, 1)) .
') AND custom_field_id IN (' .
implode(', ', array_unique(Helpers::arrayColumn($subscribers, 0)))
. ') ',
Helpers::flattenArray($subscribers)
);
}
}

View File

@ -21,14 +21,13 @@ class Import {
function addSegment($data) {
$segment = Segment::createOrUpdate($data, $returnObject = true);
wp_send_json(
(!is_array($segment)) ?
(!is_object($segment)) ?
array(
'status' => 'error',
'message' => $segment
'result' => false,
) :
array(
'status' => 'success',
'segment' => $segment
'result' => true,
'segment' => $segment->asArray()
)
);
}
@ -40,17 +39,27 @@ class Import {
wp_send_json(
(!$result) ?
array(
'status' => 'error'
'result' => false
) :
array(
'status' => 'success',
'result' => true,
'customField' => $customField->asArray()
)
);
}
function process($data) {
$data = file_get_contents(dirname(__FILE__) . '/../../export.txt');
$import = new \MailPoet\Import\Import(json_decode($data, true));
wp_send_json($import->process());
try {
wp_send_json($import->process());
} catch (\Exception $e) {
wp_send_json(
array(
'result' => false,
'error' => $e->getMessage()
)
);
}
}
}

View File

@ -75,7 +75,7 @@ class Helpers {
static function getMaxPostSize($bytes = false) {
$maxPostSize = ini_get('post_max_size');
if (!$bytes) return $maxPostSize;
if(!$bytes) return $maxPostSize;
$maxPostSizeBytes = (int) $maxPostSize;
$unit = strtolower($maxPostSize[strlen($maxPostSize) - 1]);
switch ($unit) {
@ -90,8 +90,82 @@ class Helpers {
}
static function flattenArray($array) {
return call_user_func_array(
'array_merge_recursive', array_map('array_values', $array)
);
if(!$array) return;
$flattened_array = array();
array_walk_recursive($array, function ($a) use (&$flattened_array) { $flattened_array[] = $a; });
return $flattened_array;
}
}
/*
* Using func_get_args() in order to check for proper number ofparameters and trigger errors exactly as the built-in array_column()
* does in PHP 5.5.
* @author Ben Ramsey (http://benramsey.com)
*/
static function arrayColumn($input = null, $columnKey = null, $indexKey = null) {
$argc = func_num_args();
$params = func_get_args();
if($argc < 2) {
trigger_error("array_column() expects at least 2 parameters, {$argc} given", E_USER_WARNING);
return null;
}
if(!is_array($params[0])) {
trigger_error(
'array_column() expects parameter 1 to be array, ' . gettype($params[0]) . ' given',
E_USER_WARNING
);
return null;
}
if(!is_int($params[1])
&& !is_float($params[1])
&& !is_string($params[1])
&& $params[1] !== null
&& !(is_object($params[1]) && method_exists($params[1], '__toString'))
) {
trigger_error('array_column(): The column key should be either a string or an integer', E_USER_WARNING);
return false;
}
if(isset($params[2])
&& !is_int($params[2])
&& !is_float($params[2])
&& !is_string($params[2])
&& !(is_object($params[2]) && method_exists($params[2], '__toString'))
) {
trigger_error('array_column(): The index key should be either a string or an integer', E_USER_WARNING);
return false;
}
$paramsInput = $params[0];
$paramsColumnKey = ($params[1] !== null) ? (string) $params[1] : null;
$paramsIndexKey = null;
if(isset($params[2])) {
if(is_float($params[2]) || is_int($params[2])) {
$paramsIndexKey = (int) $params[2];
} else {
$paramsIndexKey = (string) $params[2];
}
}
$resultArray = array();
foreach ($paramsInput as $row) {
$key = $value = null;
$keySet = $valueSet = false;
if($paramsIndexKey !== null && array_key_exists($paramsIndexKey, $row)) {
$keySet = true;
$key = (string) $row[$paramsIndexKey];
}
if($paramsColumnKey === null) {
$valueSet = true;
$value = $row;
} elseif(is_array($row) && array_key_exists($paramsColumnKey, $row)) {
$valueSet = true;
$value = $row[$paramsColumnKey];
}
if($valueSet) {
if($keySet) {
$resultArray[$key] = $value;
} else {
$resultArray[] = $value;
}
}
}
return $resultArray;
}
}

View File

@ -8,6 +8,7 @@
<!-- STEP 2: subscriber data manipulation -->
<% include 'import/step2.html' %>
<!-- STEP 3: results -->
<% include 'import/step3.html' %>
</div>
<%= stylesheet('import.css') %>
@ -52,7 +53,9 @@
'columnContainsInvalidDate': __('One of the columns contains an invalid date. Please fix before continuing.'),
'listCreateError': __('Error adding a new segment:'),
'columnContainsInvalidElement': __('One of the columns contains an invalid email. Please fix before continuing.'),
'customFieldCreateError': __('Custom field could not be created.')
'customFieldCreateError': __('Custom field could not be created.'),
'subscribersAdded': __('%1$s subscribers added to %2$s.'),
'subscribersUpdated': __('%1$s existing subscribers were updated and added to %2$s.')
}) %>
<script type="text/javascript">

View File

@ -48,7 +48,7 @@
</tbody>
</table>
<div class="mailpoet_method_process">
<!-- WILL BE INSERTED: Next button & spam notice template -->
<!-- Template data -->
</div>
</div>
@ -82,7 +82,7 @@
</tbody>
</table>
<div class="mailpoet_method_process">
<!-- WILL BE INSERTED: Next button & spam notice template -->
<!-- Template data -->
</div>
</div>
@ -115,7 +115,7 @@
</tbody>
</table>
<div class="mailpoet_method_process">
<!-- WILL BE INSERTED: Next button & spam notice template -->
<!-- Template data -->
</div>
</div>

View File

@ -79,7 +79,7 @@
</tr>
<tr>
<th>
<a href="javascript:;" id="step_2_process"
<a href="javascript:;" id="step2_process"
class="button-primary wysija disabled"><%= __('Next step') %> </a>
</th>
</tr>

32
views/import/step3.html Normal file
View File

@ -0,0 +1,32 @@
<div id="step3" class="mailpoet_hidden">
<div id="subscribers_data_import_results" class="mailpoet_hidden notice">
<!-- Template data -->
</div>
<table class="mailpoet_subscribers form-table">
<tbody>
<tr>
<th scope="row">
<a href="javascript:;"
class="button-primary wysija mailpoet_import_again"><%= __('Import again') %></a>
<a href="javascript:;"
class="button-primary wysija mailpoet_view_subscribers"><%= __('View subscribers') %></a>
</th>
</tr>
</tbody>
</table>
<script id="subscribers_data_import_results_template" type="text/x-handlebars-template">
<ul>
{{#if added}}
<li>{{{added}}}</li>
{{/if}}
{{#if updated}}
<li>{{{updated}}}</li>
{{/if}}
{{#if noaction}}
<li><%= __('No new subscribers were found/added.') %></li>
{{/if}}
</ul>
</script>
</div>