define( [ 'backbone', 'underscore', 'jquery', 'mailpoet', 'handlebars', 'papaparse', 'asyncqueue', 'moment', 'select2' ], function ( Backbone, _, jQuery, MailPoet, Handlebars, Papa, AsyncQueue, Moment, select2 ) { if (!jQuery('#mailpoet_subscribers_import').length) { return; } jQuery(document).ready(function () { jQuery('input[name="select_method"]').attr('checked', false); // configure router var router = new (Backbone.Router.extend({ routes: { '': 'home', step1: 'step1', step2: 'step2', step3: 'step3' }, home: function () { this.navigate('step1', { trigger: true }); } })); function showCurrentStep() { MailPoet.Notice.hide(); MailPoet.Modal.loading(false); jQuery('#mailpoet_subscribers_import > div[id^="step"]').hide(); jQuery(location.hash).show(); } /* * STEP 1 (upload or copy/paste) */ router.on('route:step1', function () { // set or reset temporary validation rule on all columns window.mailpoetColumns = jQuery.map(window.mailpoetColumns, function (column, columnIndex) { var col = column; col.validation_rule = false; return col; }); if (typeof (window.importData.step1) !== 'undefined') { showCurrentStep(); return; } // render process button for each method var methodProcessContainerTemplate = Handlebars.compile(jQuery('#method_process_template').html()); jQuery('.mailpoet_method_process').html(methodProcessContainerTemplate()); // define reusable variables var currentStepE = jQuery(location.hash), methodSelectionElement = jQuery('#select_method'), pasteInputElement = jQuery('#paste_input'), pasteInputPlaceholderElement = pasteInputElement.data('placeholder').replace(/\\n/g, '\n'), pasteProcessButtonElement = jQuery('#method_paste > div.mailpoet_method_process') .find('a.mailpoet_process'), mailChimpKeyInputElement = jQuery('#mailchimp_key'), mailChimpKeyVerifyButtonElement = jQuery('#mailchimp_key_verify'), mailChimpListsContainerElement = jQuery('#mailchimp_lists'), mailChimpProcessButtonElement = jQuery('#method_mailchimp > div.mailpoet_method_process') .find('a.mailpoet_process'), uploadElement = jQuery('#file_local'), uploadProcessButtonElement = jQuery('#method_file > div.mailpoet_method_process') .find('a.mailpoet_process'); // define method change behavior methodSelectionElement.change(function () { MailPoet.Notice.hide(); var available_methods = jQuery(':radio[name="select_method"]'), selected_method = available_methods.index(available_methods.filter(':checked')); // hide all methods currentStepE.find('.inside') .children('div[id^="method_"]') .hide(); // show selected method currentStepE.find('.inside') .children('div[id^="method_"]:eq(' + selected_method + ')') .show() .find('table') .show(); }); // start step 1 showCurrentStep(); /* * Paste */ pasteInputElement .attr('value', pasteInputPlaceholderElement).css('color', '#999') .focus(function () { if (jQuery(this).val() === pasteInputPlaceholderElement) { jQuery(this).attr('value', '').css('color', '#222'); } }) .blur(function () { if (jQuery(this).val() === '') { jQuery(this).attr('value', pasteInputPlaceholderElement).css('color', '#999'); } }) .keyup(function () { toggleNextStepButton( pasteProcessButtonElement, (this.value.trim() !== '') ? 'on' : 'off' ); }); pasteProcessButtonElement.click(function () { MailPoet.Notice.hide(); // get an approximate size of textarea paste in bytes var pasteSize = encodeURI(pasteInputElement.val()).split(/%..|./).length - 1; if (pasteSize > window.maxPostSizeBytes) { MailPoet.Notice.error(MailPoet.I18n.t('maxPostSizeNotice')); return; } // delay loading indicator for 10ms or else it's just too fast :) MailPoet.Modal.loading(true); setTimeout(function () { Papa.parse(pasteInputElement.val(), parseCSV(false)); }, 10); }); /* * CSV file */ uploadElement.change(function () { MailPoet.Notice.hide(); var ext = this.value.match(/\.(.+)$/); if (ext === null || ext[1].toLowerCase() !== 'csv') { this.value = ''; MailPoet.Notice.error(MailPoet.I18n.t('wrongFileFormat')); } toggleNextStepButton( uploadProcessButtonElement, (this.value.trim() !== '') ? 'on' : 'off' ); }); uploadProcessButtonElement.click(function () { if (uploadElement.val().trim() !== '') { // delay loading indicator for 10ms or else it's just too fast :) MailPoet.Modal.loading(true); setTimeout(function () { uploadElement.parse({ config: parseCSV(true) }); }, 10); } }); /* * MailChimp */ mailChimpKeyInputElement.keyup(function () { if (this.value.trim() === '' || !/[a-zA-Z0-9]{32}-/.exec(this.value.trim())) { mailChimpListsContainerElement.hide(); jQuery('.mailpoet_mailchimp-key-status') .html('') .removeClass('mailpoet_mailchimp-ok mailpoet_mailchimp-error'); toggleNextStepButton(mailChimpProcessButtonElement, 'off'); } }); mailChimpKeyVerifyButtonElement.click(function () { MailPoet.Modal.loading(true); MailPoet.Ajax.post({ api_version: window.mailpoet_api_version, endpoint: 'importExport', action: 'getMailChimpLists', data: { api_key: mailChimpKeyInputElement.val() } }).always(function () { MailPoet.Modal.loading(false); }).done(function (response) { jQuery('.mailpoet_mailchimp-key-status') .html('') .removeClass() .addClass('mailpoet_mailchimp-key-status mailpoet_mailchimp-ok'); if (response.data.length === 0) { jQuery('.mailpoet_mailchimp-key-status').html(MailPoet.I18n.t('noMailChimpLists')); mailChimpListsContainerElement.hide(); toggleNextStepButton(mailChimpProcessButtonElement, 'off'); } else { displayMailChimpLists(response.data); } }).fail(function (response) { if (response.errors.length > 0) { MailPoet.Notice.error( response.errors.map(function (error) { return error.message; }), { scroll: true } ); } }); }); mailChimpProcessButtonElement.click(function () { if (mailChimpProcessButtonElement.closest('table a').hasClass('button-disabled')) { return; } MailPoet.Modal.loading(true); MailPoet.Ajax.post({ api_version: window.mailpoet_api_version, endpoint: 'importExport', action: 'getMailChimpSubscribers', data: { api_key: mailChimpKeyInputElement.val(), lists: mailChimpListsContainerElement.find('select').val() } }).always(function (response) { MailPoet.Modal.loading(false); }).done(function (response) { window.importData.step1 = response.data; MailPoet.trackEvent('Subscribers import started', { source: 'MailChimp', 'MailPoet Free version': window.mailpoet_version }); router.navigate('step2', { trigger: true }); }).fail(function (response) { if (response.errors.length > 0) { MailPoet.Notice.error( response.errors.map(function (error) { return error.message; }), { scroll: true } ); } }); }); function displayMailChimpLists(data) { var listSelectElement = mailChimpListsContainerElement.find('select'); if (listSelectElement.data('select2')) { listSelectElement.select2('data', data); listSelectElement.trigger('change'); } else { listSelectElement .select2({ data: data, width: '20em', templateResult: function (item) { return item.name; }, templateSelection: function (item) { return item.name; } }) .change(function () { if (jQuery(this).val() !== null) { toggleNextStepButton(mailChimpProcessButtonElement, 'on'); } else { toggleNextStepButton(mailChimpProcessButtonElement, 'off'); } }) .trigger('change'); } mailChimpListsContainerElement.show(); } function toggleNextStepButton(element, condition) { var disabled = 'button-disabled'; if (condition === 'on') { element.closest('table a').removeClass(disabled); return; } element.closest('table a').addClass(disabled); } function parseCSV(isFile) { var processedSubscribers = [], parsedEmails = [], duplicateEmails = [], invalidEmails = [], emailColumnPosition = null, columnCount = null, isHeaderFound = false, advancedOptionHeader = true, advancedOptionDelimiter = '', advancedOptionNewline = '', advancedOptionComments = false, // trim spaces, commas, periods, // single/double quotes and convert to lowercase detectAndCleanupEmail = function (emailString) { var test; // decode HTML entities var email = jQuery('
').html(emailString).text(); email = email .toLowerCase() // left/right trim spaces, punctuation (e.g., " 'email@email.com'; ") // right trim non-printable characters (e.g., "email@email.com�") .replace(/^["';.,\s]+|[^\x20-\x7E]+$|["';.,_\s]+$/g, '') // remove spaces (e.g., "email @ email . com") // remove urlencoded characters .replace(/\s+|%\d+|,+/g, ''); // detect e-mails that will be otherwise rejected by email regex test = /<(.*?)>/.exec(email); if (test) { // is the email inside angle brackets (e.g., 'some@email.com ')? email = test[1].trim(); } test = /mailto:(?:\s+)?(.*)/.exec(email); if (test) { // is the email in 'mailto:email' format? email = test[1].trim(); } // test for valid characters using WP's rule (https://core.trac.wordpress.org/browser/tags/4.7.3/src/wp-includes/formatting.php#L2902) if (!/^[a-zA-Z0-9!#$%&\'*+\/=?^_`{|}~\.\-@]+$/.test(email)) { return false; } return email; }; return { skipEmptyLines: true, delimiter: advancedOptionDelimiter, newline: advancedOptionNewline, comments: advancedOptionComments, error: function () { MailPoet.Notice.hide(); MailPoet.Notice.error(MailPoet.I18n.t('dataProcessingError')); }, complete: function (CSV) { for (var rowCount in CSV.data) { var rowData = CSV.data[rowCount].map(function (el) { return el.trim(); }); var rowColumnCount = rowData.length; // set the number of row elements based on the first non-empty row if (columnCount === null) { columnCount = rowColumnCount; } // Process the row with the following assumptions: // 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 // 3. Duplicate addresses are skipped 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 if (emailColumnPosition === null) { for (var column in rowData) { var emailAddress = detectAndCleanupEmail(rowData[column]); if (emailColumnPosition === null && window.emailRegex.test(emailAddress)) { emailColumnPosition = column; parsedEmails[emailAddress] = true; // add current e-mail to an object index rowData[column] = emailAddress; processedSubscribers[emailAddress] = rowData; } } if (emailColumnPosition === null && advancedOptionHeader && parseInt(rowCount) === 0) { isHeaderFound = true; processedSubscribers[0] = rowData; } } else if (rowData[emailColumnPosition] !== '') { var email = detectAndCleanupEmail(rowData[emailColumnPosition]); if (_.has(parsedEmails, email)) { duplicateEmails.push(email); } else if (!window.emailRegex.test(email)) { invalidEmails.push(rowData[emailColumnPosition]); } // if we haven't yet processed this e-mail and it passed // the regex test, then process the row else { parsedEmails[email] = true; rowData[emailColumnPosition] = email; processedSubscribers[email] = rowData; } } } } // reindex array to avoid non-numeric indices processedSubscribers = _.values(processedSubscribers); // if the header options is set, there should be at least // 2 data rows, otherwise at least 1 data row if (processedSubscribers && (isHeaderFound && processedSubscribers.length >= 2) || (!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 window.importData.step1 = { header: (!window.emailRegex.test( processedSubscribers[0][emailColumnPosition]) ) ? processedSubscribers.shift() : null, subscribers: processedSubscribers, subscribersCount: processedSubscribers.length, duplicate: duplicateEmails, invalid: invalidEmails }; MailPoet.trackEvent('Subscribers import started', { source: isFile ? 'file upload' : 'pasted data', 'MailPoet Free version': window.mailpoet_version }); router.navigate('step2', { trigger: true }); } else { MailPoet.Modal.loading(false); var errorNotice = MailPoet.I18n.t('noValidRecords'); errorNotice = errorNotice.replace('[link]', MailPoet.I18n.t('csvKBLink')); errorNotice = errorNotice.replace('[/link]', ''); MailPoet.Notice.error(errorNotice); } } }; } }); router.on('route:step2', function () { if (typeof (window.importData.step1) === 'undefined') { router.navigate('step1', { trigger: true }); return; } // define reusable variables var nextStepButton = jQuery('#step2_process'), previousStepButton = jQuery('#return_to_step1'), // create a copy of subscribers object for further manipulation subscribers = jQuery.extend(true, {}, window.importData.step1), subscribersDataTemplate = 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'), maxRowsToShow = 10, filler = '. . .', // create an array of filler data with the same number of // elements as in the subscribers' data row fillerArray = Array.apply( null, new Array(subscribers.subscribers[0].length) ).map(String.prototype.valueOf, filler), fillerPosition; showCurrentStep(); // hide previous statistics/import results jQuery('#subscribers_data_parse_results:visible').html(''); jQuery('#subscribers_data_import_results:visible').hide(); // show parse statistics if any duplicate/invalid records were found if (subscribers.invalid.length || subscribers.duplicate.length) { // count repeating e-mails inside duplicate array and present them in // 'email (xN)' format var duplicates = {}; subscribers.duplicate.forEach(function (email) { duplicates[email] = (duplicates[email] || 0) + 1; }); subscribers.duplicate = []; for (var email in duplicates) { if (duplicates[email] > 1) { subscribers.duplicate.push(email + ' (x' + duplicates[email] + ')'); } else { subscribers.duplicate.push(email); } } var import_results = { notice: MailPoet.I18n.t('importNoticeSkipped').replace( '%1$s', '' + (subscribers.invalid.length + subscribers.duplicate.length) + '' ), invalid: (subscribers.invalid.length) ? MailPoet.I18n.t('importNoticeInvalid') .replace('%1$s', '' + subscribers.invalid.length.toLocaleString() + '') .replace('%2$s', subscribers.invalid.join(', ')) : null, duplicate: (subscribers.duplicate.length) ? MailPoet.I18n.t('importNoticeDuplicate') .replace('%1$s', '' + subscribers.duplicate.length + '') .replace('%2$s', subscribers.duplicate.join(', ')) : null }; jQuery('#subscribers_data_parse_results').html( subscribersDataParseResultsTemplate(import_results) ); } jQuery('.mailpoet_subscribers_data_parse_results_details_show') .click(function () { var details = jQuery('.mailpoet_subscribers_data_parse_results_details'); jQuery(details).toggle(); this.text = (jQuery(details).is(':visible')) ? MailPoet.I18n.t('hideDetails') : MailPoet.I18n.t('showDetails'); }); // show available segments if (window.mailpoetSegments.length) { jQuery('.mailpoet_segments').show(); } else { jQuery('.mailpoet_no_segments').show(); } function enableSegmentSelection(segments) { if (segmentSelectElement.data('select2')) { segmentSelectElement .html('') .select2('destroy'); toggleNextStepButton('off'); } segmentSelectElement .select2({ data: segments, width: '20em', templateResult: function (item) { var i = item; i.subscriberCount = parseInt(i.subscriberCount, 10); return i.name + ' (' + i.subscriberCount.toLocaleString() + ')'; }, templateSelection: function (item) { var i = item; i.subscriberCount = parseInt(i.subscriberCount, 10); return i.name + ' (' + i.subscriberCount.toLocaleString() + ')'; } }) .change(function () { var segmentSelectionNotice = jQuery('[data-id="notice_segmentSelection"]'); if (!this.value) { if (!segmentSelectionNotice.length) { MailPoet.Notice.error(MailPoet.I18n.t('segmentSelectionRequired'), { static: true, scroll: true, id: 'notice_segmentSelection', hideClose: true }); } toggleNextStepButton('off'); } else { jQuery('[data-id="notice_segmentSelection"]').remove(); if (!jQuery('.mailpoet_notice.error:visible').length) { toggleNextStepButton('on'); } } }); } jQuery('.mailpoet_create_segment').click(function () { MailPoet.Modal.popup({ title: MailPoet.I18n.t('addNewList'), template: jQuery('#new_segment_template').html() }); jQuery('#new_segment_name').keypress(function (e) { if (e.which == 13) { jQuery('#new_segment_process').click(); } }); jQuery('#new_segment_process').click(function () { var segmentName = jQuery('#new_segment_name').val().trim(); var segmentDescription = jQuery('#new_segment_description').val().trim(); MailPoet.Ajax.post({ api_version: window.mailpoet_api_version, endpoint: 'ImportExport', action: 'addSegment', data: { name: segmentName, description: segmentDescription } }).done(function (response) { window.mailpoetSegments.push({ id: response.data.id, name: response.data.name, subscriberCount: 0 }); var selected_values = segmentSelectElement.val(); if (selected_values === null) { selected_values = [response.data.id]; } else { selected_values.push(response.data.id); } enableSegmentSelection(window.mailpoetSegments); segmentSelectElement.val(selected_values).trigger('change'); jQuery('.mailpoet_segments:hidden').show(); jQuery('.mailpoet_no_segments:visible').hide(); MailPoet.Modal.close(); }).fail(function (response) { if (response.errors.length > 0) { MailPoet.Notice.hide(); MailPoet.Notice.error( response.errors.map(function (error) { return error.message; }), { positionAfter: '#new_segment_name' } ); } }); }); jQuery('#new_segment_cancel').click(function () { MailPoet.Modal.close(); }); }); // register partial template that will contain subscribers data Handlebars.registerPartial( 'subscribers_data_template_partial', subscribersDataTemplatePartial ); // autodetect column types Handlebars.registerHelper( 'show_and_match_columns', function (subscribers, options) { var displayedColumns = [], displayedColumnsIds = []; // go through all elements of the first row in subscribers data for (var i in subscribers.subscribers[0]) { var columnData = subscribers.subscribers[0][i], columnId = 'ignore'; // set default column type // if the column is not undefined and has a valid e-mail, set type as email if (columnData % 1 !== 0 && window.emailRegex.test(columnData)) { columnId = 'email'; } else if (subscribers.header) { var headerName = subscribers.header[i], headerNameMatch = window.mailpoetColumns.map(function (el) { return el.name; }).indexOf(headerName); // set column type using header if (headerNameMatch !== -1) { columnId = window.mailpoetColumns[headerNameMatch].id; }// set column type using header name else if (headerName) { if (/first|first name|given name/i.test(headerName)) { columnId = 'first_name'; } else if (/last|last name/i.test(headerName)) { columnId = 'last_name'; } else if (/status/i.test(headerName)) { columnId = 'status'; } } } // 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); } return options.fn(displayedColumns); }); // sanitize unsafe data Handlebars.registerHelper('sanitize_data', function (data) { return (data instanceof Handlebars.SafeString) ? data : new Handlebars.SafeString(Handlebars.Utils.escapeExpression(data)); }); // start array index from 1 Handlebars.registerHelper('calculate_index', function (index) { var index = parseInt(index); // display filler data (e.g., ellipsis) if we've reached the maximum number of rows and // subscribers count is greater than the maximum number of rows we're displaying if (index === maxRowsToShow && subscribers.subscribersCount > (maxRowsToShow + 1)) { fillerPosition = index; return filler; } // if we're on the last line, show the total count of subscribers data else if (index === (subscribers.subscribers.length - 1)) { return subscribers.subscribersCount.toLocaleString(); } else { return index + 1; } }); // reduce subscribers object if the total length is greater than the // maximum number of defined rows if (subscribers.subscribersCount > (maxRowsToShow + 1)) { subscribers.subscribers.splice( maxRowsToShow, subscribers.subscribersCount - (maxRowsToShow + 1), fillerArray ); } // render template jQuery('#subscribers_data > table').html(subscribersDataTemplate(subscribers)); // filter displayed data jQuery('select.mailpoet_subscribers_column_data_match') .select2({ data: window.mailpoetColumnsSelect2, width: '15em', templateResult: function (item) { return item.name; }, templateSelection: function (item) { return item.name; } }) .on('select2:selecting', function (selectEvent) { var selectElement = this, selectedOptionId = selectEvent.params.args.data.id; // CREATE CUSTOM FIELD if (selectedOptionId === 'create') { selectEvent.preventDefault(); jQuery(selectElement).select2('close'); MailPoet.Modal.popup({ title: MailPoet.I18n.t('addNewField'), template: jQuery('#form_template_field_form').html() }); jQuery('#form_field_new').parsley().on('form:submit', function (parsley) { // get data var data = jQuery(this.$element).serializeObject(); // save custom field MailPoet.Ajax.post({ api_version: window.mailpoet_api_version, endpoint: 'customFields', action: 'save', data: data }).done(function (response) { var new_column_data = { id: response.data.id, name: response.data.name, type: response.data.type, params: response.data.params, custom: true }; // if this is the first custom column, create an "optgroup" if (window.mailpoetColumnsSelect2.length === 2) { window.mailpoetColumnsSelect2.push({ name: MailPoet.I18n.t('userColumns'), children: [] }); } window.mailpoetColumnsSelect2[2].children.push(new_column_data); window.mailpoetColumns.push(new_column_data); jQuery('select.mailpoet_subscribers_column_data_match') .each(function () { jQuery(this) .html('') .select2('destroy') .select2({ data: window.mailpoetColumnsSelect2, width: '15em', templateResult: function (item) { return item.name; }, templateSelection: function (item) { return item.name; } }); }); jQuery(selectElement).data('column-id', new_column_data.id); jQuery(selectElement).data('validation-rule', false); filterSubscribers(); // close popup MailPoet.Modal.close(); }).fail(function (response) { if (response.errors.length > 0) { MailPoet.Notice.error( response.errors.map(function (error) { return error.message; }), { positionAfter: '#field_name' } ); } }); return false; }); } // CHANGE COLUMN else { // check for duplicate values in all select options jQuery('select.mailpoet_subscribers_column_data_match') .each(function () { var element = this, elementId = jQuery(element).val(); // if another column has the same value and it's not an 'ignore', prompt user if (elementId === selectedOptionId && elementId !== 'ignore') { if (confirm(MailPoet.I18n.t('selectedValueAlreadyMatched') + ' ' + MailPoet.I18n.t('confirmCorrespondingColumn'))) { jQuery(element).data('column-id', 'ignore'); } else { selectEvent.preventDefault(); jQuery(selectElement).select2('close'); } } }); } }) .on('select2:select', function (selectEvent) { var selectElement = this, selectedOptionId = selectEvent.params.data.id; jQuery(selectElement).data('column-id', selectedOptionId); filterSubscribers(); }); // filter subscribers' data to detect dates, emails, etc. function filterSubscribers() { jQuery( '[data-id="notice_invalidEmail"], [data-id="notice_invalidDate"]') .remove(); var subscribersClone = jQuery.extend(true, {}, subscribers), preventNextStep = false, displayedColumns = jQuery.map( jQuery('.mailpoet_subscribers_column_data_match'), function (element, elementIndex) { var columnId = jQuery(element).data('column-id'); var validationRule = jQuery(element).data('validation-rule'); jQuery(element).val(columnId).trigger('change'); return { id: columnId, index: elementIndex, validationRule: validationRule, element: element }; }); // iterate through the object of mailpoet columns jQuery.map(window.mailpoetColumns, function (column, columnIndex) { // check if the column id matches the selected id of one of the // subscriber's data columns var matchedColumn = _.find(displayedColumns, function (data) { return data.id === column.id; }); // EMAIL filter: if the first value in the column doesn't have a valid // email, hide the next button if (column.id === 'email') { if (!window.emailRegex.test(subscribersClone.subscribers[0][matchedColumn.index])) { preventNextStep = true; if (!jQuery('[data-id="notice_invalidEmail"]').length) { MailPoet.Notice.error(MailPoet.I18n.t('columnContainsInvalidElement'), { static: true, scroll: true, hideClose: true, id: 'invalidEmail' }); } } else { MailPoet.Notice.hide('invalidEmail'); } } // DATE filter: if column type is date, check if we can recognize it if (column.type === 'date' && matchedColumn) { var allowedDateFormats = [ Moment.ISO_8601, 'YYYY/MM/DD', 'MM/DD/YYYY', 'DD/MM/YYYY', 'YYYY/MM/DD', 'YYYY/DD/MM', 'MM/YYYY', 'YYYY/MM', 'YYYY' ]; var firstRowData = subscribersClone.subscribers[0][matchedColumn.index]; var validationRule = false; var testedFormat; // check if date exists if (firstRowData.trim() === '') { subscribersClone.subscribers[0][matchedColumn.index] = '' + MailPoet.I18n.t('emptyFirstRowDate') + ' '; preventNextStep = true; } else { for (var format in allowedDateFormats) { testedFormat = allowedDateFormats[format]; if (Moment(firstRowData, testedFormat, true).isValid()) { validationRule = (typeof(testedFormat) === 'function') ? 'datetime' : testedFormat; // set validation on the column element jQuery(matchedColumn.element).data('validation-rule', validationRule); break; } if (validationRule === 'datetime') { validationRule = Moment.ISO_8601; } } } jQuery.map(subscribersClone.subscribers, function (dataSubscribers, index) { var data = dataSubscribers; var rowData = data[matchedColumn.index]; if (index === fillerPosition || rowData.trim() === '') return; var date = Moment(rowData, testedFormat, true); // validate date if (date.isValid()) { data[matchedColumn.index] = new Handlebars.SafeString( Handlebars.Utils.escapeExpression(data[matchedColumn.index]) + '' + MailPoet.Date.format(date) + ' ' ); } else { data[matchedColumn.index] = new Handlebars.SafeString( Handlebars.Utils.escapeExpression(data[matchedColumn.index]) + '' + (new Handlebars.SafeString(MailPoet.I18n.t('dateMatchError'))) + ' ' ); preventNextStep = true; } }); if (preventNextStep && !jQuery('.mailpoet_invalidDate').length) { MailPoet.Notice.error(MailPoet.I18n.t('columnContainsInvalidDate'), { static: true, scroll: true, hideClose: true, id: 'invalidDate' }); } } }); // refresh table with susbcribers' data jQuery('#subscribers_data > table > tbody') .html(subscribersDataTemplatePartial(subscribersClone)); if (preventNextStep) { toggleNextStepButton('off'); } else if (!jQuery('.mailpoet_notice.error:visible').length && segmentSelectElement.val()) { toggleNextStepButton('on'); } } function toggleNextStepButton(condition) { var disabled = 'button-disabled'; if (condition === 'on') { nextStepButton.removeClass(disabled); return; } nextStepButton.addClass(disabled); } previousStepButton.off().on('click', function () { router.navigate('step1', { trigger: true }); }); nextStepButton.off().on('click', function () { if (jQuery(this).hasClass('button-disabled')) { return; } MailPoet.Modal.loading(true); var columns = {}, queue = new jQuery.AsyncQueue(), batchNumber = 0, batchSize = 2000, timestamp = Date.now() / 1000, subscribers = [], importResults = { created: 0, updated: 0, errors: [], segments: [] }, splitSubscribers = function (subscribers, size) { return subscribers.reduce(function (res, item, index) { if (index % size === 0) { res.push([]); } res[res.length - 1].push(item); return res; }, []); }, subscribers = splitSubscribers(window.importData.step1.subscribers, batchSize); _.each(jQuery('select.mailpoet_subscribers_column_data_match'), function (column, columnIndex) { var columnId = jQuery(column).data('column-id'); var validationRule = jQuery(column).data('validation-rule'); if (columnId === 'ignore') { return; } columns[columnId] = { index: columnIndex, validation_rule: validationRule }; }); _.each(subscribers, function () { queue.add(function (queue) { queue.pause(); MailPoet.Ajax.post({ api_version: window.mailpoet_api_version, endpoint: 'ImportExport', action: 'processImport', data: JSON.stringify({ columns: columns, subscribers: subscribers[batchNumber], timestamp: timestamp, segments: segmentSelectElement.val(), updateSubscribers: (jQuery(':radio[name="subscriber_update_option"]:checked').val() === 'yes') }) }).done(function (response) { importResults.created += response.data.created; importResults.updated += response.data.updated; importResults.segments = response.data.segments; importResults.added_to_segment_with_welcome_notification = response.data.added_to_segment_with_welcome_notification; queue.run(); }).fail(function (response) { MailPoet.Modal.loading(false); if (response.errors.length > 0) { MailPoet.Notice.error( response.errors.map(function (error) { return error.message; }), { scroll: true } ); } }); batchNumber++; }); }); queue.run(); queue.onComplete(function () { MailPoet.Modal.loading(false); if (importResults.errors.length > 0 && !importResults.updated && !importResults.created) { MailPoet.Notice.error(_.flatten(importResults.errors) ); } else { window.mailpoetSegments = importResults.segments; importResults.segments = _.map(segmentSelectElement.select2('data'), function (data) { return data.name; }); window.importData.step2 = importResults; enableSegmentSelection(window.mailpoetSegments); router.navigate('step3', { trigger: true }); } }); }); filterSubscribers(); enableSegmentSelection(window.mailpoetSegments); }); router.on('route:step3', function () { if (typeof (window.importData.step2) === 'undefined') { router.navigate('step2', { trigger: true }); return; } showCurrentStep(); if (window.importData.step2.errors.length > 0) { MailPoet.Notice.error(_.flatten(window.importData.step2.errors)); } MailPoet.trackEvent('Subscribers import finished', { 'Subscribers created': window.importData.step2.created, 'Subscribers updated': window.importData.step2.updated, 'MailPoet Free version': window.mailpoet_version }); // display statistics var subscribersDataImportResultsTemplate = Handlebars .compile(jQuery('#subscribers_data_import_results_template') .html()), exportMenuElement = jQuery('span.mailpoet_export'), importResults = { created: (window.importData.step2.created) ? MailPoet.I18n.t('subscribersCreated') .replace('%1$s', '' + window.importData.step2.created.toLocaleString() + '') .replace('%2$s', '"' + window.importData.step2.segments.join('", "') + '"') : false, updated: (window.importData.step2.updated) ? MailPoet.I18n.t('subscribersUpdated') .replace('%1$s', '' + window.importData.step2.updated.toLocaleString() + '') .replace('%2$s', '"' + window.importData.step2.segments.join('", "') + '"') : false, no_action: (!window.importData.step2.created && !window.importData.step2.updated), added_to_segment_with_welcome_notification: window.importData.step2.added_to_segment_with_welcome_notification }; jQuery('#subscribers_data_import_results') .html(subscribersDataImportResultsTemplate(importResults)) .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 created and the export menu item is hidden // (it's shown only when there are subscribers), display it if (importResults.created && exportMenuElement.not(':visible')) { exportMenuElement.show(); } // reset previous step's data so that coming back to this step is prevented window.importData.step2 = undefined; }); if (!Backbone.History.started) { Backbone.history.start(); } }); });