mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-01-06 22:40:14 -05:00
🐛 Fixed members import unsubscribing members when subscribe_to_emails
is empty (#19658)
fixes ENG-611 - Previously, if an existing member with newsletter subscriptions was imported, and `subscribe_to_emails` was blank/empty, the member would be unsubscribed from all newsletters, which is not the expected behavior. - This PR changes the behavior so if `subscribe_to_emails` is blank, it will not unsubscribe existing members.
This commit is contained in:
parent
65e9900578
commit
c2fd22a246
4 changed files with 255 additions and 23 deletions
|
@ -14,53 +14,53 @@ const DEFAULT_COLUMNS = [
|
||||||
'tiers'
|
'tiers'
|
||||||
];
|
];
|
||||||
|
|
||||||
const unparse = (members, columns = DEFAULT_COLUMNS.slice()) => {
|
const unparse = (rows, columns = DEFAULT_COLUMNS.slice()) => {
|
||||||
columns = columns.map((column) => {
|
columns = columns.map((column) => {
|
||||||
if (column === 'subscribed') {
|
if (column === 'subscribed') {
|
||||||
return 'subscribed_to_emails';
|
return 'subscribed_to_emails';
|
||||||
}
|
}
|
||||||
return column;
|
return column;
|
||||||
});
|
});
|
||||||
const mappedMembers = members.map((member) => {
|
const mappedRows = rows.map((row) => {
|
||||||
if (member.error && !columns.includes('error')) {
|
if (row.error && !columns.includes('error')) {
|
||||||
columns.push('error');
|
columns.push('error');
|
||||||
}
|
}
|
||||||
|
|
||||||
let labels = '';
|
let labels = '';
|
||||||
if (typeof member.labels === 'string') {
|
if (typeof row.labels === 'string') {
|
||||||
labels = member.labels;
|
labels = row.labels;
|
||||||
} else if (Array.isArray(member.labels)) {
|
} else if (Array.isArray(row.labels)) {
|
||||||
labels = member.labels.map((l) => {
|
labels = row.labels.map((l) => {
|
||||||
return typeof l === 'string' ? l : l.name;
|
return typeof l === 'string' ? l : l.name;
|
||||||
}).join(',');
|
}).join(',');
|
||||||
}
|
}
|
||||||
|
|
||||||
let tiers = '';
|
let tiers = '';
|
||||||
|
|
||||||
if (Array.isArray(member.tiers)) {
|
if (Array.isArray(row.tiers)) {
|
||||||
tiers = member.tiers.map((tier) => {
|
tiers = row.tiers.map((tier) => {
|
||||||
return tier.name;
|
return tier.name;
|
||||||
}).join(',');
|
}).join(',');
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: member.id,
|
id: row.id,
|
||||||
email: member.email,
|
email: row.email,
|
||||||
name: member.name,
|
name: row.name,
|
||||||
note: member.note,
|
note: row.note,
|
||||||
subscribed_to_emails: member.subscribed || member.subscribed_to_emails ? true : false,
|
subscribed_to_emails: 'subscribed' in row ? row.subscribed : row.subscribed_to_emails,
|
||||||
complimentary_plan: member.comped || member.complimentary_plan,
|
complimentary_plan: row.comped || row.complimentary_plan,
|
||||||
stripe_customer_id: _.get(member, 'subscriptions[0].customer.id') || member.stripe_customer_id,
|
stripe_customer_id: _.get(row, 'subscriptions[0].customer.id') || row.stripe_customer_id,
|
||||||
created_at: member.created_at,
|
created_at: row.created_at,
|
||||||
deleted_at: member.deleted_at,
|
deleted_at: row.deleted_at,
|
||||||
labels: labels,
|
labels: labels,
|
||||||
tiers: tiers,
|
tiers: tiers,
|
||||||
import_tier: member.import_tier || null,
|
import_tier: row.import_tier || null,
|
||||||
error: member.error || null
|
error: row.error || null
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
return papaparse.unparse(mappedMembers, {
|
return papaparse.unparse(mappedRows, {
|
||||||
columns
|
columns
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -13,7 +13,7 @@ describe('unparse', function () {
|
||||||
|
|
||||||
assert.ok(result);
|
assert.ok(result);
|
||||||
|
|
||||||
const expected = `id,email,name,note,subscribed_to_emails,complimentary_plan,stripe_customer_id,created_at,deleted_at,labels,tiers\r\n,email@example.com,Sam Memberino,Early supporter,false,,,,,,`;
|
const expected = `id,email,name,note,subscribed_to_emails,complimentary_plan,stripe_customer_id,created_at,deleted_at,labels,tiers\r\n,email@example.com,Sam Memberino,Early supporter,,,,,,,`;
|
||||||
assert.equal(result, expected);
|
assert.equal(result, expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,7 @@ describe('MembersCSVImporter', function () {
|
||||||
email: 'email',
|
email: 'email',
|
||||||
name: 'name',
|
name: 'name',
|
||||||
note: 'note',
|
note: 'note',
|
||||||
subscribed_to_emails: 'subscribed',
|
subscribed_to_emails: 'subscribed_to_emails',
|
||||||
created_at: 'created_at',
|
created_at: 'created_at',
|
||||||
complimentary_plan: 'complimentary_plan',
|
complimentary_plan: 'complimentary_plan',
|
||||||
stripe_customer_id: 'stripe_customer_id',
|
stripe_customer_id: 'stripe_customer_id',
|
||||||
|
@ -221,6 +221,222 @@ describe('MembersCSVImporter', function () {
|
||||||
}]);
|
}]);
|
||||||
should.deepEqual(membersRepositoryStub.update.args[0][1].id, 'test_member_id');
|
should.deepEqual(membersRepositoryStub.update.args[0][1].id, 'test_member_id');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should subscribe or unsubscribe members as per the `subscribe_to_emails` column', async function () {
|
||||||
|
/**
|
||||||
|
* @NOTE This tests all the different scenarios for the `subscribed_to_emails` column for existing and new members
|
||||||
|
* For existing members with at least one newsletter subscription:
|
||||||
|
* CASE 1: If `subscribe_to_emails` is `true`, the member should remain subscribed (but not added to any additional newsletters)
|
||||||
|
* CASE 2: If `subscribe_to_emails` is `false`, the member should be unsubscribed from all newsletters
|
||||||
|
* CASE 3: If `subscribe_to_emails` is NULL, the member should remain subscribed (but not added to any additional newsletters)
|
||||||
|
* CASE 4: If `subscribe_to_emails` is empty, the member should remain subscribed (but not added to any additional newsletters)
|
||||||
|
* CASE 5: If `subscribe_to_emails` is invalid, the member should remain subscribed (but not added to any additional newsletters)
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* For existing members with no newsletter subscriptions:
|
||||||
|
* CASE 6: If `subscribe_to_emails` is `true`, the member should remain unsubscribed (as they likely have previously unsubscribed)
|
||||||
|
* CASE 7: If `subscribe_to_emails` is `false`, the member should remain unsubscribed
|
||||||
|
* CASE 8: If `subscribe_to_emails` is NULL, the member should remain unsubscribed
|
||||||
|
* CASE 9: If `subscribe_to_emails` is empty, the member should remain unsubscribed
|
||||||
|
* CASE 10: If `subscribe_to_emails` is invalid, the member should remain unsubscribed
|
||||||
|
*
|
||||||
|
* - In summary, an existing member with no pre-existing newsletter subscriptions should _never_ be subscribed to newsletters upon import
|
||||||
|
*
|
||||||
|
* For new members:
|
||||||
|
* CASE 11: If `subscribe_to_emails` is `true`, the member should be created and subscribed
|
||||||
|
* CASE 12: If `subscribe_to_emails` is `false`, the member should be created but not subscribed
|
||||||
|
* CASE 13: If `subscribe_to_emails` is NULL, the member should be created and subscribed
|
||||||
|
* CASE 14: If `subscribe_to_emails` is empty, the member should be created and subscribed
|
||||||
|
* CASE 15: If `subscribe_to_emails` is invalid, the member should be created and subscribed
|
||||||
|
*/
|
||||||
|
|
||||||
|
const internalLabel = {
|
||||||
|
name: 'Test Subscription Import'
|
||||||
|
};
|
||||||
|
const LabelModelStub = {
|
||||||
|
findOne: sinon.stub()
|
||||||
|
.withArgs({
|
||||||
|
name: 'Test Subscription Import'
|
||||||
|
})
|
||||||
|
.resolves({
|
||||||
|
name: 'Test Subscription Import'
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
const importer = buildMockImporterInstance();
|
||||||
|
|
||||||
|
const defaultNewsletters = [
|
||||||
|
{id: 'newsletter_1'},
|
||||||
|
{id: 'newsletter_2'}
|
||||||
|
];
|
||||||
|
|
||||||
|
const existingMembers = [
|
||||||
|
{email: 'member_subscribed_true@example.com', newsletters: defaultNewsletters},
|
||||||
|
{email: 'member_subscribed_null@example.com', newsletters: defaultNewsletters},
|
||||||
|
{email: 'member_subscribed_false@example.com', newsletters: defaultNewsletters},
|
||||||
|
{email: 'member_subscribed_empty@example.com', newsletters: defaultNewsletters},
|
||||||
|
{email: 'member_subscribed_invalid@example.com', newsletters: defaultNewsletters},
|
||||||
|
{email: 'member_not_subscribed_true@example.com', newsletters: []},
|
||||||
|
{email: 'member_not_subscribed_null@example.com', newsletters: []},
|
||||||
|
{email: 'member_not_subscribed_false@example.com', newsletters: []},
|
||||||
|
{email: 'member_not_subscribed_empty@example.com', newsletters: []},
|
||||||
|
{email: 'member_not_subscribed_invalid@example.com', newsletters: []}
|
||||||
|
];
|
||||||
|
|
||||||
|
membersRepositoryStub.get = sinon.stub();
|
||||||
|
|
||||||
|
for (const existingMember of existingMembers) {
|
||||||
|
const newslettersCollection = {
|
||||||
|
length: existingMember.newsletters.length,
|
||||||
|
toJSON: sinon.stub().returns(existingMember.newsletters)
|
||||||
|
};
|
||||||
|
const memberRecord = {
|
||||||
|
related: sinon.stub()
|
||||||
|
};
|
||||||
|
memberRecord.related.withArgs('labels').returns(null);
|
||||||
|
memberRecord.related.withArgs('newsletters').returns(newslettersCollection);
|
||||||
|
membersRepositoryStub.get.withArgs({email: existingMember.email}).resolves(memberRecord);
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await importer.process({
|
||||||
|
pathToCSV: `${csvPath}/subscribed-to-emails-cases.csv`,
|
||||||
|
headerMapping: defaultAllowedFields,
|
||||||
|
importLabel: {
|
||||||
|
name: 'Test Subscription Import'
|
||||||
|
},
|
||||||
|
user: {
|
||||||
|
email: 'test@example.com'
|
||||||
|
},
|
||||||
|
LabelModel: LabelModelStub,
|
||||||
|
forceInline: true,
|
||||||
|
verificationTrigger: {
|
||||||
|
testImportThreshold: () => {}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
should.exist(result.meta);
|
||||||
|
should.exist(result.meta.stats);
|
||||||
|
should.exist(result.meta.stats.imported);
|
||||||
|
result.meta.stats.imported.should.equal(5);
|
||||||
|
|
||||||
|
should.exist(result.meta.stats.invalid);
|
||||||
|
should.deepEqual(result.meta.import_label, internalLabel);
|
||||||
|
|
||||||
|
should.exist(result.meta.originalImportSize);
|
||||||
|
result.meta.originalImportSize.should.equal(15);
|
||||||
|
|
||||||
|
fsWriteSpy.calledOnce.should.be.true();
|
||||||
|
|
||||||
|
// member records get inserted
|
||||||
|
should.equal(membersRepositoryStub.create.callCount, 5);
|
||||||
|
|
||||||
|
should.equal(membersRepositoryStub.create.args[0][1].context.import, true, 'inserts are done in the "import" context');
|
||||||
|
|
||||||
|
// CASE 1: Existing member with at least one newsletter subscription, `subscribed_to_emails` column is true
|
||||||
|
// Member's newsletter subscriptions should not change
|
||||||
|
should.deepEqual(Object.keys(membersRepositoryStub.update.args[0][0]), ['email', 'name', 'note', 'subscribed', 'created_at', 'labels', 'newsletters']);
|
||||||
|
should.equal(membersRepositoryStub.update.args[0][0].email, 'member_subscribed_true@example.com');
|
||||||
|
should.equal(membersRepositoryStub.update.args[0][0].subscribed, true);
|
||||||
|
should.deepEqual(membersRepositoryStub.update.args[0][0].newsletters, defaultNewsletters);
|
||||||
|
|
||||||
|
// CASE 2: Existing member with at least one newsletter subscription, `subscribed_to_emails` column is false
|
||||||
|
// Member's newsletter subscriptions should be removed
|
||||||
|
should.deepEqual(Object.keys(membersRepositoryStub.update.args[1][0]), ['email', 'name', 'note', 'subscribed', 'created_at', 'labels']);
|
||||||
|
should.equal(membersRepositoryStub.update.args[1][0].email, 'member_subscribed_false@example.com');
|
||||||
|
should.equal(membersRepositoryStub.update.args[1][0].subscribed, false);
|
||||||
|
should.equal(membersRepositoryStub.update.args[1][0].newsletters, undefined);
|
||||||
|
|
||||||
|
// CASE 3: Existing member with at least one newsletter subscription, `subscribed_to_emails` column is NULL
|
||||||
|
// Member's newsletter subscriptions should not change
|
||||||
|
should.deepEqual(Object.keys(membersRepositoryStub.update.args[2][0]), ['email', 'name', 'note', 'subscribed', 'created_at', 'labels', 'newsletters']);
|
||||||
|
should.equal(membersRepositoryStub.update.args[2][0].email, 'member_subscribed_null@example.com');
|
||||||
|
should.equal(membersRepositoryStub.update.args[2][0].subscribed, true);
|
||||||
|
should.deepEqual(membersRepositoryStub.update.args[2][0].newsletters, defaultNewsletters);
|
||||||
|
|
||||||
|
// CASE 4: Existing member with at least one newsletter subscription, `subscribed_to_emails` column is empty
|
||||||
|
// Member's newsletter subscriptions should not change
|
||||||
|
should.deepEqual(Object.keys(membersRepositoryStub.update.args[3][0]), ['email', 'name', 'note', 'subscribed', 'created_at', 'labels', 'newsletters']);
|
||||||
|
should.equal(membersRepositoryStub.update.args[3][0].email, 'member_subscribed_empty@example.com');
|
||||||
|
should.equal(membersRepositoryStub.update.args[3][0].subscribed, true);
|
||||||
|
should.deepEqual(membersRepositoryStub.update.args[3][0].newsletters, defaultNewsletters);
|
||||||
|
|
||||||
|
// CASE 5: Existing member with at least one newsletter subscription, `subscribed_to_emails` column is invalid
|
||||||
|
// Member's newsletter subscriptions should not change
|
||||||
|
should.deepEqual(Object.keys(membersRepositoryStub.update.args[4][0]), ['email', 'name', 'note', 'subscribed', 'created_at', 'labels', 'newsletters']);
|
||||||
|
should.equal(membersRepositoryStub.update.args[4][0].email, 'member_subscribed_invalid@example.com');
|
||||||
|
should.equal(membersRepositoryStub.update.args[4][0].subscribed, true);
|
||||||
|
should.deepEqual(membersRepositoryStub.update.args[4][0].newsletters, defaultNewsletters);
|
||||||
|
|
||||||
|
// CASE 6: Existing member with no newsletter subscriptions, `subscribed_to_emails` column is true
|
||||||
|
// Member should remain unsubscribed and not added to any newsletters
|
||||||
|
should.deepEqual(Object.keys(membersRepositoryStub.update.args[5][0]), ['email', 'name', 'note', 'subscribed', 'created_at', 'labels']);
|
||||||
|
should.equal(membersRepositoryStub.update.args[5][0].email, 'member_not_subscribed_true@example.com');
|
||||||
|
should.equal(membersRepositoryStub.update.args[5][0].subscribed, false);
|
||||||
|
should.equal(membersRepositoryStub.update.args[5][0].newsletters, undefined);
|
||||||
|
|
||||||
|
// CASE 7: Existing member with no newsletter subscriptions, `subscribed_to_emails` column is false
|
||||||
|
// Member should remain unsubscribed and not added to any newsletters
|
||||||
|
should.deepEqual(Object.keys(membersRepositoryStub.update.args[6][0]), ['email', 'name', 'note', 'subscribed', 'created_at', 'labels']);
|
||||||
|
should.equal(membersRepositoryStub.update.args[6][0].email, 'member_not_subscribed_false@example.com');
|
||||||
|
should.equal(membersRepositoryStub.update.args[6][0].subscribed, false);
|
||||||
|
should.equal(membersRepositoryStub.update.args[6][0].newsletters, undefined);
|
||||||
|
|
||||||
|
// CASE 8: Existing member with no newsletter subscriptions, `subscribed_to_emails` column is NULL
|
||||||
|
// Member should remain unsubscribed and not added to any newsletters
|
||||||
|
should.deepEqual(Object.keys(membersRepositoryStub.update.args[7][0]), ['email', 'name', 'note', 'subscribed', 'created_at', 'labels']);
|
||||||
|
should.equal(membersRepositoryStub.update.args[7][0].email, 'member_not_subscribed_null@example.com');
|
||||||
|
should.equal(membersRepositoryStub.update.args[7][0].subscribed, false);
|
||||||
|
should.equal(membersRepositoryStub.update.args[7][0].newsletters, undefined);
|
||||||
|
|
||||||
|
// CASE 9: Existing member with no newsletter subscriptions, `subscribed_to_emails` column is empty
|
||||||
|
// Member should remain unsubscribed and not added to any newsletters
|
||||||
|
should.deepEqual(Object.keys(membersRepositoryStub.update.args[8][0]), ['email', 'name', 'note', 'subscribed', 'created_at', 'labels']);
|
||||||
|
should.equal(membersRepositoryStub.update.args[8][0].email, 'member_not_subscribed_empty@example.com');
|
||||||
|
should.equal(membersRepositoryStub.update.args[8][0].subscribed, false);
|
||||||
|
should.equal(membersRepositoryStub.update.args[8][0].newsletters, undefined);
|
||||||
|
|
||||||
|
// CASE 10: Existing member with no newsletter subscriptions, `subscribed_to_emails` column is invalid
|
||||||
|
// Member should remain unsubscribed and not added to any newsletters
|
||||||
|
should.deepEqual(Object.keys(membersRepositoryStub.update.args[9][0]), ['email', 'name', 'note', 'subscribed', 'created_at', 'labels']);
|
||||||
|
should.equal(membersRepositoryStub.update.args[9][0].email, 'member_not_subscribed_invalid@example.com');
|
||||||
|
should.equal(membersRepositoryStub.update.args[9][0].subscribed, false);
|
||||||
|
should.equal(membersRepositoryStub.update.args[9][0].newsletters, undefined);
|
||||||
|
|
||||||
|
// CASE 11: New member, `subscribed_to_emails` column is true
|
||||||
|
// Member should be created and subscribed
|
||||||
|
should.deepEqual(Object.keys(membersRepositoryStub.create.args[0][0]), ['email', 'name', 'note', 'subscribed', 'created_at', 'labels']);
|
||||||
|
should.equal(membersRepositoryStub.create.args[0][0].email, 'new_member_true@example.com');
|
||||||
|
should.equal(membersRepositoryStub.create.args[0][0].subscribed, true);
|
||||||
|
should.equal(membersRepositoryStub.create.args[0][0].newsletters, undefined);
|
||||||
|
|
||||||
|
// CASE 12: New member, `subscribed_to_emails` column is false
|
||||||
|
// Member should be created but not subscribed
|
||||||
|
should.deepEqual(Object.keys(membersRepositoryStub.create.args[1][0]), ['email', 'name', 'note', 'subscribed', 'created_at', 'labels']);
|
||||||
|
should.equal(membersRepositoryStub.create.args[1][0].email, 'new_member_false@example.com');
|
||||||
|
should.equal(membersRepositoryStub.create.args[1][0].subscribed, false);
|
||||||
|
should.equal(membersRepositoryStub.create.args[1][0].newsletters, undefined);
|
||||||
|
|
||||||
|
// CASE 13: New member, `subscribed_to_emails` column is NULL
|
||||||
|
// Member should be created but not subscribed
|
||||||
|
should.deepEqual(Object.keys(membersRepositoryStub.create.args[2][0]), ['email', 'name', 'note', 'subscribed', 'created_at', 'labels']);
|
||||||
|
should.equal(membersRepositoryStub.create.args[2][0].email, 'new_member_null@example.com');
|
||||||
|
should.equal(membersRepositoryStub.create.args[2][0].subscribed, true);
|
||||||
|
should.equal(membersRepositoryStub.create.args[2][0].newsletters, undefined);
|
||||||
|
|
||||||
|
// CASE 14: New member, `subscribed_to_emails` column is empty
|
||||||
|
// Member should be created but not subscribed
|
||||||
|
should.deepEqual(Object.keys(membersRepositoryStub.create.args[3][0]), ['email', 'name', 'note', 'subscribed', 'created_at', 'labels']);
|
||||||
|
should.equal(membersRepositoryStub.create.args[3][0].email, 'new_member_empty@example.com');
|
||||||
|
should.equal(membersRepositoryStub.create.args[3][0].subscribed, true);
|
||||||
|
should.equal(membersRepositoryStub.create.args[3][0].newsletters, undefined);
|
||||||
|
|
||||||
|
// CASE 15: New member, `subscribed_to_emails` column is invalid
|
||||||
|
// Member should be created but not subscribed
|
||||||
|
should.deepEqual(Object.keys(membersRepositoryStub.create.args[4][0]), ['email', 'name', 'note', 'subscribed', 'created_at', 'labels']);
|
||||||
|
should.equal(membersRepositoryStub.create.args[4][0].email, 'new_member_invalid@example.com');
|
||||||
|
should.equal(membersRepositoryStub.create.args[4][0].subscribed, true);
|
||||||
|
should.equal(membersRepositoryStub.create.args[4][0].newsletters, undefined);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('sendErrorEmail', function () {
|
describe('sendErrorEmail', function () {
|
||||||
|
|
16
ghost/members-importer/test/fixtures/subscribed-to-emails-cases.csv
vendored
Normal file
16
ghost/members-importer/test/fixtures/subscribed-to-emails-cases.csv
vendored
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
email,name,note,subscribed_to_emails,stripe_customer_id,complimentary_plan,labels,created_at
|
||||||
|
member_subscribed_true@example.com,Test Email,This is a test template for importing your members list to Ghost,true,Example: cus_GdsYH4fZbHx9hF,false,"vip,promotion",2019-10-30T14:52:08.000Z
|
||||||
|
member_subscribed_false@example.com,Test Email,This is a test template for importing your members list to Ghost,false,Example: cus_GdsYH4fZbHx9hF,false,"vip,promotion",2019-10-30T14:52:08.000Z
|
||||||
|
member_subscribed_null@example.com,Test Email,This is a test template for importing your members list to Ghost,null,Example: cus_GdsYH4fZbHx9hF,false,"vip,promotion",2019-10-30T14:52:08.000Z
|
||||||
|
member_subscribed_empty@example.com,Test Email,This is a test template for importing your members list to Ghost,,Example: cus_GdsYH4fZbHx9hF,false,"vip,promotion",2019-10-30T14:52:08.000Z
|
||||||
|
member_subscribed_invalid@example.com,Test Email,This is a test template for importing your members list to Ghost,asdfasdf,Example: cus_GdsYH4fZbHx9hF,false,"vip,promotion",2019-10-30T14:52:08.000Z
|
||||||
|
member_not_subscribed_true@example.com,Test Email,This is a test template for importing your members list to Ghost,true,Example: cus_GdsYH4fZbHx9hF,false,"vip,promotion",2019-10-30T14:52:08.000Z
|
||||||
|
member_not_subscribed_false@example.com,Test Email,This is a test template for importing your members list to Ghost,false,Example: cus_GdsYH4fZbHx9hF,false,"vip,promotion",2019-10-30T14:52:08.000Z
|
||||||
|
member_not_subscribed_null@example.com,Test Email,This is a test template for importing your members list to Ghost,null,Example: cus_GdsYH4fZbHx9hF,false,"vip,promotion",2019-10-30T14:52:08.000Z
|
||||||
|
member_not_subscribed_empty@example.com,Test Email,This is a test template for importing your members list to Ghost,,Example: cus_GdsYH4fZbHx9hF,false,"vip,promotion",2019-10-30T14:52:08.000Z
|
||||||
|
member_not_subscribed_invalid@example.com,Test Email,This is a test template for importing your members list to Ghost,asdfasdf,Example: cus_GdsYH4fZbHx9hF,false,"vip,promotion",2019-10-30T14:52:08.000Z
|
||||||
|
new_member_true@example.com,Test Email,This is a test template for importing your members list to Ghost,true,Example: cus_GdsYH4fZbHx9hF,false,"vip,promotion",2019-10-30T14:52:08.000Z
|
||||||
|
new_member_false@example.com,Test Email,This is a test template for importing your members list to Ghost,false,Example: cus_GdsYH4fZbHx9hF,false,"vip,promotion",2019-10-30T14:52:08.000Z
|
||||||
|
new_member_null@example.com,Test Email,This is a test template for importing your members list to Ghost,null,Example: cus_GdsYH4fZbHx9hF,false,"vip,promotion",2019-10-30T14:52:08.000Z
|
||||||
|
new_member_empty@example.com,Test Email,This is a test template for importing your members list to Ghost,,Example: cus_GdsYH4fZbHx9hF,false,"vip,promotion",2019-10-30T14:52:08.000Z
|
||||||
|
new_member_invalid@example.com,Test Email,This is a test template for importing your members list to Ghost,asdfasdf,Example: cus_GdsYH4fZbHx9hF,false,"vip,promotion",2019-10-30T14:52:08.000Z
|
|
Loading…
Reference in a new issue