diff --git a/core/server/api/settings.js b/core/server/api/settings.js index 4ceaf6951c..9b6eb09f6b 100644 --- a/core/server/api/settings.js +++ b/core/server/api/settings.js @@ -17,6 +17,8 @@ var _ = require('lodash'), readSettingsResult, settingsResult, canEditAllSettings, + populateDefaultSetting, + hasPopulatedDefaults = false, /** * ## Cache @@ -183,6 +185,38 @@ settingsResult = function (settings, type) { return result; }; +/** + * ### Populate Default Setting + * @private + * @param key + * @param type + * @returns Promise(Setting) + */ +populateDefaultSetting = function (key) { + // Call populateDefault and update the settings cache + return dataProvider.Settings.populateDefault(key).then(function (defaultSetting) { + // Process the default result and add to settings cache + var readResult = readSettingsResult(defaultSetting); + + // Add to the settings cache + return updateSettingsCache(readResult).then(function () { + // Update theme with the new settings + return config.theme.update(settings, config().url); + }).then(function () { + // Get the result from the cache with permission checks + return defaultSetting; + }); + }).otherwise(function (err) { + // Pass along NotFoundError + if (typeof err === errors.NotFoundError) { + return when.reject(err); + } + + // TODO: Different kind of error? + return when.reject(new errors.NotFoundError('Problem finding setting: ' + key)); + }); +}; + /** * ### Can Edit All Settings * Check that this edit request is allowed for all settings requested to be updated @@ -191,21 +225,28 @@ settingsResult = function (settings, type) { * @returns {*} */ canEditAllSettings = function (settingsInfo, options) { - var checks = _.map(settingsInfo, function (settingInfo) { - var setting = settingsCache[settingInfo.key]; + var checkSettingPermissions = function (setting) { + if (setting.type === 'core' && !(options.context && options.context.internal)) { + return when.reject( + new errors.NoPermissionError('Attempted to access core setting from external request') + ); + } - if (!setting) { - return when.reject(new errors.NotFoundError('Unable to find setting: ' + settingInfo.key)); - } + return canThis(options.context).edit.setting(setting.key); + }, + checks = _.map(settingsInfo, function (settingInfo) { + var setting = settingsCache[settingInfo.key]; - if (setting.type === 'core' && !(options.context && options.context.internal)) { - return when.reject( - new errors.NoPermissionError('Attempted to access core setting from external request') - ); - } + if (!setting) { + // Try to populate a default setting if not in the cache + return populateDefaultSetting(settingInfo.key).then(function (defaultSetting) { + // Get the result from the cache with permission checks + return checkSettingPermissions(defaultSetting); + }); + } - return canThis(options.context).edit.setting(settingInfo.key); - }); + return checkSettingPermissions(setting); + }); return when.all(checks); }; @@ -223,6 +264,14 @@ settings = { * @returns {*} */ browse: function browse(options) { + // First, check if we have populated the settings from default-settings yet + if (!hasPopulatedDefaults) { + return dataProvider.Settings.populateDefaults().then(function () { + hasPopulatedDefaults = true; + return settings.browse(options); + }); + } + options = options || {}; var result = settingsResult(settingsCache, options.type); @@ -253,30 +302,40 @@ settings = { options = { key: options }; } - var setting = settingsCache[options.key], - result = {}; + var getSettingsResult = function () { + var setting = settingsCache[options.key], + result = {}; - if (!setting) { - return when.reject(new errors.NotFoundError('Unable to find setting: ' + options.key)); + result[options.key] = setting; + + if (setting.type === 'core' && !(options.context && options.context.internal)) { + return when.reject( + new errors.NoPermissionError('Attempted to access core setting from external request') + ); + } + + if (setting.type === 'blog') { + return when(settingsResult(result)); + } + + return canThis(options.context).read.setting(options.key).then(function () { + return settingsResult(result); + }, function () { + return when.reject(new errors.NoPermissionError('You do not have permission to read settings.')); + }); + }; + + // If the setting is not already in the cache + if (!settingsCache[options.key]) { + // Try to populate the setting from default-settings file + return populateDefaultSetting(options.key).then(function () { + // Get the result from the cache with permission checks + return getSettingsResult(); + }); } - result[options.key] = setting; - - if (setting.type === 'core' && !(options.context && options.context.internal)) { - return when.reject( - new errors.NoPermissionError('Attempted to access core setting from external request') - ); - } - - if (setting.type === 'blog') { - return when(settingsResult(result)); - } - - return canThis(options.context).read.setting(options.key).then(function () { - return settingsResult(result); - }, function () { - return when.reject(new errors.NoPermissionError('You do not have permission to read settings.')); - }); + // Get the result from the cache with permission checks + return getSettingsResult(); }, /** diff --git a/core/server/data/versioning/index.js b/core/server/data/versioning/index.js index 950caaea5d..ed235579c3 100644 --- a/core/server/data/versioning/index.js +++ b/core/server/data/versioning/index.js @@ -13,9 +13,7 @@ var _ = require('lodash'), function getDefaultDatabaseVersion() { if (!defaultDatabaseVersion) { // This be the current version according to the software - defaultDatabaseVersion = _.find(defaultSettings.core, function (setting) { - return setting.key === 'databaseVersion'; - }).defaultValue; + defaultDatabaseVersion = defaultSettings.core.databaseVersion.defaultValue; } return defaultDatabaseVersion; diff --git a/core/server/index.js b/core/server/index.js index 88d579420b..6a1adbd2d6 100644 --- a/core/server/index.js +++ b/core/server/index.js @@ -215,9 +215,6 @@ function init(server) { return builtFilesExist().then(function () { // Initialise the models return models.init(); - }).then(function () { - // Populate any missing default settings - return models.Settings.populateDefaults(); }).then(function () { // Initialize the settings cache return api.init(); diff --git a/core/server/models/settings.js b/core/server/models/settings.js index 3fe34612d8..bc509adea0 100644 --- a/core/server/models/settings.js +++ b/core/server/models/settings.js @@ -25,7 +25,14 @@ function parseDefaultSettings() { return defaultSettingsFlattened; } -defaultSettings = parseDefaultSettings(); + +function getDefaultSettings() { + if (!defaultSettings) { + defaultSettings = parseDefaultSettings(); + } + + return defaultSettings; +} // Each setting is saved as a separate row in the database, // but the overlying API treats them as a single key:value mapping @@ -43,7 +50,7 @@ Settings = ghostBookshelf.Model.extend({ validate: function () { var self = this; return when(validation.validateSchema(self.tableName, self.toJSON())).then(function () { - return validation.validateSettings(defaultSettings, self); + return validation.validateSettings(getDefaultSettings(), self); }); }, @@ -117,12 +124,31 @@ Settings = ghostBookshelf.Model.extend({ }); }, + populateDefault: function (key) { + if (!getDefaultSettings()[key]) { + return when.reject(new errors.NotFoundError('Unable to find default setting: ' + key)); + } + + // TOOD: databaseVersion and currentVersion special cases? + + this.findOne({ key: key }).then(function (foundSetting) { + if (foundSetting) { + return foundSetting; + } + + var defaultSetting = _.clone(getDefaultSettings()[key]); + defaultSetting.value = defaultSetting.defaultValue; + + return Settings.forge(defaultSetting).save(null, {user: 1}); + }); + }, + populateDefaults: function () { return this.findAll().then(function (allSettings) { var usedKeys = allSettings.models.map(function (setting) { return setting.get('key'); }), insertOperations = []; - _.each(defaultSettings, function (defaultSetting, defaultSettingKey) { + _.each(getDefaultSettings(), function (defaultSetting, defaultSettingKey) { var isMissingFromDB = usedKeys.indexOf(defaultSettingKey) === -1; // Temporary code to deal with old databases with currentVersion settings if (defaultSettingKey === 'databaseVersion' && usedKeys.indexOf('currentVersion') !== -1) {