Adding theme switcher to settings/general

closes #488 and #107
- added dropdown for theme selection on general page
- added GET /api/v0.1/themes to retrieve available themes
- modified settings model to get available themes
- modified updateSettignsCache to remove path from settings.activeTheme
This commit is contained in:
Sebastian 2013-08-30 13:20:30 +02:00
parent b55ed1c33c
commit 4525c355af
9 changed files with 81 additions and 11 deletions

View File

@ -11,10 +11,7 @@ config.defaultLang = 'en';
// Force i18n to be on
config.forceI18n = true;
// ## Themes & Plugins
// Current active theme
config.activeTheme = 'casper';
// ## Plugins
// Current active plugins
config.activePlugins = [

View File

@ -4,7 +4,7 @@
// Set the url manually and id to '0' to force PUT requests
Ghost.Models.Settings = Backbone.Model.extend({
url: '/api/v0.1/settings/',
url: Ghost.settings.apiRoot + '/settings',
id: "0",
defaults: {
title: 'My Blog',

View File

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

View File

@ -50,6 +50,15 @@
</select>
</div>
<div class="form-group">
<label for="activeTheme"><strong>Theme</strong></label>
<select id="activeTheme" name="general[activeTheme]">
{{#each availableThemes}}
<option value="{{ name }}" {{#if active}}selected{{/if}}>{{ name }}</option>
{{/each}}
</select>
</div>
</fieldset>
<hr />

View File

@ -41,7 +41,8 @@
showContent: function (id) {
var self = this,
model;
model,
themes;
Ghost.router.navigate('/settings/' + id);
Ghost.trigger('urlchange');
@ -53,9 +54,13 @@
this.pane = new Settings[id]({ el: '.settings-content'});
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.fetch().then(function () {
self.renderPane(model);
themes.fetch().then(function () {
model.fetch().then(function () {
model.set({availableThemes: themes.toJSON()});
self.renderPane(model);
});
});
} else {
model = this.models[this.pane.options.modelType];
@ -134,12 +139,13 @@
},
saveSettings: function () {
this.model.unset('availableThemes');
this.model.save({
title: this.$('#blog-title').val(),
email: this.$('#email-address').val(),
logo: this.$('#logo').attr("src"),
icon: this.$('#icon').attr("src")
icon: this.$('#icon').attr("src"),
activeTheme: this.$('#activeTheme').val()
}, {
success: this.saveSuccess,
error: this.saveError

View File

@ -111,7 +111,7 @@ Ghost = function () {
'appRoot': appRoot,
'themePath': themePath,
'pluginPath': pluginPath,
'activeTheme': path.join(themePath, config.activeTheme),
'activeTheme': path.join(themePath, !instance.settingsCache ? "" : instance.settingsCache.activeTheme),
'adminViews': path.join(appRoot, '/core/server/views/'),
'helperTemplates': path.join(appRoot, '/core/server/helpers/tpl/'),
'lang': path.join(appRoot, '/core/shared/lang/'),
@ -168,6 +168,17 @@ Ghost.prototype.updateSettingsCache = function (settings) {
var settings = {};
_.map(result.models, function (member) {
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;
}
});

View File

@ -14,6 +14,7 @@ var Ghost = require('../ghost'),
users,
notifications,
settings,
themes,
requestHandler,
cachedSettingsRequestHandler,
settingsObject,
@ -247,6 +248,39 @@ settings = {
}
};
// ## 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
// ### requestHandler
@ -308,5 +342,6 @@ module.exports.posts = posts;
module.exports.users = users;
module.exports.notifications = notifications;
module.exports.settings = settings;
module.exports.themes = themes;
module.exports.requestHandler = requestHandler;
module.exports.cachedSettingsRequestHandler = cachedSettingsRequestHandler;

View File

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

View File

@ -199,6 +199,8 @@ when.all([ghost.init(), filters.loadCoreFilters(ghost), helpers.loadCoreHelpers(
ghost.app().get('/api/v0.1/settings', authAPI, disableCachedResult, api.cachedSettingsRequestHandler(api.settings.browse));
ghost.app().get('/api/v0.1/settings/:key', authAPI, disableCachedResult, api.cachedSettingsRequestHandler(api.settings.read));
ghost.app().put('/api/v0.1/settings', authAPI, disableCachedResult, api.cachedSettingsRequestHandler(api.settings.edit));
// #### Themes
ghost.app().get('/api/v0.1/themes', authAPI, disableCachedResult, api.requestHandler(api.themes.browse));
// #### Users
ghost.app().get('/api/v0.1/users', authAPI, disableCachedResult, api.requestHandler(api.users.browse));
ghost.app().get('/api/v0.1/users/:id', authAPI, disableCachedResult, api.requestHandler(api.users.read));