Migrated members_allow_free_signup setting to members_signup_access (#12886)

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

Currently the members signup setting is explicitly yes/no to allowing free members signup, with the implication that when set to "no" members is still active but members have to be created via Stripe or the admin API.

This change renames the setting and changes its type to allow more than a binary option.

- migration to create/update the new setting based on the old value
  - free signup = "all", no free signup = "invite"; matches the current UI for this setting
- rename setting everywhere it's used/tested against
- modify `getAllowSelfSignup()` used to configure members packages to only return `true` when the new setting is set to `'all'` to match behaviour to the older setting
- update importer to rename the setting when importing from an older Ghost version
This commit is contained in:
Kevin Ansfield 2021-04-19 16:36:30 +01:00 committed by GitHub
parent 5fedb44e0b
commit fff6a04c54
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 149 additions and 14 deletions

View File

@ -164,6 +164,13 @@ class SettingsImporter extends BaseImporter {
data.value = '#15171A';
}
// members_allow_free_signup was renamed to members_signup_access in 4.3
if (data.key === 'members_allow_free_signup') {
data.key = 'members_signup_access';
data.value = data.value ? 'all' : 'invite';
data.type = 'string';
}
return data;
});

View File

@ -0,0 +1,109 @@
const logging = require('../../../../../shared/logging');
const {createTransactionalMigration} = require('../../utils');
const ObjectId = require('bson-objectid');
module.exports = createTransactionalMigration(
async function up(connection) {
const oldSetting = await connection('settings')
.where('key', 'members_allow_free_signup')
.select('value', 'created_by', 'updated_by')
.first();
// no need to create new setting in this case, Ghost will create through default settings
if (!oldSetting) {
logging.warn('Could not find setting `members_allow_free_signup`, skipping update of `members_signup_access` setting');
return;
}
const newSetting = await connection('settings')
.where('key', 'members_signup_access')
.select('value')
.first();
const migrateValue = oldSetting.value ? 'all' : 'invite';
if (newSetting) {
// new setting already exists, *update* with migrated value
logging.info('Updating `members_signup_access` setting with value from `members_allow_free_signup`');
await connection('settings')
.where('key', 'members_signup_access')
.update('value', migrateValue);
} else {
// setting does not exist yet, *create* with migrated value
logging.info('Creating `members_signup_access` setting with value from `members_allow_free_signup`');
const currentTimestamp = connection.raw('CURRENT_TIMESTAMP');
const newSettingData = {
id: ObjectId.generate(),
group: 'members',
key: 'members_signup_access',
value: migrateValue,
type: 'string',
created_at: currentTimestamp,
updated_at: currentTimestamp,
created_by: oldSetting.created_by,
updated_by: oldSetting.updated_by
};
await connection('settings').insert(newSettingData);
}
logging.info('Deleting `members_allow_free_signup` setting');
await connection('settings')
.where('key', 'members_allow_free_signup')
.del();
},
async function down(connection) {
const newSetting = await connection('settings')
.where('key', 'members_signup_access')
.select('value', 'created_by', 'updated_by')
.first();
const oldSetting = await connection('settings')
.where('key', 'members_allow_free_signup')
.select('value')
.first();
// if newSetting doesn't exist then the old setting still exists and will be used,
// or it will be created with defaults on next Ghost boot
if (!newSetting) {
logging.warn('Could not find setting `members_signup_access`, skipping rollback of `members_allow_free_signup` setting');
return;
}
// this can potentially be lossy if going from "nobody" but it matches to the
// most appropriate setting available in earlier versions of Ghost
const rollbackValue = newSetting.value === 'all' ? true : false;
if (oldSetting) {
logging.info('Updating `members_allow_free_signup` based on value from `members_signup_access`');
await connection('settings')
.where('key', 'members_allow_free_signup')
.update('value', rollbackValue);
} else {
logging.info('Creating `members_allow_free_signup` based on value from `members_signup_access`');
const currentTimestamp = connection.raw('CURRENT_TIMESTAMP');
await connection('settings')
.insert({
id: ObjectId.generate(),
key: 'members_allow_free_signup',
group: 'members',
type: 'boolean',
value: rollbackValue,
created_at: currentTimestamp,
updated_at: currentTimestamp,
created_by: newSetting.created_by,
updated_by: newSetting.updated_by
});
}
logging.info('Deleting `members_signup_access` setting');
await connection('settings')
.where('key', 'members_signup_access')
.del();
}
);

View File

