Skip to content

Commit

Permalink
πŸ”₯✨ No more availableThemes (#8085)
Browse files Browse the repository at this point in the history
no issue

🎨 Switch themes API to use config.availableThemes
- this gets rid of the only places where settings.availableThemes are used

πŸ”₯ Get rid of settings.availableThemes
- this is no longer used anywhere
- also get rid of every related call to updateSettingsCache

πŸ”₯ Replace config.availableThemes with theme cache
- Creates a tailor-made in-memory cache for themes inside the theme module
- Add methods for getting & setting items on the cache
- Move all references to config.availableThemes to use the new cache
- This can be abstracted later to support other kinds of caches?

🎨 Start improving theme lib's API
Still TODO: simplifying/clarifying:
- what is the structure of the internal list
- what is the difference between a package list, and a theme list?
- what is the difference between reading a theme and loading it?
- how do we update the theme list (add/remove)
- how do we refresh the theme list? (hot reload?!)
- how do we get from an internal list, to one that is sent as part of the API?
- how are we going to handle theme storage: read/write, such that the path is configurable

🎨 Use themeList consistently
🎨 Update list after storage
  • Loading branch information
ErisDS authored and kirrg001 committed Mar 2, 2017
1 parent 0b68458 commit f8b498d
Show file tree
Hide file tree
Showing 19 changed files with 277 additions and 154 deletions.
30 changes: 13 additions & 17 deletions core/server/api/settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,10 @@
var _ = require('lodash'),
dataProvider = require('../models'),
Promise = require('bluebird'),
config = require('../config'),
canThis = require('../permissions').canThis,
errors = require('../errors'),
utils = require('./utils'),
i18n = require('../i18n'),
filterPackages = require('../utils/packages').filterPackages,

docName = 'settings',
settings,
Expand Down Expand Up @@ -68,6 +66,17 @@ settingsFilter = function (settings, filter) {

/**
* ### Read Settings Result
*
* Converts the models to keyed JSON
* E.g.
* dbHash: {
* id: '123abc',
* key: 'dbash',
* value: 'xxxx',
* type: 'core',
* timestamps
* }
*
* @private
* @param {Array} settingsModels
* @returns {Settings}
Expand All @@ -79,20 +88,7 @@ readSettingsResult = function (settingsModels) {
}

return memo;
}, {}),
themes = config.get('paths').availableThemes,
res;

// @TODO: remove availableThemes from settings cache and create an endpoint to fetch themes
if (settings.activeTheme && themes) {
res = filterPackages(themes, settings.activeTheme.value);

settings.availableThemes = {
key: 'availableThemes',
value: res,
type: 'theme'
};
}
}, {});

return settings;
};
Expand Down Expand Up @@ -255,7 +251,7 @@ settings = {
}

object.settings = _.reject(object.settings, function (setting) {
return setting.key === 'type' || setting.key === 'availableThemes';
return setting.key === 'type';
});

return canEditAllSettings(object.settings, options).then(function () {
Expand Down
42 changes: 19 additions & 23 deletions core/server/api/themes.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// # Themes API
// RESTful API for Themes
var Promise = require('bluebird'),
var debug = require('debug')('ghost:api:themes'),
Promise = require('bluebird'),
_ = require('lodash'),
gscan = require('gscan'),
fs = require('fs-extra'),
Expand All @@ -9,12 +10,12 @@ var Promise = require('bluebird'),
events = require('../events'),
logging = require('../logging'),
storage = require('../storage'),
settings = require('./settings'),
settingsCache = require('../settings/cache'),
apiUtils = require('./utils'),
utils = require('./../utils'),
i18n = require('../i18n'),
themeUtils = require('../themes'),
themeList = themeUtils.list,
packageUtils = require('../utils/packages'),
themes;

/**
Expand All @@ -24,7 +25,10 @@ var Promise = require('bluebird'),
*/
themes = {
browse: function browse() {
return Promise.resolve({themes: settingsCache.get('availableThemes')});
debug('browsing');
var result = packageUtils.filterPackages(themeList.getAll());
debug('got result');
return Promise.resolve({themes: result});
},

upload: function upload(options) {
Expand Down Expand Up @@ -80,23 +84,18 @@ themes = {
}, config.getContentPath('themes'));
})
.then(function () {
// force reload of availableThemes
// right now the logic is in the ConfigManager
// if we create a theme collection, we don't have to read them from disk
return themeUtils.load();
return themeUtils.loadOne(zip.shortName);
})
.then(function () {
// the settings endpoint is used to fetch the availableThemes
// so we have to force updating the in process cache
return settings.updateSettingsCache();
})
.then(function (settings) {
.then(function (themeObject) {
// @TODO fix this craziness
var toFilter = {};
toFilter[zip.shortName] = themeObject;
themeObject = packageUtils.filterPackages(toFilter);
// gscan theme structure !== ghost theme structure
var themeObject = _.find(settings.availableThemes.value, {name: zip.shortName}) || {};
if (theme.results.warning.length > 0) {
themeObject.warnings = _.cloneDeep(theme.results.warning);
}
return {themes: [themeObject]};
return {themes: themeObject};
})
.finally(function () {
// remove zip upload from multer
Expand All @@ -119,7 +118,7 @@ themes = {

download: function download(options) {
var themeName = options.name,
theme = config.get('paths').availableThemes[themeName],
theme = themeList.get(themeName),
storageAdapter = storage.getStorage('themes');

if (!theme) {
Expand Down Expand Up @@ -148,20 +147,17 @@ themes = {
throw new errors.ValidationError({message: i18n.t('errors.api.themes.destroyCasper')});
}

theme = config.get('paths').availableThemes[name];
theme = themeList.get(name);

if (!theme) {
throw new errors.NotFoundError({message: i18n.t('errors.api.themes.themeDoesNotExist')});
}

events.emit('theme.deleted', name);
return storageAdapter.delete(name, config.getContentPath('themes'));
})
.then(function () {
return themeUtils.load();
})
.then(function () {
return settings.updateSettingsCache();
themeList.del(name);
events.emit('theme.deleted', name);
});
}
};
Expand Down
14 changes: 8 additions & 6 deletions core/server/apps/amp/tests/router_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ var rewire = require('rewire'),
errors = require('../../../errors'),
should = require('should'),
configUtils = require('../../../../test/utils/configUtils'),
themeList = require('../../../themes').list,
sandbox = sinon.sandbox.create();

// Helper function to prevent unit tests
Expand Down Expand Up @@ -53,10 +54,11 @@ describe('AMP Controller', function () {
afterEach(function () {
sandbox.restore();
configUtils.restore();
themeList.init();
});

it('should render default amp page when theme has no amp template', function (done) {
configUtils.set({paths: {availableThemes: {casper: {}}}});
themeList.init({casper: {}});

setResponseContextStub = sandbox.stub();
ampController.__set__('setResponseContext', setResponseContextStub);
Expand All @@ -70,9 +72,9 @@ describe('AMP Controller', function () {
});

it('should render theme amp page when theme has amp template', function (done) {
configUtils.set({paths: {availableThemes: {casper: {
themeList.init({casper: {
'amp.hbs': '/content/themes/casper/amp.hbs'
}}}});
}});

setResponseContextStub = sandbox.stub();
ampController.__set__('setResponseContext', setResponseContextStub);
Expand All @@ -86,7 +88,7 @@ describe('AMP Controller', function () {
});

it('should render with error when error is passed in', function (done) {
configUtils.set({paths: {availableThemes: {casper: {}}}});
themeList.init({casper: {}});
res.error = 'Test Error';

setResponseContextStub = sandbox.stub();
Expand All @@ -103,7 +105,7 @@ describe('AMP Controller', function () {

it('does not render amp page when amp context is missing', function (done) {
var renderSpy;
configUtils.set({paths: {availableThemes: {casper: {}}}});
themeList.init({casper: {}});

setResponseContextStub = sandbox.stub();
ampController.__set__('setResponseContext', setResponseContextStub);
Expand All @@ -121,7 +123,7 @@ describe('AMP Controller', function () {

it('does not render amp page when context is other than amp and post', function (done) {
var renderSpy;
configUtils.set({paths: {availableThemes: {casper: {}}}});
themeList.init({casper: {}});

setResponseContextStub = sandbox.stub();
ampController.__set__('setResponseContext', setResponseContextStub);
Expand Down
10 changes: 6 additions & 4 deletions core/server/apps/private-blogging/tests/controller_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ var privateController = require('../lib/router').controller,
path = require('path'),
sinon = require('sinon'),
configUtils = require('../../../../test/utils/configUtils'),
themeList = require('../../../themes').list,
sandbox = sinon.sandbox.create();

describe('Private Controller', function () {
Expand Down Expand Up @@ -42,10 +43,11 @@ describe('Private Controller', function () {
afterEach(function () {
sandbox.restore();
configUtils.restore();
themeList.init();
});

it('Should render default password page when theme has no password template', function (done) {
configUtils.set({paths: {availableThemes: {casper: {}}}});
themeList.init({casper: {}});

res.render = function (view) {
view.should.eql(defaultPath);
Expand All @@ -56,9 +58,9 @@ describe('Private Controller', function () {
});

it('Should render theme password page when it exists', function (done) {
configUtils.set({paths: {availableThemes: {casper: {
themeList.init({casper: {
'private.hbs': '/content/themes/casper/private.hbs'
}}}});
}});

res.render = function (view) {
view.should.eql('private');
Expand All @@ -69,7 +71,7 @@ describe('Private Controller', function () {
});

it('Should render with error when error is passed in', function (done) {
configUtils.set({paths: {availableThemes: {casper: {}}}});
themeList.init({casper: {}});
res.error = 'Test Error';

res.render = function (view, context) {
Expand Down
6 changes: 3 additions & 3 deletions core/server/controllers/frontend/templates.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
//
// Figure out which template should be used to render a request
// based on the templates which are allowed, and what is available in the theme
var _ = require('lodash'),
config = require('../../config');
var _ = require('lodash'),
themeList = require('../../themes').list;

function getActiveThemePaths(activeTheme) {
return config.get('paths').availableThemes[activeTheme];
return themeList.get(activeTheme);
}

/**
Expand Down
3 changes: 2 additions & 1 deletion core/server/middleware/theme-handler.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ var _ = require('lodash'),
logging = require('../logging'),
errors = require('../errors'),
i18n = require('../i18n'),
themeList = require('../themes').list,
themeHandler;

themeHandler = {
Expand Down Expand Up @@ -95,7 +96,7 @@ themeHandler = {
// Check if the theme changed
if (activeTheme.value !== blogApp.get('activeTheme')) {
// Change theme
if (!config.get('paths').availableThemes.hasOwnProperty(activeTheme.value)) {
if (!themeList.get(activeTheme.value)) {
if (!res.isAdmin) {
return next(new errors.NotFoundError({
message: i18n.t('errors.middleware.themehandler.missingTheme', {theme: activeTheme.value})
Expand Down
6 changes: 5 additions & 1 deletion core/server/themes/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
var themeLoader = require('./loader');

// @TODO: reduce the amount of things we expose to the outside world
// Make this a nice clean sensible API we can all understand!
module.exports = {
init: themeLoader.init,
load: themeLoader.load,
loadAll: themeLoader.loadAllThemes,
loadOne: themeLoader.loadOneTheme,
list: require('./list'),
validate: require('./validate')
};
36 changes: 36 additions & 0 deletions core/server/themes/list.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/**
* Store themes after loading them from the file system
*/
var _ = require('lodash'),
themeListCache = {};

module.exports = {
get: function get(key) {
return themeListCache[key];
},

getAll: function getAll() {
return themeListCache;
},

set: function set(key, theme) {
themeListCache[key] = _.cloneDeep(theme);
return themeListCache[key];
},

del: function del(key) {
delete themeListCache[key];
},

init: function init(themes) {
var self = this;
// First, reset the cache
themeListCache = {};
// For each theme, call set. Allows us to do processing on set later.
_.each(themes, function (theme, key) {
self.set(key, theme);
});

return themeListCache;
}
};
Loading

0 comments on commit f8b498d

Please sign in to comment.