mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-17 05:21:36 +03:00
d6aaf2dbc7
no issue - if you upload a huge import file, parallel operations can throw errors e.g. lock wait exceeds - this can happen if multiple transactions run in parallel - there is no need to run: 1. the removal of active tokens on import, because imported users have no active session 2. rescheduling logic on timezone, because importing scheduled posts works out of the box via the model layer (if a published date is detected and it's in the future, the post get's scheduled)
175 lines
5.8 KiB
JavaScript
175 lines
5.8 KiB
JavaScript
var Settings,
|
|
Promise = require('bluebird'),
|
|
_ = require('lodash'),
|
|
uuid = require('uuid'),
|
|
ghostBookshelf = require('./base'),
|
|
errors = require('../errors'),
|
|
events = require('../events'),
|
|
i18n = require('../i18n'),
|
|
validation = require('../data/validation'),
|
|
|
|
internalContext = {context: {internal: true}},
|
|
|
|
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/schema/').defaultSettings,
|
|
defaultSettingsFlattened = {},
|
|
dynamicDefault = {
|
|
db_hash: uuid.v4()
|
|
};
|
|
|
|
_.each(defaultSettingsInCategories, function each(settings, categoryName) {
|
|
_.each(settings, function each(setting, settingName) {
|
|
setting.type = categoryName;
|
|
setting.key = settingName;
|
|
if (dynamicDefault[setting.key]) {
|
|
setting.defaultValue = dynamicDefault[setting.key];
|
|
}
|
|
|
|
defaultSettingsFlattened[settingName] = setting;
|
|
});
|
|
});
|
|
|
|
return defaultSettingsFlattened;
|
|
}
|
|
|
|
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
|
|
Settings = ghostBookshelf.Model.extend({
|
|
|
|
tableName: 'settings',
|
|
|
|
defaults: function defaults() {
|
|
return {
|
|
type: 'core'
|
|
};
|
|
},
|
|
|
|
emitChange: function emitChange(event, options) {
|
|
events.emit('settings' + '.' + event, this, options);
|
|
},
|
|
|
|
onDestroyed: function onDestroyed(model, response, options) {
|
|
model.emitChange('deleted');
|
|
model.emitChange(model.attributes.key + '.' + 'deleted', options);
|
|
},
|
|
|
|
onCreated: function onCreated(model, response, options) {
|
|
model.emitChange('added');
|
|
model.emitChange(model.attributes.key + '.' + 'added', options);
|
|
},
|
|
|
|
onUpdated: function onUpdated(model, response, options) {
|
|
model.emitChange('edited');
|
|
model.emitChange(model.attributes.key + '.' + 'edited', options);
|
|
},
|
|
|
|
onValidate: function onValidate() {
|
|
var self = this,
|
|
setting = this.toJSON();
|
|
|
|
return validation.validateSchema(self.tableName, setting).then(function then() {
|
|
return validation.validateSettings(getDefaultSettings(), self);
|
|
});
|
|
}
|
|
}, {
|
|
findOne: function (data, options) {
|
|
if (_.isEmpty(data)) {
|
|
options = data;
|
|
}
|
|
|
|
// Allow for just passing the key instead of attributes
|
|
if (!_.isObject(data)) {
|
|
data = {key: data};
|
|
}
|
|
|
|
return Promise.resolve(ghostBookshelf.Model.findOne.call(this, data, options));
|
|
},
|
|
|
|
edit: function (data, options) {
|
|
var self = this;
|
|
options = this.filterOptions(options, 'edit');
|
|
|
|
if (!Array.isArray(data)) {
|
|
data = [data];
|
|
}
|
|
|
|
return Promise.map(data, function (item) {
|
|
// Accept an array of models as input
|
|
if (item.toJSON) { item = item.toJSON(); }
|
|
if (!(_.isString(item.key) && item.key.length > 0)) {
|
|
return Promise.reject(new errors.ValidationError({message: i18n.t('errors.models.settings.valueCannotBeBlank')}));
|
|
}
|
|
|
|
item = self.filterData(item);
|
|
|
|
return Settings.forge({key: item.key}).fetch(options).then(function then(setting) {
|
|
var saveData = {};
|
|
|
|
if (setting) {
|
|
if (item.hasOwnProperty('value')) {
|
|
saveData.value = item.value;
|
|
}
|
|
// Internal context can overwrite type (for fixture migrations)
|
|
if (options.context && options.context.internal && item.hasOwnProperty('type')) {
|
|
saveData.type = item.type;
|
|
}
|
|
// it's allowed to edit all attributes in case of importing/migrating
|
|
if (options.importing) {
|
|
saveData = item;
|
|
}
|
|
|
|
return setting.save(saveData, options);
|
|
}
|
|
|
|
return Promise.reject(new errors.NotFoundError({message: i18n.t('errors.models.settings.unableToFindSetting', {key: item.key})}));
|
|
});
|
|
});
|
|
},
|
|
|
|
populateDefaults: function populateDefaults(options) {
|
|
var self = this;
|
|
|
|
options = _.merge({}, options || {}, internalContext);
|
|
|
|
return this
|
|
.findAll(options)
|
|
.then(function checkAllSettings(allSettings) {
|
|
var usedKeys = allSettings.models.map(function mapper(setting) { return setting.get('key'); }),
|
|
insertOperations = [];
|
|
|
|
_.each(getDefaultSettings(), function forEachDefault(defaultSetting, defaultSettingKey) {
|
|
var isMissingFromDB = usedKeys.indexOf(defaultSettingKey) === -1;
|
|
if (isMissingFromDB) {
|
|
defaultSetting.value = defaultSetting.defaultValue;
|
|
insertOperations.push(Settings.forge(defaultSetting).save(null, options));
|
|
}
|
|
});
|
|
|
|
if (insertOperations.length > 0) {
|
|
return Promise.all(insertOperations).then(function fetchAllToReturn() {
|
|
return self.findAll(options);
|
|
});
|
|
}
|
|
|
|
return allSettings;
|
|
});
|
|
}
|
|
});
|
|
|
|
module.exports = {
|
|
Settings: ghostBookshelf.model('Settings', Settings)
|
|
};
|