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

Merge pull request #55 from ricardobeat/settings_api

Settings api
This commit is contained in:
Hannah Wolfe 2013-05-23 14:44:39 -07:00
commit 4becf72228
10 changed files with 176 additions and 18 deletions

View file

@ -22,7 +22,8 @@
// Unit test all the things! // Unit test all the things!
nodeunit: { nodeunit: {
all: ['core/test/ghost/**/test-*.js'] all: ['core/test/ghost/**/test-*.js'],
api: ['core/test/ghost/test-api.js']
}, },
// Compile all the SASS! // Compile all the SASS!
@ -45,6 +46,9 @@
// TODO: Git submodule init/update (https://github.com/jaubourg/grunt-update-submodules)? // TODO: Git submodule init/update (https://github.com/jaubourg/grunt-update-submodules)?
grunt.registerTask("init", ["compass:admin"]); grunt.registerTask("init", ["compass:admin"]);
// Run API tests only
grunt.registerTask("test-api", ["nodeunit:api"]);
// Run tests and lint code // Run tests and lint code
grunt.registerTask("validate", ["jslint", "nodeunit:all"]); grunt.registerTask("validate", ["jslint", "nodeunit:all"]);
}; };

6
app.js
View file

@ -25,7 +25,8 @@
ghost.app().configure('development', function () { ghost.app().configure('development', function () {
ghost.app().use(express.favicon(__dirname + '/content/images/favicon.ico')); 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(I18n.load(ghost));
ghost.app().use(express.bodyParser()); ghost.app().use(express.bodyParser());
ghost.app().use(express.cookieParser('try-ghost')); 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().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().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()['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.. * Admin routes..

View file

@ -56,6 +56,13 @@
config.homepage.posts = 4; config.homepage.posts = 4;
config.database = { config.database = {
testing: {
client: 'sqlite3',
connection: {
filename: './core/shared/data/tests.db'
}
},
development: { development: {
client: 'sqlite3', client: 'sqlite3',
connection: { connection: {

View file

@ -13,6 +13,18 @@
$('.settings-content').fadeOut().delay(250); $('.settings-content').fadeOut().delay(250);
$(newPage).fadeIn(); $(newPage).fadeIn();
},
defaultSettings = {
title: 'My Blog',
description: ''
},
getSettings = function () {
return $.extend(defaultSettings, {
title : $('#blog-title').val(),
description : $('#blog-description').val()
});
}; };
$(document).ready(function () { $(document).ready(function () {
@ -30,6 +42,19 @@
$('input').iCheck({ $('input').iCheck({
checkboxClass: 'icheckbox_square-grey' 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)); }(jQuery));

View file

@ -131,9 +131,15 @@
}); });
}, },
'settings': function (req, res) { 'settings': function (req, res) {
api.settings.browse()
.then(function (settings) {
settings = settings.toJSON();
settings = _.object(_.pluck(settings, 'key'), _.pluck(settings, 'value'));
res.render('settings', { res.render('settings', {
bodyClass: 'settings', bodyClass: 'settings',
adminNav: setSelected(adminNavbar, 'settings') adminNav: setSelected(adminNavbar, 'settings'),
settings: settings
});
}); });
}, },
'debug': { /* ugly temporary stuff for managing the app before it's properly finished */ 'debug': { /* ugly temporary stuff for managing the app before it's properly finished */

View file

@ -27,11 +27,12 @@
</section> </section>
</header> </header>
<section class="content"> <section class="content">
{{#with settings}}
<form id="settings-general"> <form id="settings-general">
<fieldset> <fieldset>
<label> <label>
<b>Blog Title</b> <b>Blog Title</b>
<input id="blog-title" type="text" value="John O'Nolan" /> <input id="blog-title" type="text" value="{{title}}" />
<p>How your blog name appears on the site</p> <p>How your blog name appears on the site</p>
</label> </label>
@ -81,7 +82,7 @@
</fieldset> </fieldset>
{{/with}}
</form> </form>
</section> </section>
</section> </section>
@ -128,13 +129,13 @@
<label> <label>
<b>SEO Title Pattern</b> <b>SEO Title Pattern</b>
<input id="blog-title" type="text" value="[Post Name] - [Site Title]" /> <input id="seo-title" type="text" value="[Post Name] - [Site Title]" />
<p>The pattern used to display your title tags</p> <p>The pattern used to display your title tags</p>
</label> </label>
<label> <label>
<b>SEO Description Pattern</b> <b>SEO Description Pattern</b>
<input id="blog-title" type="text" value="Auto" /> <input id="seo-description" type="text" value="Auto" />
<p>The pattern used to display your meta descriptions</p> <p>The pattern used to display your meta descriptions</p>
</label> </label>

View file

@ -16,6 +16,7 @@
ghost = new Ghost(), ghost = new Ghost(),
posts, posts,
users, users,
settings,
requestHandler; requestHandler;
// # Posts // # Posts
@ -56,9 +57,22 @@
return when.call(ghost.dataProvider().users.check, postData); return when.call(ghost.dataProvider().users.check, postData);
} }
}; };
// settings: {},
// categories: {}, // # Settings
// post_categories: {} 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 // requestHandler
@ -78,5 +92,6 @@
module.exports.posts = posts; module.exports.posts = posts;
module.exports.users = users; module.exports.users = users;
module.exports.settings = settings;
module.exports.requestHandler = requestHandler; module.exports.requestHandler = requestHandler;
}()); }());

View file

@ -9,6 +9,8 @@
var knex = require('./knex_init'), var knex = require('./knex_init'),
models = require('./models'), models = require('./models'),
bcrypt = require('bcrypt'), bcrypt = require('bcrypt'),
when = require("when"),
_ = require("underscore"),
DataProvider, DataProvider,
instance; 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; module.exports = DataProvider;
}()); }());

View file

@ -12,7 +12,8 @@
Post, Post,
Posts, Posts,
User, User,
Setting; Setting,
Settings;
Post = Bookshelf.Model.extend({ Post = Bookshelf.Model.extend({
@ -68,15 +69,19 @@
}); });
Setting = Bookshelf.Model.extend({ Setting = Bookshelf.Model.extend({
tableName: 'settings',
hasTimestamps: true
});
tableName: 'settings' Settings = Bookshelf.Collection.extend({
model: Setting
}); });
module.exports = { module.exports = {
Post: Post, Post: Post,
Posts: Posts, Posts: Posts,
User: User, User: User,
Setting: Setting Setting: Setting,
Settings: Settings
}; };
}()); }());

View file

@ -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);
}
};
}());