mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-03-11 02:12:21 -05:00
Allowed custom theme settings to be passed through preview data
refs https://github.com/TryGhost/Team/issues/1097 - added `customThemeSettingKeys` as an argument to `preview.handle()` because we can't know which keys should be allowed through up-front - added `custom` as a supported setting in the preview header data - `custom` should be a JSON object containing any custom theme settings - we parse the object but only set properties on `@custom` that are known custom theme setting keys - if parsing fails or it's not an object then no custom data is set - updated `updateLocalTemplateOptions()` to pull `.custom` off of the preview data and pass it through so it's accessible on `@custom` as an override to the saved custom data
This commit is contained in:
parent
8a17e723a1
commit
b8e2bb7b6d
3 changed files with 154 additions and 7 deletions
|
@ -161,7 +161,15 @@ function updateLocalTemplateOptions(req, res, next) {
|
|||
};
|
||||
|
||||
// @TODO: it would be nicer if this was proper middleware somehow...
|
||||
const previewData = preview.handle(req);
|
||||
const previewData = preview.handle(req, Object.keys(customThemeSettingsCache.getAll()));
|
||||
|
||||
// strip custom off of preview data so it doesn't get merged into @site
|
||||
const customThemeSettingsPreviewData = previewData.custom;
|
||||
delete previewData.custom;
|
||||
let customData = {};
|
||||
if (labs.isSet('customThemeSettings')) {
|
||||
customData = customThemeSettingsPreviewData;
|
||||
}
|
||||
|
||||
// update site data with any preview values from the request
|
||||
Object.assign(siteData, previewData);
|
||||
|
@ -184,6 +192,7 @@ function updateLocalTemplateOptions(req, res, next) {
|
|||
data: {
|
||||
member: member,
|
||||
site: siteData,
|
||||
custom: customData,
|
||||
// @deprecated: a gscan warning for @blog was added before 3.0 which replaced it with @site
|
||||
blog: siteData
|
||||
}
|
||||
|
|
|
@ -14,13 +14,14 @@ function decodeValue(value) {
|
|||
return value;
|
||||
}
|
||||
|
||||
function getPreviewData(previewHeader) {
|
||||
function getPreviewData(previewHeader, customThemeSettingKeys = []) {
|
||||
// Keep the string shorter with short codes for certain parameters
|
||||
const supportedSettings = {
|
||||
c: 'accent_color',
|
||||
icon: 'icon',
|
||||
logo: 'logo',
|
||||
cover: 'cover_image'
|
||||
cover: 'cover_image',
|
||||
custom: 'custom'
|
||||
};
|
||||
|
||||
let opts = new URLSearchParams(previewHeader);
|
||||
|
@ -34,17 +35,34 @@ function getPreviewData(previewHeader) {
|
|||
}
|
||||
});
|
||||
|
||||
if (previewData.custom) {
|
||||
try {
|
||||
const custom = {};
|
||||
const previewCustom = JSON.parse(previewData.custom);
|
||||
|
||||
if (typeof previewCustom === 'object') {
|
||||
customThemeSettingKeys.forEach((key) => {
|
||||
custom[key] = previewCustom[key];
|
||||
});
|
||||
}
|
||||
|
||||
previewData.custom = custom;
|
||||
} catch (e) {
|
||||
previewData.custom = {};
|
||||
}
|
||||
}
|
||||
|
||||
previewData._preview = previewHeader;
|
||||
|
||||
return previewData;
|
||||
}
|
||||
|
||||
module.exports._PREVIEW_HEADER_NAME = PREVIEW_HEADER_NAME;
|
||||
module.exports.handle = (req) => {
|
||||
module.exports.handle = (req, customThemeSettingKeys) => {
|
||||
let previewData = {};
|
||||
|
||||
if (req && req.header(PREVIEW_HEADER_NAME)) {
|
||||
previewData = getPreviewData(req.header(PREVIEW_HEADER_NAME));
|
||||
previewData = getPreviewData(req.header(PREVIEW_HEADER_NAME), customThemeSettingKeys);
|
||||
}
|
||||
|
||||
return previewData;
|
||||
|
|
|
@ -5,6 +5,7 @@ const middleware = require('../../../../core/frontend/services/theme-engine').mi
|
|||
// is only exposed via themeEngine.getActive()
|
||||
const activeTheme = require('../../../../core/frontend/services/theme-engine/active');
|
||||
const settingsCache = require('../../../../core/shared/settings-cache');
|
||||
const customThemeSettingsCache = require('../../../../core/shared/custom-theme-settings-cache');
|
||||
|
||||
const sandbox = sinon.createSandbox();
|
||||
|
||||
|
@ -34,6 +35,7 @@ describe('Themes middleware', function () {
|
|||
let fakeActiveThemeName;
|
||||
let fakeSiteData;
|
||||
let fakeLabsData;
|
||||
let fakeCustomThemeSettingsData;
|
||||
|
||||
beforeEach(function () {
|
||||
req = {app: {}, header: () => {}};
|
||||
|
@ -52,7 +54,12 @@ describe('Themes middleware', function () {
|
|||
// labs data is deep cloned,
|
||||
// if we want to compare it
|
||||
// we will need some unique content
|
||||
members: true
|
||||
members: true,
|
||||
customThemeSettings: true
|
||||
};
|
||||
|
||||
fakeCustomThemeSettingsData = {
|
||||
header_typography: 'Sans-Serif'
|
||||
};
|
||||
|
||||
sandbox.stub(activeTheme, 'get')
|
||||
|
@ -65,6 +72,9 @@ describe('Themes middleware', function () {
|
|||
sandbox.stub(settingsCache, 'getPublic')
|
||||
.returns(fakeSiteData);
|
||||
|
||||
sandbox.stub(customThemeSettingsCache, 'getAll')
|
||||
.returns(fakeCustomThemeSettingsData);
|
||||
|
||||
sandbox.stub(hbs, 'updateTemplateOptions');
|
||||
sandbox.stub(hbs, 'updateLocalTemplateOptions');
|
||||
});
|
||||
|
@ -151,7 +161,7 @@ describe('Themes middleware', function () {
|
|||
const templateOptions = hbs.updateTemplateOptions.firstCall.args[0];
|
||||
const data = templateOptions.data;
|
||||
|
||||
data.should.be.an.Object().with.properties('site', 'labs', 'config');
|
||||
data.should.be.an.Object().with.properties('site', 'labs', 'config', 'custom');
|
||||
|
||||
// Check Theme Config
|
||||
data.config.should.be.an.Object()
|
||||
|
@ -167,6 +177,8 @@ describe('Themes middleware', function () {
|
|||
should.exist(data.site.signup_url);
|
||||
data.site.signup_url.should.equal('#/portal');
|
||||
|
||||
should.deepEqual(data.custom, fakeCustomThemeSettingsData);
|
||||
|
||||
done();
|
||||
} catch (error) {
|
||||
done(error);
|
||||
|
@ -249,5 +261,113 @@ describe('Themes middleware', function () {
|
|||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('calls updateLocalTemplateOptions with correct custom theme settings data', function (done) {
|
||||
const customPreviewObject = {header_typography: 'Serif'};
|
||||
const previewString = `custom=${encodeURIComponent(JSON.stringify(customPreviewObject))}`;
|
||||
req.header = () => {
|
||||
return previewString;
|
||||
};
|
||||
|
||||
executeMiddleware(middleware, req, res, function next(err) {
|
||||
try {
|
||||
should.not.exist(err);
|
||||
|
||||
hbs.updateLocalTemplateOptions.calledOnce.should.be.true();
|
||||
const templateOptions = hbs.updateLocalTemplateOptions.firstCall.args[1];
|
||||
const data = templateOptions.data;
|
||||
|
||||
data.should.be.an.Object().with.properties('site', 'custom');
|
||||
|
||||
data.custom.should.be.an.Object().with.properties('header_typography');
|
||||
data.custom.header_typography.should.eql('Serif');
|
||||
|
||||
done();
|
||||
} catch (error) {
|
||||
done(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('calls updateLocalTemplateOptions with correct without unknown custom theme settings', function (done) {
|
||||
const customPreviewObject = {header_typography: 'Serif', unknown_setting: true};
|
||||
const previewString = `custom=${encodeURIComponent(JSON.stringify(customPreviewObject))}`;
|
||||
req.header = () => {
|
||||
return previewString;
|
||||
};
|
||||
|
||||
executeMiddleware(middleware, req, res, function next(err) {
|
||||
try {
|
||||
should.not.exist(err);
|
||||
|
||||
hbs.updateLocalTemplateOptions.calledOnce.should.be.true();
|
||||
const templateOptions = hbs.updateLocalTemplateOptions.firstCall.args[1];
|
||||
const data = templateOptions.data;
|
||||
|
||||
data.should.be.an.Object().with.properties('site', 'custom');
|
||||
|
||||
data.custom.should.be.an.Object().with.properties('header_typography');
|
||||
data.custom.header_typography.should.eql('Serif');
|
||||
|
||||
data.custom.should.not.have.property('unknown_setting');
|
||||
|
||||
done();
|
||||
} catch (error) {
|
||||
done(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('calls updateLocalTemplateOptions with no custom data when custom param is not parseable JSON', function (done) {
|
||||
const previewString = `custom=${encodeURIComponent('<html>')}`;
|
||||
req.header = () => {
|
||||
return previewString;
|
||||
};
|
||||
|
||||
executeMiddleware(middleware, req, res, function next(err) {
|
||||
try {
|
||||
should.not.exist(err);
|
||||
|
||||
hbs.updateLocalTemplateOptions.calledOnce.should.be.true();
|
||||
const templateOptions = hbs.updateLocalTemplateOptions.firstCall.args[1];
|
||||
const data = templateOptions.data;
|
||||
|
||||
data.should.be.an.Object().with.properties('site', 'custom');
|
||||
|
||||
data.custom.should.be.an.Object();
|
||||
data.custom.should.be.empty();
|
||||
|
||||
done();
|
||||
} catch (error) {
|
||||
done(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('calls updateLocalTemplateOptions with no custom data when custom param is not an object', function (done) {
|
||||
const previewString = `custom=${encodeURIComponent('"header_typography"')}`;
|
||||
req.header = () => {
|
||||
return previewString;
|
||||
};
|
||||
|
||||
executeMiddleware(middleware, req, res, function next(err) {
|
||||
try {
|
||||
should.not.exist(err);
|
||||
|
||||
hbs.updateLocalTemplateOptions.calledOnce.should.be.true();
|
||||
const templateOptions = hbs.updateLocalTemplateOptions.firstCall.args[1];
|
||||
const data = templateOptions.data;
|
||||
|
||||
data.should.be.an.Object().with.properties('site', 'custom');
|
||||
|
||||
data.custom.should.be.an.Object();
|
||||
data.custom.should.be.empty();
|
||||
|
||||
done();
|
||||
} catch (error) {
|
||||
done(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Add table
Reference in a new issue