From e2b3c40a34daf5936e36b065bb4557883b17ff4e Mon Sep 17 00:00:00 2001 From: Ricardo Tomasi Date: Sat, 8 Jun 2013 02:05:40 -0300 Subject: [PATCH] Migrate settings page to Backbone --- app.js | 2 +- core/admin/assets/js/models/settings.js | 16 ++ core/admin/assets/js/router.js | 17 +- core/admin/assets/js/settings.js | 60 ---- core/admin/assets/js/views/settings.js | 135 +++++++++ core/admin/controllers/index.js | 2 - core/admin/views/default.hbs | 2 + core/admin/views/settings.hbs | 362 ++++++++++++------------ core/shared/api.js | 35 ++- core/shared/models/setting.js | 45 --- 10 files changed, 376 insertions(+), 300 deletions(-) create mode 100644 core/admin/assets/js/models/settings.js delete mode 100644 core/admin/assets/js/settings.js create mode 100644 core/admin/assets/js/views/settings.js delete mode 100644 core/shared/models/setting.js diff --git a/app.js b/app.js index 9db9c6a720..191ffc5ef4 100755 --- a/app.js +++ b/app.js @@ -130,7 +130,7 @@ ghost.app().get('/ghost/editor/:id', auth, admin.editor); ghost.app().get('/ghost/editor', auth, admin.editor); ghost.app().get('/ghost/content', auth, admin.content); - ghost.app().get('/ghost/settings', auth, admin.settings); + ghost.app().get('/ghost/settings*', auth, admin.settings); ghost.app().get('/ghost/debug', auth, admin.debug.index); ghost.app().get('/ghost/debug/db/delete/', auth, admin.debug.dbdelete); ghost.app().get('/ghost/debug/db/populate/', auth, admin.debug.dbpopulate); diff --git a/core/admin/assets/js/models/settings.js b/core/admin/assets/js/models/settings.js new file mode 100644 index 0000000000..a30123b9aa --- /dev/null +++ b/core/admin/assets/js/models/settings.js @@ -0,0 +1,16 @@ +/*global window, document, Ghost, $, Backbone, _ */ +(function () { + "use strict"; + + // Set the url manually and id to '0' to force PUT requests + Ghost.Models.Settings = Backbone.Model.extend({ + url: '/api/v0.1/settings/', + id: "0", + defaults: { + title: 'My Blog', + description: '', + email: 'admin@tryghost.org' + } + }); + +}()); diff --git a/core/admin/assets/js/router.js b/core/admin/assets/js/router.js index c2c332d406..352ceee2ab 100644 --- a/core/admin/assets/js/router.js +++ b/core/admin/assets/js/router.js @@ -6,18 +6,25 @@ Ghost.Router = Backbone.Router.extend({ routes: { - 'content/': 'blog', - 'editor': 'editor', - 'editor/': 'editor', - 'editor/:id': 'editor' + 'content/' : 'blog', + 'settings/' : 'settings', + 'settings(/:pane)' : 'settings', + 'editor/' : 'editor', + 'editor(/:id)' : 'editor' }, blog: function () { var posts = new Ghost.Collections.Posts(); - posts.fetch({data: {status: 'all'}}).then(function () { + posts.fetch({ data: { status: 'all' } }).then(function () { Ghost.currentView = new Ghost.Views.Blog({ el: '#main', collection: posts }); }); + }, + settings: function (pane) { + var settings = new Ghost.Models.Settings(); + settings.fetch().then(function () { + Ghost.currentView = new Ghost.Views.Settings({ el: '#main', model: settings, pane: pane }); + }); }, editor: function (id) { diff --git a/core/admin/assets/js/settings.js b/core/admin/assets/js/settings.js deleted file mode 100644 index d768075332..0000000000 --- a/core/admin/assets/js/settings.js +++ /dev/null @@ -1,60 +0,0 @@ -/*globals document, location, jQuery */ -(function ($) { - "use strict"; - - var changePage = function (e) { - var newPage = $(this).children('a').attr('href'); - - e.preventDefault(); - $('.settings-menu .active').removeClass('active'); - location.hash = $(this).attr('class'); // Placed here so never gets given the active attribute. - $(this).addClass('active'); - - $('.settings-content').fadeOut().delay(250); - $(newPage).fadeIn(); - - }, - - defaultSettings = { - title: 'My Blog', - description: '' - }, - - getSettings = function () { - return $.extend(defaultSettings, { - title : $('#blog-title').val(), - description : $('#blog-description').val() - }); - }; - - $(document).ready(function () { - if (location.hash) { - var page = $(".settings-menu li." + location.hash.replace('#', '')), - newPage = page.children('a').attr('href'); - $('.settings-menu .active').removeClass('active'); - page.addClass('active'); - - $('.settings-content').hide().delay(250); - $(newPage).show(); - } - $('.settings-menu li').on('click', changePage); - - $('input').iCheck({ - checkboxClass: 'icheckbox_square-grey' - }); - - $('.button-save').click(function (e) { - e.preventDefault(); - var data = getSettings(); - $.ajax({ - method: 'PUT', - url: '/api/v0.1/settings', - data: data, - success: function (res, xhr, c) { - console.log(xhr, c); - } - }); - }); - }); - -}(jQuery)); \ No newline at end of file diff --git a/core/admin/assets/js/views/settings.js b/core/admin/assets/js/views/settings.js new file mode 100644 index 0000000000..1e7a408ca0 --- /dev/null +++ b/core/admin/assets/js/views/settings.js @@ -0,0 +1,135 @@ +/*global window, document, Ghost, Backbone, $, _, alert */ +(function () { + "use strict"; + + var Settings = {}; + + // Base view + // ---------- + Ghost.Views.Settings = Ghost.View.extend({ + initialize: function (options) { + this.addSubview(new Settings.Sidebar({ + el: '.settings-sidebar', + pane: options.pane, + model: this.model + })); + + this.$('input').iCheck({ + checkboxClass: 'icheckbox_square-grey' + }); + } + }); + + // Sidebar (tabs) + // --------------- + Settings.Sidebar = Ghost.View.extend({ + initialize: function (options) { + this.menu = this.$('.settings-menu'); + this.showContent(options.pane || 'general'); + }, + + events: { + 'click .settings-menu li' : 'switchPane' + }, + + switchPane: function (e) { + e.preventDefault(); + var item = $(e.currentTarget), + id = item.find('a').attr('href').substring(1); + this.showContent(id); + }, + + showContent: function (id) { + Backbone.history.navigate('/settings/' + id); + if (this.pane && '#' + id === this.pane.el) { + return; + } + _.result(this.pane, 'destroy'); + this.setActive(id); + this.pane = new Settings[id]({ model: this.model }); + this.pane.render(); + }, + + setActive: function (id) { + this.menu.find('li').removeClass('active'); + this.menu.find('a[href=#' + id + ']').parent().addClass('active'); + } + }); + + // Content panes + // -------------- + Settings.Pane = Ghost.View.extend({ + destroy: function () { + this.$el.removeClass('active'); + }, + + render: function () { + this.$el.addClass('active'); + } + }); + + // TODO: render templates on the client + // TODO: use some kind of data-binding for forms + + // ### General settings + Settings.general = Settings.Pane.extend({ + el: '#general', + events: { + 'click .button-save': 'saveSettings' + }, + + saveSettings: function () { + this.model.save({ + title: this.$('#blog-title').val(), + email: this.$('#email-address').val() + }, { + success: function () { + alert('Saved'); + } + }); + }, + + render: function () { + var settings = this.model.toJSON(); + this.$('#blog-title').val(settings.title); + this.$('#email-address').val(settings.email); + Settings.Pane.prototype.render.call(this); + } + }); + + // ### Content settings + Settings.content = Settings.Pane.extend({ + el: '#content', + events: { + } + }); + + // ### User settings + Settings.users = Settings.Pane.extend({ + el: '#users', + events: { + } + }); + + // ### Appearance settings + Settings.appearance = Settings.Pane.extend({ + el: '#appearance', + events: { + } + }); + + // ### Services settings + Settings.services = Settings.Pane.extend({ + el: '#services', + events: { + } + }); + + // ### Plugins settings + Settings.plugins = Settings.Pane.extend({ + el: '#plugins', + events: { + } + }); + +}()); \ No newline at end of file diff --git a/core/admin/controllers/index.js b/core/admin/controllers/index.js index bb7dde14f6..a6d0b66183 100755 --- a/core/admin/controllers/index.js +++ b/core/admin/controllers/index.js @@ -144,8 +144,6 @@ 'settings': function (req, res) { api.settings.browse() .then(function (settings) { - settings = settings.toJSON(); - settings = _.object(_.pluck(settings, 'key'), _.pluck(settings, 'value')); res.render('settings', { bodyClass: 'settings', adminNav: setSelected(adminNavbar, 'settings'), diff --git a/core/admin/views/default.hbs b/core/admin/views/default.hbs index 377afa46e0..1d7cd3ca38 100644 --- a/core/admin/views/default.hbs +++ b/core/admin/views/default.hbs @@ -55,10 +55,12 @@ + + {{{block "bodyScripts"}}} -{{/contentFor}} - {{!< default}}
-
-
-

General

-
- +
+
+
+

General

+
+ +
+
+
+ {{#with settings}} +
+
+ + + + + + + + + + +
+ +
+ +
+ + + +
+ + {{/with}} +
-
-
- {{#with settings}} -
-
- - - - - - - - - - -
- -
- -
- - - -
- - {{/with}} -
-
-
-
-

Content

-
- +
+
+

Content

+
+ +
+
+
+
+
+ + + + +
+ +
+ +
+ + + + + + + + + +
+ +
-
-
-
-
- - - - -
- -
- -
- - - - - - - - - -
- -
-
-
-
-

Users

-
- +
+
+

Users

+
+ +
+
+
+
Invited Users
+
    +
  • +
    + +
  • +
+
Active Users
+
    +
  • +
    + user +
    + + +
  • +
  • +
    + user +
    + + +
  • +
-
-
-
Invited Users
-
    -
  • -
    - -
  • -
-
Active Users
-
    -
  • -
    - user -
    - - -
  • -
  • -
    - user -
    - - -
  • -
-
+
\ No newline at end of file diff --git a/core/shared/api.js b/core/shared/api.js index 2ddbf0c5c2..305f0cf709 100644 --- a/core/shared/api.js +++ b/core/shared/api.js @@ -17,7 +17,9 @@ posts, users, settings, - requestHandler; + requestHandler, + settingsObject, + settingsCollection; // # Posts posts = { @@ -59,15 +61,38 @@ }; // # Settings + + // Turn a settings collection into a single object/hashmap + settingsObject = function (settings) { + return (settings.toJSON ? settings.toJSON() : settings).reduce(function (res, item) { + if (item.toJSON) { item = item.toJSON(); } + if (item.key) { res[item.key] = item.value; } + return res; + }, {}); + }; + // Turn an object into a collection + settingsCollection = function (settings) { + return _.map(settings, function (value, key) { + return { key: key, value: value }; + }); + }; + settings = { browse: function (options) { - return dataProvider.Setting.browse(options); + return dataProvider.Settings.browse(options).then(settingsObject); }, read: function (options) { - return dataProvider.Setting.read(options.key); + return dataProvider.Settings.read(options.key).then(function (setting) { + return _.pick(setting.toJSON(), 'key', 'value'); + }); }, - edit: function (options) { - return dataProvider.Setting.edit(options); + edit: function (settings) { + settings = settingsCollection(settings); + return dataProvider.Settings.edit(settings).then(settingsObject); + }, + add: function (settings) { + settings = settingsCollection(settings); + return dataProvider.Settings.add(settings).then(settingsObject); } }; diff --git a/core/shared/models/setting.js b/core/shared/models/setting.js deleted file mode 100644 index e27f0612b4..0000000000 --- a/core/shared/models/setting.js +++ /dev/null @@ -1,45 +0,0 @@ -(function () { - "use strict"; - - var Setting, - Settings, - GhostBookshelf = require('./base'), - _ = require('underscore'), - when = require('when'); - - Setting = GhostBookshelf.Model.extend({ - - tableName: 'settings', - - hasTimestamps: true - - }, { - - read: function (_key) { - // Allow for just passing the key instead of attributes - if (!_.isObject(_key)) { - _key = { key: _key }; - } - return GhostBookshelf.Model.read.call(this, _key); - }, - - edit: function (_data) { - return when.all(_.map(_data, function (value, key) { - return this.forge({ key: key }).fetch().then(function (setting) { - return setting.set('value', value).save(); - }); - }, this)); - } - - }); - - Settings = GhostBookshelf.Collection.extend({ - model: Setting - }); - - module.exports = { - Setting: Setting, - Settings: Settings - }; - -}()); \ No newline at end of file