From c99ee980af5caa17281c1a601114ff1d97ee3f43 Mon Sep 17 00:00:00 2001 From: Rishabh Date: Thu, 3 Feb 2022 14:40:59 +0530 Subject: [PATCH] Added migration to transform default tiers visibility from nql string refs https://github.com/TryGhost/Team/issues/1071 Default content visiblity for specific tiers is now stored split between `default_content_visiblity` and `default_content_visibility_tiers` setting, with former storing the value as `tiers` and the latter stores the list of tiers that the visibility is restricted to. This migration transforms all existing sites that have default visibility stored as an NQL string from previous versions to follow the new model and store correctly on the new setting. --- ...ecific-tiers-default-content-visibility.js | 147 ++++++++++++++++++ test/regression/models/model_settings.test.js | 2 +- 2 files changed, 148 insertions(+), 1 deletion(-) create mode 100644 core/server/data/migrations/versions/4.35/2022-02-02-13-10-transform-specific-tiers-default-content-visibility.js diff --git a/core/server/data/migrations/versions/4.35/2022-02-02-13-10-transform-specific-tiers-default-content-visibility.js b/core/server/data/migrations/versions/4.35/2022-02-02-13-10-transform-specific-tiers-default-content-visibility.js new file mode 100644 index 0000000000..69117553ab --- /dev/null +++ b/core/server/data/migrations/versions/4.35/2022-02-02-13-10-transform-specific-tiers-default-content-visibility.js @@ -0,0 +1,147 @@ +const logging = require('@tryghost/logging'); + +const {createTransactionalMigration} = require('../../utils'); + +module.exports = createTransactionalMigration( + async function up(knex) { + logging.info('Checking default_content_visibility for specific tiers'); + + const settings = await knex('settings') + .select() + .whereIn('key', ['default_content_visibility', 'default_content_visibility_tiers']); + const contentVisibilitySetting = settings.find(d => d.key === 'default_content_visibility'); + const visibilityTiersSetting = settings.find(d => d.key === 'default_content_visibility_tiers'); + if (!contentVisibilitySetting) { + logging.warn('No default_content_visibility setting found.'); + return; + } + + if (!visibilityTiersSetting) { + logging.warn('No default_content_visibility_tiers setting found.'); + return; + } + + const contentVisibility = contentVisibilitySetting.value; + + if (['public', 'members', 'paid'].includes(contentVisibility)) { + logging.info(`Ignoring default_content_visibility change as already set to ${contentVisibility}.`); + return; + } + // Transform visibility to tiers when stored as nql string + const isValidProductNqlFilter = /^(?:product:[\w-]+,?)+$/.test(contentVisibility); + const now = knex.raw('CURRENT_TIMESTAMP'); + // Reset visibility value to paid if invalid string/filter + if (!isValidProductNqlFilter) { + logging.warn(`Found invalid default_content_visibility value - ${contentVisibility}, resetting to paid`); + await knex('settings') + .where({ + key: 'default_content_visibility' + }) + .update({ + value: 'paid', + updated_at: now + }); + + logging.info(`Resetting default_content_visibility_tiers to []`); + await knex('settings') + .where({ + key: 'default_content_visibility_tiers' + }) + .update({ + value: JSON.stringify([]), + updated_at: now + }); + return; + } + + // fetch product slugs from nql filter + const productSlugs = contentVisibility.split(',').map((segment) => { + return segment.replace('product:', ''); + }); + + // get product ids for slugs + const products = await knex('products') + .select('id') + .whereIn('slug', productSlugs); + const productList = products.map((product) => { + return product.id; + }); + + logging.info(`Updating default_content_visibility to tiers`); + await knex('settings') + .where({ + key: 'default_content_visibility' + }) + .update({ + value: 'tiers', + updated_at: now + }); + + logging.info(`Updating default_content_visibility_tiers to ${productList}`); + await knex('settings') + .where({ + key: 'default_content_visibility_tiers' + }) + .update({ + value: JSON.stringify(productList), + updated_at: now + }); + }, + async function down(knex) { + logging.info('Reverting default_content_visibility for specific tiers'); + + const settings = await knex('settings') + .select() + .whereIn('key', ['default_content_visibility', 'default_content_visibility_tiers']); + const contentVisibilitySetting = settings.find(d => d.key === 'default_content_visibility'); + const visibilityTiersSetting = settings.find(d => d.key === 'default_content_visibility_tiers'); + + const visibilityValue = contentVisibilitySetting && contentVisibilitySetting.value; + const visibilityTiersValue = visibilityTiersSetting && visibilityTiersSetting.value; + + if (visibilityValue !== 'tiers') { + logging.info(`Ignoring default_content_visibility as is set to ${visibilityValue}.`); + return; + } + + if (!visibilityTiersValue) { + logging.warn(`Ignoring, found empty default_content_visibility_tiers value`); + return; + } + + try { + const parsedTiersValue = JSON.parse(visibilityTiersValue); + const products = await knex('products') + .select('slug') + .whereIn('id', parsedTiersValue); + const productSlugs = products.map((product) => { + return `product:${product.slug}`; + }).join(','); + const now = knex.raw('CURRENT_TIMESTAMP'); + + logging.info(`Setting default_content_visibility to ${productSlugs}`); + await knex('settings') + .where({ + key: 'default_content_visibility' + }) + .update({ + value: productSlugs, + updated_at: now + }); + + logging.info(`Setting default_content_visibility_tiers to []`); + await knex('settings') + .where({ + key: 'default_content_visibility_tiers' + }) + .update({ + value: JSON.stringify([]), + updated_at: now + }); + } catch (e) { + logging.warn(`Invalid default_content_visibility_tiers value - ${visibilityTiersValue}`); + logging.warn(e); + return; + } + } +); diff --git a/test/regression/models/model_settings.test.js b/test/regression/models/model_settings.test.js index 9b128b712d..ce8ede216e 100644 --- a/test/regression/models/model_settings.test.js +++ b/test/regression/models/model_settings.test.js @@ -42,7 +42,7 @@ describe('Settings Model', function () { await models.Settings.populateDefaults(); const settingsPopulated = await models.Settings.findAll(); - settingsPopulated.length.should.equal(94); + settingsPopulated.length.should.equal(95); const titleSetting = settingsPopulated.models.find(s => s.get('key') === 'title'); titleSetting.get('value').should.equal('Testing Defaults');