diff --git a/app.js b/app.js
index bbc0da3830..834624c479 100755
--- a/app.js
+++ b/app.js
@@ -138,7 +138,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/init.js b/core/admin/assets/js/init.js
index 8f302daa82..79ea27c7fe 100644
--- a/core/admin/assets/js/init.js
+++ b/core/admin/assets/js/init.js
@@ -21,46 +21,15 @@
router: null
};
- Ghost.View = Backbone.View.extend({
-
- // Adds a subview to the current view, which will
- // ensure its removal when this view is removed,
- // or when view.removeSubviews is called
- addSubview: function (view) {
- if (!(view instanceof Backbone.View)) {
- throw new Error("Subview must be a Backbone.View");
- }
- this.subviews = this.subviews || [];
- this.subviews.push(view);
- return view;
- },
-
- // Removes any subviews associated with this view
- // by `addSubview`, which will in-turn remove any
- // children of those views, and so on.
- removeSubviews: function () {
- var i, l, children = this.subviews;
- if (!children) {
- return this;
- }
- for (i = 0, l = children.length; i < l; i += 1) {
- children[i].remove();
- }
- this.subviews = [];
- return this;
- },
-
- // Extends the view's remove, by calling `removeSubviews`
- // if any subviews exist.
- remove: function () {
- if (this.subviews) {
- this.removeSubviews();
- }
- return Backbone.View.prototype.remove.apply(this, arguments);
- }
-
- });
+ Ghost.init = function () {
+ Ghost.router = new Ghost.Router();
+ Backbone.history.start({
+ pushState: true,
+ hashChange: false,
+ root: '/ghost'
+ });
+ };
window.Ghost = Ghost;
-}());
\ No newline at end of file
+}());
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 dd2f0b958a..4916fb25c2 100644
--- a/core/admin/assets/js/router.js
+++ b/core/admin/assets/js/router.js
@@ -6,12 +6,41 @@
Ghost.Router = Backbone.Router.extend({
routes: {
- '': 'dashboard',
- 'content/': 'blog',
- 'editor': 'editor',
- 'editor/': 'editor',
- 'editor/:id': 'editor'
+ '' : 'dashboard',
+ '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 () {
+ 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) {
+ var post = new Ghost.Models.Post();
+ post.urlRoot = Ghost.settings.apiRoot + '/posts';
+ if (id) {
+ post.id = id;
+ post.fetch().then(function () {
+ Ghost.currentView = new Ghost.Views.Editor({ el: '#main', model: post });
+ });
+ } else {
+ Ghost.currentView = new Ghost.Views.Editor({ el: '#main', model: post });
+ }
+ },
+
dashboard: function () {
var widgets = new Ghost.Collections.Widgets();
@@ -418,36 +447,9 @@
}
});
-
-
-
-
//widgets.fetch().then(function () {
Ghost.currentView = new Ghost.Views.Dashboard({ el: '#main', collection: widgets });
//});
- },
-
- blog: function () {
- var posts = new Ghost.Collections.Posts();
- posts.fetch({data: {status: 'all'}}).then(function () {
- Ghost.currentView = new Ghost.Views.Blog({ el: '#main', collection: posts });
- });
-
- },
-
- editor: function (id) {
- var post = new Ghost.Models.Post();
- post.urlRoot = Ghost.settings.apiRoot + '/posts';
- if (id) {
- post.id = id;
- post.fetch().then(function () {
- Ghost.currentView = new Ghost.Views.Editor({ el: '#main', model: post });
- });
- } else {
- Ghost.currentView = new Ghost.Views.Editor({ el: '#main', model: post });
- }
}
-
});
-
}());
\ No newline at end of file
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/starter.js b/core/admin/assets/js/starter.js
deleted file mode 100644
index adb6c2112d..0000000000
--- a/core/admin/assets/js/starter.js
+++ /dev/null
@@ -1,15 +0,0 @@
-/*global window, document, Ghost, Backbone, $, _ */
-(function () {
-
- "use strict";
-
- Ghost.router = new Ghost.Router();
-
- $(function () {
-
- Backbone.history.start({pushState: true, hashChange: false, root: '/ghost'});
-
- });
-
-
-}());
\ No newline at end of file
diff --git a/core/admin/assets/js/views/base.js b/core/admin/assets/js/views/base.js
new file mode 100644
index 0000000000..3c89bf5e35
--- /dev/null
+++ b/core/admin/assets/js/views/base.js
@@ -0,0 +1,45 @@
+/*global window, document, Ghost, Backbone, $, _ */
+(function () {
+ "use strict";
+
+ Ghost.View = Backbone.View.extend({
+
+ // Adds a subview to the current view, which will
+ // ensure its removal when this view is removed,
+ // or when view.removeSubviews is called
+ addSubview: function (view) {
+ if (!(view instanceof Backbone.View)) {
+ throw new Error("Subview must be a Backbone.View");
+ }
+ this.subviews = this.subviews || [];
+ this.subviews.push(view);
+ return view;
+ },
+
+ // Removes any subviews associated with this view
+ // by `addSubview`, which will in-turn remove any
+ // children of those views, and so on.
+ removeSubviews: function () {
+ var i, l, children = this.subviews;
+ if (!children) {
+ return this;
+ }
+ for (i = 0, l = children.length; i < l; i += 1) {
+ children[i].remove();
+ }
+ this.subviews = [];
+ return this;
+ },
+
+ // Extends the view's remove, by calling `removeSubviews`
+ // if any subviews exist.
+ remove: function () {
+ if (this.subviews) {
+ this.removeSubviews();
+ }
+ return Backbone.View.prototype.remove.apply(this, arguments);
+ }
+
+ });
+
+}());
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 b12205f94d..4d2f9cf1fe 100644
--- a/core/admin/views/default.hbs
+++ b/core/admin/views/default.hbs
@@ -58,12 +58,17 @@
+
+
+
-
{{{block "bodyScripts"}}}
+