Updated settings API to return all settings in edit endpoint (#14889)

refs https://github.com/TryGhost/Team/issues/1650

Since we introduced calculated settings, we need to return all settings when editing a setting, because those calculated settings can change.

- When editing settings, return all the settings. Previously we didn't include the calculated settings (which caused issues with admin not having up to date values for those)
- Updated Stripe <head> script to be injected based on the calculated settings

Required for https://github.com/TryGhost/Admin/pull/2405
This commit is contained in:
Simon Backx 2022-05-24 16:42:15 +02:00 committed by GitHub
parent c46303cb2b
commit c052652559
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 598 additions and 249 deletions

View File

@ -44,17 +44,14 @@ function finaliseStructuredData(meta) {
}
function getMembersHelper(data, frontendKey) {
if (settingsCache.get('members_signup_access') === 'none') {
if (!settingsCache.get('members_enabled')) {
return '';
}
const stripeDirectSecretKey = settingsCache.get('stripe_secret_key');
const stripeDirectPublishableKey = settingsCache.get('stripe_publishable_key');
const stripeConnectAccountId = settingsCache.get('stripe_connect_account_id');
const colorString = _.has(data, 'site._preview') && data.site.accent_color ? ` data-accent-color="${data.site.accent_color}"` : '';
let membersHelper = `<script defer src="${config.get('portal:url')}" data-ghost="${urlUtils.getSiteUrl()}"${colorString} data-key="${frontendKey}" data-api="${urlUtils.urlFor('api', {type: 'content'}, true)}" crossorigin="anonymous"></script>`;
membersHelper += (`<style id="gh-members-styles">${templateStyles}</style>`);
if ((!!stripeDirectSecretKey && !!stripeDirectPublishableKey) || !!stripeConnectAccountId) {
if (settingsCache.get('paid_members_enabled')) {
membersHelper += '<script async src="https://js.stripe.com/v3/"></script>';
}
return membersHelper;

View File

@ -195,7 +195,9 @@ module.exports = {
this.headers.cacheInvalidate = true;
}
return result;
// We need to return all settings here, because we have calculated settings that might change
const browse = await settingsBREADService.browse(frame.options.context);
return browse;
}
},

View File

@ -118,7 +118,7 @@ module.exports = {
return connectKeys;
},
isStripeConnected() {
arePaidMembersEnabled() {
return this.isMembersEnabled() && this.getActiveStripeKeys() !== null;
},
@ -137,7 +137,7 @@ module.exports = {
fields.push(new CalculatedField({key: 'members_enabled', type: 'boolean', group: 'members', fn: this.isMembersEnabled.bind(this), dependents: ['members_signup_access']}));
fields.push(new CalculatedField({key: 'members_invite_only', type: 'boolean', group: 'members', fn: this.isMembersInviteOnly.bind(this), dependents: ['members_signup_access']}));
fields.push(new CalculatedField({key: 'paid_members_enabled', type: 'boolean', group: 'members', fn: this.isStripeConnected.bind(this), dependents: ['members_signup_access', 'stripe_secret_key', 'stripe_publishable_key', 'stripe_connect_secret_key', 'stripe_connect_publishable_key']}));
fields.push(new CalculatedField({key: 'paid_members_enabled', type: 'boolean', group: 'members', fn: this.arePaidMembersEnabled.bind(this), dependents: ['members_signup_access', 'stripe_secret_key', 'stripe_publishable_key', 'stripe_connect_secret_key', 'stripe_connect_publishable_key']}));
fields.push(new CalculatedField({key: 'firstpromoter_account', type: 'string', group: 'firstpromoter', fn: this.getFirstpromoterId.bind(this), dependents: ['firstpromoter', 'firstpromoter_id']}));
return fields;

View File

@ -1286,21 +1286,57 @@ Object {
"key": "title",
"value": "[]",
},
Object {
"key": "description",
"value": "Thoughts, stories and ideas",
},
Object {
"key": "logo",
"value": "",
},
Object {
"key": "cover_image",
"value": "https://static.ghost.org/v4.0.0/images/publication-cover.jpg",
},
Object {
"key": "icon",
"value": "",
},
Object {
"key": "accent_color",
"value": "#FF1A75",
},
Object {
"key": "locale",
"value": "ua",
},
Object {
"key": "timezone",
"value": "Pacific/Auckland",
},
Object {
"key": "codeinjection_head",
"value": null,
},
Object {
"key": "codeinjection_foot",
"value": "",
},
Object {
"key": "facebook",
"value": "ghost",
},
Object {
"key": "twitter",
"value": "@ghost",
},
Object {
"key": "navigation",
"value": "[{\\"label\\":\\"label1\\"}]",
},
Object {
"key": "slack_username",
"value": "New Slack Username",
},
Object {
"key": "is_private",
"value": false,
"key": "secondary_navigation",
"value": "[{\\"label\\":\\"Data & privacy\\",\\"url\\":\\"/privacy/\\"},{\\"label\\":\\"Contact\\",\\"url\\":\\"/contact/\\"},{\\"label\\":\\"Contribute →\\",\\"url\\":\\"/contribute/\\"}]",
},
Object {
"key": "meta_title",
@ -1335,21 +1371,185 @@ Object {
"value": "twitter description",
},
Object {
"key": "locale",
"value": "ua",
"key": "active_theme",
"value": "casper",
},
Object {
"key": "is_private",
"value": false,
},
Object {
"key": "password",
"value": "",
},
Object {
"key": "public_hash",
"value": StringMatching /\\[a-z0-9\\]\\{30\\}/,
},
Object {
"key": "default_content_visibility",
"value": "public",
},
Object {
"key": "default_content_visibility_tiers",
"value": "[]",
},
Object {
"key": "members_signup_access",
"value": "all",
},
Object {
"key": "members_support_address",
"value": "noreply",
},
Object {
"key": "stripe_secret_key",
"value": null,
},
Object {
"key": "stripe_publishable_key",
"value": null,
},
Object {
"key": "stripe_plans",
"value": "[]",
},
Object {
"key": "stripe_connect_publishable_key",
"value": "pk_test_for_stripe",
},
Object {
"key": "stripe_connect_secret_key",
"value": "••••••••",
},
Object {
"key": "stripe_connect_livemode",
"value": null,
},
Object {
"key": "stripe_connect_display_name",
"value": null,
},
Object {
"key": "stripe_connect_account_id",
"value": null,
},
Object {
"key": "members_monthly_price_id",
"value": null,
},
Object {
"key": "members_yearly_price_id",
"value": null,
},
Object {
"key": "portal_name",
"value": true,
},
Object {
"key": "portal_button",
"value": true,
},
Object {
"key": "portal_plans",
"value": "[\\"free\\"]",
},
Object {
"key": "portal_products",
"value": "[]",
},
Object {
"key": "portal_button_style",
"value": "icon-and-text",
},
Object {
"key": "portal_button_icon",
"value": null,
},
Object {
"key": "portal_button_signup_text",
"value": "Subscribe",
},
Object {
"key": "mailgun_domain",
"value": null,
},
Object {
"key": "mailgun_api_key",
"value": null,
},
Object {
"key": "mailgun_base_url",
"value": null,
},
Object {
"key": "email_track_opens",
"value": true,
},
Object {
"key": "email_verification_required",
"value": false,
},
Object {
"key": "amp",
"value": false,
},
Object {
"key": "amp_gtag_id",
"value": null,
},
Object {
"key": "firstpromoter",
"value": false,
},
Object {
"key": "firstpromoter_id",
"value": null,
},
Object {
"key": "labs",
"value": "{\\"multipleProducts\\":true,\\"tierWelcomePages\\":true,\\"tierName\\":true,\\"selectablePortalLinks\\":true,\\"membersTableStatus\\":true,\\"multipleNewsletters\\":true,\\"multipleNewslettersUI\\":true,\\"membersActivityFeed\\":true,\\"dashboardV5\\":true,\\"members\\":true}",
},
Object {
"key": "timezone",
"value": "Pacific/Auckland",
"key": "slack_url",
"value": "",
},
Object {
"key": "slack_username",
"value": "New Slack Username",
},
Object {
"key": "unsplash",
"value": false,
},
Object {
"key": "shared_views",
"value": "[]",
},
Object {
"key": "editor_default_email_recipients",
"value": "visibility",
},
Object {
"key": "editor_default_email_recipients_filter",
"value": "all",
},
Object {
"key": "members_enabled",
"value": true,
},
Object {
"key": "members_invite_only",
"value": false,
},
Object {
"key": "paid_members_enabled",
"value": true,
},
Object {
"key": "firstpromoter_account",
"value": null,
},
],
}
`;
@ -1358,7 +1558,7 @@ exports[`Settings API Edit Can edit a setting 2: [headers] 1`] = `
Object {
"access-control-allow-origin": "http://127.0.0.1:2369",
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
"content-length": "1112",
"content-length": "3536",
"content-type": "application/json; charset=utf-8",
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
"vary": "Origin, Accept-Encoding",
@ -1370,7 +1570,276 @@ Object {
exports[`Settings API Edit cannot edit uneditable settings 1: [body] 1`] = `
Object {
"meta": Object {},
"settings": Array [],
"settings": Array [
Object {
"key": "title",
"value": "[]",
},
Object {
"key": "description",
"value": "Thoughts, stories and ideas",
},
Object {
"key": "logo",
"value": "",
},
Object {
"key": "cover_image",
"value": "https://static.ghost.org/v4.0.0/images/publication-cover.jpg",
},
Object {
"key": "icon",
"value": "",
},
Object {
"key": "accent_color",
"value": "#FF1A75",
},
Object {
"key": "locale",
"value": "ua",
},
Object {
"key": "timezone",
"value": "Pacific/Auckland",
},
Object {
"key": "codeinjection_head",
"value": null,
},
Object {
"key": "codeinjection_foot",
"value": "",
},
Object {
"key": "facebook",
"value": "ghost",
},
Object {
"key": "twitter",
"value": "@ghost",
},
Object {
"key": "navigation",
"value": "[{\\"label\\":\\"label1\\"}]",
},
Object {
"key": "secondary_navigation",
"value": "[{\\"label\\":\\"Data & privacy\\",\\"url\\":\\"/privacy/\\"},{\\"label\\":\\"Contact\\",\\"url\\":\\"/contact/\\"},{\\"label\\":\\"Contribute →\\",\\"url\\":\\"/contribute/\\"}]",
},
Object {
"key": "meta_title",
"value": "SEO title",
},
Object {
"key": "meta_description",
"value": "SEO description",
},
Object {
"key": "og_image",
"value": "http://127.0.0.1:2369/content/images/2019/07/facebook.png",
},
Object {
"key": "og_title",
"value": "facebook title",
},
Object {
"key": "og_description",
"value": "facebook description",
},
Object {
"key": "twitter_image",
"value": "http://127.0.0.1:2369/content/images/2019/07/twitter.png",
},
Object {
"key": "twitter_title",
"value": "twitter title",
},
Object {
"key": "twitter_description",
"value": "twitter description",
},
Object {
"key": "active_theme",
"value": "casper",
},
Object {
"key": "is_private",
"value": false,
},
Object {
"key": "password",
"value": "",
},
Object {
"key": "public_hash",
"value": StringMatching /\\[a-z0-9\\]\\{30\\}/,
},
Object {
"key": "default_content_visibility",
"value": "public",
},
Object {
"key": "default_content_visibility_tiers",
"value": "[]",
},
Object {
"key": "members_signup_access",
"value": "all",
},
Object {
"key": "members_support_address",
"value": "noreply",
},
Object {
"key": "stripe_secret_key",
"value": null,
},
Object {
"key": "stripe_publishable_key",
"value": null,
},
Object {
"key": "stripe_plans",
"value": "[]",
},
Object {
"key": "stripe_connect_publishable_key",
"value": "pk_test_for_stripe",
},
Object {
"key": "stripe_connect_secret_key",
"value": "••••••••",
},
Object {
"key": "stripe_connect_livemode",
"value": null,
},
Object {
"key": "stripe_connect_display_name",
"value": null,
},
Object {
"key": "stripe_connect_account_id",
"value": null,
},
Object {
"key": "members_monthly_price_id",
"value": null,
},
Object {
"key": "members_yearly_price_id",
"value": null,
},
Object {
"key": "portal_name",
"value": true,
},
Object {
"key": "portal_button",
"value": true,
},
Object {
"key": "portal_plans",
"value": "[\\"free\\"]",
},
Object {
"key": "portal_products",
"value": "[]",
},
Object {
"key": "portal_button_style",
"value": "icon-and-text",
},
Object {
"key": "portal_button_icon",
"value": null,
},
Object {
"key": "portal_button_signup_text",
"value": "Subscribe",
},
Object {
"key": "mailgun_domain",
"value": null,
},
Object {
"key": "mailgun_api_key",
"value": null,
},
Object {
"key": "mailgun_base_url",
"value": null,
},
Object {
"key": "email_track_opens",
"value": true,
},
Object {
"key": "email_verification_required",
"value": false,
},
Object {
"key": "amp",
"value": false,
},
Object {
"key": "amp_gtag_id",
"value": null,
},
Object {
"key": "firstpromoter",
"value": false,
},
Object {
"key": "firstpromoter_id",
"value": null,
},
Object {
"key": "labs",
"value": "{\\"multipleProducts\\":true,\\"tierWelcomePages\\":true,\\"tierName\\":true,\\"selectablePortalLinks\\":true,\\"membersTableStatus\\":true,\\"multipleNewsletters\\":true,\\"multipleNewslettersUI\\":true,\\"membersActivityFeed\\":true,\\"dashboardV5\\":true,\\"members\\":true}",
},
Object {
"key": "slack_url",
"value": "",
},
Object {
"key": "slack_username",
"value": "New Slack Username",
},
Object {
"key": "unsplash",
"value": false,
},
Object {
"key": "shared_views",
"value": "[]",
},
Object {
"key": "editor_default_email_recipients",
"value": "visibility",
},
Object {
"key": "editor_default_email_recipients_filter",
"value": "all",
},
Object {
"key": "members_enabled",
"value": true,
},
Object {
"key": "members_invite_only",
"value": false,
},
Object {
"key": "paid_members_enabled",
"value": true,
},
Object {
"key": "firstpromoter_account",
"value": null,
},
],
}
`;
@ -1390,7 +1859,7 @@ exports[`Settings API Edit cannot edit uneditable settings 2: [headers] 1`] = `
Object {
"access-control-allow-origin": "http://127.0.0.1:2369",
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
"content-length": "25",
"content-length": "3536",
"content-type": "application/json; charset=utf-8",
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
"vary": "Origin, Accept-Encoding",

View File

@ -157,7 +157,7 @@ describe('Settings API', function () {
})
.expectStatus(200)
.matchBodySnapshot({
settings: matchSettingsArray(settingsToChange.length)
settings: matchSettingsArray(CURRENT_SETTINGS_COUNT)
})
.matchHeaderSnapshot({
etag: anyEtag
@ -167,12 +167,18 @@ describe('Settings API', function () {
it('cannot edit uneditable settings', async function () {
await agent.put('settings/')
.body({
settings: [{key: 'email_verification_required', value: false}]
settings: [{key: 'email_verification_required', value: true}]
})
.expectStatus(200)
.matchBodySnapshot()
.matchBodySnapshot({
settings: matchSettingsArray(CURRENT_SETTINGS_COUNT)
})
.matchHeaderSnapshot({
etag: anyEtag
})
.expect(({body}) => {
const emailVerificationRequired = body.settings.find(setting => setting.key === 'email_verification_required');
assert.strictEqual(emailVerificationRequired.value, false);
});
});
});

View File

@ -5,10 +5,63 @@ const config = require('../../../../core/shared/config');
const testUtils = require('../../../utils');
const localUtils = require('./utils');
const db = require('../../../../core/server/data/db');
const settingsCache = require('../../../../core/shared/settings-cache');
describe('Settings API (canary)', function () {
let request;
async function checkCanEdit(key, value, expectedValue) {
if (!expectedValue) {
expectedValue = value;
}
const settingToChange = {
settings: [{key, value}]
};
await request.put(localUtils.API.getApiQuery('settings/'))
.set('Origin', config.get('url'))
.send(settingToChange)
.expect('Content-Type', /json/)
.expect('Cache-Control', testUtils.cacheRules.private)
.expect(200)
.expect((response) => {
should.exist(response.headers['x-cache-invalidate']);
response.headers['x-cache-invalidate'].should.eql('/*');
});
// Check if not changed (also check internal ones)
const afterValue = settingsCache.get(key);
should.deepEqual(afterValue, expectedValue);
}
async function checkCantEdit(key, value) {
// Get current value (internal)
const currentValue = settingsCache.get(key);
const settingToChange = {
settings: [{key, value}]
};
if (currentValue === value) {
throw new Error('This test requires a different value than the current one');
}
await request.put(localUtils.API.getApiQuery('settings/'))
.set('Origin', config.get('url'))
.send(settingToChange)
.expect('Content-Type', /json/)
.expect('Cache-Control', testUtils.cacheRules.private)
.expect(200)
.expect((response) => {
should.not.exist(response.headers['x-cache-invalidate']);
});
// Check if not changed (also check internal ones)
const afterValue = settingsCache.get(key);
should.deepEqual(afterValue, currentValue);
}
describe('As Owner', function () {
before(async function () {
await localUtils.startGhost();
@ -16,152 +69,32 @@ describe('Settings API (canary)', function () {
await localUtils.doAuth(request);
});
it('Can edit newly introduced locale setting', function () {
return request.put(localUtils.API.getApiQuery('settings/'))
.set('Origin', config.get('url'))
.send({
settings: [{key: 'locale', value: 'ge'}]
})
.expect('Content-Type', /json/)
.expect('Cache-Control', testUtils.cacheRules.private)
.expect(200)
.then(function (res) {
should.exist(res.headers['x-cache-invalidate']);
const jsonResponse = res.body;
should.exist(jsonResponse);
should.exist(jsonResponse.settings);
jsonResponse.settings.length.should.eql(1);
testUtils.API.checkResponseValue(jsonResponse.settings[0], ['key', 'value']);
jsonResponse.settings[0].key.should.eql('locale');
jsonResponse.settings[0].value.should.eql('ge');
});
it('Can edit newly introduced locale setting', async function () {
await checkCanEdit('locale', 'ge');
});
it('Can\'t edit permalinks', async function () {
const settingToChange = {
settings: [{key: 'permalinks', value: '/:primary_author/:slug/'}]
};
await request.put(localUtils.API.getApiQuery('settings/'))
.set('Origin', config.get('url'))
.send(settingToChange)
.expect('Content-Type', /json/)
.expect('Cache-Control', testUtils.cacheRules.private)
.expect(200)
.expect(({body, headers}) => {
// it didn't actually edit anything, but we don't error anymore
body.should.eql({
settings: [],
meta: {}
});
should.not.exist(headers['x-cache-invalidate']);
});
await checkCantEdit('permalinks', '/:primary_author/:slug/');
});
it('Can edit only allowed labs keys', async function () {
const settingToChange = {
settings: [{
key: 'labs',
value: JSON.stringify({
activitypub: true,
gibberish: true
})
}]
};
const res = await request.put(localUtils.API.getApiQuery('settings/'))
.set('Origin', config.get('url'))
.send(settingToChange)
.expect('Content-Type', /json/)
.expect('Cache-Control', testUtils.cacheRules.private)
.expect(200);
const jsonResponse = res.body;
should.exist(jsonResponse);
should.exist(jsonResponse.settings);
jsonResponse.settings.length.should.eql(1);
testUtils.API.checkResponseValue(jsonResponse.settings[0], ['key', 'value']);
jsonResponse.settings[0].key.should.eql('labs');
const responseObj = JSON.parse(jsonResponse.settings[0].value);
should.not.exist(responseObj.gibberish);
await checkCanEdit('labs',
JSON.stringify({
activitypub: true,
gibberish: true
}),
{
activitypub: true
}
);
});
it('Can\'t edit non existent setting', function () {
return request.get(localUtils.API.getApiQuery('settings/'))
.set('Origin', config.get('url'))
.set('Accept', 'application/json')
.expect('Content-Type', /json/)
.expect('Cache-Control', testUtils.cacheRules.private)
.then(function (res) {
let jsonResponse = res.body;
const newValue = 'new value';
should.exist(jsonResponse);
should.exist(jsonResponse.settings);
jsonResponse.settings = [{key: 'testvalue', value: newValue}];
return request.put(localUtils.API.getApiQuery('settings/'))
.set('Origin', config.get('url'))
.send(jsonResponse)
.expect('Content-Type', /json/)
.expect('Cache-Control', testUtils.cacheRules.private)
.expect(200)
.expect(({body, headers}) => {
// it didn't actually edit anything, but we don't error anymore
body.should.eql({
settings: [],
meta: {}
});
should.not.exist(headers['x-cache-invalidate']);
});
});
it('Can\'t edit non existent setting', async function () {
await checkCantEdit('non-existent-setting', 'value');
});
it('Will transform "1"', function () {
return request.get(localUtils.API.getApiQuery('settings/'))
.set('Origin', config.get('url'))
.expect('Content-Type', /json/)
.expect('Cache-Control', testUtils.cacheRules.private)
.then(function (res) {
const jsonResponse = res.body;
const settingToChange = {
settings: [
{
key: 'is_private',
value: '1'
}
]
};
should.exist(jsonResponse);
should.exist(jsonResponse.settings);
return request.put(localUtils.API.getApiQuery('settings/'))
.set('Origin', config.get('url'))
.send(settingToChange)
.expect('Content-Type', /json/)
.expect('Cache-Control', testUtils.cacheRules.private)
.expect(200)
.then(function ({body, headers}) {
const putBody = body;
headers['x-cache-invalidate'].should.eql('/*');
should.exist(putBody);
putBody.settings[0].key.should.eql('is_private');
putBody.settings[0].value.should.eql(true);
localUtils.API.checkResponse(putBody, 'settings');
});
});
return checkCanEdit('is_private', '1', true);
});
it('Can edit multiple setting along with a deprecated one from v4', async function () {
@ -194,13 +127,11 @@ describe('Settings API (canary)', function () {
headers['x-cache-invalidate'].should.eql('/*');
should.exist(putBody);
putBody.settings.length.should.equal(2);
let setting = putBody.settings.find(s => s.key === 'unsplash');
should.equal(setting.value, true);
putBody.settings[0].key.should.eql('unsplash');
should.equal(putBody.settings[0].value, true);
putBody.settings[1].key.should.eql('title');
should.equal(putBody.settings[1].value, 'New Value');
setting = putBody.settings.find(s => s.key === 'title');
should.equal(setting.value, 'New Value');
localUtils.API.checkResponse(putBody, 'settings');
});
@ -226,11 +157,9 @@ describe('Settings API (canary)', function () {
headers['x-cache-invalidate'].should.eql('/*');
should.exist(putBody);
putBody.settings.length.should.equal(1);
localUtils.API.checkResponse(putBody, 'settings');
putBody.settings[0].key.should.eql('slack_username');
putBody.settings[0].value.should.eql('can edit me');
const setting = putBody.settings.find(s => s.key === 'slack_username');
setting.value.should.eql('can edit me');
});
it('Can edit URLs without internal storage format leaking', async function () {
@ -297,27 +226,7 @@ describe('Settings API (canary)', function () {
});
it('Cannot edit notifications key through API', async function () {
const settingsToChange = {
settings: [
{key: 'notifications', value: JSON.stringify(['do not touch me'])}
]
};
await request.put(localUtils.API.getApiQuery('settings/'))
.set('Origin', config.get('url'))
.send(settingsToChange)
.expect('Content-Type', /json/)
.expect('Cache-Control', testUtils.cacheRules.private)
.expect(200)
.expect(({body, headers}) => {
// it didn't actually edit anything, but we don't error anymore
body.should.eql({
settings: [],
meta: {}
});
should.not.exist(headers['x-cache-invalidate']);
});
await checkCantEdit('notifications', JSON.stringify(['do not touch me']));
});
});
@ -452,14 +361,22 @@ describe('Settings API (canary)', function () {
});
it('allows editing settings that cannot be edited via HTTP', async function () {
// Get current value
const {settings} = await api.settings.browse({}, testUtils.context.internal);
const currentValue = settings.find(s => s.key === 'email_verification_required');
if (!currentValue || currentValue.value === true) {
throw new Error('Invalid key or unchanged value');
}
let jsonResponse = await api.settings.edit({
settings: [{key: 'email_verification_required', value: false}]
settings: [{key: 'email_verification_required', value: true}]
}, testUtils.context.internal);
jsonResponse.should.eql({
settings: [{key: 'email_verification_required', value: false}],
meta: {}
});
const setting = jsonResponse.settings.find(s => s.key === 'email_verification_required');
should.exist(setting);
setting.value.should.eql(true);
});
});
});

View File

@ -1577,6 +1577,8 @@ describe('{{ghost_head}} helper', function () {
});
it('attaches style tag to existing script/style tag', function (done) {
settingsCache.get.withArgs('members_enabled').returns(true);
const renderObject = {
post: posts[1]
};
@ -1656,8 +1658,8 @@ describe('{{ghost_head}} helper', function () {
});
describe('members scripts', function () {
it('includes portal when signup is "all"', function (done) {
settingsCache.get.withArgs('members_signup_access').returns('all');
it('includes portal when members enabled', function (done) {
settingsCache.get.withArgs('members_enabled').returns(true);
ghost_head(testUtils.createHbsResponse({
locals: {
@ -1674,29 +1676,9 @@ describe('{{ghost_head}} helper', function () {
}).catch(done);
});
it('includes portal when signup is "invite"', function (done) {
settingsCache.get.withArgs('members_signup_access').returns('invite');
ghost_head(testUtils.createHbsResponse({
locals: {
relativeUrl: '/',
context: ['home', 'index'],
safeVersion: '4.3'
}
})).then(function (rendered) {
should.exist(rendered);
rendered.string.should.containEql('<script defer src="https://unpkg.com/@tryghost/portal');
rendered.string.should.containEql('data-ghost="http://127.0.0.1:2369/" data-key="xyz" data-api="http://127.0.0.1:2369/ghost/api/content/"');
rendered.string.should.containEql('<style id="gh-members-styles">');
done();
}).catch(done);
});
it('includes stripe when set up as direct', function (done) {
settingsCache.get.withArgs('members_signup_access').returns('all');
settingsCache.get.withArgs('stripe_secret_key').returns('secret');
settingsCache.get.withArgs('stripe_publishable_key').returns('publishable');
settingsCache.get.withArgs('stripe_connect_account_id').returns(null);
it('includes stripe when connected', function (done) {
settingsCache.get.withArgs('members_enabled').returns(true);
settingsCache.get.withArgs('paid_members_enabled').returns(true);
ghost_head(testUtils.createHbsResponse({
locals: {
@ -1714,31 +1696,9 @@ describe('{{ghost_head}} helper', function () {
}).catch(done);
});
it('includes stripe when set up as connect', function (done) {
settingsCache.get.withArgs('members_signup_access').returns('all');
settingsCache.get.withArgs('stripe_secret_key').returns(null);
settingsCache.get.withArgs('stripe_publishable_key').returns(null);
settingsCache.get.withArgs('stripe_connect_account_id').returns('connect_account');
ghost_head(testUtils.createHbsResponse({
locals: {
relativeUrl: '/',
context: ['home', 'index'],
safeVersion: '4.3'
}
})).then(function (rendered) {
should.exist(rendered);
rendered.string.should.containEql('<script defer src="https://unpkg.com/@tryghost/portal');
rendered.string.should.containEql('data-ghost="http://127.0.0.1:2369/" data-key="xyz" data-api="http://127.0.0.1:2369/ghost/api/content/"');
rendered.string.should.containEql('<style id="gh-members-styles">');
rendered.string.should.containEql('<script async src="https://js.stripe.com');
done();
}).catch(done);
});
it('skips portal and stripe when signup is "none"', function (done) {
settingsCache.get.withArgs('members_signup_access').returns('none');
settingsCache.get.withArgs('stripe_connect_account_id').returns('connect_account');
it('skips portal and stripe when members are disabled', function (done) {
settingsCache.get.withArgs('members_enabled').returns(false);
settingsCache.get.withArgs('paid_members_enabled').returns(true);
ghost_head(testUtils.createHbsResponse({
locals: {
@ -1757,10 +1717,8 @@ describe('{{ghost_head}} helper', function () {
});
it('skips stripe if not set up', function (done) {
settingsCache.get.withArgs('members_signup_access').returns('all');
settingsCache.get.withArgs('stripe_secret_key', null);
settingsCache.get.withArgs('stripe_publishable_key', null);
settingsCache.get.withArgs('stripe_connect_account_id', null);
settingsCache.get.withArgs('members_enabled').returns(true);
settingsCache.get.withArgs('paid_members_enabled').returns(false);
ghost_head(testUtils.createHbsResponse({
locals: {