0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-02-03 23:00:14 -05:00

Merge pull request #752 from sebgie/settingsapi

Add setting filter
This commit is contained in:
Hannah Wolfe 2013-09-15 09:53:23 -07:00
commit 117f70dcfd
12 changed files with 136 additions and 167 deletions

View file

@ -3,7 +3,7 @@
"use strict"; "use strict";
//id:0 is used to issue PUT requests //id:0 is used to issue PUT requests
Ghost.Models.Settings = Backbone.Model.extend({ Ghost.Models.Settings = Backbone.Model.extend({
url: Ghost.settings.apiRoot + '/settings', url: Ghost.settings.apiRoot + '/settings?type=blog,theme',
id: "0" id: "0"
}); });

View file

@ -1,9 +0,0 @@
/*global window, document, Ghost, $, _, Backbone */
(function () {
"use strict";
Ghost.Models.Themes = Backbone.Model.extend({
url: Ghost.settings.apiRoot + '/themes'
});
}());

View file

@ -57,8 +57,7 @@
showContent: function (id) { showContent: function (id) {
var self = this, var self = this,
model, model;
themes;
Ghost.router.navigate('/settings/' + id); Ghost.router.navigate('/settings/' + id);
Ghost.trigger('urlchange'); Ghost.trigger('urlchange');
@ -70,13 +69,9 @@
this.pane = new Settings[id]({ el: '.settings-content'}); this.pane = new Settings[id]({ el: '.settings-content'});
if (!this.models.hasOwnProperty(this.pane.options.modelType)) { if (!this.models.hasOwnProperty(this.pane.options.modelType)) {
themes = this.models.Themes = new Ghost.Models.Themes();
model = this.models[this.pane.options.modelType] = new Ghost.Models[this.pane.options.modelType](); model = this.models[this.pane.options.modelType] = new Ghost.Models[this.pane.options.modelType]();
themes.fetch().then(function () { model.fetch().then(function () {
model.fetch().then(function () { self.renderPane(model);
model.set({availableThemes: themes.toJSON()});
self.renderPane(model);
});
}); });
} else { } else {
model = this.models[this.pane.options.modelType]; model = this.models[this.pane.options.modelType];
@ -157,8 +152,7 @@
}, },
saveSettings: function () { saveSettings: function () {
var themes = this.model.get('availableThemes'), var title = this.$('#blog-title').val(),
title = this.$('#blog-title').val(),
description = this.$('#blog-description').val(), description = this.$('#blog-description').val(),
email = this.$('#email-address').val(), email = this.$('#email-address').val(),
postsPerPage = this.$('#postsPerPage').val(); postsPerPage = this.$('#postsPerPage').val();
@ -180,8 +174,6 @@
if (Ghost.Validate._errors.length > 0) { if (Ghost.Validate._errors.length > 0) {
Ghost.Validate.handleErrors(); Ghost.Validate.handleErrors();
} else { } else {
this.model.unset('availableThemes');
this.model.save({ this.model.save({
title: title, title: title,
description: description, description: description,
@ -194,7 +186,6 @@
success: this.saveSuccess, success: this.saveSuccess,
error: this.saveError error: this.saveError
}); });
this.model.set({availableThemes: themes});
} }
}, },
showLogo: function (e) { showLogo: function (e) {
@ -210,16 +201,12 @@
showUpload: function (key, src) { showUpload: function (key, src) {
var self = this, upload = new Ghost.Models.uploadModal({'key': key, 'src': src, 'accept': { var self = this, upload = new Ghost.Models.uploadModal({'key': key, 'src': src, 'accept': {
func: function () { // The function called on acceptance func: function () { // The function called on acceptance
var data = {}, var data = {};
themes;
data[key] = this.$('.js-upload-target').attr('src'); data[key] = this.$('.js-upload-target').attr('src');
themes = self.model.get('availableThemes');
self.model.unset('availableThemes');
self.model.save(data, { self.model.save(data, {
success: self.saveSuccess, success: self.saveSuccess,
error: self.saveError error: self.saveError
}); });
self.model.set({availableThemes: themes});
self.render(); self.render();
return true; return true;
}, },

View file

@ -100,10 +100,10 @@ Ghost = function () {
* this data is what becomes globally available to themes */ * this data is what becomes globally available to themes */
return { return {
url: instance.config().url, url: instance.config().url,
title: instance.settings().title, title: instance.settings().title.value,
description: instance.settings().description, description: instance.settings().description.value,
logo: instance.settings().logo, logo: instance.settings().logo.value,
cover: instance.settings().cover cover: instance.settings().cover.value
}; };
}, },
statuses: function () { return statuses; }, statuses: function () { return statuses; },
@ -121,7 +121,7 @@ Ghost = function () {
'appRoot': appRoot, 'appRoot': appRoot,
'themePath': themePath, 'themePath': themePath,
'pluginPath': pluginPath, 'pluginPath': pluginPath,
'activeTheme': path.join(themePath, !instance.settingsCache ? "" : instance.settingsCache.activeTheme), 'activeTheme': path.join(themePath, !instance.settingsCache ? "" : instance.settingsCache.activeTheme.value),
'adminViews': path.join(appRoot, '/core/server/views/'), 'adminViews': path.join(appRoot, '/core/server/views/'),
'helperTemplates': path.join(appRoot, '/core/server/helpers/tpl/'), 'helperTemplates': path.join(appRoot, '/core/server/helpers/tpl/'),
'lang': path.join(appRoot, '/core/shared/lang/'), 'lang': path.join(appRoot, '/core/shared/lang/'),
@ -142,15 +142,14 @@ Ghost.prototype.init = function () {
instance.dataProvider.init(), instance.dataProvider.init(),
instance.getPaths(), instance.getPaths(),
instance.mail.init(self) instance.mail.init(self)
).then(function () { ).then(function () {
return models.Settings.populateDefaults(); return models.Settings.populateDefaults();
}).then(function () {
// Initialize plugins
return self.initPlugins();
}).then(function () { }).then(function () {
// Initialize the settings cache // Initialize the settings cache
return self.updateSettingsCache(); return self.updateSettingsCache();
}).then(function () { }).then(function () {
// Initialize plugins
return self.initPlugins(); return self.initPlugins();
}).then(function () { }).then(function () {
// Initialize the permissions actions and objects // Initialize the permissions actions and objects
@ -164,7 +163,7 @@ Ghost.prototype.init = function () {
}).otherwise(function (error) { }).otherwise(function (error) {
// this is where all the "first run" functionality should go // this is where all the "first run" functionality should go
var dbhash = uuid.v4(); var dbhash = uuid.v4();
return when(models.Settings.add({key: 'dbHash', value: dbhash})).then(function (returned) { return when(models.Settings.add({key: 'dbHash', value: dbhash, type: 'core'})).then(function (returned) {
self.dbHash = dbhash; self.dbHash = dbhash;
return dbhash; return dbhash;
}); });
@ -179,33 +178,62 @@ Ghost.prototype.updateSettingsCache = function (settings) {
settings = settings || {}; settings = settings || {};
if (!_.isEmpty(settings)) { if (!_.isEmpty(settings)) {
self.settingsCache = settings; _.map(settings, function (setting, key) {
self.settingsCache[key].value = setting.value;
});
} else { } else {
// TODO: this should use api.browse // TODO: this should use api.browse
return models.Settings.findAll().then(function (result) { return when(models.Settings.findAll()).then(function (result) {
var settings = {}; return when(self.readSettingsResult(result)).then(function (s) {
_.map(result.models, function (member) { self.settingsCache = s;
if (!settings.hasOwnProperty(member.attributes.key)) {
if (member.attributes.key === 'activeTheme') {
member.attributes.value = member.attributes.value.substring(member.attributes.value.lastIndexOf('/') + 1);
var settingsThemePath = path.join(themePath, member.attributes.value);
fs.exists(settingsThemePath, function (exists) {
if (!exists) {
member.attributes.value = "casper";
}
settings[member.attributes.key] = member.attributes.value;
});
return;
}
settings[member.attributes.key] = member.attributes.value;
}
}); });
});
self.settingsCache = settings;
}, errors.logAndThrowError);
} }
}; };
Ghost.prototype.readSettingsResult = function (result) {
var settings = {};
return when(_.map(result.models, function (member) {
if (!settings.hasOwnProperty(member.attributes.key)) {
var val = {};
if (member.attributes.key === 'activeTheme') {
member.attributes.value = member.attributes.value.substring(member.attributes.value.lastIndexOf('/') + 1);
val.value = member.attributes.value;
val.type = member.attributes.type;
settings[member.attributes.key] = val;
} else {
val.value = member.attributes.value;
val.type = member.attributes.type;
settings[member.attributes.key] = val;
}
}
})).then(function () {
return when(instance.paths().availableThemes).then(function (themes) {
var themeKeys = Object.keys(themes),
res = [],
i,
item;
for (i = 0; i < themeKeys.length; i += 1) {
//do not include hidden files
if (themeKeys[i].indexOf('.') !== 0) {
item = {};
item.name = themeKeys[i];
//data about files currently not used
//item.details = themes[themeKeys[i]];
if (themeKeys[i] === settings.activeTheme.value) {
item.active = true;
}
res.push(item);
}
}
settings.availableThemes = {};
settings.availableThemes.value = res;
settings.availableThemes.type = 'theme';
return settings;
});
});
};
// ## Template utils // ## Template utils
// Compile a template for a handlebars helper // Compile a template for a handlebars helper
@ -303,7 +331,6 @@ Ghost.prototype.doFilter = function (name, args, callback) {
// Initialise plugins. Will load from config.activePlugins by default // Initialise plugins. Will load from config.activePlugins by default
Ghost.prototype.initPlugins = function (pluginsToLoad) { Ghost.prototype.initPlugins = function (pluginsToLoad) {
pluginsToLoad = pluginsToLoad || models.Settings.activePlugins; pluginsToLoad = pluginsToLoad || models.Settings.activePlugins;
var self = this; var self = this;
return plugins.init(this, pluginsToLoad).then(function (loadedPlugins) { return plugins.init(this, pluginsToLoad).then(function (loadedPlugins) {
@ -326,11 +353,11 @@ Ghost.prototype.initTheme = function (app) {
// self.globals is a hack til we have a better way of getting combined settings & config // self.globals is a hack til we have a better way of getting combined settings & config
hbsOptions = {templateOptions: {data: {blog: self.blogGlobals()}}}; hbsOptions = {templateOptions: {data: {blog: self.blogGlobals()}}};
if (!self.themeDirectories.hasOwnProperty(self.settings().activeTheme)) { if (!self.themeDirectories.hasOwnProperty(self.settings().activeTheme.value)) {
// Throw an error if the theme is not available... // Throw an error if the theme is not available...
// TODO: move this to happen on app start // TODO: move this to happen on app start
errors.logAndThrowError('The currently active theme ' + self.settings().activeTheme + ' is missing.'); errors.logAndThrowError('The currently active theme ' + self.settings().activeTheme.value + ' is missing.');
} else if (self.themeDirectories[self.settings().activeTheme].hasOwnProperty('partials')) { } else if (self.themeDirectories[self.settings().activeTheme.value].hasOwnProperty('partials')) {
// Check that the theme has a partials directory before trying to use it // Check that the theme has a partials directory before trying to use it
hbsOptions.partialsDir = path.join(self.paths().activeTheme, 'partials'); hbsOptions.partialsDir = path.join(self.paths().activeTheme, 'partials');
} }

View file

@ -192,11 +192,9 @@ when.all([ghost.init(), helpers.loadCoreHelpers(ghost)]).then(function () {
server.put('/api/v0.1/posts/:id', authAPI, disableCachedResult, api.requestHandler(api.posts.edit)); server.put('/api/v0.1/posts/:id', authAPI, disableCachedResult, api.requestHandler(api.posts.edit));
server.del('/api/v0.1/posts/:id', authAPI, disableCachedResult, api.requestHandler(api.posts.destroy)); server.del('/api/v0.1/posts/:id', authAPI, disableCachedResult, api.requestHandler(api.posts.destroy));
// #### Settings // #### Settings
server.get('/api/v0.1/settings', authAPI, disableCachedResult, api.cachedSettingsRequestHandler(api.settings.browse)); server.get('/api/v0.1/settings', authAPI, disableCachedResult, api.requestHandler(api.settings.browse));
server.get('/api/v0.1/settings/:key', authAPI, disableCachedResult, api.cachedSettingsRequestHandler(api.settings.read)); server.get('/api/v0.1/settings/:key', authAPI, disableCachedResult, api.requestHandler(api.settings.read));
server.put('/api/v0.1/settings', authAPI, disableCachedResult, api.cachedSettingsRequestHandler(api.settings.edit)); server.put('/api/v0.1/settings', authAPI, disableCachedResult, api.requestHandler(api.settings.edit));
// #### Themes
server.get('/api/v0.1/themes', authAPI, disableCachedResult, api.requestHandler(api.themes.browse));
// #### Users // #### Users
server.get('/api/v0.1/users', authAPI, disableCachedResult, api.requestHandler(api.users.browse)); server.get('/api/v0.1/users', authAPI, disableCachedResult, api.requestHandler(api.users.browse));
server.get('/api/v0.1/users/:id', authAPI, disableCachedResult, api.requestHandler(api.users.read)); server.get('/api/v0.1/users/:id', authAPI, disableCachedResult, api.requestHandler(api.users.read));

View file

@ -17,9 +17,9 @@ var Ghost = require('../ghost'),
settings, settings,
themes, themes,
requestHandler, requestHandler,
cachedSettingsRequestHandler,
settingsObject, settingsObject,
settingsCollection; settingsCollection,
settingsFilter;
// ## Posts // ## Posts
posts = { posts = {
@ -195,6 +195,16 @@ notifications = {
// ### Helpers // ### Helpers
// Turn a settings collection into a single object/hashmap // Turn a settings collection into a single object/hashmap
settingsObject = function (settings) { settingsObject = function (settings) {
if (_.isObject(settings)) {
return _.reduce(settings, function (res, item, key) {
if (_.isArray(item)) {
res[key] = item;
} else {
res[key] = item.value;
}
return res;
}, {});
}
return (settings.toJSON ? settings.toJSON() : settings).reduce(function (res, item) { return (settings.toJSON ? settings.toJSON() : settings).reduce(function (res, item) {
if (item.toJSON) { item = item.toJSON(); } if (item.toJSON) { item = item.toJSON(); }
if (item.key) { res[item.key] = item.value; } if (item.key) { res[item.key] = item.value; }
@ -208,13 +218,28 @@ settingsCollection = function (settings) {
}); });
}; };
settingsFilter = function (settings, filter) {
return _.object(_.filter(_.pairs(settings), function (setting) {
if (filter) {
return _.some(filter.split(","), function (f) {
return setting[1].type === f;
});
}
return true;
}));
};
settings = { settings = {
// #### Browse // #### Browse
// **takes:** options object // **takes:** options object
browse: function browse(options) { browse: function browse(options) {
// **returns:** a promise for a settings json object // **returns:** a promise for a settings json object
return dataProvider.Settings.browse(options).then(settingsObject, errors.logAndThrowError); if (ghost.settings()) {
return when(ghost.settings()).then(function (settings) {
return settingsObject(settingsFilter(settings, options.type));
}, errors.logAndThrowError);
}
}, },
// #### Read // #### Read
@ -225,14 +250,17 @@ settings = {
options = { key: options }; options = { key: options };
} }
// **returns:** a promise for a single key-value pair if (ghost.settings()) {
return dataProvider.Settings.read(options.key).then(function (setting) { return when(ghost.settings()[options.key]).then(function (setting) {
if (!setting) { if (!setting) {
return when.reject("Unable to find setting: " + options.key); return when.reject("Unable to find setting: " + options.key);
} }
var res = {};
return _.pick(setting.toJSON(), 'key', 'value'); res.key = options.key;
}, errors.logAndThrowError); res.value = setting.value;
return res;
}, errors.logAndThrowError);
}
}, },
// #### Edit // #### Edit
@ -241,61 +269,36 @@ settings = {
edit: function edit(key, value) { edit: function edit(key, value) {
// Check for passing a collection of settings first // Check for passing a collection of settings first
if (_.isObject(key)) { if (_.isObject(key)) {
//clean data
var type = key.type;
delete key.type;
delete key.availableThemes;
key = settingsCollection(key); key = settingsCollection(key);
return dataProvider.Settings.edit(key).then(function (result) {
return dataProvider.Settings.edit(key).then(settingsObject, errors.logAndThrowError); result.models = result;
return when(ghost.readSettingsResult(result)).then(function (settings) {
ghost.updateSettingsCache(settings);
return settingsObject(settingsFilter(ghost.settings(), type));
});
}, errors.logAndThrowError);
} }
// **returns:** a promise for a settings json object
return dataProvider.Settings.read(key).then(function (setting) { return dataProvider.Settings.read(key).then(function (setting) {
if (!setting) { if (!setting) {
return when.reject("Unable to find setting: " + key); return when.reject("Unable to find setting: " + key);
} }
if (!_.isString(value)) { if (!_.isString(value)) {
value = JSON.stringify(value); value = JSON.stringify(value);
} }
setting.set('value', value); setting.set('value', value);
return dataProvider.Settings.edit(setting).then(function (result) {
return dataProvider.Settings.edit(setting); ghost.settings()[_.first(result).attributes.key].value = _.first(result).attributes.value;
}, errors.logAndThrowError); return settingsObject(ghost.settings());
} }, errors.logAndThrowError);
};
// ## Themes
themes = {
// #### Browse
// **takes:** options object
browse: function browse() {
// **returns:** a promise for a themes json object
return when(ghost.paths().availableThemes).then(function (themes) {
var themeKeys = Object.keys(themes),
res = [],
i,
activeTheme = ghost.paths().activeTheme.substring(ghost.paths().activeTheme.lastIndexOf('/') + 1),
item;
for (i = 0; i < themeKeys.length; i += 1) {
//do not include hidden files
if (themeKeys[i].indexOf('.') !== 0) {
item = {};
item.name = themeKeys[i];
item.details = themes[themeKeys[i]];
if (themeKeys[i] === activeTheme) {
item.active = true;
}
res.push(item);
}
}
return res;
}); });
} }
}; };
// ## Request Handlers // ## Request Handlers
// ### requestHandler // ### requestHandler
@ -317,41 +320,6 @@ requestHandler = function (apiMethod) {
}; };
}; };
// ### cachedSettingsRequestHandler
// Special request handler for settings to access the internal cache version of the settings object
cachedSettingsRequestHandler = function (apiMethod) {
if (!ghost.settings()) {
return requestHandler(apiMethod);
}
return function (req, res) {
var options = _.extend(req.body, req.query, req.params),
promise;
switch (apiMethod.name) {
case 'browse':
promise = when(ghost.settings());
break;
case 'read':
promise = when(ghost.settings()[options.key]);
break;
case 'edit':
promise = apiMethod(options).then(function (result) {
ghost.updateSettingsCache(result);
return result;
});
break;
default:
errors.logAndThrowError(new Error('Unknown method name for settings API: ' + apiMethod.name));
}
return promise.then(function (result) {
res.json(result || {});
}, function (error) {
res.json(400, {error: error});
});
};
};
// Public API // Public API
module.exports.posts = posts; module.exports.posts = posts;
module.exports.users = users; module.exports.users = users;
@ -360,4 +328,3 @@ module.exports.notifications = notifications;
module.exports.settings = settings; module.exports.settings = settings;
module.exports.themes = themes; module.exports.themes = themes;
module.exports.requestHandler = requestHandler; module.exports.requestHandler = requestHandler;
module.exports.cachedSettingsRequestHandler = cachedSettingsRequestHandler;

View file

@ -15,7 +15,7 @@ frontendControllers = {
'homepage': function (req, res) { 'homepage': function (req, res) {
// Parse the page number // Parse the page number
var pageParam = req.params.page !== undefined ? parseInt(req.params.page, 10) : 1, var pageParam = req.params.page !== undefined ? parseInt(req.params.page, 10) : 1,
postsPerPage = parseInt(ghost.settings().postsPerPage, 10), postsPerPage = parseInt(ghost.settings().postsPerPage.value, 10),
options = {}; options = {};
// No negative pages // No negative pages
@ -67,10 +67,10 @@ frontendControllers = {
// Initialize RSS // Initialize RSS
var siteUrl = ghost.config().url, var siteUrl = ghost.config().url,
feed = new RSS({ feed = new RSS({
title: ghost.settings().title, title: ghost.settings().title.value,
description: ghost.settings().description, description: ghost.settings().description.value,
generator: 'Ghost v' + res.locals.version, generator: 'Ghost v' + res.locals.version,
author: ghost.settings().author, author: ghost.settings().author.value,
feed_url: siteUrl + '/rss/', feed_url: siteUrl + '/rss/',
site_url: siteUrl, site_url: siteUrl,
ttl: '60' ttl: '60'

View file

@ -46,14 +46,14 @@
} }
}, },
"theme": { "theme": {
"activePlugins": {
"defaultValue": ""
},
"activeTheme": { "activeTheme": {
"defaultValue": "casper" "defaultValue": "casper"
} }
}, },
"plugin": { "plugin": {
"activePlugins": {
"defaultValue": "[]"
},
"installedPlugins": { "installedPlugins": {
"defaultValue": "[]" "defaultValue": "[]"
} }

View file

@ -234,7 +234,7 @@ coreHelpers = function (ghost) {
ghost.registerThemeHelper('e', function (key, defaultString, options) { ghost.registerThemeHelper('e', function (key, defaultString, options) {
var output; var output;
if (ghost.settings().defaultLang === 'en' && _.isEmpty(options.hash) && !ghost.settings().forceI18n) { if (ghost.settings().defaultLang.value === 'en' && _.isEmpty(options.hash) && !ghost.settings().forceI18n.value) {
output = defaultString; output = defaultString;
} else { } else {
output = ghost.polyglot().t(key, options.hash); output = ghost.polyglot().t(key, options.hash);

View file

@ -96,7 +96,7 @@ GhostMailer.prototype.send = function (message) {
} }
var from = 'ghost-mailer@' + url.parse(this.ghost.config().url).hostname, var from = 'ghost-mailer@' + url.parse(this.ghost.config().url).hostname,
to = message.to || this.ghost.settings().email, to = message.to || this.ghost.settings().email.value,
sendMail = nodefn.lift(this.transport.sendMail.bind(this.transport)); sendMail = nodefn.lift(this.transport.sendMail.bind(this.transport));
message = _.extend(message, { message = _.extend(message, {

View file

@ -80,7 +80,6 @@
<script src="/public/models/tag.js"></script> <script src="/public/models/tag.js"></script>
<script src="/public/models/widget.js"></script> <script src="/public/models/widget.js"></script>
<script src="/public/models/settings.js"></script> <script src="/public/models/settings.js"></script>
<script src="/public/models/themes.js"></script>
<script src="/public/models/uploadModal.js"></script> <script src="/public/models/uploadModal.js"></script>
<!-- // require '/public/views/*' --> <!-- // require '/public/views/*' -->
<script src="/public/views/base.js"></script> <script src="/public/views/base.js"></script>

View file

@ -8,7 +8,7 @@ var fs = require('fs'),
I18n = function (ghost) { I18n = function (ghost) {
// TODO: validate // TODO: validate
var lang = ghost.settings().defaultLang, var lang = ghost.settings().defaultLang.value,
path = ghost.paths().lang, path = ghost.paths().lang,
langFilePath = path + lang + '.json'; langFilePath = path + lang + '.json';