- Removes file size limit in import

- Implements chunked import processing
- Updates tests/migrator/Subscriber model
This commit is contained in:
Vlad
2016-02-20 18:53:24 -05:00
parent 580ac989aa
commit 499936e3ab
9 changed files with 203 additions and 5135 deletions

View File

@ -6,7 +6,8 @@ define(
'mailpoet', 'mailpoet',
'handlebars', 'handlebars',
'papaparse', 'papaparse',
'select2' 'select2',
'asyncqueue'
], ],
function ( function (
Backbone, Backbone,
@ -14,12 +15,14 @@ define(
jQuery, jQuery,
MailPoet, MailPoet,
Handlebars, Handlebars,
Papa Papa,
AsyncQueue
) { ) {
if (!jQuery('#mailpoet_subscribers_import').length) { if (!jQuery('#mailpoet_subscribers_import').length) {
return; return;
} }
jQuery(document).ready(function () { jQuery(document).ready(function () {
console.log = function() {};
jQuery('input[name="select_method"]').attr('checked', false); jQuery('input[name="select_method"]').attr('checked', false);
// configure router // configure router
router = new (Backbone.Router.extend({ router = new (Backbone.Router.extend({
@ -1050,64 +1053,96 @@ define(
} }
MailPoet.Modal.loading(true); MailPoet.Modal.loading(true);
var subscribers = {}; var columns = {},
queue = new jQuery.AsyncQueue(),
batch = 0,
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(importData.step1.subscribers, 500);
_.each(jQuery('select.mailpoet_subscribers_column_data_match'), _.each(jQuery('select.mailpoet_subscribers_column_data_match'),
function (column, index) { function (column, columnIndex) {
var columnId = jQuery(column).data('column-id'); var columnId = jQuery(column).data('column-id');
if (columnId === 'ignore') { if (columnId === 'ignore') {
return; return;
} }
subscribers[columnId] = []; columns[columnId] = columnIndex;
_.each(importData.step1.subscribers, function (subsciber) { });
subscribers[columnId].push(
_.chain(subsciber)
.pick(index)
.toArray()
.flatten()
.value()
);
});
subscribers[columnId] = _.flatten(subscribers[columnId]);
});
MailPoet.Ajax.post({ _.each(subscribers, function () {
endpoint: 'ImportExport', queue.add(function (queue) {
action: 'processImport', queue.pause();
data: JSON.stringify({ MailPoet.Ajax
subscribers: subscribers, .post({
segments: segmentSelectElement.val(), endpoint: 'ImportExport',
updateSubscribers: (jQuery(':radio[name="subscriber_update_option"]:checked').val() === 'yes') ? true : false action: 'processImport',
data: JSON.stringify({
columns: columns,
subscribers: subscribers[batch],
length: subscribers[batch].length,
segments: segmentSelectElement.val(),
updateSubscribers: (jQuery(':radio[name="subscriber_update_option"]:checked').val() === 'yes') ? true : false
})
})
.done(function (response) {
if (response.result === false) {
importResults.errors.push(response.errors);
} else {
importResults.created += response.data.created;
importResults.updated += response.data.updated;
importResults.segments = response.data.segments;
}
queue.run();
})
.error(function (error) {
importResults.errors.push(
MailPoetI18n.serverError + error.statusText.toLowerCase() + '.'
);
queue.run();
});
batch++;
}) })
}).done(function (response) { });
queue.run();
queue.onComplete(function () {
MailPoet.Modal.loading(false); MailPoet.Modal.loading(false);
if (response.result === false) { if (importResults.errors.length > 0 && !importResults.updated && !importResults.created) {
MailPoet.Notice.error(response.errors, { MailPoet.Notice.error(_.flatten(importResults.errors), {
timeout: 3000, timeout: 3000,
}); }
} else { );
mailpoetSegments = response.data.segments; }
response.data.segments = _.map(segmentSelectElement.select2('data'), else {
function (data) { mailpoetSegments = importResults.segments;
return data.name; importResults.segments = _.map(segmentSelectElement.select2('data'),
}); function (data) {
importData.step2 = response.data; return data.name;
});
importData.step2 = importResults;
enableSegmentSelection(mailpoetSegments); enableSegmentSelection(mailpoetSegments);
router.navigate('step3', {trigger: true}); router.navigate('step3', {trigger: true});
} }
}).error(function (error) {
MailPoet.Modal.loading(false);
MailPoet.Notice.error(
MailPoetI18n.serverError + error.statusText.toLowerCase() + '.', {
timeout: 3000,
}
);
}); });
}); });
filterSubscribers(); filterSubscribers();
enableSegmentSelection(mailpoetSegments); enableSegmentSelection(mailpoetSegments);
}); });
router.on('route:step3', function () { router.on('route:step3', function () {
@ -1118,6 +1153,12 @@ define(
showCurrentStep(); showCurrentStep();
if (importData.step2.errors.length > 0) {
MailPoet.Notice.error(_.flatten(importData.step2.errors), {
timeout: 3000,
});
}
// display statistics // display statistics
var subscribersDataImportResultsTemplate = var subscribersDataImportResultsTemplate =
Handlebars Handlebars

View File

@ -57,6 +57,7 @@ class Migrator {
'last_name tinytext NOT NULL,', 'last_name tinytext NOT NULL,',
'email varchar(150) NOT NULL,', 'email varchar(150) NOT NULL,',
'status varchar(12) NOT NULL DEFAULT "unconfirmed",', 'status varchar(12) NOT NULL DEFAULT "unconfirmed",',
'import_batch varchar(12) NULL,',
'created_at TIMESTAMP NOT NULL DEFAULT 0,', 'created_at TIMESTAMP NOT NULL DEFAULT 0,',
'deleted_at TIMESTAMP NULL DEFAULT NULL,', 'deleted_at TIMESTAMP NULL DEFAULT NULL,',
'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,', 'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',

View File

@ -498,7 +498,7 @@ class Subscriber extends Model {
); );
} }
static function updateMultiple($columns, $subscribers, $currentTime = false) { static function updateMultiple($columns, $subscribers, $import_batch = false) {
$ignoreColumnsOnUpdate = array( $ignoreColumnsOnUpdate = array(
'email', 'email',
'created_at' 'created_at'
@ -538,7 +538,7 @@ class Subscriber extends Model {
return self::rawExecute( return self::rawExecute(
'UPDATE `' . self::$_table . '` ' . 'UPDATE `' . self::$_table . '` ' .
'SET ' . implode(', ', $sql('statement')) . ' '. 'SET ' . implode(', ', $sql('statement')) . ' '.
(($currentTime) ? ', updated_at = "' . $currentTime . '" ' : '') . (($import_batch) ? ', import_batch = "' . $import_batch . '" ' : '') .
'WHERE email IN ' . 'WHERE email IN ' .
'(' . rtrim(str_repeat('?,', count($subscribers)), ',') . ')', '(' . rtrim(str_repeat('?,', count($subscribers)), ',') . ')',
array_merge( array_merge(

View File

@ -6,6 +6,7 @@ use MailPoet\Models\SubscriberCustomField;
use MailPoet\Models\SubscriberSegment; use MailPoet\Models\SubscriberSegment;
use MailPoet\Subscribers\ImportExport\BootStrapMenu; use MailPoet\Subscribers\ImportExport\BootStrapMenu;
use MailPoet\Util\Helpers; use MailPoet\Util\Helpers;
use MailPoet\Util\Security;
class Import { class Import {
public $subscribers_data; public $subscribers_data;
@ -15,20 +16,25 @@ class Import {
public $subscriber_custom_fields; public $subscriber_custom_fields;
public $subscribers_count; public $subscribers_count;
public $import_time; public $import_time;
public $import_batch;
public $profiler_start; public $profiler_start;
public function __construct($data) { public function __construct($data) {
$this->subscribers_data = $data['subscribers']; $this->subscribers_data = $this->transformSubscribersData(
$data['subscribers'],
$data['columns']
);
$this->segments = $data['segments']; $this->segments = $data['segments'];
$this->update_subscribers = $data['updateSubscribers']; $this->update_subscribers = $data['updateSubscribers'];
$this->subscriber_fields = $this->getSubscriberFields( $this->subscriber_fields = $this->getSubscriberFields(
array_keys($this->subscribers_data) array_keys($data['columns'])
); );
$this->subscriber_custom_fields = $this->getCustomSubscriberFields( $this->subscriber_custom_fields = $this->getCustomSubscriberFields(
array_keys($this->subscribers_data) array_keys($data['columns'])
); );
$this->subscribers_count = count(reset($this->subscribers_data)); $this->subscribers_count = count(reset($this->subscribers_data));
$this->import_time = date('Y-m-d H:i:s'); $this->import_time = date('Y-m-d H:i:s');
$this->import_batch = Security::generateRandomString();
$this->profiler_start = microtime(true); $this->profiler_start = microtime(true);
} }
@ -63,14 +69,6 @@ class Import {
$subscriber_fields, $subscriber_fields,
$subscriber_custom_fields $subscriber_custom_fields
); );
if($created_subscribers) {
// subtract added from updated subscribers when DB operation takes <1s
$updated_subscribers = array_diff_key(
$updated_subscribers,
$created_subscribers,
$subscriber_custom_fields
);
}
} }
} catch(\PDOException $e) { } catch(\PDOException $e) {
return array( return array(
@ -86,13 +84,21 @@ class Import {
'updated' => count($updated_subscribers), 'updated' => count($updated_subscribers),
'segments' => $segments->getSegments() 'segments' => $segments->getSegments()
), ),
'time' => date('Y-m-d H:i:s'),
'profiler' => $this->timeExecution() 'profiler' => $this->timeExecution()
); );
} }
function transformSubscribersData($subscribers, $columns) {
foreach($columns as $column => $index) {
$transformed_subscribers[$column] = Helpers::arrayColumn($subscribers, $index);
}
return $transformed_subscribers;
}
function filterExistingAndNewSubscribers($subscribers_data) { function filterExistingAndNewSubscribers($subscribers_data) {
$existing_records = array_filter( $existing_records = array_filter(
array_map(function($subscriber_emails) { array_map(function ($subscriber_emails) {
return Subscriber::selectMany(array('email')) return Subscriber::selectMany(array('email'))
->whereIn('email', $subscriber_emails) ->whereIn('email', $subscriber_emails)
->whereNull('deleted_at') ->whereNull('deleted_at')
@ -120,18 +126,18 @@ class Import {
} }
$new_subscribers = $new_subscribers =
array_filter( array_filter(
array_map(function($subscriber) use ($new_records) { array_map(function ($subscriber) use ($new_records) {
return array_map(function($index) use ($subscriber) { return array_map(function ($index) use ($subscriber) {
return $subscriber[$index]; return $subscriber[$index];
}, $new_records); }, $new_records);
}, $subscribers_data) }, $subscribers_data)
); );
$existing_subscribers = $existing_subscribers =
array_map(function($subscriber) use ($new_records) { array_map(function ($subscriber) use ($new_records) {
return array_values( // reindex array return array_values( // reindex array
array_filter( // remove NULL entries array_filter( // remove NULL entries
array_map(function($index, $data) use ($new_records) { array_map(function ($index, $data) use ($new_records) {
if(!in_array($index, $new_records)) return $data; if(!in_array($index, $new_records)) return $data;
}, array_keys($subscriber), $subscriber) }, array_keys($subscriber), $subscriber)
) )
@ -145,7 +151,7 @@ class Import {
function deleteExistingTrashedSubscribers($subscribers_data) { function deleteExistingTrashedSubscribers($subscribers_data) {
$existing_trashed_records = array_filter( $existing_trashed_records = array_filter(
array_map(function($subscriber_emails) { array_map(function ($subscriber_emails) {
return Subscriber::selectMany(array('id')) return Subscriber::selectMany(array('id'))
->whereIn('email', $subscriber_emails) ->whereIn('email', $subscriber_emails)
->whereNotNull('deleted_at') ->whereNotNull('deleted_at')
@ -163,8 +169,17 @@ class Import {
} }
function extendSubscribersAndFields($subscribers_data, $subscriber_fields) { function extendSubscribersAndFields($subscribers_data, $subscriber_fields) {
$subscribers_data['created_at'] = $this->filterSubscriberCreatedAtDate(); $subscribers_data['created_at'] =
$subscriber_fields[] = 'created_at'; array_fill(0, $this->subscribers_count, $this->import_time);
$subscribers_data['import_batch'] =
array_fill(0, $this->subscribers_count, $this->import_batch);
$subscriber_fields = array_merge(
$subscriber_fields,
array(
'created_at',
'import_batch'
)
);
return array( return array(
$subscribers_data, $subscribers_data,
$subscriber_fields $subscriber_fields
@ -174,7 +189,7 @@ class Import {
function getSubscriberFields($subscriber_fields) { function getSubscriberFields($subscriber_fields) {
return array_values( return array_values(
array_filter( array_filter(
array_map(function($field) { array_map(function ($field) {
if(!is_int($field)) return $field; if(!is_int($field)) return $field;
}, $subscriber_fields) }, $subscriber_fields)
) )
@ -184,17 +199,13 @@ class Import {
function getCustomSubscriberFields($subscriber_fields) { function getCustomSubscriberFields($subscriber_fields) {
return array_values( return array_values(
array_filter( array_filter(
array_map(function($field) { array_map(function ($field) {
if(is_int($field)) return $field; if(is_int($field)) return $field;
}, $subscriber_fields) }, $subscriber_fields)
) )
); );
} }
function filterSubscriberCreatedAtDate() {
return array_fill(0, $this->subscribers_count, $this->import_time);
}
function filterSubscriberStatus($subscribers_data, $subscriber_fields) { function filterSubscriberStatus($subscribers_data, $subscriber_fields) {
if(!in_array('status', $subscriber_fields)) { if(!in_array('status', $subscriber_fields)) {
$subscribers_data['status'] = $subscribers_data['status'] =
@ -225,7 +236,7 @@ class Import {
'false' 'false'
) )
); );
$subscribers_data['status'] = array_map(function($state) use ($statuses) { $subscribers_data['status'] = array_map(function ($state) use ($statuses) {
if(in_array(strtolower($state), $statuses['subscribed'])) { if(in_array(strtolower($state), $statuses['subscribed'])) {
return 'subscribed'; return 'subscribed';
} }
@ -250,12 +261,12 @@ class Import {
$subscriber_custom_fields $subscriber_custom_fields
) { ) {
$subscribers_count = count(reset($subscribers_data)) - 1; $subscribers_count = count(reset($subscribers_data)) - 1;
$subscribers = array_map(function($index) use ($subscribers_data, $subscriber_fields) { $subscribers = array_map(function ($index) use ($subscribers_data, $subscriber_fields) {
return array_map(function($field) use ($index, $subscribers_data) { return array_map(function ($field) use ($index, $subscribers_data) {
return $subscribers_data[$field][$index]; return $subscribers_data[$field][$index];
}, $subscriber_fields); }, $subscriber_fields);
}, range(0, $subscribers_count)); }, range(0, $subscribers_count));
$import_time = ($action === 'update') ? date('Y-m-d H:i:s') : $this->import_time; $batch = ($action === 'update') ? Security::generateRandomString() : $this->import_batch;
foreach(array_chunk($subscribers, 100) as $data) { foreach(array_chunk($subscribers, 100) as $data) {
if($action == 'create') { if($action == 'create') {
Subscriber::createMultiple( Subscriber::createMultiple(
@ -267,7 +278,7 @@ class Import {
Subscriber::updateMultiple( Subscriber::updateMultiple(
$subscriber_fields, $subscriber_fields,
$data, $data,
$import_time $batch
); );
} }
} }
@ -277,7 +288,7 @@ class Import {
'id', 'id',
'email' 'email'
)) ))
->where(($action === 'create') ? 'created_at' : 'updated_at', $import_time) ->where('import_batch', $batch)
->findArray(), ->findArray(),
'email', 'id' 'email', 'id'
); );
@ -303,10 +314,10 @@ class Import {
$subscriber_custom_fields $subscriber_custom_fields
) { ) {
$subscribers = array_map( $subscribers = array_map(
function($column) use ($db_subscribers, $subscribers_data) { function ($column) use ($db_subscribers, $subscribers_data) {
$count = range(0, count($subscribers_data[$column]) - 1); $count = range(0, count($subscribers_data[$column]) - 1);
return array_map( return array_map(
function($index, $value) function ($index, $value)
use ($db_subscribers, $subscribers_data, $column) { use ($db_subscribers, $subscribers_data, $column) {
$subscriber_id = array_search( $subscriber_id = array_search(
$subscribers_data['email'][$index], $subscribers_data['email'][$index],

View File

@ -5,7 +5,8 @@
}, },
"napa": { "napa": {
"blob": "eligrey/Blob.js.git", "blob": "eligrey/Blob.js.git",
"filesaver": "eligrey/FileSaver.js.git" "filesaver": "eligrey/FileSaver.js.git",
"asyncqueue": "mjward/Jquery-Async-queue.git"
}, },
"dependencies": { "dependencies": {
"backbone": "1.2.3", "backbone": "1.2.3",

View File

@ -8,24 +8,31 @@ use MailPoet\Util\Helpers;
class ImportCest { class ImportCest {
function __construct() { function __construct() {
$this->JSON_data = json_decode(file_get_contents(dirname(__FILE__) . '/ImportTestData.json'), true); $this->data = array(
$this->subscribers_data = array( 'subscribers' => array(
'first_name' => array( array(
'Adam', 'Adam',
'Mary' 'Smith',
'adam@smith.com',
'France'
),
array(
'Mary',
'Jane',
'mary@jane.com',
'Brazil'
)
), ),
'last_name' => array( 'columns' => array(
'Smith', 'first_name' => 0,
'Jane' 'last_name' => 1,
'email' => 2,
777 => 3
), ),
'email' => array( 'segments' => array(
'adam@smith.com', 195
'mary@jane.com'
), ),
777 => array( 'updateSubscribers' => true
'France',
'Brazil'
)
); );
$this->subscriber_fields = array( $this->subscriber_fields = array(
'first_name', 'first_name',
@ -34,25 +41,37 @@ class ImportCest {
); );
$this->segments = range(0, 1); $this->segments = range(0, 1);
$this->subscriber_custom_fields = array(777); $this->subscriber_custom_fields = array(777);
$this->import = new Import($this->JSON_data); $this->import = new Import($this->data);
$this->subscribers_data = $this->import->transformSubscribersData(
$this->data['subscribers'],
$this->data['columns']
);
} }
function itCanConstruct() { function itCanConstruct() {
expect($this->import->subscribers_data)->equals($this->JSON_data['subscribers']); expect(is_array($this->import->subscribers_data))->true();
expect($this->import->segments)->equals($this->JSON_data['segments']); expect($this->import->segments)->equals($this->data['segments']);
expect(is_array($this->import->subscriber_fields))->true(); expect(is_array($this->import->subscriber_fields))->true();
expect(is_array($this->import->subscriber_custom_fields))->true(); expect(is_array($this->import->subscriber_custom_fields))->true();
expect($this->import->subscribers_count)->equals( expect($this->import->subscribers_count)->equals(2);
count($this->JSON_data['subscribers']['email']) expect($this->import->import_batch)->notEmpty();
); expect($this->import->import_time)->notEmpty();
expect( expect($this->import->import_batch)->notEmpty();
preg_match( }
'/\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}/',
$this->import->import_time) function itCanTransformSubscribers() {
)->equals(1); expect($this->import->subscribers_data['first_name'][0])
->equals($this->data['subscribers'][0][0]);
expect($this->import->subscribers_data['last_name'][0])
->equals($this->data['subscribers'][0][1]);
expect($this->import->subscribers_data['email'][0])
->equals($this->data['subscribers'][0][2]);
expect($this->import->subscribers_data['777'][0])
->equals($this->data['subscribers'][0][3]);
} }
function itCanFilterExistingAndNewSubscribers() { function itCanFilterExistingAndNewSubscribers() {
$subscribers_data = $this->subscribers_data;
$subscriber = Subscriber::create(); $subscriber = Subscriber::create();
$subscriber->hydrate( $subscriber->hydrate(
array( array(
@ -62,10 +81,10 @@ class ImportCest {
)); ));
$subscriber->save(); $subscriber->save();
list($existing, $new) = $this->import->filterExistingAndNewSubscribers( list($existing, $new) = $this->import->filterExistingAndNewSubscribers(
$this->subscribers_data $subscribers_data
); );
expect($existing['email'][0])->equals($this->subscribers_data['email'][0]); expect($existing['email'][0])->equals($subscribers_data['email'][0]);
expect($new['email'][0])->equals($this->subscribers_data['email'][1]); expect($new['email'][0])->equals($subscribers_data['email'][1]);
} }
function itCanExtendSubscribersAndFields() { function itCanExtendSubscribersAndFields() {
@ -106,17 +125,17 @@ class ImportCest {
} }
function itCanFilterSubscriberStatus() { function itCanFilterSubscriberStatus() {
$subscibers_data = $this->subscribers_data; $subscribers_data = $this->subscribers_data;
$subscriber_fields = $this->subscriber_fields; $subscriber_fields = $this->subscriber_fields;
list($subscibers_data, $subsciber_fields) = list($subscribers_data, $subsciber_fields) =
$this->import->filterSubscriberStatus($subscibers_data, $subscriber_fields); $this->import->filterSubscriberStatus($subscribers_data, $subscriber_fields);
// subscribers' status was set to "subscribed" & status column was added // subscribers' status was set to "subscribed" & status column was added
// to subscribers fields // to subscribers fields
expect(array_pop($subsciber_fields))->equals('status'); expect(array_pop($subsciber_fields))->equals('status');
expect($subscibers_data['status'][0])->equals('subscribed'); expect($subscribers_data['status'][0])->equals('subscribed');
expect(count($subscibers_data['status']))->equals(2); expect(count($subscribers_data['status']))->equals(2);
$subscriber_fields[] = 'status'; $subscriber_fields[] = 'status';
$subscibers_data = array( $subscribers_data = array(
'status' => array( 'status' => array(
#subscribed #subscribed
'subscribed', 'subscribed',
@ -135,9 +154,9 @@ class ImportCest {
'false' 'false'
), ),
); );
list($subscibers_data, $subsciber_fields) = list($subscribers_data, $subsciber_fields) =
$this->import->filterSubscriberStatus($subscibers_data, $subscriber_fields); $this->import->filterSubscriberStatus($subscribers_data, $subscriber_fields);
expect($subscibers_data)->equals( expect($subscribers_data)->equals(
array( array(
'status' => array( 'status' => array(
'subscribed', 'subscribed',
@ -185,8 +204,8 @@ class ImportCest {
$subscribers_data = $this->subscribers_data; $subscribers_data = $this->subscribers_data;
$subscriber_fields = $this->subscriber_fields; $subscriber_fields = $this->subscriber_fields;
$subscribers_data['deleted_at'] = array( $subscribers_data['deleted_at'] = array(
null, null,
date('Y-m-d H:i:s') date('Y-m-d H:i:s')
); );
$subscriber_fields[] = 'deleted_at'; $subscriber_fields[] = 'deleted_at';
$this->import->createOrUpdateSubscribers( $this->import->createOrUpdateSubscribers(
@ -255,6 +274,7 @@ class ImportCest {
->equals($subscribers_data[777][1]); ->equals($subscribers_data[777][1]);
} }
function itCanaddSubscribersToSegments() { function itCanaddSubscribersToSegments() {
$subscribers_data = $this->subscribers_data; $subscribers_data = $this->subscribers_data;
$this->import->createOrUpdateSubscribers( $this->import->createOrUpdateSubscribers(
@ -296,19 +316,17 @@ class ImportCest {
function itCanProcess() { function itCanProcess() {
$import = clone($this->import); $import = clone($this->import);
$result = $import->process(); $result = $import->process();
expect($result['data']['created'])->equals(997); expect($result['data']['created'])->equals(2);
expect($result['data']['updated'])->equals(0); expect($result['data']['updated'])->equals(0);
$result = $import->process(); $result = $import->process();
expect($result['data']['created'])->equals(0); expect($result['data']['created'])->equals(0);
expect($result['data']['updated'])->equals(997); expect($result['data']['updated'])->equals(2);
Subscriber::where('email', 'mbanks4@blinklist.com') Subscriber::where('email', 'mary@jane.com')
->findOne() ->findOne()
->delete(); ->delete();
// TODO: find a more elegant way to test this
$import->import_time = date('Y-m-d 12:i:s');
$result = $import->process(); $result = $import->process();
expect($result['data']['created'])->equals(1); expect($result['data']['created'])->equals(1);
expect($result['data']['updated'])->equals(996); expect($result['data']['updated'])->equals(1);
$import->update_subscribers = false; $import->update_subscribers = false;
$result = $import->process(); $result = $import->process();
expect($result['data']['created'])->equals(0); expect($result['data']['created'])->equals(0);

File diff suppressed because it is too large Load Diff

View File

@ -60,8 +60,6 @@
</th> </th>
<td> <td>
<input type="file" id="file_local" accept=".csv" /> <input type="file" id="file_local" accept=".csv" />
&nbsp;
<%= __('total max upload file size : %s')|format(maxPostSize) %>
</td> </td>
</tr> </tr>
</tbody> </tbody>

View File

@ -30,7 +30,8 @@ baseConfig = {
'filesaver$': 'filesaver/FileSaver.js', 'filesaver$': 'filesaver/FileSaver.js',
'papaparse': 'papaparse/papaparse.min.js', 'papaparse': 'papaparse/papaparse.min.js',
'helpscout': 'helpscout.js', 'helpscout': 'helpscout.js',
'html2canvas': 'html2canvas/dist/html2canvas.js' 'html2canvas': 'html2canvas/dist/html2canvas.js',
'asyncqueue': 'asyncqueue/jquery.asyncqueue.js'
}, },
}, },
node: { node: {