@ -235,13 +235,13 @@
"defaultValue": "public",
"type": "string"
},
"members_allow_free_signup": {
"defaultValue": "true",
"members_signup_access": {
"defaultValue": "all",
"validations": {
"isEmpty": false,
"isIn": [["true", "false"]]
"isIn": [["all", "invite", "none"]]
},
"type": "boolean"
"type": "string"
},
"members_from_address": {
"defaultValue": "noreply",

View File

@ -215,7 +215,7 @@ class MembersConfigProvider {
}
getAllowSelfSignup() {
return this._settingsCache.get('members_allow_free_signup');
return this._settingsCache.get('members_signup_access') === 'all';
}
getTokenConfig() {

View File

@ -39,7 +39,7 @@ const debouncedReconfigureMembersAPI = _.debounce(reconfigureMembersAPI, 600);
// Bind to events to automatically keep subscription info up-to-date from settings
events.on('settings.edited', function updateSettingFromModel(settingModel) {
if (![
'members_allow_free_signup',
'members_signup_access',
'members_from_address',
'members_support_address',
'members_reply_address',

View File

@ -37,7 +37,7 @@ const defaultSettingsKeyTypes = [
{key: 'password', type: 'private'},
{key: 'public_hash', type: 'private'},
{key: 'default_content_visibility', type: 'members'},
{key: 'members_allow_free_signup', type: 'members'},
{key: 'members_signup_access', type: 'members'},
{key: 'members_from_address', type: 'members'},
{key: 'members_support_address', type: 'members'},
{key: 'members_reply_address', type: 'members'},

View File

@ -32,7 +32,7 @@ const defaultSettingsKeyTypes = [
{key: 'password', type: 'private'},
{key: 'public_hash', type: 'private'},
{key: 'default_content_visibility', type: 'members'},
{key: 'members_allow_free_signup', type: 'members'},
{key: 'members_signup_access', type: 'members'},
{key: 'members_from_address', type: 'members'},
{key: 'members_support_address', type: 'members'},
{key: 'members_reply_address', type: 'members'},

View File

@ -35,7 +35,7 @@ const defaultSettingsKeyTypes = [
{key: 'password', type: 'private'},
{key: 'public_hash', type: 'private'},
{key: 'default_content_visibility', type: 'members'},
{key: 'members_allow_free_signup', type: 'members'},
{key: 'members_signup_access', type: 'members'},
{key: 'members_from_address', type: 'members'},
{key: 'members_support_address', type: 'members'},
{key: 'members_reply_address', type: 'members'},

View File

@ -142,5 +142,24 @@ describe('SettingsImporter', function () {
importer.dataToImport[0].key.should.equal('slack_username');
importer.dataToImport[0].value.should.equal('Test Name');
});
it('Renames the members_allow_free_signup setting', function () {
const fakeSettings = [{
key: 'members_allow_free_signup',
type: 'boolean',
value: false
}];
const importer = new SettingsImporter({settings: fakeSettings}, {dataKeyToImport: 'settings'});
importer.beforeImport();
importer.problems.length.should.equal(0);
importer.dataToImport.length.should.equal(1);
importer.dataToImport[0].key.should.equal('members_signup_access');
importer.dataToImport[0].value.should.equal('invite');
importer.dataToImport[0].type.should.equal('string');
});
});
});

View File

@ -34,7 +34,7 @@ describe('DB version integrity', function () {
// Only these variables should need updating
const currentSchemaHash = '19f3f2750320798dac398be2eb51d3e5';
const currentFixturesHash = '3dc9747eadecec34958dfba14c5332db';
const currentSettingsHash = 'b7c859988a6195db8daf8cfa8a65b171';
const currentSettingsHash = 'b943cc3956eee3dd042f8394b2701d21';
const currentRoutesHash = '3d180d52c663d173a6be791ef411ed01';
// If this test is failing, then it is likely a change has been made that requires a DB version bump,

View File

@ -23,7 +23,7 @@ function createSettingsMock({setDirect, setConnect}) {
const getStub = sinon.stub();
getStub.withArgs('members_from_address').returns('noreply');
getStub.withArgs('members_allow_free_signup').returns(true);
getStub.withArgs('members_signup_access').returns('all');
getStub.withArgs('stripe_secret_key').returns(setDirect ? 'direct_secret' : null);
getStub.withArgs('stripe_publishable_key').returns(setDirect ? 'direct_publishable' : null);
getStub.withArgs('stripe_product_name').returns('Test');

View File

@ -3888,9 +3888,9 @@
{
"id": "605ac142a2d5a6aa9e101fe4",
"group": "members",
"key": "members_allow_free_signup",
"value": "true",
"type": "boolean",
"key": "members_signup_access",
"value": "all",
"type": "string",
"flags": null,
"created_at": "2021-03-24T17:34:10.000Z",
"updated_at": "2021-03-24T17:34:10.000Z"