diff --git a/Gruntfile.js b/Gruntfile.js index 0e42681150..827b78654b 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -22,7 +22,8 @@ // Unit test all the things! nodeunit: { - all: ['core/test/ghost/**/test-*.js'] + all: ['core/test/ghost/**/test-*.js'], + api: ['core/test/ghost/test-api.js'] }, // Compile all the SASS! @@ -45,6 +46,9 @@ // TODO: Git submodule init/update (https://github.com/jaubourg/grunt-update-submodules)? grunt.registerTask("init", ["compass:admin"]); + // Run API tests only + grunt.registerTask("test-api", ["nodeunit:api"]); + // Run tests and lint code grunt.registerTask("validate", ["jslint", "nodeunit:all"]); }; diff --git a/app.js b/app.js index 4c148b908d..e43a627f11 100644 --- a/app.js +++ b/app.js @@ -25,7 +25,8 @@ ghost.app().configure('development', function () { ghost.app().use(express.favicon(__dirname + '/content/images/favicon.ico')); - ghost.app().use(express.errorHandler()); + ghost.app().use(express.errorHandler({ dumpExceptions: true, showStack: true })); + ghost.app().use(express.logger('dev')); ghost.app().use(I18n.load(ghost)); ghost.app().use(express.bodyParser()); ghost.app().use(express.cookieParser('try-ghost')); @@ -66,6 +67,9 @@ ghost.app().post('/api/v0.1/posts/create', auth, api.requestHandler(api.posts.add)); ghost.app().put('/api/v0.1/posts/edit', auth, api.requestHandler(api.posts.edit)); ghost.app()['delete']('/api/v0.1/posts/:id', auth, api.requestHandler(api.posts.destroy)); + ghost.app().get('/api/v0.1/settings', auth, api.requestHandler(api.settings.browse)); + ghost.app().get('/api/v0.1/settings/:key', auth, api.requestHandler(api.settings.read)); + ghost.app().put('/api/v0.1/settings/edit', auth, api.requestHandler(api.settings.edit)); /** * Admin routes.. diff --git a/config.js b/config.js index d6f263fdb6..5e6813f00a 100644 --- a/config.js +++ b/config.js @@ -56,6 +56,13 @@ config.homepage.posts = 4; config.database = { + testing: { + client: 'sqlite3', + connection: { + filename: './core/shared/data/tests.db' + } + }, + development: { client: 'sqlite3', connection: { diff --git a/core/admin/assets/js/settings.js b/core/admin/assets/js/settings.js index d28d068ed0..4668686f0b 100644 --- a/core/admin/assets/js/settings.js +++ b/core/admin/assets/js/settings.js @@ -13,7 +13,19 @@ $('.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) { @@ -30,6 +42,19 @@ $('input').iCheck({ checkboxClass: 'icheckbox_square-grey' }); + + $('.button-save').click(function (e) { + e.preventDefault(); + var data = getSettings(); + $.ajax({ + method: 'PUT', + url: '/api/v0.1/settings/edit', + data: data, + success: function (res, xhr, c) { + console.log(xhr, c); + } + }); + }); }); }(jQuery)); \ No newline at end of file diff --git a/core/admin/controllers/index.js b/core/admin/controllers/index.js index 9f8aec81b9..9182951679 100644 --- a/core/admin/controllers/index.js +++ b/core/admin/controllers/index.js @@ -131,10 +131,16 @@ }); }, 'settings': function (req, res) { - res.render('settings', { - bodyClass: 'settings', - adminNav: setSelected(adminNavbar, 'settings') - }); + 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'), + settings: settings + }); + }); }, 'debug': { /* ugly temporary stuff for managing the app before it's properly finished */ index: function (req, res) { diff --git a/core/admin/views/settings.hbs b/core/admin/views/settings.hbs index b8ad64409f..8ecdbf52b3 100644 --- a/core/admin/views/settings.hbs +++ b/core/admin/views/settings.hbs @@ -27,11 +27,12 @@
+ {{#with settings}}
@@ -81,7 +82,7 @@
- + {{/with}}
@@ -128,13 +129,13 @@ diff --git a/core/shared/api.js b/core/shared/api.js index 6c15dfe9ef..db32e6f835 100644 --- a/core/shared/api.js +++ b/core/shared/api.js @@ -16,6 +16,7 @@ ghost = new Ghost(), posts, users, + settings, requestHandler; // # Posts @@ -56,9 +57,22 @@ return when.call(ghost.dataProvider().users.check, postData); } }; -// settings: {}, -// categories: {}, -// post_categories: {} + + // # Settings + settings = { + browse: function (options) { + return when.call(ghost.dataProvider().settings.browse, options); + }, + read: function (options) { + return when.call(ghost.dataProvider().settings.read, options.key); + }, + edit: function (options) { + return when.call(ghost.dataProvider().settings.edit, options); + } + }; + + // categories: {}; + // post_categories: {}; // requestHandler @@ -78,5 +92,6 @@ module.exports.posts = posts; module.exports.users = users; + module.exports.settings = settings; module.exports.requestHandler = requestHandler; }()); \ No newline at end of file diff --git a/core/shared/models/dataProvider.bookshelf.js b/core/shared/models/dataProvider.bookshelf.js index a42a145e2e..5abc72d18e 100644 --- a/core/shared/models/dataProvider.bookshelf.js +++ b/core/shared/models/dataProvider.bookshelf.js @@ -9,6 +9,8 @@ var knex = require('./knex_init'), models = require('./models'), bcrypt = require('bcrypt'), + when = require("when"), + _ = require("underscore"), DataProvider, instance; @@ -123,5 +125,30 @@ }); }; + // ## Settings + DataProvider.prototype.settings = function () { }; + + DataProvider.prototype.settings.browse = function (_args, callback) { + models.Settings.forge(_args).fetch().then(function (settings) { + callback(null, settings); + }, callback); + }; + + DataProvider.prototype.settings.read = function (_key, callback) { + models.Setting.forge({ key: _key }).fetch().then(function (setting) { + callback(null, setting); + }, callback); + }; + + DataProvider.prototype.settings.edit = function (_data, callback) { + when.all(_.map(_data, function (value, key) { + return models.Setting.forge({ key: key }).fetch().then(function (setting) { + return setting.set('value', value).save(); + }); + })).then(function (settings) { + callback(null, settings); + }, callback); + }; + module.exports = DataProvider; }()); \ No newline at end of file diff --git a/core/shared/models/models.js b/core/shared/models/models.js index 6592ce54c8..56dcabd16d 100644 --- a/core/shared/models/models.js +++ b/core/shared/models/models.js @@ -12,7 +12,8 @@ Post, Posts, User, - Setting; + Setting, + Settings; Post = Bookshelf.Model.extend({ @@ -68,15 +69,19 @@ }); Setting = Bookshelf.Model.extend({ + tableName: 'settings', + hasTimestamps: true + }); - tableName: 'settings' - + Settings = Bookshelf.Collection.extend({ + model: Setting }); module.exports = { Post: Post, Posts: Posts, User: User, - Setting: Setting + Setting: Setting, + Settings: Settings }; }()); \ No newline at end of file diff --git a/core/test/ghost/test-api.js b/core/test/ghost/test-api.js new file mode 100644 index 0000000000..0ec5225f46 --- /dev/null +++ b/core/test/ghost/test-api.js @@ -0,0 +1,64 @@ +/*global require, module */ +(function () { + "use strict"; + + // Use 'testing' Ghost config + process.env.NODE_ENV = 'testing'; + + var fs = require('fs'), + path = require('path'), + _ = require('underscore'), + assert = require('assert'), + delay = require('when/delay'), + config = require('../../../config'), + fixtures = require('../../shared/data/fixtures/001'), + api; + + function fail(err) { + process.nextTick(function () { + assert.ifError(err); + }); + } + + module.exports = { + setUp: function (done) { + // Clear database + var dbpath = path.resolve(__dirname, '../../../', config.database.testing.connection.filename); + fs.unlink(dbpath, function () { + // There is currently no way to tell when Ghost is loaded. api instantiates it's own `Ghost` + // which will run migrations without making the promise externally accessible + api = require('../../shared/api'); + // So we just sit for a while :/ + setTimeout(done, 3000); + }); + }, + + 'settings:browse': function (test) { + test.expect(1); + api.settings.browse().then(function (settings) { + settings = _.map(settings.toJSON(), function (item) { + return _.omit(item, 'id', 'updated_at', 'created_at'); + }); + test.deepEqual(settings, fixtures.settings); + test.done(); + }).then(null, fail); + }, + + 'settings:read': function (test) { + api.settings.read('title', function (setting) { + test.done(); + }).then(null, fail); + }, + + 'settings:edit': function (test) { + test.expect(2); + api.settings.edit('title', "Jenna O'Neil").then(function (title) { + title = title.toJSON(); + test.equal(title.key, 'title'); + test.equal(title.value, "Jenna O'Neil"); + test.done(); + }).then(null, fail); + } + }; + +}()); \ No newline at end of file