Merge pull request #728 from skattyadz/default-settings-validations

Conflicts:
	Gruntfile.js
	core/server.js
	core/server/data/default-settings.json
	core/test/unit/admin_spec.js
This commit is contained in:
Hannah Wolfe 2013-09-14 14:37:52 +01:00
commit 3fab1f708a
5 changed files with 115 additions and 66 deletions

View File

@ -144,6 +144,9 @@ Ghost.prototype.init = function () {
instance.mail.init(self)
).then(function () {
return models.Settings.populateDefaults();
}).then(function () {
// Initialize plugins
return self.initPlugins();
}).then(function () {
// Initialize the settings cache
return self.updateSettingsCache();

View File

@ -1,62 +1,59 @@
[
{
"key": "title",
"value": "Ghost",
"type": "blog"
{
"blog": {
"title": {
"default": "Ghost"
},
"description": {
"default": "Just a blogging platform."
},
"logo": {
"default": ""
},
"cover": {
"default": ""
},
"defaultLang": {
"default": "en",
"validations": {
"notNull": true
}
},
"postsPerPage": {
"default": "6",
"validations": {
"notNull": true,
"isNumeric": true
}
},
"forceI18n": {
"default": "true",
"validations": {
"notNull": true,
"isIn": ["true", "false"]
}
}
},
{
"key": "description",
"value": "Just a blogging platform.",
"type": "blog"
"general": {
"email": {
"default": "ghost@example.com",
"validations": {
"notNull": true,
"isEmail": true
}
},
"activePlugins": {
"default": ""
},
"activeTheme": {
"default": "casper"
}
},
{
"key": "email",
"value": "ghost@example.com",
"type": "general"
},
{
"key": "activePlugins",
"value": "",
"type": "general"
},
{
"key": "activeTheme",
"value": "casper",
"type": "general"
},
{
"key": "currentVersion",
"value": "002",
"type": "core"
},
{
"key": "installedPlugins",
"value": "[]",
"type": "core"
},
{
"key": "logo",
"value": "",
"type": "blog"
},
{
"key": "cover",
"value": "",
"type": "blog"
},
{
"key": "defaultLang",
"value": "en",
"type": "blog"
},
{
"key": "forceI18n",
"value": true,
"type": "blog"
},
{
"key": "postsPerPage",
"value": "6",
"type": "blog"
"core": {
"currentVersion": {
"default": "000"
},
"installedPlugins": {
"default": "[]"
}
}
]
}

View File

@ -1,10 +1,31 @@
var Settings,
GhostBookshelf = require('./base'),
validator = GhostBookshelf.validator,
uuid = require('node-uuid'),
_ = require('underscore'),
errors = require('../errorHandling'),
when = require('when'),
defaultSettings = require('../data/default-settings.json');
defaultSettings;
// For neatness, the defaults file is split into categories.
// It's much easier for us to work with it as a single level
// instead of iterating those categories every time
function parseDefaultSettings() {
var defaultSettingsInCategories = require('../data/default-settings.json'),
defaultSettingsFlattened = {};
_.each(defaultSettingsInCategories, function (settings, categoryName) {
_.each(settings, function (setting, settingName) {
setting.type = categoryName;
setting.key = settingName;
defaultSettingsFlattened[settingName] = setting;
});
});
return defaultSettingsFlattened;
}
defaultSettings = parseDefaultSettings();
// Each setting is saved as a separate row in the database,
// but the overlying API treats them as a single key:value mapping
@ -28,10 +49,36 @@ Settings = GhostBookshelf.Model.extend({
this.on('saving', this.validate, this);
},
// Validate default settings using the validator module.
// Each validation's key is a name and its value is an array of options
// Use true (boolean) if options aren't applicable
//
// eg:
// validations: { isUrl: true, len: [20, 40] }
//
// will validate that a setting's length is a URL between 20 and 40 chars,
// available validators: https://github.com/chriso/node-validator#list-of-validation-methods
validate: function () {
// TODO: validate value, check type is one of the allowed values etc
GhostBookshelf.validator.check(this.get('key'), "Setting key cannot be blank").notEmpty();
GhostBookshelf.validator.check(this.get('type'), "Setting type cannot be blank").notEmpty();
validator.check(this.get('key'), "Setting key cannot be blank").notEmpty();
validator.check(this.get('type'), "Setting type cannot be blank").notEmpty();
var matchingDefault = defaultSettings[this.get('key')];
if (matchingDefault && matchingDefault.validations) {
_.each(matchingDefault.validations, function (validationOptions, validationName) {
var validation = validator.check(this.get('value'));
if (validationOptions === true) {
validationOptions = null;
}
if (typeof validationOptions !== 'array') {
validationOptions = [validationOptions];
}
// equivalent of validation.isSomething(option1, option2)
validation[validationName].apply(validation, validationOptions);
}, this);
}
},
saving: function () {
@ -72,9 +119,10 @@ Settings = GhostBookshelf.Model.extend({
var usedKeys = allSettings.models.map(function (setting) { return setting.get('key'); }),
insertOperations = [];
defaultSettings.forEach(function (defaultSetting) {
var isMissingFromDB = usedKeys.indexOf(defaultSetting.key) === -1;
_.each(defaultSettings, function (defaultSetting, defaultSettingKey) {
var isMissingFromDB = usedKeys.indexOf(defaultSettingKey) === -1;
if (isMissingFromDB) {
defaultSetting.value = defaultSetting.default;
insertOperations.push(Settings.forge(defaultSetting).save());
}
});

View File

@ -38,6 +38,7 @@ describe('Admin Controller', function() {
});
});
describe('valid file', function() {
var clock;

View File

@ -83,4 +83,4 @@ testUtils = {
};
module.exports = testUtils;
module.exports = testUtils;