- Rebased master

- Cleaned up import & moved it under Subscribers menu
This commit is contained in:
MrCasual
2015-11-07 14:38:38 -05:00
parent 3f168d052f
commit b1ae07d38e
12 changed files with 546 additions and 316 deletions

View File

@ -50,6 +50,14 @@ span
padding 0 1em 0 1em !important padding 0 1em 0 1em !important
vertical-align inherit !important vertical-align inherit !important
#subscribers_data
& > table
& > thead
& > tr
& > th
& > span
width 15em !important
.mailpoet_data_match .mailpoet_data_match
color #0e90d2 color #0e90d2
margin-left 0.25em margin-left 0.25em
@ -58,8 +66,11 @@ span
color #900 color #900
tr tr
&.mailpoet_lists &.mailpoet_segments
& > td & > td
& > a & > a
margin-left 15px margin-left 15px
.select2-dropdown--below
display none

View File

@ -16,6 +16,9 @@ define(
Handlebars, Handlebars,
Papa Papa
) { ) {
if (typeof(importData) === 'undefined') {
return;
}
jQuery(document).ready(function () { jQuery(document).ready(function () {
// configure router // configure router
router = new (Backbone.Router.extend({ router = new (Backbone.Router.extend({
@ -47,31 +50,46 @@ define(
} }
// render process button for each method // render process button for each method
var methodProcessContainerTemplate = Handlebars.compile(jQuery('#method_process_template').html()); var methodProcessContainerTemplate =
Handlebars.compile(jQuery('#method_process_template').html());
jQuery('.mailpoet_method_process').html(methodProcessContainerTemplate()); jQuery('.mailpoet_method_process').html(methodProcessContainerTemplate());
// define reusable variables // define reusable variables
var currentStepE = jQuery(location.hash), var currentStepE = jQuery(location.hash),
methodSelectionElement = jQuery('#select_method'), methodSelectionElement = jQuery('#select_method'),
pasteInputElement = jQuery('#paste_input'), pasteInputElement = jQuery('#paste_input'),
pasteInputPlaceholderElement = pasteInputElement.data('placeholder').replace(/\\n/g, '\n'), pasteInputPlaceholderElement =
pasteProcessButtonElement = jQuery('#method_paste > div.mailpoet_method_process').find('a.mailpoet_process'), pasteInputElement.data('placeholder').replace(/\\n/g, '\n'),
pasteProcessButtonElement =
jQuery('#method_paste > div.mailpoet_method_process')
.find('a.mailpoet_process'),
mailChimpKeyInputElement = jQuery('#mailchimp_key'), mailChimpKeyInputElement = jQuery('#mailchimp_key'),
mailChimpKeyVerifyButtonEelement = jQuery('#mailchimp_key_verify'), mailChimpKeyVerifyButtonEelement = jQuery('#mailchimp_key_verify'),
mailChimpListsContainerElement = jQuery('#mailchimp_lists'), mailChimpListsContainerElement = jQuery('#mailchimp_lists'),
mailChimpProcessButtonElement = jQuery('#method_mailchimp > div.mailpoet_method_process').find('a.mailpoet_process'), mailChimpProcessButtonElement =
jQuery('#method_mailchimp > div.mailpoet_method_process')
.find('a.mailpoet_process'),
uploadElement = jQuery('#file_local'), uploadElement = jQuery('#file_local'),
uploadProcessButtonElement = jQuery('#method_file > div.mailpoet_method_process').find('a.mailpoet_process'); uploadProcessButtonElement =
jQuery('#method_file > div.mailpoet_method_process')
.find('a.mailpoet_process');
// define method change behavior // define method change behavior
methodSelectionElement.change(function () { methodSelectionElement.change(function () {
MailPoet.Notice.hide(); MailPoet.Notice.hide();
var available_methods = jQuery(':radio[name="select_method"]'), var available_methods = jQuery(':radio[name="select_method"]'),
selected_method = available_methods.index(available_methods.filter(':checked')); selected_method =
available_methods.index(available_methods.filter(':checked'));
// hide all methods // hide all methods
currentStepE.find('.inside').children('div[id^="method_"]').hide(); currentStepE.find('.inside')
.children('div[id^="method_"]')
.hide();
// show selected method // show selected method
currentStepE.find('.inside').children('div[id^="method_"]:eq(' + selected_method + ')').show().find('table').show(); currentStepE.find('.inside')
.children('div[id^="method_"]:eq(' + selected_method + ')')
.show()
.find('table')
.show();
}); });
// start step 1 // start step 1
@ -141,9 +159,12 @@ define(
* MailChimp * MailChimp
*/ */
mailChimpKeyInputElement.keyup(function () { mailChimpKeyInputElement.keyup(function () {
if (this.value.trim() === '' || !/[a-zA-Z0-9]{32}-/.exec(this.value.trim())) { if (this.value.trim() === ''
|| !/[a-zA-Z0-9]{32}-/.exec(this.value.trim())) {
mailChimpListsContainerElement.hide(); mailChimpListsContainerElement.hide();
jQuery('.mailpoet_mailchimp-key-status').html('').removeClass('mailpoet_mailchimp-ok mailpoet_mailchimp-error'); jQuery('.mailpoet_mailchimp-key-status')
.html('')
.removeClass('mailpoet_mailchimp-ok mailpoet_mailchimp-error');
mailChimpKeyVerifyButtonEelement.prop('disabled', true); mailChimpKeyVerifyButtonEelement.prop('disabled', true);
toggleNextStepButton(mailChimpProcessButtonElement, 'off'); toggleNextStepButton(mailChimpProcessButtonElement, 'off');
} }
@ -162,11 +183,16 @@ define(
if (request.result === false) { if (request.result === false) {
MailPoet.Notice.hide(); MailPoet.Notice.hide();
MailPoet.Notice.error(request.message); MailPoet.Notice.error(request.message);
jQuery('.mailpoet_mailchimp-key-status').removeClass().addClass('mailpoet_mailchimp-key-status mailpoet_mailchimp-error'); jQuery('.mailpoet_mailchimp-key-status')
.removeClass()
.addClass('mailpoet_mailchimp-key-status mailpoet_mailchimp-error');
mailChimpListsContainerElement.hide(); mailChimpListsContainerElement.hide();
toggleNextStepButton(mailChimpProcessButtonElement, 'off'); toggleNextStepButton(mailChimpProcessButtonElement, 'off');
} else { } else {
jQuery('.mailpoet_mailchimp-key-status').html('').removeClass().addClass('mailpoet_mailchimp-key-status mailpoet_mailchimp-ok'); jQuery('.mailpoet_mailchimp-key-status')
.html('')
.removeClass()
.addClass('mailpoet_mailchimp-key-status mailpoet_mailchimp-ok');
if (!request.data) { if (!request.data) {
jQuery('.mailpoet_mailchimp-key-status').html(MailPoetI18n.noMailChimpLists); jQuery('.mailpoet_mailchimp-key-status').html(MailPoetI18n.noMailChimpLists);
mailChimpListsContainerElement.hide(); mailChimpListsContainerElement.hide();
@ -178,7 +204,9 @@ define(
MailPoet.Modal.loading(false); MailPoet.Modal.loading(false);
}).error(function (error) { }).error(function (error) {
MailPoet.Modal.loading(false); MailPoet.Modal.loading(false);
MailPoet.Notice.error(MailPoetI18n.serverError + error.statusText.toLowerCase() + '.'); MailPoet.Notice.error(
MailPoetI18n.serverError + error.statusText.toLowerCase() + '.'
);
}); });
MailPoet.Modal.loading(false); MailPoet.Modal.loading(false);
}); });
@ -207,7 +235,9 @@ define(
MailPoet.Modal.loading(false); MailPoet.Modal.loading(false);
}).error(function () { }).error(function () {
MailPoet.Modal.loading(false); MailPoet.Modal.loading(false);
MailPoet.Notice.error(MailPoetI18n.serverError + result.statusText.toLowerCase() + '.'); MailPoet.Notice.error(
MailPoetI18n.serverError + result.statusText.toLowerCase() + '.'
);
}); });
}); });
@ -262,7 +292,8 @@ define(
advancedOptionDelimiter = '', advancedOptionDelimiter = '',
advancedOptionNewline = '', advancedOptionNewline = '',
advancedOptionComments = false, advancedOptionComments = false,
// trim spaces, commas, periods, single/double quotes and convert to lowercase // trim spaces, commas, periods,
// single/double quotes and convert to lowercase
detectAndCleanupEmail = function (email) { detectAndCleanupEmail = function (email) {
var test, var test,
cleanEmail = cleanEmail =
@ -308,21 +339,26 @@ define(
} }
// Process the row with the following assumptions: // Process the row with the following assumptions:
// 1. Each row should contain the same number of elements // 1. Each row should contain the same number of elements
// 2. There should be at least 1 valid (as per HTML5 e-mail regex) e-mail address on each row EXCEPT when the header option is set to true // 2. There should be at least 1 valid (as per HTML5 e-mail regex)
// e-mail address on each row EXCEPT when the header option is set to true
// 3. Duplicate addresses are skipped // 3. Duplicate addresses are skipped
if (rowColumnCount === columnCount) { if (rowColumnCount === columnCount) {
// determine position of email address inside an array; this is done once and then email regex is run just on that element for each row // determine position of email address inside an array; this is
// done once and then email regex is run just on that element for each row
if (emailColumnPosition === null) { if (emailColumnPosition === null) {
for (var column in rowData) { for (var column in rowData) {
var email = detectAndCleanupEmail(rowData[column]); var email = detectAndCleanupEmail(rowData[column]);
if (emailColumnPosition === null && emailRegex.test(email)) { if (emailColumnPosition === null
&& emailRegex.test(email)) {
emailColumnPosition = column; emailColumnPosition = column;
parsedEmails[email] = true; // add current e-mail to an object index parsedEmails[email] = true; // add current e-mail to an object index
rowData[column] = email; rowData[column] = email;
processedSubscribers[email] = rowData; processedSubscribers[email] = rowData;
} }
} }
if (emailColumnPosition === null && advancedOptionHeader && parseInt(rowCount) === 0) { if (emailColumnPosition === null
&& advancedOptionHeader
&& parseInt(rowCount) === 0) {
isHeaderFound = true; isHeaderFound = true;
processedSubscribers[0] = rowData; processedSubscribers[0] = rowData;
} }
@ -335,9 +371,10 @@ define(
else if (!emailRegex.test(email)) { else if (!emailRegex.test(email)) {
invalidEmails.push(rowData[emailColumnPosition]); invalidEmails.push(rowData[emailColumnPosition]);
} }
// if we haven't yet processed this e-mail and it passed the regex test, then process the row // if we haven't yet processed this e-mail and it passed
// the regex test, then process the row
else { else {
parsedEmails[email] = true; // add current e-mail to an object index parsedEmails[email] = true;
rowData[emailColumnPosition] = email; rowData[emailColumnPosition] = email;
processedSubscribers[email] = rowData; processedSubscribers[email] = rowData;
} }
@ -346,14 +383,18 @@ define(
} }
// reindex array to avoid non-numeric indices // reindex array to avoid non-numeric indices
processedSubscribers = _.values(processedSubscribers); processedSubscribers = _.values(processedSubscribers);
// if the header options is set, there should be at least 2 data rows, otherwise at least 1 data row // if the header options is set, there should be at least
// 2 data rows, otherwise at least 1 data row
if (processedSubscribers && if (processedSubscribers &&
(isHeaderFound && processedSubscribers.length >= 2) || (isHeaderFound && processedSubscribers.length >= 2) ||
(!isHeaderFound && processedSubscribers.length >= 1) (!isHeaderFound && processedSubscribers.length >= 1)
) { ) {
// since we assume that the header line is always present, we need to detect the header by checking if it contains a valid e-mail address // since we assume that the header line is always present, we need
// to detect the header by checking if it contains a valid e-mail address
importData.step1 = { importData.step1 = {
'header': (!emailRegex.test(processedSubscribers[0][emailColumnPosition])) ? processedSubscribers.shift() : null, 'header': (!emailRegex.test(
processedSubscribers[0][emailColumnPosition])
) ? processedSubscribers.shift() : null,
'subscribers': processedSubscribers, 'subscribers': processedSubscribers,
'subscribersCount': processedSubscribers.length, 'subscribersCount': processedSubscribers.length,
'duplicate': duplicateEmails, 'duplicate': duplicateEmails,
@ -377,15 +418,30 @@ define(
} }
// define reusable variables // define reusable variables
var nextStepButton = jQuery('#step2_process'), var nextStepButton = jQuery('#step2_process'),
subscribers = jQuery.extend(true, {}, importData.step1), // create a copy of subscribers object for further manipulation // create a copy of subscribers object for further manipulation
subscribersDataTemplate = Handlebars.compile(jQuery('#subscribers_data_template').html()), subscribers = jQuery.extend(true, {}, importData.step1),
subscribersDataTemplatePartial = Handlebars.compile(jQuery('#subscribers_data_template_partial').html()), subscribersDataTemplate =
subscribersDataParseResultsTemplate = Handlebars.compile(jQuery('#subscribers_data_parse_results_template').html()), Handlebars
.compile(jQuery('#subscribers_data_template')
.html()),
subscribersDataTemplatePartial =
Handlebars
.compile(jQuery('#subscribers_data_template_partial')
.html()),
subscribersDataParseResultsTemplate =
Handlebars
.compile(jQuery('#subscribers_data_parse_results_template')
.html()),
segmentSelectElement = jQuery('#mailpoet_segments_select'), segmentSelectElement = jQuery('#mailpoet_segments_select'),
maxRowsToShow = 10, maxRowsToShow = 10,
filler = '. . .', filler = '. . .',
fillerArray = Array.apply(null, new Array(subscribers.subscribers[0].length)).map(String.prototype.valueOf, filler), // create an array of filler data with the same number of elements as in the subscribers' data row // create an array of filler data with the same number of
fillterPosition; // keep track of filler row number (used to avoid replacing filler when filtering data with filter_subscribers_data() // elements as in the subscribers' data row
fillerArray = Array.apply(
null,
new Array(subscribers.subscribers[0].length)
).map(String.prototype.valueOf, filler),
fillterPosition;
showCurrentStep(); showCurrentStep();
@ -396,7 +452,8 @@ define(
// show parse statistics if any duplicate/invalid records were found // show parse statistics if any duplicate/invalid records were found
if (subscribers.invalid.length || subscribers.duplicate.length) { if (subscribers.invalid.length || subscribers.duplicate.length) {
// count repeating e-mails inside duplicate array and present them in 'email (xN)' format // count repeating e-mails inside duplicate array and present them in
// 'email (xN)' format
var duplicates = {}; var duplicates = {};
subscribers.duplicate.forEach(function (email) { subscribers.duplicate.forEach(function (email) {
duplicates[email] = (duplicates[email] || 0) + 1; duplicates[email] = (duplicates[email] || 0) + 1;
@ -412,29 +469,45 @@ define(
} }
var import_results = { var import_results = {
notice: MailPoetI18n.importNoticeSkipped.replace('%1$s', '<strong>' + (subscribers.invalid.length + subscribers.duplicate.length) + '</strong>'), notice: MailPoetI18n.importNoticeSkipped.replace(
invalid: (subscribers.invalid.length) ? MailPoetI18n.importNoticeInvalid.replace('%1$s', '<strong>' + subscribers.invalid.length + '</strong>').replace('%2$s', subscribers.invalid.join(', ')) : null, '%1$s',
duplicate: (subscribers.duplicate.length) ? MailPoetI18n.importNoticeDuplicate.replace('%1$s', '<strong>' + subscribers.duplicate.length + '</strong>').replace('%2$s', subscribers.duplicate.join(', ')) : null '<strong>' + (subscribers.invalid.length + subscribers.duplicate.length) + '</strong>'
),
invalid: (subscribers.invalid.length)
? MailPoetI18n.importNoticeInvalid
.replace('%1$s', '<strong>' + subscribers.invalid.length + '</strong>')
.replace('%2$s', subscribers.invalid.join(', '))
: null,
duplicate: (subscribers.duplicate.length)
? MailPoetI18n.importNoticeDuplicate
.replace('%1$s', '<strong>' + subscribers.duplicate.length + '</strong>')
.replace('%2$s', subscribers.duplicate.join(', '))
: null
}; };
jQuery('#subscribers_data_parse_results').html(subscribersDataParseResultsTemplate(import_results)); jQuery('#subscribers_data_parse_results').html(
subscribersDataParseResultsTemplate(import_results)
);
} }
jQuery('.mailpoet_subscribers_data_parse_results_details_show').click(function () { jQuery('.mailpoet_subscribers_data_parse_results_details_show')
.click(function () {
var details = jQuery('.mailpoet_subscribers_data_parse_results_details'); var details = jQuery('.mailpoet_subscribers_data_parse_results_details');
jQuery(details).toggle(); jQuery(details).toggle();
this.text = this.text =
(jQuery(details).is(":visible")) ? MailPoetI18n.hideDetails : MailPoetI18n.showDetails; (jQuery(details).is(":visible"))
}) ? MailPoetI18n.hideDetails
: MailPoetI18n.showDetails;
});
// show available segments // show available segments
if (mailpoetLists.length) { if (mailpoetSegments.length) {
jQuery('.mailpoet_segments').show(); jQuery('.mailpoet_segments').show();
} }
else { else {
jQuery(".mailpoet_no_segments").show(); jQuery(".mailpoet_no_segments").show();
} }
function enableListSelection(segments) { function enableSegmentSelection(segments) {
if (segmentSelectElement.data('select2')) { if (segmentSelectElement.data('select2')) {
segmentSelectElement segmentSelectElement
.html('') .html('')
@ -445,16 +518,21 @@ define(
data: segments, data: segments,
width: '20em', width: '20em',
templateResult: function (item) { templateResult: function (item) {
return (item.subscribers) ? item.name + ' (' + item.subscribers + ')' : item.name; return (item.subscriberCount > 0)
? item.name + ' (' + item.subscriberCount + ')'
: item.name;
}, },
templateSelection: function (item) { templateSelection: function (item) {
return (item.subscribers) ? item.name + ' (' + item.subscribers + ')' : item.name; return (item.subscriberCount > 0)
? item.name + ' (' + item.subscriberCount + ')'
: item.name;
} }
}) })
.change(function () { .change(function () {
var segmentSelectionNotice = jQuery('[data-id="notice_segmentSelection"]');
if (!this.value) { if (!this.value) {
toggleNextStepButton('off'); toggleNextStepButton('off');
if (!jQuery('.mailpoet_segmentSelection').length) { if (!segmentSelectionNotice.length) {
MailPoet.Notice.error(MailPoetI18n.segmentSelectionRequired, { MailPoet.Notice.error(MailPoetI18n.segmentSelectionRequired, {
static: true, static: true,
scroll: true, scroll: true,
@ -463,9 +541,9 @@ define(
}); });
} }
} else { } else {
jQuery('.mailpoet_segmentSelection').remove(); jQuery('[data-id="notice_segmentSelection"]').remove();
} }
if (!jQuery('.mailpoet_notice.error:visible').length) { if (!segmentSelectionNotice.length) {
toggleNextStepButton('on'); toggleNextStepButton('on');
} }
}) })
@ -484,7 +562,7 @@ define(
jQuery('#new_segment_process').click(function () { jQuery('#new_segment_process').click(function () {
var segmentName = jQuery('#new_segment_name').val().trim(), var segmentName = jQuery('#new_segment_name').val().trim(),
segmentDescription = jQuery('#new_segment_description').val().trim(), segmentDescription = jQuery('#new_segment_description').val().trim(),
isDuplicateListName = ( jQuery.map(mailpoetLists, function (el) { isDuplicateListName = ( jQuery.map(mailpoetSegments, function (el) {
if (el.name.toLowerCase() === segmentName.toLowerCase()) { if (el.name.toLowerCase() === segmentName.toLowerCase()) {
return true; return true;
} }
@ -512,7 +590,7 @@ define(
}) })
.done(function (request) { .done(function (request) {
if (request.result === true) { if (request.result === true) {
mailpoetLists.push({ mailpoetSegments.push({
'id': request.segment.id, 'id': request.segment.id,
'name': request.segment.name 'name': request.segment.name
}); });
@ -524,7 +602,7 @@ define(
selected_values.push(request.segment.id); selected_values.push(request.segment.id);
} }
enableListSelection(mailpoetLists); enableSegmentSelection(mailpoetSegments);
segmentSelectElement.val(selected_values).trigger('change'); segmentSelectElement.val(selected_values).trigger('change');
jQuery('.mailpoet_segments:hidden').show(); jQuery('.mailpoet_segments:hidden').show();
jQuery(".mailpoet_no_segments:visible").hide(); jQuery(".mailpoet_no_segments:visible").hide();
@ -532,12 +610,16 @@ define(
} }
else { else {
MailPoet.Modal.close(); MailPoet.Modal.close();
MailPoet.Notice.error(MailPoetI18n.segmentCreateError + request.message + '.'); MailPoet.Notice.error(
MailPoetI18n.segmentCreateError + request.message + '.'
);
} }
}) })
.error(function (error) { .error(function (error) {
MailPoet.Modal.close(); MailPoet.Modal.close();
MailPoet.Notice.error(MailPoetI18n.serverError + error.statusText.toLowerCase() + '.'); MailPoet.Notice.error(
MailPoetI18n.serverError + error.statusText.toLowerCase() + '.'
);
}); });
} }
}); });
@ -547,49 +629,59 @@ define(
}); });
// register partial template that will contain subscribers data // register partial template that will contain subscribers data
Handlebars.registerPartial("subscribers_data_template_partial", subscribersDataTemplatePartial); Handlebars.registerPartial(
"subscribers_data_template_partial",
subscribersDataTemplatePartial
);
// autodetect column types // autodetect column types
Handlebars.registerHelper('show_and_match_columns', function (subscribers, options) { Handlebars.registerHelper(
var displayed_columns = [], 'show_and_match_columns',
displayed_columns_ids = []; function (subscribers, options) {
var displayedColumns = [],
displayedColumnsIds = [];
// go through all elements of the first row in subscribers data // go through all elements of the first row in subscribers data
for (var i in subscribers.subscribers[0]) { for (var i in subscribers.subscribers[0]) {
var column_data = subscribers.subscribers[0][i], var columnData = subscribers.subscribers[0][i],
column_id = 'ignore'; // set default column type columnId = 'ignore'; // set default column type
// if the column is not undefined and has a valid e-mail, set type as email // 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)) { if (columnData % 1 !== 0 && emailRegex.test(columnData)) {
column_id = 's_email'; columnId = 's_email';
} else if (subscribers.header) { } else if (subscribers.header) {
var header_name = subscribers.header[i], var headerName = subscribers.header[i],
header_name_match = mailpoet_columns.map(function (el) { header_name_match = mailpoetColumns.map(function (el) {
return el.id; return el.id;
}).indexOf(header_name); }).indexOf(headerName);
// set column type using header // set column type using header
if (header_name_match !== -1) { if (header_name_match !== -1) {
column_id = header_name; columnId = headerName;
}// set column type using header name }// set column type using header name
else if (header_name) { else if (headerName) {
if (/first|first name|given name/i.test(header_name)) { if (/first|first name|given name/i.test(headerName)) {
column_id = 's_first_name'; columnId = 's_first_name';
} else if (/last|last name/i.test(header_name)) { } else if (/last|last name/i.test(headerName)) {
column_id = 's_last_name'; columnId = 's_last_name';
} else if (/status/i.test(header_name)) { } else if (/status/i.test(headerName)) {
column_id = 's_status'; columnId = 's_status';
} else if (/subscribed|subscription/i.test(header_name)) { } /*else if (/subscribed|subscription/i.test(headerName)) {
column_id = 's_confirmed_at'; columnId = 's_confirmed_at';
} else if (/ip/i.test(header_name)) { } else if (/ip/i.test(headerName)) {
column_id = 's_confirmed_ip'; columnId = 's_confirmed_ip';
}*/
} }
} }
// make sure the column id has not been previously selected
// (e.g., subscriber_first_name shouldn't be autodetected twice),
// except for "ignore"
columnId =
(columnId !== 'ignore'
&& displayedColumnsIds.indexOf(columnId) === -1)
? columnId
: 'ignore';
displayedColumns[i] = {'column_id': columnId};
displayedColumnsIds.push(columnId);
} }
// make sure the column id has not been previously selected (e.g., subscriber_firstname shouldn't be autodetected twice), except for "ignore"| return options.fn(displayedColumns);
column_id =
(column_id !== 'ignore' && displayed_columns_ids.indexOf(column_id) === -1) ? column_id : 'ignore';
displayed_columns[i] = {'column_id': column_id};
displayed_columns_ids.push(column_id);
}
return options.fn(displayed_columns);
}); });
// start array index from 1 // start array index from 1
@ -609,9 +701,13 @@ define(
} }
}); });
// reduce subscribers object if the total length is geater than the maximum number of defined rows // reduce subscribers object if the total length is geater than the
// maximum number of defined rows
if (subscribers.subscribersCount > (maxRowsToShow + 1)) { if (subscribers.subscribersCount > (maxRowsToShow + 1)) {
subscribers.subscribers.splice(maxRowsToShow, subscribers.subscribersCount - (maxRowsToShow + 1), fillerArray); subscribers.subscribers.splice(
maxRowsToShow, subscribers.subscribersCount - (maxRowsToShow + 1),
fillerArray
);
} }
// render template // render template
@ -621,7 +717,7 @@ define(
// filter_subscribers_data(); // filter_subscribers_data();
jQuery('select.mailpoet_subscribers_column_data_match') jQuery('select.mailpoet_subscribers_column_data_match')
.select2({ .select2({
data: mailpoet_columns_select2, data: mailpoetColumnsSelect2,
width: '15em', width: '15em',
templateResult: function (item) { templateResult: function (item) {
return item.name; return item.name;
@ -648,24 +744,33 @@ define(
jQuery('#new_column_process').click(function () { jQuery('#new_column_process').click(function () {
var name = jQuery('#new_column_name').val().trim(), var name = jQuery('#new_column_name').val().trim(),
type = jQuery('#new_column_type').val().trim(), type = jQuery('#new_column_type').val().trim(),
columnNames = mailpoet_columns.map(function (el) { columnNames = mailpoetColumns.map(function (el) {
return el.name.toLowerCase(); return el.name.toLowerCase();
}); });
isDuplicateColumnName = (name && columnNames.indexOf(name.toLowerCase()) > -1) ? true : false; isDuplicateColumnName =
(name && columnNames.indexOf(name.toLowerCase()) > -1)
? true
: false;
if (name === '') { if (name === '') {
jQuery('.mailpoet_validation_error[data-error="name_required"]').show(); jQuery('.mailpoet_validation_error[data-error="name_required"]')
.show();
} else { } else {
jQuery('.mailpoet_validation_error[data-error="name_required"]').hide(); jQuery('.mailpoet_validation_error[data-error="name_required"]')
.hide();
} }
if (type === '') { if (type === '') {
jQuery('.mailpoet_validation_error[data-error="type_required"]').show(); jQuery('.mailpoet_validation_error[data-error="type_required"]')
.show();
} else { } else {
jQuery('.mailpoet_validation_error[data-error="type_required"]').hide(); jQuery('.mailpoet_validation_error[data-error="type_required"]')
.hide();
} }
if (isDuplicateColumnName) { if (isDuplicateColumnName) {
jQuery('.mailpoet_validation_error[data-error="name_not_unique"]').show(); jQuery('.mailpoet_validation_error[data-error="name_not_unique"]')
.show();
} else { } else {
jQuery('.mailpoet_validation_error[data-error="name_not_unique"]').hide(); jQuery('.mailpoet_validation_error[data-error="name_not_unique"]')
.hide();
} }
// create new field // create new field
if (name && type && !isDuplicateColumnName) { if (name && type && !isDuplicateColumnName) {
@ -690,20 +795,21 @@ define(
'custom': true, 'custom': true,
}; };
// if this is the first custom column, create an "optgroup" // if this is the first custom column, create an "optgroup"
if (mailpoet_columns_select2.length === 2) { if (mailpoetColumnsSelect2.length === 2) {
mailpoet_columns_select2.push({ mailpoetColumnsSelect2.push({
'name': MailPoetI18n.userColumns, 'name': MailPoetI18n.userColumns,
'children': [] 'children': []
}); });
} }
mailpoet_columns_select2[2].children.push(new_column_data); mailpoetColumnsSelect2[2].children.push(new_column_data);
mailpoet_columns.push(new_column_data); mailpoetColumns.push(new_column_data);
jQuery('select.mailpoet_subscribers_column_data_match').each(function () { jQuery('select.mailpoet_subscribers_column_data_match')
.each(function () {
jQuery(this) jQuery(this)
.html('') .html('')
.select2('destroy') .select2('destroy')
.select2({ .select2({
data: mailpoet_columns_select2, data: mailpoetColumnsSelect2,
width: '15em', width: '15em',
templateResult: function (item) { templateResult: function (item) {
return item.name; return item.name;
@ -723,7 +829,9 @@ define(
}) })
.error(function (error) { .error(function (error) {
MailPoet.Modal.loading(false); MailPoet.Modal.loading(false);
MailPoet.Notice.error(MailPoetI18n.serverError + error.statusText.toLowerCase() + '.'); MailPoet.Notice.error(
MailPoetI18n.serverError + error.statusText.toLowerCase() + '.'
);
}); });
} }
}); });
@ -734,12 +842,16 @@ define(
// CHANGE COLUMN // CHANGE COLUMN
else { else {
// check for duplicate values in all select options // check for duplicate values in all select options
jQuery('select.mailpoet_subscribers_column_data_match').each(function () { jQuery('select.mailpoet_subscribers_column_data_match')
.each(function () {
var element = this, var element = this,
elementId = jQuery(element).val(); elementId = jQuery(element).val();
// if another column has the same value and it's not an 'ignore', prompt user // if another column has the same value and it's not an 'ignore', prompt user
if (elementId === selectedOptionId && element.id !== selectElement.id && elementId !== 'ignore') { if (elementId === selectedOptionId
if (confirm(MailPoetI18n.selectedValueAlreadyMatched + ' ' + MailPoetI18n.confirmCorrespondingColumn)) { && element.id !== selectElement.id
&& elementId !== 'ignore') {
if (confirm(
MailPoetI18n.selectedValueAlreadyMatched + ' ' + MailPoetI18n.confirmCorrespondingColumn)) {
jQuery(element).data('column-id', 'ignore'); jQuery(element).data('column-id', 'ignore');
jQuery(selectElement).data('column-id', selectedOptionId); jQuery(selectElement).data('column-id', selectedOptionId);
filterSubscribers(); filterSubscribers();
@ -759,22 +871,26 @@ define(
// filter subscribers' data to detect dates, emails, etc. // filter subscribers' data to detect dates, emails, etc.
function filterSubscribers() { function filterSubscribers() {
jQuery('[data-id="notice_invalidEmail"], [data-id="notice_invalidDate"]').remove(); jQuery('[data-id="notice_invalidEmail"], [data-id="notice_invalidDate"]')
.remove();
var subscribersClone = jQuery.extend(true, {}, subscribers), var subscribersClone = jQuery.extend(true, {}, subscribers),
preventNextStep = false, preventNextStep = false,
displayedColumnsIds = jQuery.map(jQuery('.mailpoet_subscribers_column_data_match'), function (data) { displayedColumnsIds = jQuery.map(
jQuery('.mailpoet_subscribers_column_data_match'), function (data) {
var columnId = jQuery(data).data('column-id'); var columnId = jQuery(data).data('column-id');
jQuery(data).select2('val', columnId); jQuery(data).select2('val', columnId);
return columnId; return columnId;
}); });
// iterate through the object of mailpoet columns // iterate through the object of mailpoet columns
jQuery.map(mailpoet_columns, function (column) { jQuery.map(mailpoetColumns, function (column) {
// check if the column id matches the selected id of one of the subscriber's data columns // check if the column id matches the selected id of one of the
// subscriber's data columns
var matchedColumn = jQuery.inArray(column.id, displayedColumnsIds); 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 // EMAIL filter: if the last value in the column doesn't have a valid
// email, hide the next button
if (column.id === "s_email") { if (column.id === "s_email") {
if (!emailRegex.test(subscribersClone.subscribers[0][matchedColumn])) { if (!emailRegex.test(subscribersClone.subscribers[0][matchedColumn])) {
preventNextStep = true; preventNextStep = true;
@ -794,8 +910,9 @@ define(
// DATE filter: if column type is date, check if we can recognize it // DATE filter: if column type is date, check if we can recognize it
if (column.type === 'date' && matchedColumn !== -1) { if (column.type === 'date' && matchedColumn !== -1) {
jQuery.map(subscribersClone.subscribers, function (data, position) { jQuery.map(subscribersClone.subscribers, function (data, position) {
var row_data = data[matchedColumn], var rowData = data[matchedColumn],
date = new Date(row_data.replace(/-/g, '/')), // IE doesn't like dashes as date separators date = new Date(rowData.replace(/-/g, '/')), // IE doesn't like
// dashes as date separators
month_name = [ month_name = [
MailPoetI18n.january, MailPoetI18n.january,
MailPoetI18n.february, MailPoetI18n.february,
@ -813,23 +930,45 @@ define(
if (position !== fillterPosition) { if (position !== fillterPosition) {
// check for valid date: // check for valid date:
// * invalid date object returns NaN for getTime() and NaN is the only object not strictly equal to itself // * invalid date object returns NaN for getTime() and NaN
// * date must have period/dash/slash OR be at least 4 characters long (e.g., year) // is the only object not strictly equal to itself
// * date must have period/dash/slash OR be at least 4
// characters long (e.g., year)
// * must be before now // * must be before now
if (row_data.trim() === '') { if (rowData.trim() === '') {
data[matchedColumn] = '<span class="mailpoet_data_match mailpoet_import_error" title="' + MailPoetI18n.noDateFieldMatch + '">' + MailPoetI18n.emptyDate + '</span>'; data[matchedColumn] =
'<span class="mailpoet_data_match mailpoet_import_error" title="'
+ MailPoetI18n.noDateFieldMatch + '">'
+ MailPoetI18n.emptyDate
+ '</span>';
preventNextStep = true; preventNextStep = true;
return; return;
} }
else if (date.getTime() === date.getTime() && else if (date.getTime() === date.getTime() &&
(/[.-\/]/.test(row_data) || row_data.length >= 4) && (/[.-\/]/.test(rowData) || rowData.length >= 4) &&
date.getTime() < (new Date()).getTime() date.getTime() < (new Date()).getTime()
) { ) {
date = '/ ' + month_name[date.getMonth()] + ' ' + date.getDate() + ', ' + date.getFullYear() + ' ' + date.getHours() + ':' + ((date.getMinutes() < 10 ? '0' : '') + date.getMinutes()) + ' ' + ((date.getHours() >= 12) ? MailPoetI18n.pm : MailPoetI18n.am); date = '/ '
data[matchedColumn] += '<span class="mailpoet_data_match" title="' + MailPoetI18n.verifyDateMatch + '">' + date + '</span>'; + month_name[date.getMonth()]
+ ' ' + date.getDate() + ', '
+ date.getFullYear() + ' '
+ date.getHours() + ':'
+ ((date.getMinutes() < 10 ? '0' : '')
+ date.getMinutes()) + ' '
+ ((date.getHours() >= 12)
? MailPoetI18n.pm
: MailPoetI18n.am
);
data[matchedColumn] +=
'<span class="mailpoet_data_match" title="'
+ MailPoetI18n.verifyDateMatch + '">'
+ date + '</span>';
} }
else { else {
data[matchedColumn] += '<span class="mailpoet_data_match mailpoet_import_error" title="' + MailPoetI18n.noDateFieldMatch + '">' + MailPoetI18n.dateMatchError + '</span>'; data[matchedColumn] +=
'<span class="mailpoet_data_match mailpoet_import_error" title="'
+ MailPoetI18n.noDateFieldMatch + '">'
+ MailPoetI18n.dateMatchError + '</span>';
preventNextStep = true; preventNextStep = true;
} }
} }
@ -846,12 +985,14 @@ define(
}); });
// refresh table with susbcribers' data // refresh table with susbcribers' data
jQuery('#subscribers_data > table > tbody').html(subscribersDataTemplatePartial(subscribersClone)); jQuery('#subscribers_data > table > tbody')
.html(subscribersDataTemplatePartial(subscribersClone));
if (preventNextStep) { if (preventNextStep) {
toggleNextStepButton('off'); toggleNextStepButton('off');
} }
else if (!jQuery('.mailpoet_notice.error:visible').length && segmentSelectElement.val()) { else if (!jQuery('.mailpoet_notice.error:visible').length
&& segmentSelectElement.val()) {
toggleNextStepButton('on'); toggleNextStepButton('on');
} }
} }
@ -873,7 +1014,8 @@ define(
var subscribers = {}; var subscribers = {};
_.each(jQuery('select.mailpoet_subscribers_column_data_match'), function (column, index) { _.each(jQuery('select.mailpoet_subscribers_column_data_match'),
function (column, index) {
var columnId = jQuery(column).data('column-id'); var columnId = jQuery(column).data('column-id');
if (columnId === 'ignore') { if (columnId === 'ignore') {
return; return;
@ -904,18 +1046,25 @@ define(
if (request.result === false) { if (request.result === false) {
MailPoet.Notice.error(request.error); MailPoet.Notice.error(request.error);
} else { } else {
request.data.lists = []; mailpoetSegments = request.data.segments;
request.data.segments = _.map(segmentSelectElement.select2('data'),
function (data) {
return data.name;
});
importData.step2 = request.data; importData.step2 = request.data;
enableSegmentSelection(mailpoetSegments);
router.navigate('step3', {trigger: true}); router.navigate('step3', {trigger: true});
} }
}).error(function (error) { }).error(function (error) {
MailPoet.Modal.loading(false); MailPoet.Modal.loading(false);
MailPoet.Notice.error(MailPoetI18n.serverError + error.statusText.toLowerCase() + '.'); MailPoet.Notice.error(
MailPoetI18n.serverError + error.statusText.toLowerCase() + '.'
);
}); });
}); });
filterSubscribers(); filterSubscribers();
enableListSelection(mailpoetLists); enableSegmentSelection(mailpoetSegments);
}); });
@ -928,15 +1077,30 @@ define(
showCurrentStep(); showCurrentStep();
// display statistics // display statistics
var subscribers_data_import_results_template = Handlebars.compile(jQuery('#subscribers_data_import_results_template').html()), var subscribersDataImportResultsTemplate =
import_results = { Handlebars
added: (importData.step2.added) ? MailPoetI18n.subscribersAdded.replace('%1$s', '<strong>' + importData.step2.added + '</strong>').replace('%2$s', '"' + importData.step2.lists.join('", "') + '"') : false, .compile(jQuery('#subscribers_data_import_results_template')
updated: (importData.step2.updated) ? MailPoetI18n.subscribersUpdated.replace('%1$s', '<strong>' + importData.step2.updated + '</strong>').replace('%2$s', '"' + importData.step2.lists.join('", "') + '"') : false, .html()),
noaction: (!importData.step2.updated && !importData.step2.added) ? true : false exportMenuElement = jQuery('span.mailpoet_export'),
}, importResults = {
export_menu_item = jQuery('span.mailpoet_export'); added: (importData.step2.added)
? MailPoetI18n.subscribersAdded
.replace('%1$s', '<strong>' + importData.step2.added + '</strong>')
.replace('%2$s', '"' + importData.step2.segments.join('", "') + '"')
: false,
updated: (importData.step2.updated)
? MailPoetI18n.subscribersUpdated
.replace('%1$s', '<strong>' + importData.step2.updated + '</strong>')
.replace('%2$s', '"' + importData.step2.segments.join('", "') + '"')
: false,
noaction: (!importData.step2.updated && !importData.step2.added)
? true
: false
};
jQuery('#subscribers_data_import_results').html(subscribers_data_import_results_template(import_results)).show(); jQuery('#subscribers_data_import_results')
.html(subscribersDataImportResultsTemplate(importResults))
.show();
jQuery('a.mailpoet_import_again').off().click(function () { jQuery('a.mailpoet_import_again').off().click(function () {
jQuery("#subscribers_data_import_results").hide(); jQuery("#subscribers_data_import_results").hide();
@ -947,9 +1111,10 @@ define(
window.location.href = 'admin.php?page=mailpoet-subscribers'; 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 new subscribers were added and the export menu item is hidden
if (import_results.added && export_menu_item.not(':visible')) { // (it's shown only when there are subscribers), display it
export_menu_item.show(); if (importResults.added && exportMenuElement.not(':visible')) {
exportMenuElement.show();
} }
// reset previous step's data so that coming back to this step is prevented // reset previous step's data so that coming back to this step is prevented

View File

@ -275,6 +275,7 @@ const SubscriberList = React.createClass({
<div> <div>
<h2 className="title"> <h2 className="title">
Subscribers <Link className="add-new-h2" to="/new">New</Link> Subscribers <Link className="add-new-h2" to="/new">New</Link>
<a className="add-new-h2" href="?page=mailpoet-import#step1">Import</a>
</h2> </h2>
<Listing <Listing

View File

@ -1,5 +1,6 @@
<?php <?php
namespace MailPoet\Config; namespace MailPoet\Config;
use \MailPoet\Import\BootstrapMenu; use \MailPoet\Import\BootstrapMenu;
use \MailPoet\Models\Segment; use \MailPoet\Models\Segment;
use \MailPoet\Models\Setting; use \MailPoet\Models\Setting;
@ -78,7 +79,7 @@ class Menu {
array($this, 'settings') array($this, 'settings')
); );
add_submenu_page( add_submenu_page(
'mailpoet', 'null',
__('Import'), __('Import'),
__('Import'), __('Import'),
'manage_options', 'manage_options',
@ -219,9 +220,6 @@ class Menu {
echo $this->renderer->render('import.html', $data); echo $this->renderer->render('import.html', $data);
} }
function formEditor() { function formEditor() {
$id = (isset($_GET['id']) ? (int)$_GET['id'] : 0); $id = (isset($_GET['id']) ? (int)$_GET['id'] : 0);
$form = Form::findOne($id); $form = Form::findOne($id);

View File

@ -1,4 +1,5 @@
<?php namespace MailPoet\Import; <?php
namespace MailPoet\Import;
use MailPoet\Models\CustomField; use MailPoet\Models\CustomField;
use MailPoet\Models\Segment; use MailPoet\Models\Segment;
@ -17,14 +18,20 @@ class BootstrapMenu {
's_email' => __('Email'), 's_email' => __('Email'),
's_first_name' => __('First name'), 's_first_name' => __('First name'),
's_last_name' => __('Last name'), 's_last_name' => __('Last name'),
/* 's_confirmed_ip' => __('IP address'),
's_confirmed_at' => __('Subscription date'),*/
's_status' => __('Status') 's_status' => __('Status')
/* 's_confirmed_ip' => __('IP address')
's_confirmed_at' => __('Subscription date')*/
); );
} }
function getSegments() { function getSegments() {
return Segment::findArray(); return array_map(function ($segment) {
return array(
'id' => $segment['id'],
'name' => $segment['name'],
'subscriberCount' => $segment['subscribers']
);
}, Segment::filter('filterWithSubscriberCount')->findArray());
} }
function getSubscriberCustomFields() { function getSubscriberCustomFields() {
@ -84,12 +91,7 @@ class BootstrapMenu {
} }
function bootstrap() { function bootstrap() {
$data['segments'] = array_map(function ($segment) { $data['segments'] = $this->segments;
return array(
'id' => $segment['id'],
'name' => $segment['name'],
);
}, $this->getSegments());
$data['subscriberFields'] = array_merge( $data['subscriberFields'] = array_merge(
$this->formatSubscriberFields(), $this->formatSubscriberFields(),

View File

@ -1,7 +1,10 @@
<?php namespace MailPoet\Import; <?php
namespace MailPoet\Import;
use MailPoet\Import\BootstrapMenu;
use MailPoet\Models\Subscriber; use MailPoet\Models\Subscriber;
use MailPoet\Models\SubscriberCustomField; use MailPoet\Models\SubscriberCustomField;
use MailPoet\Models\SubscriberSegment;
use MailPoet\Util\Helpers; use MailPoet\Util\Helpers;
class Import { class Import {
@ -23,17 +26,17 @@ class Import {
list($subscribersData, $subscriberFields) = $this->extendSubscribersAndFields( list($subscribersData, $subscriberFields) = $this->extendSubscribersAndFields(
$subscribersData, $subscriberFields $subscribersData, $subscriberFields
); );
list($existingSubscribers, $newSubscribers) = $this->splitSubscribers( list($existingSubscribers, $newSubscribers) =
$subscribersData $this->filterExistingAndNewSubscribers($subscribersData);
);
$addedSubscribers = $updatedSubscribers = array(); $addedSubscribers = $updatedSubscribers = array();
try {
if($newSubscribers) { if($newSubscribers) {
$addedSubscribers = $this->addOrUpdateSubscribers( $addedSubscribers = $this->addOrUpdateSubscribers(
'create', 'create',
$newSubscribers, $newSubscribers,
$subscriberFields $subscriberFields
); );
$this->addSubscribersToSegments(array_keys($addedSubscribers));
} }
if($existingSubscribers && $this->updateSubscribers) { if($existingSubscribers && $this->updateSubscribers) {
$updatedSubscribers = $this->addOrUpdateSubscribers( $updatedSubscribers = $this->addOrUpdateSubscribers(
@ -41,24 +44,34 @@ class Import {
$existingSubscribers, $existingSubscribers,
$subscriberFields $subscriberFields
); );
$this->addSubscribersToSegments(array_keys($updatedSubscribers));
if($addedSubscribers) { if($addedSubscribers) {
// subtract added from updated subscribers when DB operation takes <1s
$updatedSubscribers = array_diff_key( $updatedSubscribers = array_diff_key(
$updatedSubscribers, $updatedSubscribers,
$addedSubscribers $addedSubscribers
); );
} }
} }
} catch (\PDOException $e) {
return array(
'result' => false,
'error' => $e->getMessage()
);
}
$segments = new BootstrapMenu();
return array( return array(
'result' => true, 'result' => true,
'data' => array( 'data' => array(
'added' => count($addedSubscribers), 'added' => count($addedSubscribers),
'updated' => count($updatedSubscribers), 'updated' => count($updatedSubscribers),
'segments' => $segments->getSegments()
), ),
'profile' => $this->timeExecution() 'profile' => $this->timeExecution()
); );
} }
function splitSubscribers($subscribersData) { function filterExistingAndNewSubscribers($subscribersData) {
$existingRecords = array_filter( $existingRecords = array_filter(
array_map(function ($subscriberEmails) { array_map(function ($subscriberEmails) {
return Subscriber::selectMany(array('email')) return Subscriber::selectMany(array('email'))
@ -178,7 +191,6 @@ class Import {
$subscriberFields = str_replace('s_', '', $subscriberFields); $subscriberFields = str_replace('s_', '', $subscriberFields);
$currentTime = ($action === 'update') ? date('Y-m-d H:i:s') : $this->currentTime; $currentTime = ($action === 'update') ? date('Y-m-d H:i:s') : $this->currentTime;
foreach (array_chunk($subscribers, 200) as $data) { foreach (array_chunk($subscribers, 200) as $data) {
try {
if($action == 'create') { if($action == 'create') {
Subscriber::createMultiple( Subscriber::createMultiple(
$subscriberFields, $subscriberFields,
@ -192,9 +204,6 @@ class Import {
$currentTime $currentTime
); );
} }
} catch (\PDOException $e) {
throw new \Exception($e->getMessage());
}
} }
$result = Helpers::arrayColumn( // return id=>email array of results $result = Helpers::arrayColumn( // return id=>email array of results
Subscriber::selectMany( Subscriber::selectMany(
@ -235,7 +244,6 @@ class Import {
}, $count, $subscribersData[$column]); }, $count, $subscribersData[$column]);
}, $this->subscriberCustomFields)[0]; }, $this->subscriberCustomFields)[0];
foreach (array_chunk($subscribers, 200) as $data) { foreach (array_chunk($subscribers, 200) as $data) {
try {
if($action === 'create') { if($action === 'create') {
SubscriberCustomField::createMultiple( SubscriberCustomField::createMultiple(
$data $data
@ -246,10 +254,13 @@ class Import {
$data $data
); );
} }
} catch (\PDOException $e) {
throw new \Exception($e->getMessage());
} }
} }
function addSubscribersToSegments($subscribers) {
foreach (array_chunk($subscribers, 200) as $data) {
SubscriberSegment::createMultiple($this->segments, $data);
}
} }
function timeExecution() { function timeExecution() {

View File

@ -94,6 +94,18 @@ class Segment extends Model {
} }
} }
static function filterWithSubscriberCount($orm) {
$orm = $orm
->selectMany(array(self::$_table.'.id', self::$_table.'.name'))
->select_expr('COUNT('.MP_SUBSCRIBER_SEGMENT_TABLE.'.subscriber_id)', 'subscribers')
->left_outer_join(
MP_SUBSCRIBER_SEGMENT_TABLE,
array(self::$_table.'.id', '=', MP_SUBSCRIBER_SEGMENT_TABLE.'.segment_id'))
->group_by(self::$_table.'.id')
->group_by(self::$_table.'.name');
return $orm;
}
static function createOrUpdate($data = array()) { static function createOrUpdate($data = array()) {
$segment = false; $segment = false;

View File

@ -306,7 +306,7 @@ class Subscriber extends Model {
static function createMultiple($columns, $values) { static function createMultiple($columns, $values) {
return self::rawExecute( return self::rawExecute(
'INSERT IGNORE INTO `' . self::$_table . '` ' . 'INSERT INTO `' . self::$_table . '` ' .
'(' . implode(', ', $columns) . ') ' . '(' . implode(', ', $columns) . ') ' .
'VALUES ' . rtrim( 'VALUES ' . rtrim(
str_repeat( str_repeat(
@ -324,9 +324,20 @@ class Subscriber extends Model {
'created_at' 'created_at'
); );
$emailPosition = array_search('email', $columns); $emailPosition = array_search('email', $columns);
$sql = function ($type) use ($columns, $subscribers, $emailPosition, $ignoreColumnsOnUpdate) { $sql =
function ($type) use (
$columns,
$subscribers,
$emailPosition,
$ignoreColumnsOnUpdate
) {
return array_filter( return array_filter(
array_map(function ($columnPosition, $columnName) use ($type, $subscribers, $emailPosition, $ignoreColumnsOnUpdate) { array_map(function ($columnPosition, $columnName) use (
$type,
$subscribers,
$emailPosition,
$ignoreColumnsOnUpdate
) {
if(in_array($columnName, $ignoreColumnsOnUpdate)) return; if(in_array($columnName, $ignoreColumnsOnUpdate)) return;
$query = array_map( $query = array_map(
function ($subscriber) use ($type, $columnPosition, $emailPosition) { function ($subscriber) use ($type, $columnPosition, $emailPosition) {
@ -347,8 +358,12 @@ class Subscriber extends Model {
'UPDATE `' . self::$_table . '` ' . 'UPDATE `' . self::$_table . '` ' .
'SET ' . implode(', ', $sql('statement')) . ', ' . 'SET ' . implode(', ', $sql('statement')) . ', ' .
'updated_at = "' . $currentTime . '" ' . 'updated_at = "' . $currentTime . '" ' .
'WHERE email IN (' . rtrim(str_repeat('?,', count($subscribers)), ',') . ')', 'WHERE email IN ' .
array_merge(Helpers::flattenArray($sql('values')), Helpers::arrayColumn($subscribers, $emailPosition)) '(' . rtrim(str_repeat('?,', count($subscribers)), ',') . ')',
array_merge(
Helpers::flattenArray($sql('values')),
Helpers::arrayColumn($subscribers, $emailPosition)
)
); );
} }
} }

View File

@ -1,6 +1,8 @@
<?php <?php
namespace MailPoet\Models; namespace MailPoet\Models;
use MailPoet\Util\Helpers;
if(!defined('ABSPATH')) exit; if(!defined('ABSPATH')) exit;
class SubscriberSegment extends Model { class SubscriberSegment extends Model {
@ -9,4 +11,26 @@ class SubscriberSegment extends Model {
function __construct() { function __construct() {
parent::__construct(); parent::__construct();
} }
static function createMultiple($segmnets, $subscribers) {
$values = Helpers::flattenArray(
array_map(function ($segment) use ($subscribers) {
return array_map(function ($subscriber) use ($segment) {
return array(
$segment,
$subscriber
);
}, $subscribers);
}, $segmnets)
);
return self::rawExecute(
'INSERT IGNORE INTO `' . self::$_table . '` ' .
'(segment_id, subscriber_id) ' .
'VALUES ' . rtrim(
str_repeat(
'(?, ?), ', count($subscribers) * count($segmnets)), ', '
),
$values
);
}
} }

View File

@ -19,15 +19,15 @@ class Import {
} }
function addSegment($data) { function addSegment($data) {
$segment = Segment::createOrUpdate($data, $returnObject = true); $segment = Segment::createOrUpdate($data);
wp_send_json( wp_send_json(
(!is_object($segment)) ? ($segment->id) ?
array(
'result' => false,
) :
array( array(
'result' => true, 'result' => true,
'segment' => $segment->asArray() 'segment' => $segment->asArray()
) :
array(
'result' => false
) )
); );
} }
@ -37,29 +37,19 @@ class Import {
$customField->hydrate($data); $customField->hydrate($data);
$result = $customField->save(); $result = $customField->save();
wp_send_json( wp_send_json(
(!$result) ? ($result) ?
array(
'result' => false
) :
array( array(
'result' => true, 'result' => true,
'customField' => $customField->asArray() 'customField' => $customField->asArray()
) :
array(
'result' => false
) )
); );
} }
function process($data) { function process($data) {
$data = file_get_contents(dirname(__FILE__) . '/../../export.txt');
$import = new \MailPoet\Import\Import(json_decode($data, true)); $import = new \MailPoet\Import\Import(json_decode($data, true));
try {
wp_send_json($import->process()); wp_send_json($import->process());
} catch (\Exception $e) {
wp_send_json(
array(
'result' => false,
'error' => $e->getMessage()
)
);
}
} }
} }

View File

@ -25,7 +25,7 @@
'importNoticeDuplicate': __('%1$s emails appear more than once in your file : %2$s.'), 'importNoticeDuplicate': __('%1$s emails appear more than once in your file : %2$s.'),
'hideDetails': __('Hide details.'), 'hideDetails': __('Hide details.'),
'showDetails': __('Show more details.'), 'showDetails': __('Show more details.'),
'listSelectionRequired': __('You need to select at least one list.'), 'segmentSelectionRequired': __('You need to select at least one list.'),
'addNewList': __('Add new list'), 'addNewList': __('Add new list'),
'addNewColumuserColumnsn': __('Add new list'), 'addNewColumuserColumnsn': __('Add new list'),
'userColumns': __('User columns'), 'userColumns': __('User columns'),
@ -62,9 +62,9 @@
var var
maxPostSize = '<%= maxPostSize %>', maxPostSize = '<%= maxPostSize %>',
importData = {}, importData = {},
mailpoet_columns_select2 = <%= subscriberFieldsSelect2|raw %>, mailpoetColumnsSelect2 = <%= subscriberFieldsSelect2|raw %>,
mailpoet_columns = <%= subscriberFields|raw %>, mailpoetColumns = <%= subscriberFields|raw %>,
mailpoetLists = <%= segments|raw %>, mailpoetSegments = <%= segments|raw %>,
emailRegex = /^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-_]{0,61}[a-zA-Z0-9])+.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/; emailRegex = /^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-_]{0,61}[a-zA-Z0-9])+.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
</script> </script>
<% endblock %> <% endblock %>

View File

@ -9,6 +9,7 @@
<th scope="row"> <th scope="row">
<a href="javascript:;" <a href="javascript:;"
class="button-primary wysija mailpoet_import_again"><%= __('Import again') %></a> class="button-primary wysija mailpoet_import_again"><%= __('Import again') %></a>
&nbsp;&nbsp;
<a href="javascript:;" <a href="javascript:;"
class="button-primary wysija mailpoet_view_subscribers"><%= __('View subscribers') %></a> class="button-primary wysija mailpoet_view_subscribers"><%= __('View subscribers') %></a>
</th> </th>