diff --git a/assets/js/src/import/import.js b/assets/js/src/import/import.js
index a428fc3767..6ebe9c32d8 100644
--- a/assets/js/src/import/import.js
+++ b/assets/js/src/import/import.js
@@ -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', '' + importData.step2.added + '').replace('%2$s', '"' + importData.step2.lists.join('", "') + '"') : false,
+ updated: (importData.step2.updated) ? MailPoetI18n.subscribersUpdated.replace('%1$s', '' + importData.step2.updated + '').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();
diff --git a/assets/js/src/notice.js b/assets/js/src/notice.js
index 2eee98f989..a196426088 100644
--- a/assets/js/src/notice.js
+++ b/assets/js/src/notice.js
@@ -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])')
diff --git a/lib/Config/Env.php b/lib/Config/Env.php
index b5d968e52b..12c48a3604 100644
--- a/lib/Config/Env.php
+++ b/lib/Config/Env.php
@@ -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);
}
-}
+}
\ No newline at end of file
diff --git a/lib/Config/Migrator.php b/lib/Config/Migrator.php
index 5814f304df..81bd028fee 100644
--- a/lib/Config/Migrator.php
+++ b/lib/Config/Migrator.php
@@ -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);
}
diff --git a/lib/Import/BootstrapMenu.php b/lib/Import/BootstrapMenu.php
index 5356261ed0..71e62d0679 100644
--- a/lib/Import/BootstrapMenu.php
+++ b/lib/Import/BootstrapMenu.php
@@ -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();
diff --git a/lib/Import/Import.php b/lib/Import/Import.php
index 9618699471..bbb9142138 100644
--- a/lib/Import/Import.php
+++ b/lib/Import/Import.php
@@ -1,5 +1,9 @@
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() {
diff --git a/lib/Import/MailChimp.php b/lib/Import/MailChimp.php
index e613ac2269..efec244934 100644
--- a/lib/Import/MailChimp.php
+++ b/lib/Import/MailChimp.php
@@ -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',
diff --git a/lib/Models/CustomField.php b/lib/Models/CustomField.php
index d44dc16bcc..e5e7f945ae 100644
--- a/lib/Models/CustomField.php
+++ b/lib/Models/CustomField.php
@@ -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()) {
diff --git a/lib/Models/Subscriber.php b/lib/Models/Subscriber.php
index 3df7c42603..3654a2d69e 100644
--- a/lib/Models/Subscriber.php
+++ b/lib/Models/Subscriber.php
@@ -1,6 +1,7 @@
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))
+ );
+ }
}
\ No newline at end of file
diff --git a/lib/Models/SubscriberCustomField.php b/lib/Models/SubscriberCustomField.php
index e12dddc2bd..2f35395e4a 100644
--- a/lib/Models/SubscriberCustomField.php
+++ b/lib/Models/SubscriberCustomField.php
@@ -1,6 +1,8 @@
'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()
+ )
+ );
+ }
}
}
\ No newline at end of file
diff --git a/lib/Util/Helpers.php b/lib/Util/Helpers.php
index 130294d34b..19171df716 100644
--- a/lib/Util/Helpers.php
+++ b/lib/Util/Helpers.php
@@ -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;
}
-}
\ No newline at end of file
+
+ /*
+ * 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;
+ }
+}
\ No newline at end of file
diff --git a/views/import.html b/views/import.html
index 256ce7a4fa..44da3f0136 100644
--- a/views/import.html
+++ b/views/import.html
@@ -8,6 +8,7 @@
<% include 'import/step2.html' %>
+ <% include 'import/step3.html' %>
<%= 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.')
}) %>
+
\ No newline at end of file